Drop vault access policy from this deploy; tracked in INFRA-94 (kept governance lock codify + selection)

This commit is contained in:
Adam Moussa 2026-06-08 16:38:54 -04:00
parent 5d2a3a46bb
commit bbda672bce

View file

@ -87,10 +87,13 @@ export class BackupStack extends cdk.Stack {
// is written to MATCH the live lock exactly, so the deploy diff is a no-op
// adoption — it does not change the live vault.
//
// The vault carries a scoped access policy (below) denying manual recovery-
// point deletion to everyone except a break-glass principal and the Backup
// service role, so lifecycle expiry still works but humans cannot prune
// recovery points by hand.
// NOTE: the scoped vault access policy that previously lived here was DROPPED
// from this deploy (INFRA-94). Attaching a BackupVaultAccessPolicy that
// denies DeleteBackupVault / PutBackupVaultLockConfiguration to all but a
// break-glass principal made the vault unmanageable by CloudFormation/CDK.
// The deny-manual-deletion control is tracked separately in INFRA-94 and
// will be reintroduced via a safe mechanism. Governance lock codify +
// backup selections land here.
const primaryVault = new backup.BackupVault(this, "PrimaryVault", {
backupVaultName: "seahaven-primary",
encryptionKey: vaultKey,
@ -102,49 +105,6 @@ export class BackupStack extends cdk.Stack {
},
});
// Break-glass principal exempt from the deny below. The SSO
// AdministratorAccess permission-set role is the only path to assume an
// admin identity in this account; deletions still require its explicit use
// (and are CloudTrail-audited). The SSO role name carries a hash suffix, so
// a wildcard is required to match it.
const breakGlassRoleArnPattern = `arn:${this.partition}:iam::${this.account}:role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_AdministratorAccess_*`;
const backupServiceRoleArn = `arn:${this.partition}:iam::${this.account}:role/seahaven-backup-service-role`;
// Scoped vault access policy (INFRA-89): deny MANUAL deletion of recovery
// points / lifecycle tampering to all principals EXCEPT the break-glass SSO
// admin and the AWS Backup service role.
//
// IMPORTANT (cross-review BLOCK, GPT-4.1): `NotPrincipal` does NOT support
// wildcard matching — a wildcarded ARN there is treated literally and would
// silently FAIL to exempt the hash-suffixed SSO role, trapping the vault.
// So we use the robust pattern instead: Effect DENY with Principal `*` and a
// `StringNotLike` condition on `aws:PrincipalArn` (which DOES support
// wildcards). Lifecycle expiry is performed by the AWS Backup service itself
// (not a caller subject to this policy), so automatic expiry is unaffected;
// `UpdateRecoveryPointLifecycle` is denied to humans to prevent retention
// tampering, while the two exempted principals retain full control.
primaryVault.addToAccessPolicy(
new iam.PolicyStatement({
sid: "DenyManualRecoveryPointDeletion",
effect: iam.Effect.DENY,
principals: [new iam.AnyPrincipal()],
actions: [
"backup:DeleteRecoveryPoint",
"backup:UpdateRecoveryPointLifecycle",
"backup:DeleteBackupVault",
"backup:DeleteBackupVaultAccessPolicy",
"backup:DeleteBackupVaultLockConfiguration",
"backup:PutBackupVaultLockConfiguration",
],
resources: ["*"],
conditions: {
StringNotLike: {
"aws:PrincipalArn": [breakGlassRoleArnPattern, backupServiceRoleArn],
},
},
})
);
// Cross-region copy destination, referenced by literal ARN (the offsite
// stack is in another region; a literal ARN avoids crossRegionReferences /
// SSM exports). Stack ordering is enforced via addDependency in bin/app.ts.