fix: grant staging deploy role the Support-confirmed Elastic Beanstalk S3 set

s3:PutObject on shoc-backend/* covered only the upload the deploy action
performs itself. AWS Support case 178526484500047 established that
UpdateEnvironment then reads, writes, versions, ACL-checks and removes
objects as the calling identity, across both the account bucket and
AWS-owned Elastic Beanstalk service buckets whose names cannot be
enumerated in advance. That is the failure the dev lane already hit, and
staging calls the same deploy action and the same rollback
update-environment, so the first OIDC deploy and every rollback would
have stopped there.

Brings the staging role to parity with deploy-dev-stack.ts.
elasticbeanstalk:UpdateEnvironment stays pinned to the staging
environment ARN, so the role still cannot update or terminate
shoc-backend-dev.
This commit is contained in:
Alexandre Brandizzi 2026-08-31 10:57:04 -03:00
parent e51924eee6
commit 4aef5b463b

View file

@ -114,25 +114,44 @@ export class DeployStagingStack extends cdk.Stack {
deployRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['s3:PutObject'],
// The pinned deployment action writes exactly
// shoc-backend/<version-label>.zip to the explicitly configured,
// pre-existing account bucket. It never reads or deletes objects.
resources: [
`arn:aws:s3:::elasticbeanstalk-${REGION}-${ACCOUNT_ID}/${APPLICATION_NAME}/*`,
],
actions: ['s3:Delete*', 's3:Get*', 's3:Put*'],
// Matches deploy-dev-stack.ts. The narrower s3:PutObject on
// shoc-backend/* covers only the upload the deploy action performs
// itself; AWS Support case 178526484500047 confirmed that
// UpdateEnvironment then reads, writes, versions, ACL-checks, and
// removes objects as the calling identity, in both the account bucket
// and AWS-owned Elastic Beanstalk service buckets whose names are not
// enumerable in advance. That is the failure the dev lane already hit;
// staging calls the same deploy action and the same rollback
// update-environment, so it needs the same set or the first OIDC
// deploy and every rollback stop there.
//
// NOTE: dev and staging are two environments of ONE Elastic Beanstalk
// application (APPLICATION_NAME is shared), so application versions for
// both live under the same shoc-backend/ prefix in the same account
// bucket. This grant therefore reaches dev's application-version
// objects. Environment authority stays separate --
// elasticbeanstalk:UpdateEnvironment below is pinned to the staging
// environment ARN, so this role still cannot update or terminate
// shoc-backend-dev.
resources: ['arn:aws:s3:::elasticbeanstalk-*/*'],
}),
);
deployRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
// HeadBucket on the explicit existing bucket requires ListBucket.
// GetBucketLocation is retained for regional SDK compatibility.
actions: ['s3:GetBucketLocation', 's3:ListBucket'],
resources: [
`arn:aws:s3:::elasticbeanstalk-${REGION}-${ACCOUNT_ID}`,
actions: [
's3:GetBucket*',
's3:ListBucket',
's3:PutBucketOwnershipControls',
's3:PutBucketPolicy',
's3:PutBucketPublicAccessBlock',
],
// AWS Support's bucket-level UpdateEnvironment set, excluding
// CreateBucket because the workflow deploys only to an existing
// application/environment and disables bucket creation.
resources: ['arn:aws:s3:::elasticbeanstalk-*'],
}),
);