Merge branch 'dev' into codex/vendor-document-detail-read-model

This commit is contained in:
Alexandre Brandizzi 2026-07-29 22:06:48 -03:00 • committed by GitHub
commit 1a169360a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 131 additions and 19 deletions

View file

@ -42,13 +42,56 @@ The role grants only:
- `elasticbeanstalk:UpdateEnvironment` on environment `shoc-backend-dev` only.
- `s3:ListBucket` and `s3:GetBucketLocation` on
`elasticbeanstalk-us-east-1-396287094661` (the official action's
ownership-safe bucket checks), plus `s3:CreateBucket` and
ownership-safe bucket checks), `s3:GetBucketPolicy` for the policy inspection
observed in attempt 11 of run `30448885838`, plus `s3:CreateBucket` and
`s3:PutBucketOwnershipControls` on the same bucket-level ARN. Under the
`shoc-backend/` object prefix only:
`s3:PutObject`, `s3:GetObject`, `s3:GetObjectAcl`, and
`s3:GetObjectVersion`, which the pinned official deployment action and
Elastic Beanstalk require to validate the `CreateApplicationVersion` source
bundle after upload.
`s3:PutObject`, `s3:GetObject`, and `s3:GetObjectVersion`, which the pinned
official deployment action requires to validate the
`CreateApplicationVersion` source bundle after upload.
- `s3:PutObject`, `s3:GetObject`, `s3:GetObjectVersionAcl`,
`s3:PutObjectVersionAcl`, and `s3:DeleteObject` on only
`elasticbeanstalk-us-east-1-396287094661/resources/environments/e-hehnrqjjrt/_runtime/_versions/shoc-backend/*`.
Elastic Beanstalk copies each uploaded source bundle into this
environment-specific runtime prefix during `UpdateEnvironment`, verifies it
with `HeadObject` (authorized by `s3:GetObject`), and removes the temporary
copy after the version is registered. Attempts 1 through 4 of run
`30448885838` exposed the exact source, destination, cleanup, and verification
operations after the earlier ACL denial was resolved. CloudTrail recorded
the exact `s3:GetObject` denial on attempt 4; attempt 6 then exposed the
version-specific ACL read performed on the copied object; attempt 7 exposed
the matching version-ACL write. The grant does not cover another
environment, another application, source bundles, object content versions,
non-version ACL mutation, tags, or retention.
- `s3:PutObject` on only the two embedded-extension prefixes
`elasticbeanstalk-us-east-1-396287094661/resources/_runtime/_embedded_extensions/shoc-backend/*`
and
`elasticbeanstalk-us-east-1-396287094661/resources/environments/e-hehnrqjjrt/_runtime/_embedded_extensions/shoc-backend/*`.
After the runtime bundle copy and version-ACL operations succeeded, attempt 8
of run `30448885838` showed Elastic Beanstalk materializing the application's
embedded-extension manifest at the application-specific shared prefix.
Attempt 9 then showed the matching write into the exact dev-environment
prefix. CloudTrail recorded both denied actions and object ARNs. The grant
does not include reads, deletes, ACL mutation, another application,
another environment, or another bucket.
- `s3:GetObject` on only the environment-specific embedded-extension prefix
above. Attempt 10 showed that Elastic Beanstalk verifies the materialized
environment copy with `HeadObject`, which S3 authorizes through
`s3:GetObject`. The shared embedded-extension prefix remains write-only.
- `s3:GetObject` and `s3:PutObject` on only
`elasticbeanstalk-us-east-1-396287094661/resources/environments/e-hehnrqjjrt/_runtime/versions/*`.
Attempt 12 showed Elastic Beanstalk reading the previous environment version
manifest and writing its replacement under this exact dev-environment
runtime prefix. The grant excludes deletes, ACL mutation, other environments,
and application bundle content.
- `s3:GetObjectAcl` on objects under the service-wide
`arn:aws:s3:::elasticbeanstalk-*/*` namespace. AWS Support case
`178526484500047` confirmed that `UpdateEnvironment` uses the initiating
role to inspect objects in AWS-owned Elastic Beanstalk buckets, not only the
account-owned source-bundle bucket. The wildcard is limited to one read-only
ACL action and the Elastic Beanstalk bucket namespace; it grants no object
content read, write, delete, bucket-management, IAM, or `PassRole`
capability.
`s3:CreateBucket` is part of the pinned
`aws-actions/aws-elastic-beanstalk-deploy@cfad3e5e...` (v1.0.6) IAM
@ -112,15 +155,11 @@ The role grants only:
successful administrator deployment. AWS supports resource-level
constraints for all three mutations, so replacement ASGs remain covered
without granting access to another environment.
- `s3:GetObjectAcl` under the existing
`elasticbeanstalk-us-east-1-396287094661/shoc-backend/*` object prefix.
Elastic Beanstalk emitted this read during the same attempt while validating
the uploaded application bundle. It grants no bucket-wide or cross-prefix
object access.
It grants **no** IAM mutation or `PassRole`, **no** RDS / Secrets Manager
access, no EC2 mutation, and **no** administrator policy. The only non-EB/S3
mutations are the three deployment-process Auto Scaling calls, restricted to
this environment's ASG name pattern. There are no wildcard mutation surfaces.
this environment's ASG name pattern. There are no wildcard mutation surfaces;
the only service-wide object grant is read-only ACL metadata.
## Prerequisites
@ -160,13 +199,16 @@ All commands run from `infra/cdk/`.
The workflow's `AWS_DEPLOY_ROLE_ARN` repository secret (environment `dev`) must
hold the ARN output by this stack (`GithubDeployRoleArn`).
The OIDC deployment is currently fail-closed, not repaired. Elastic Beanstalk
still reports a generic `s3:GetObjectAcl` denial after the account-owned source
prefix and every exact cross-account object referenced by the sanitized live
stack were tested independently. The runtime-prefix, platform-assets, and
launch-control hypotheses were disproved and are intentionally absent from the
policy. Do not broaden `GetObjectAcl` without an exact principal/action/resource
record from AWS Support or the AWS-owned bucket's diagnostic owner.
The previous OIDC deployment remained fail-closed after Elastic Beanstalk
reported a generic `s3:GetObjectAcl` denial outside the account-owned source
prefix. AWS Support case `178526484500047` subsequently confirmed that
`UpdateEnvironment` checks objects in AWS-owned Elastic Beanstalk buckets
using the initiating role and requires the `elasticbeanstalk-*/*` resource
namespace. This policy adds only the denied ACL-read action on that namespace;
it intentionally does not copy the managed
`AdministratorAccess-AWSElasticBeanstalk` policy's broad `s3:Get*`,
`s3:Put*`, or `s3:Delete*` grants. Any later denial must be evaluated and
granted independently.
The pinned deployment action can return success after Elastic Beanstalk emits a
fatal deployment event. The following workflow step therefore verifies that

