Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

# CDK output (recursive include if not excluded)
cdk/cdk.out/
cdk/cdk.out.*/
cdk/lib/
cdk/node_modules/

Expand Down Expand Up @@ -58,6 +59,14 @@ coverage/
.coverage.*
**/.coverage
**/.coverage.*
# Jest transform cache — same vanishing-file class as the coverage files above,
# and the same consequence. Jest workers write and evict
# ``.jest-cache/jest-transform-cache-*/<n>/<name>_<hash>.map.<random>`` entries
# throughout a run, so a suite that synthesizes the agent image asset (which
# fingerprints this tree) can hit ENOENT on an entry another worker just evicted.
# Already in .gitignore, but .dockerignore is what CDK's fingerprint honours.
.jest-cache/
**/.jest-cache/

# IDE / OS
.idea/
Expand Down
2 changes: 1 addition & 1 deletion cdk/bootstrap/BOOTSTRAP_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.2.0
1.3.0
8 changes: 6 additions & 2 deletions cdk/bootstrap/bootstrap-template.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# GENERATED FILE - DO NOT EDIT DIRECTLY
# This template is generated by: npx tsx scripts/generate-bootstrap-template.ts
# ABCA Bootstrap Policy Version: 1.2.0
# ABCA Bootstrap Policy Version: 1.3.0
# ABCA Bootstrap Policy Hash: b0501c8a57f20e4b5bf50d6fe8bbb934310adc4b64480228565d8392a19d1503
#
# Based on the default CDK bootstrap template with the following modifications:
Expand Down Expand Up @@ -1099,6 +1099,8 @@ Resources:
- sqs:UntagQueue
- sqs:GetQueueUrl
- sqs:ListQueueTags
- sqs:AddPermission
- sqs:RemovePermission
Effect: Allow
Resource: arn:aws:sqs:*:*:backgroundagent-dev-*
Sid: SQS
Expand Down Expand Up @@ -1228,9 +1230,11 @@ Resources:
- s3:DeleteBucket
- s3:PutBucketPolicy
- s3:DeleteBucketPolicy
- s3:GetBucketPolicy
- s3:PutBucketPublicAccessBlock
- s3:GetBucketPublicAccessBlock
- s3:PutEncryptionConfiguration
- s3:GetEncryptionConfiguration
- s3:PutLifecycleConfiguration
- s3:PutBucketVersioning
- s3:GetBucketVersioning
Expand Down Expand Up @@ -1370,7 +1374,7 @@ Outputs:
Value: '32'
BootstrapPolicyVersion:
Description: The version of the ABCA bootstrap policy bundle
Value: 1.2.0
Value: 1.3.0
BootstrapPolicyHash:
Description: SHA-256 hash of the ABCA bootstrap policy bundle for drift detection
Value: b0501c8a57f20e4b5bf50d6fe8bbb934310adc4b64480228565d8392a19d1503
Expand Down
4 changes: 3 additions & 1 deletion cdk/bootstrap/policies/application.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,9 @@
"sqs:TagQueue",
"sqs:UntagQueue",
"sqs:GetQueueUrl",
"sqs:ListQueueTags"
"sqs:ListQueueTags",
"sqs:AddPermission",
"sqs:RemovePermission"
],
"Effect": "Allow",
"Resource": "arn:aws:sqs:*:*:backgroundagent-dev-*",
Expand Down
2 changes: 2 additions & 0 deletions cdk/bootstrap/policies/observability.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,11 @@
"s3:DeleteBucket",
"s3:PutBucketPolicy",
"s3:DeleteBucketPolicy",
"s3:GetBucketPolicy",
"s3:PutBucketPublicAccessBlock",
"s3:GetBucketPublicAccessBlock",
"s3:PutEncryptionConfiguration",
"s3:GetEncryptionConfiguration",
"s3:PutLifecycleConfiguration",
"s3:PutBucketVersioning",
"s3:GetBucketVersioning",
Expand Down
7 changes: 7 additions & 0 deletions cdk/src/bootstrap/policies/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,13 @@ export function applicationPolicy(): iam.PolicyDocument {
'sqs:UntagQueue',
'sqs:GetQueueUrl',
'sqs:ListQueueTags',
// AWS::SQS::QueuePolicy is a distinct CFN resource from the queue, and
// CloudFormation manages it with Add/RemovePermission — not
// SetQueueAttributes. The stack creates one (the DLQ redrive policy),
// so without these a queue-policy create/update/delete fails
// (#124 review — previously excluded via KNOWN_GAP rather than granted).
'sqs:AddPermission',
'sqs:RemovePermission',
],
resources: ['arn:aws:sqs:*:*:backgroundagent-dev-*'],
}),
Expand Down
40 changes: 40 additions & 0 deletions cdk/src/bootstrap/policies/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { computeAgentcorePolicy } from './compute-agentcore';
import { computeEcsPolicy } from './compute-ecs';
import { infrastructurePolicy } from './infrastructure';
import { observabilityPolicy } from './observability';
import { getRequiredBootstrapPolicies } from '../required-policies';

export { applicationPolicy } from './application';
export { computeAgentcorePolicy } from './compute-agentcore';
Expand All @@ -33,6 +34,12 @@ export { observabilityPolicy } from './observability';

