fix(scp): carve out Bedrock InvokeModel/Converse to us-east-2 for cross-region inference (#54)
Some checks failed
Deploy / deploy-management (push) Has been cancelled
Deploy / deploy-external-dev (push) Has been cancelled
Deploy / deploy-security (push) Has been cancelled
Deploy / deploy-dev (push) Has been cancelled
Deploy / deploy-prod (push) Has been cancelled

* fix(scp): carve out Bedrock InvokeModel/Converse to us-east-2 for cross-region inference

Add bedrock:InvokeModel, bedrock:InvokeModelWithResponseStream,
bedrock:Converse, and bedrock:ConverseStream to the existing
DenyRegionsOutsideApproved NotAction list so the us-east-1/us-west-2
region condition no longer denies them. Add a companion
DenyBedrockInvokeOutsideInference statement that re-denies those same
four actions outside {us-east-1, us-west-2, us-east-2}, bounding the
carve-out to us-east-2 only.

Without this, Anthropic cross-region inference profiles (us.anthropic.*)
that route InvokeModel to us-east-2 are denied, blocking all Claude
generation in workload accounts.

Refs: #53

* fix: add ACCEPTED RISK disposition, hoist Bedrock actions to shared const, mark security-asymmetry

- ACCEPTED RISK: Bedrock carve-out is resource-unscoped (NotAction
  can't be resource-scoped); us-east-2 window admits four actions
  against any Bedrock resource. Per-account IAM and model-access
  enablement gate actual access.
- Hoist the four Bedrock invoke actions into BEDROCK_INVOKE_ACTIONS
  shared const referenced by both NotAction and DenyBedrockInvoke
  statements to prevent future one-sided edit divergence.
- Mark asymmetry in security-guardrails DenyRegionsOutsideApproved:
  no Bedrock carve-out by design — security account runs no Bedrock
  workloads.

---------

Co-authored-by: amoussa1229 <166072409+amoussa1229@users.noreply.github.com>
This commit is contained in:
seahaven-openswe[bot] 2026-07-15 18:52:05 -04:00 • committed by GitHub
parent ed26ff937d
commit 88fa5777d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -56,10 +56,33 @@ const scpContent = (name: string): Record<string, unknown> =>
* decision as the external-dev guardrails): it is the only generic
* cross-account expression for CDK exec roles; member baselines protect
* those roles from takeover (ProtectPrivilegedRoles pattern).
* - Region-lock NotAction list deliberately matches battle-tested
* p-i59g24mz; regional services (s3, kms, logs, ssm...) stay region-locked
* BY DESIGN — do not add them to NotAction (that would exempt them).
* - Region-lock NotAction list extends battle-tested p-i59g24mz with
* BEDROCK_INVOKE_ACTIONS to allow cross-region inference profiles
* (us.anthropic.*) that route to us-east-2; a companion
* DenyBedrockInvokeOutsideInference statement re-denies those actions
* outside {us-east-1, us-west-2, us-east-2}. Regional services (s3, kms,
* logs, ssm...) stay region-locked BY DESIGN — do not add them to
* NotAction (that would exempt them).
* - Bedrock carve-out is resource-unscoped (NotAction cannot be
* resource-scoped): the us-east-2 window admits the four
* BEDROCK_INVOKE_ACTIONS against ANY Bedrock resource (any provider's
* foundation model, marketplace, custom/imported), not just the
* us.anthropic.* inference-profile path. ACCEPTED RISK — per-account IAM
* policies and model-access enablement gate actual access; the SCP
* provides coarse region enforcement only. (Same risk disposition as the
* cdk-hnb659fds-* exemption above.)
*/
/** Bedrock inference actions carved out of the region lock so
* cross-region inference profiles (us.anthropic.*) that route to us-east-2
* are not blocked. The DenyBedrockInvokeOutsideInference statement limits this
* carve-out to {us-east-1, us-west-2, us-east-2} only. */
const BEDROCK_INVOKE_ACTIONS = [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream",
"bedrock:Converse",
"bedrock:ConverseStream",
];
export class OrgGovernanceStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
@ -116,7 +139,10 @@ export class OrgGovernanceStack extends cdk.Stack {
// Region lock for workload accounts: us-east-1 (primary) + us-west-2
// (offsite backup/DR). Global services exempted via NotAction — the same
// list proven on the external-dev region lock.
// list proven on the external-dev region lock. Bedrock Invoke/Converse
// are carved out of the general deny and re-denied only outside
// {us-east-1, us-west-2, us-east-2} so cross-region inference profiles
// (us.anthropic.*) that route to us-east-2 are not blocked.
const workloadsRegionLock = new organizations.CfnPolicy(this, "WorkloadsRegionLock", {
name: "workloads-region-lock",
type: "SERVICE_CONTROL_POLICY",
@ -135,6 +161,7 @@ export class OrgGovernanceStack extends cdk.Stack {
"globalaccelerator:*", "budgets:*", "ce:*", "cur:*", "health:*",
"support:*", "supportplans:*", "trustedadvisor:*", "artifact:*",
"aws-portal:*",
...BEDROCK_INVOKE_ACTIONS,
],
Resource: "*",
Condition: {
@ -143,6 +170,17 @@ export class OrgGovernanceStack extends cdk.Stack {
},
},
},
{
Sid: "DenyBedrockInvokeOutsideInference",
Effect: "Deny",
Action: BEDROCK_INVOKE_ACTIONS,
Resource: "*",
Condition: {
StringNotEquals: {
"aws:RequestedRegion": ["us-east-1", "us-west-2", "us-east-2"],
},
},
},
],
},
});
@ -222,6 +260,9 @@ export class OrgGovernanceStack extends cdk.Stack {
{
Sid: "DenyRegionsOutsideApproved",
Effect: "Deny",
// NO Bedrock carve-out BY DESIGN — security account runs no
// Bedrock workloads; do not sync BEDROCK_INVOKE_ACTIONS from
// workloads-region-lock.
NotAction: [
"iam:*", "organizations:*", "account:*", "sts:*", "route53:*",
"route53domains:*", "cloudfront:*", "waf:*", "shield:*",