View file

@ -18,6 +18,18 @@ export class DeployDevStack extends cdk.Stack {
const applicationArn = `arn:aws:elasticbeanstalk:${REGION}:${ACCOUNT_ID}:application/${APPLICATION_NAME}`;
const environmentArn = `arn:aws:elasticbeanstalk:${REGION}:${ACCOUNT_ID}:environment/${APPLICATION_NAME}/${ENVIRONMENT_NAME}`;
const bucketArn = `arn:aws:s3:::${BUCKET_NAME}`;
const runtimeVersionArn =
`${bucketArn}/resources/environments/${ENVIRONMENT_ID}` +
`/_runtime/_versions/${APPLICATION_NAME}/*`;
const embeddedExtensionArn =
`${bucketArn}/resources/_runtime/_embedded_extensions/` +
`${APPLICATION_NAME}/*`;
const environmentEmbeddedExtensionArn =
`${bucketArn}/resources/environments/${ENVIRONMENT_ID}` +
`/_runtime/_embedded_extensions/${APPLICATION_NAME}/*`;
const runtimeManifestArn =
`${bucketArn}/resources/environments/${ENVIRONMENT_ID}` +
'/_runtime/versions/*';
const oidcProviderArn = `arn:aws:iam::${ACCOUNT_ID}:oidc-provider/token.actions.githubusercontent.com`;
const deployRole = new iam.Role(this, 'GithubDeployRole', {
@ -134,6 +146,7 @@ export class DeployDevStack extends cdk.Stack {
's3:CreateBucket',
's3:PutBucketOwnershipControls',
's3:GetBucketLocation',
's3:GetBucketPolicy',
],
resources: [bucketArn],
}),
@ -142,11 +155,68 @@ export class DeployDevStack extends cdk.Stack {
deployRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['s3:GetObject', 's3:GetObjectAcl', 's3:GetObjectVersion', 's3:PutObject'],
actions: ['s3:GetObject', 's3:GetObjectVersion', 's3:PutObject'],
resources: [`${bucketArn}/${APPLICATION_NAME}/*`],
}),
);
deployRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
's3:DeleteObject',
's3:GetObject',
's3:GetObjectVersionAcl',
's3:PutObject',
's3:PutObjectVersionAcl',
],
// UpdateEnvironment copies the uploaded source bundle into this
// environment-specific runtime prefix, verifies the temporary copy,
// preserves its version ACL, and removes it after registration.
resources: [runtimeVersionArn],
}),
);
deployRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['s3:PutObject'],
// UpdateEnvironment materializes the application's embedded-extension
// manifest under the shared and environment-specific runtime prefixes.
resources: [embeddedExtensionArn, environmentEmbeddedExtensionArn],
}),
);
deployRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['s3:GetObject'],
// Elastic Beanstalk verifies the environment copy with HeadObject.
resources: [environmentEmbeddedExtensionArn],
}),
);
deployRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['s3:GetObject', 's3:PutObject'],
// UpdateEnvironment reads the prior environment version manifest and
// writes its replacement under this environment-only runtime prefix.
resources: [runtimeManifestArn],
}),
);
deployRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['s3:GetObjectAcl'],
// UpdateEnvironment also checks objects in AWS-owned Elastic Beanstalk
// buckets. AWS Support case 178526484500047 confirmed that the caller's
// identity policy must cover the service-wide bucket namespace.
resources: ['arn:aws:s3:::elasticbeanstalk-*/*'],
}),
);
new cdk.CfnOutput(this, 'GithubDeployRoleArn', {
value: deployRole.roleArn,
description: 'ARN of the GitHub OIDC deploy role for shoc-backend dev.',