/**
* Returns all bootstrap IAM PolicyDocuments as an array.
*
* This is the UNION of every variant. Validating an app against it answers
* "could some bootstrap configuration allow this?", not "does THIS deploy's
* bootstrap allow it" — so prefer {@link policiesForComputeType} when the
* compute substrate is known. See RFC #120's sufficiency model
* (`deployed PolicySet ⊇ the app's required set`).
*/
export function allPolicies(): iam.PolicyDocument[] {
return [
Expand All @@ -43,3 +50,36 @@ export function allPolicies(): iam.PolicyDocument[] {
computeEcsPolicy(),
];
}

/** Policy documents keyed by the artifact name emitted under `cdk/bootstrap/policies/`. */
const POLICY_BY_NAME: Record<string, () => iam.PolicyDocument> = {
'infrastructure': infrastructurePolicy,
'application': applicationPolicy,
'observability': observabilityPolicy,
'compute-agentcore': computeAgentcorePolicy,
'compute-ecs': computeEcsPolicy,
};

/**
* The PolicyDocuments an operator actually deploys for ``computeType``.
*
* An agentcore-only operator never deploys `compute-ecs`, so validating their
* app against {@link allPolicies} silently accepts `ecs:*` actions their real
* IaCRole cannot perform — the over-permissive direction this map exists to
* catch. Resolves names through {@link getRequiredBootstrapPolicies} so the
* selection and the generated artifacts cannot drift.
*/
export function policiesForComputeType(computeType: string): iam.PolicyDocument[] {
return getRequiredBootstrapPolicies(computeType).map((name) => {
const factory = POLICY_BY_NAME[name];
if (!factory) {
// Fail loud: a name with no document means the selection list and this
// registry have drifted, which would silently under-scope validation.
throw new Error(
`No bootstrap policy document registered for '${name}'. `
+ `Known: ${Object.keys(POLICY_BY_NAME).join(', ')}.`,
);
}
return factory();
});
}
7 changes: 7 additions & 0 deletions cdk/src/bootstrap/policies/observability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,16 @@ export function observabilityPolicy(): iam.PolicyDocument {
's3:DeleteBucket',
's3:PutBucketPolicy',
's3:DeleteBucketPolicy',
// CloudFormation reads a bucket's policy and encryption config back on
// stack UPDATE (drift/no-op detection), so the Put* grants above are
// insufficient on their own. Every other Put* here has its Get* pair;
// these two were the omissions (#124 review — previously excluded via
// the resource-action-map's KNOWN_GAP set rather than granted).
's3:GetBucketPolicy',
's3:PutBucketPublicAccessBlock',
's3:GetBucketPublicAccessBlock',
's3:PutEncryptionConfiguration',
's3:GetEncryptionConfiguration',
's3:PutLifecycleConfiguration',
's3:PutBucketVersioning',
's3:GetBucketVersioning',
Expand Down
25 changes: 25 additions & 0 deletions cdk/src/bootstrap/preflight/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* MIT No Attribution
*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

export {
RESOURCE_ACTION_MAP,
getActionsForResource,
getAllMappedActions,
} from './resource-action-map';
export type { ResourceActions, LifecyclePhase } from './resource-action-map';
62 changes: 62 additions & 0 deletions cdk/src/bootstrap/preflight/resource-action-map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* MIT No Attribution
*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

/**
* Verification helpers over the bootstrap resource-action map.
*
* This module deliberately holds NO map data. It previously carried a second,
* parallel copy: this directory's version had CRUD depth but no production
* consumer, while ``../resource-action-map.ts`` was create-only and wired into
* the live synth-coverage gate. Two maps with disjoint test suites and no shared
* consumer drift by construction, and adding a resource type to only one of them
* is silent. The CRUD depth was merged INTO the live map (#124); what remains
* here are the query helpers the preflight/validation layer (#125/#126) reads it
* through.
*/

import {
RESOURCE_ACTION_MAP,
actionsForResource,
type ResourceActions,
} from '../resource-action-map';

export { RESOURCE_ACTION_MAP } from '../resource-action-map';
export type { ResourceActions, LifecyclePhase } from '../resource-action-map';

/**
* Returns the ResourceActions entry for a given CloudFormation resource type,
* or undefined if the type is not mapped.
*/
export function getActionsForResource(cfnType: string): ResourceActions | undefined {
return RESOURCE_ACTION_MAP[cfnType];
}

/**
* Returns the set of all unique IAM actions referenced across all map entries,
* across every lifecycle phase.
*/
export function getAllMappedActions(): Set<string> {
const actions = new Set<string>();
for (const cfnType of Object.keys(RESOURCE_ACTION_MAP)) {
for (const action of actionsForResource(cfnType)) {
actions.add(action);
}
}
return actions;
}
36 changes: 36 additions & 0 deletions cdk/src/bootstrap/required-policies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* MIT No Attribution
*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

const CORE_POLICIES = [
'infrastructure',
'application',
'observability',
] as const;

const COMPUTE_VARIANT_POLICIES: Record<string, string[]> = {
agentcore: ['compute-agentcore'],
ecs: ['compute-ecs'],
Comment thread
scottschreckengaust marked this conversation as resolved.
};

export function getRequiredBootstrapPolicies(computeType: string): string[] {
const base: string[] = [...CORE_POLICIES];
const variants = COMPUTE_VARIANT_POLICIES[computeType];
if (variants) base.push(...variants);
return base;
}
Loading