From 88fa5777d17342a912b85edf8e95abbfb9f1c7e3 Mon Sep 17 00:00:00 2001 From: "seahaven-openswe[bot]" <296972425+seahaven-openswe[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 18:52:05 -0400 Subject: [PATCH] fix(scp): carve out Bedrock InvokeModel/Converse to us-east-2 for cross-region inference (#54) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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> --- lib/org-governance-stack.ts | 49 ++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/lib/org-governance-stack.ts b/lib/org-governance-stack.ts index bfb80f6..a6faf3a 100644 --- a/lib/org-governance-stack.ts +++ b/lib/org-governance-stack.ts @@ -56,10 +56,33 @@ const scpContent = (name: string): Record => * 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:*",