目次を表示する

Bedrockで Production Ready な AI 機能を作る ── 設計・運用・現場の知恵

セキュリティを設計する ── IAM 強制パターンと VPC Endpoint と KMS

セキュリティを設計する ── IAM 強制パターンと VPC Endpoint と KMS

対象読者:機能は動くが、セキュリティレビュー / 監査 / コンプライアンスの観点から「これで本番出していいか」を判断する立場の読者 難易度:★★★★☆(実装ハンズオン) 対象バージョン:@aws-sdk/client-bedrock v3 系 / AWS CDK v2 想定読了時間:約 50 分 関連章:第 11 章(Guardrails IAM 強制)/ 第 13 章(Log Data Protection)/ 第 17 章 #9(VPC Endpoint 未使用)

なぜセキュリティが Production Ready の本気度を決めるのか

エンタープライズで「AI 機能を入れたい」と言うと、最初に返ってくる質問は機能でも UX でもない。「会社のデータが学習に使われませんよね?」ログにユーザーの個人情報が残らないですよね?」「監査で説明できますよね?」── この 3 問だ。

LLM API を呼ぶ行為そのものが、エンタープライズではコンプライアンスリスクと見なされる。「外部 API に社内データを投げる」というだけで、情シスから赤旗が上がる。OpenAI 直叩きで PoC を作ったまでは良かったが、「セキュリティレビューで止まった」という相談が多いのは、ここに理由がある。

Bedrock の差別化はここに出る。Bedrock はマネージドサービスでありながら、IAM・VPC・KMS・SCP・Compliance プログラムという AWS のセキュリティ機構をそのまま継承する。そして 2025〜2026 年にかけて、bedrock:GuardrailIdentifier 条件キーや Cross-Account Safeguards といった「Production 強制パターン」が次々に追加された。

本章では、ここまで育てた helpdesk-ai に、4 つの層でセキュリティを着せていく。

graph TB
    subgraph "層 4:SCP(組織レベル)"
        SCP["承認モデルファミリーのみ許可<br/>Guardrail なし呼び出しを全アカウントで禁止"]
    end
    subgraph "層 3:IAM(最小権限)"
        IAM1["特定モデルのみ呼べる"]
        IAM2["Guardrail 必須(条件キー)"]
        IAM3["Agent 経由のみ、直接 InvokeModel は Deny"]
        IAM4["aws:SourceAccount / aws:SourceArn で<br/>confused deputy 対策"]
    end
    subgraph "層 2:VPC Endpoint(ネットワーク)"
        VPCE["Interface VPC Endpoint<br/>Endpoint policy で VPC 単位の制御<br/>Internet Gateway / NAT 不要"]
    end
    subgraph "層 1:KMS(暗号化)"
        KMS1["保管時:AWS KMS(CMK 指定可)"]
        KMS2["転送時:TLS 1.2 以上"]
        KMS3["Guardrails / KB / Logs に CMK"]
    end
    SCP --> IAM1
    SCP --> IAM2
    SCP --> IAM3
    SCP --> IAM4
    IAM1 --> VPCE
    IAM2 --> VPCE
    IAM3 --> VPCE
    IAM4 --> VPCE
    VPCE --> KMS1
    VPCE --> KMS2
    VPCE --> KMS3

「全部やる必要があるのか?」と思うかもしれない。答えは イエスだ。1 つでも欠けると、攻撃者・うっかり開発者・退職者・コンプライアンス監査のいずれかから穴が空く。Production Ready は「動く」ではなく「全層で破られない」状態を意味する。


出発点:Bedrock のデータ取り扱いポリシー

技術的な設計に入る前に、最大の前提を確認しておく。これは ch03 で他社比較として触れた話の続きであり、後の ch16 で金融・医療事例の根拠として再登場する伏線でもある。

AWS 公式の Bedrock Security & Privacy は、3 つのことを明言している。

  • Your data is not shared with model providers.(顧客データはモデルプロバイダーに共有されない)
  • Your data is not used to train the underlying models.(FM の学習に使用されない)
  • Bedrock service team も、Anthropic 等のモデルプロバイダーも、推論データにアクセスできない。

図にするとこうなる。

graph LR
    A["helpdesk-ai<br/>(顧客アプリ)"] -->|TLS 1.2+| B[Amazon Bedrock]
    B -->|"推論のみ"| C[Anthropic / Meta /<br/>Cohere / Mistral など<br/>モデルプロバイダー]
    C -.->|"❌ データ非共有<br/>❌ 学習に非使用"| D[(プロバイダー側<br/>データレイク)]
    B -->|"暗号化保管"| E[("CMK 暗号化<br/>S3 / DynamoDB")]

    style D fill:#fff0f0,stroke:#d33,stroke-dasharray: 5 5
    style C fill:#f0f4ff

ここが OpenAI 直叩きから Bedrock への移行の最大動機になる。OpenAI 直叩きだと「OpenAI ↔ 顧客」「AWS ↔ 顧客」の 二重 processor 関係 になり、GDPR・SOC 2・社内データガバナンス上の説明責任が複雑化する。Bedrock なら AWS DPA(Data Processing Addendum)が 1 本で全モデルをカバーするため、プロセッサ関係を一本化できる。

「データが共有されない・学習に使われない」というのは、契約レベルだけでなくアーキテクチャ的にも、Anthropic 等のモデルプロバイダーが顧客の推論データにアクセスできない構造になっている。これが Bedrock のセキュリティ設計のスタート地点である。


層 1:IAM ── 最小権限と「Guardrail 強制」パターン

セキュリティ設計の出発点は IAM だ。Bedrock の IAM 設計には、Production Ready のための定型パターンが 4 つある。すべて helpdesk-ai に適用していこう。

パターン 1:特定モデルのみ呼べる

「Bedrock の全モデルが叩ける」という権限を与えるのは、コスト面でも安全面でも危険だ。helpdesk-ai は Claude Sonnet 4 系(Cross-Region Inference Profile)のみ使う前提なので、それだけ許可する。

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "AllowOnlyApprovedModels",
    "Effect": "Allow",
    "Action": [
      "bedrock:InvokeModel",
      "bedrock:InvokeModelWithResponseStream",
      "bedrock:Converse",
      "bedrock:ConverseStream"
    ],
    "Resource": [
      "arn:aws:bedrock:*::foundation-model/anthropic.claude-sonnet-4-*",
      "arn:aws:bedrock:*:123456789012:inference-profile/us.anthropic.claude-sonnet-4-5"
    ]
  }]
}

ポイントは 2 つ。Cross-Region Inference Profile を使う場合は、foundation-model だけでなく inference-profile の ARN も Allow が必要な点(ch15 で扱う Application Inference Profile も同じ)。そしてバージョン番号は claude-sonnet-4-* のようにワイルドカードで縛ると、マイナーアップデートに追随しやすい。

パターン 2:Guardrail を必ず通すことを強制する(2025-03 追加)

ここが本章の核心の 1 つ目。bedrock:GuardrailIdentifier という IAM 条件キーが 2025 年 3 月に追加され、「Guardrail を通さない呼び出しを IAM レベルで拒否する」ことが可能になった。

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "DenyInvocationWithoutGuardrail",
    "Effect": "Deny",
    "Action": [
      "bedrock:InvokeModel",
      "bedrock:InvokeModelWithResponseStream",
      "bedrock:Converse",
      "bedrock:ConverseStream"
    ],
    "Resource": "*",
    "Condition": {
      "Null": { "bedrock:GuardrailIdentifier": "true" }
    }
  }]
}

Null: true は「bedrock:GuardrailIdentifier が設定されていない場合」という意味なので、Guardrail を指定しない呼び出しは IAM が拒否する。「Guardrails を後付けにして、いつか付け忘れる」というアンチパターン(ch17 で再登場)を、IAM の段階で潰せる。

さらに「**特定の Guardrail(自社承認版)**しか使えない」と縛りたい場合は、StringEquals を併用する。

{
  "Sid": "AllowOnlyApprovedGuardrail",
  "Effect": "Deny",
  "Action": ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"],
  "Resource": "*",
  "Condition": {
    "StringNotEquals": {
      "bedrock:GuardrailIdentifier": "arn:aws:bedrock:us-east-1:123456789012:guardrail/helpdesk-prod-v3"
    }
  }
}

これで「helpdesk-ai は helpdesk-prod-v3 以外の Guardrail を使えない」を IAM で保証できる。「組織で承認した Guardrail Policy」と「アプリで使う Guardrail」のずれを構造的に防ぐ強力な仕組みだ。

パターン 3:Agent 経由のみ許可・直接 InvokeModel を禁止する

ch09ch10 で見たように、helpdesk-ai は Bedrock Agents(後に AgentCore)を経由してモデルを呼ぶ。アプリのランタイムロールには Agent 呼び出しの権限だけを与え、InvokeModel への直接アクセスは Deny で塞ぐ。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowAgentInvocationOnly",
      "Effect": "Allow",
      "Action": ["bedrock:InvokeAgent"],
      "Resource": "arn:aws:bedrock:us-east-1:123456789012:agent-alias/HELPDESK01/PROD"
    },
    {
      "Sid": "DenyDirectModelInvocation",
      "Effect": "Deny",
      "Action": [
        "bedrock:InvokeModel",
        "bedrock:InvokeModelWithResponseStream"
      ],
      "Resource": "arn:aws:bedrock:*::foundation-model/*"
    }
  ]
}

これで Action Group を迂回して LLM を直接叩く経路が物理的に塞がる。Guardrails も Action Group のフィルタも全部すり抜けて生 prompt を投げる、というショートカットが取れなくなる。

パターン 4:confused deputy 対策(aws:SourceAccountaws:SourceArn

Bedrock Agents や Knowledge Bases は、AWS がサービスロールを assume してリソースを操作する。このロールの Trust Policy を雑に書くと「Bedrock サービス自体は誰の依頼でも assume できてしまう」という穴ができる。これが confused deputy 問題で、Agent / KB の Service Role を作るときは必ず aws:SourceAccountaws:SourceArn で絞る。

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "Service": "bedrock.amazonaws.com" },
    "Action": "sts:AssumeRole",
    "Condition": {
      "StringEquals": {
        "aws:SourceAccount": "123456789012"
      },
      "ArnLike": {
        "aws:SourceArn": "arn:aws:bedrock:us-east-1:123456789012:agent/HELPDESK01"
      }
    }
  }]
}

❌ 悪い例:scope-down なし & PassRole を広く渡す

{
  "Effect": "Allow",
  "Principal": { "Service": "bedrock.amazonaws.com" },
  "Action": "sts:AssumeRole"
}

このうえで Agent 管理者ロールに iam:PassRoleResource: "*" で渡すと、攻撃者が他の高権限ロールを Bedrock に渡して特権昇格(privilege escalation)できる温床になる。AWS Security Blog はこれを 「現場で最も踏みやすい落とし穴」 と名指ししている。

✅ 良い例:scope-down + PassRole も特定ロールに限定

{
  "Sid": "AllowPassRoleForAgentOnly",
  "Effect": "Allow",
  "Action": "iam:PassRole",
  "Resource": "arn:aws:iam::123456789012:role/HelpdeskAgentServiceRole",
  "Condition": {
    "StringEquals": { "iam:PassedToService": "bedrock.amazonaws.com" }
  }
}

iam:PassedToService 条件キーで「Bedrock に渡す目的でしか PassRole できない」を強制し、Resource で「helpdesk 専用のサービスロールしか渡せない」を組み合わせる。IAM Access Analyzer の policy check(100+ ルール)で事前検証する習慣も組み込んでおくと、レビュー漏れを機械的に拾える。


「外部 API を呼ぶ」という事実そのものをセキュリティ監査で説明するのは、しんどい。bedrock-runtime への通信が NAT Gateway → Internet Gateway → AWS の public endpoint と外を回るたびに、TLS で守られているとはいえ「インターネット越え」という言葉が監査で引っかかる。

Interface VPC Endpoint(AWS PrivateLink) を使えば、Bedrock への通信を VPC 内で完結させられる。Internet Gateway も NAT Gateway も不要だ。helpdesk-ai のように VPC 内 ECS / Lambda から Bedrock を叩く構成では、これは ほぼ必須 と考えていい(ch17 で「PrivateLink 未使用」をアンチパターンとして再登場させる)。

CDK での実装

import { Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import {
  Vpc,
  InterfaceVpcEndpoint,
  InterfaceVpcEndpointAwsService,
  SubnetType,
  SecurityGroup,
  Peer,
  Port,
} from "aws-cdk-lib/aws-ec2";
import { PolicyDocument, PolicyStatement, Effect, AnyPrincipal } from "aws-cdk-lib/aws-iam";

export class HelpdeskNetworkStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const vpc = new Vpc(this, "HelpdeskVpc", { maxAzs: 2 });

    // VPC Endpoint 用 SG:VPC CIDR からの HTTPS のみ許可
    const endpointSg = new SecurityGroup(this, "BedrockEndpointSg", {
      vpc,
      description: "Allow HTTPS from VPC CIDR only",
      allowAllOutbound: false,
    });
    endpointSg.addIngressRule(Peer.ipv4(vpc.vpcCidrBlock), Port.tcp(443));

    // Endpoint policy:自社承認モデル & 自社 Guardrail のみ許可
    const endpointPolicy = new PolicyDocument({
      statements: [
        new PolicyStatement({
          effect: Effect.ALLOW,
          principals: [new AnyPrincipal()],
          actions: [
            "bedrock:InvokeModel",
            "bedrock:InvokeModelWithResponseStream",
            "bedrock:Converse",
            "bedrock:ConverseStream",
          ],
          resources: [
            "arn:aws:bedrock:*::foundation-model/anthropic.claude-sonnet-4-*",
            `arn:aws:bedrock:*:${this.account}:inference-profile/us.anthropic.claude-sonnet-4-5`,
          ],
          conditions: {
            StringEquals: {
              "bedrock:GuardrailIdentifier":
                `arn:aws:bedrock:us-east-1:${this.account}:guardrail/helpdesk-prod-v3`,
            },
          },
        }),
      ],
    });

    new InterfaceVpcEndpoint(this, "BedrockRuntimeEndpoint", {
      vpc,
      service: InterfaceVpcEndpointAwsService.BEDROCK_RUNTIME,
      subnets: { subnetType: SubnetType.PRIVATE_ISOLATED },
      privateDnsEnabled: true,
      securityGroups: [endpointSg],
      policyDocument: endpointPolicy,
    });

    // Agent / Knowledge Bases も別 Endpoint で公開
    new InterfaceVpcEndpoint(this, "BedrockAgentRuntimeEndpoint", {
      vpc,
      service: InterfaceVpcEndpointAwsService.BEDROCK_AGENT_RUNTIME,
      subnets: { subnetType: SubnetType.PRIVATE_ISOLATED },
      privateDnsEnabled: true,
      securityGroups: [endpointSg],
    });
  }
}

Endpoint policy で **「この VPC から出る Bedrock 呼び出しは、承認モデル × 承認 Guardrail の組み合わせに限る」**を縛れるのが効く。IAM ロールが「うっかり広い権限を持っている」場合でも、ネットワーク境界で締めれば事故の被害を抑えられる。**多層防御(defense in depth)**は、こうやって 1 層ずつ重ねる。

PRIVATE_ISOLATED サブネットに置くことで、Internet Gateway / NAT を一切経由せずに Bedrock に到達する。VPC Flow Logs を有効にしておけば「インターネット越えのトラフィックが本当に存在しないこと」を監査で示せる証跡にもなる。


層 3:KMS ── 保管時暗号化と CMK 指定

Bedrock 周辺のデータ保管は、デフォルトで AWS-managed KMS key により暗号化されている。ただし「鍵を自社で持つ」というコンプライアンス要件がある場合は、ほぼすべての地点で Customer Managed Key(CMK) を指定できる。

暗号化対象CMK 指定可否設定方法
Guardrails の Policy / TraceCreateGuardrailkmsKeyId
Knowledge Bases のベクトル / 取り込みデータKB 作成時の serverSideEncryptionConfiguration
カスタムモデル / ファインチューニングデータCreateModelCustomizationJoboutputDataConfig.kmsKeyId
Model Invocation Logs(S3)バケット SSE-KMS + Bedrock service principal への許可
Model Invocation Logs(CloudWatch Logs)Log Group に CMK を関連付け
Agent / AgentCore のセッション・MemoryAgentCore Memory 作成時に指定

転送時は TLS 1.2 以上が強制される。bedrock-runtime の API はすべて HTTPS のみで、TLS 1.0/1.1 はそもそも受け付けない。

Model Invocation Logs の S3 + KMS 暗号化(CDK)

ch13 で見た Model Invocation Logs は、helpdesk-ai のセキュリティ設計では特に CMK 必須で扱うべきデータだ。Guardrails Mask モードを使っていても、Model Invocation Logs の input フィールドには原文 PII がそのまま残るch11 で示した落とし穴。ch17 で再登場)。だからログ側を自社鍵で守る意味は大きい。

import { Bucket, BucketEncryption, BlockPublicAccess, ObjectOwnership } from "aws-cdk-lib/aws-s3";
import { Key } from "aws-cdk-lib/aws-kms";
import { PolicyStatement, ServicePrincipal } from "aws-cdk-lib/aws-iam";

const logKey = new Key(this, "BedrockLogsKey", {
  description: "CMK for helpdesk-ai Bedrock invocation logs",
  enableKeyRotation: true,
});

const logBucket = new Bucket(this, "BedrockLogsBucket", {
  encryption: BucketEncryption.KMS,
  encryptionKey: logKey,
  enforceSSL: true,
  blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
  objectOwnership: ObjectOwnership.BUCKET_OWNER_ENFORCED,
});

// Bedrock サービスから書けるよう、CMK 側で許可を出す
logKey.addToResourcePolicy(
  new PolicyStatement({
    sid: "AllowBedrockToUseKey",
    principals: [new ServicePrincipal("bedrock.amazonaws.com")],
    actions: ["kms:GenerateDataKey", "kms:Decrypt"],
    resources: ["*"],
    conditions: {
      StringEquals: {
        "aws:SourceAccount": this.account,
      },
      ArnLike: {
        "aws:SourceArn": `arn:aws:bedrock:*:${this.account}:*`,
      },
    },
  })
);

// バケットポリシーで Bedrock からの PutObject を許可
logBucket.addToResourcePolicy(
  new PolicyStatement({
    sid: "AllowBedrockLogDelivery",
    principals: [new ServicePrincipal("bedrock.amazonaws.com")],
    actions: ["s3:PutObject"],
    resources: [`${logBucket.bucketArn}/AWSLogs/${this.account}/BedrockModelInvocationLogs/*`],
    conditions: {
      StringEquals: {
        "s3:x-amz-acl": "bucket-owner-full-control",
        "aws:SourceAccount": this.account,
      },
    },
  })
);

ここでも aws:SourceAccountaws:SourceArn を入れている。KMS Key Policy にも confused deputy 対策を仕込むのが鉄則。Key Policy で SourceArn を絞らないと、別アカウントの Bedrock 経由で自社キーを使われる経路が理屈上開く。


層 4:SCP ── 組織全体での Guard Rail

ここまでの 3 層は単一アカウント内の話だが、企業規模になると AWS Organizations で複数アカウントを束ねていることが多い。組織レベルで「全アカウント共通の禁止事項」を強制するのが Service Control Policy(SCP) だ。

helpdesk-ai のような業務 AI を組織全体で展開する場合、最低でも次の 2 つの SCP を OU レベルで強制しておく。

SCP 1:承認モデルファミリーのみ許可

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "DenyUnapprovedFoundationModels",
    "Effect": "Deny",
    "Action": [
      "bedrock:InvokeModel",
      "bedrock:InvokeModelWithResponseStream",
      "bedrock:Converse",
      "bedrock:ConverseStream"
    ],
    "Resource": "*",
    "Condition": {
      "ArnNotLike": {
        "bedrock:FoundationModel": [
          "arn:aws:bedrock:*::foundation-model/anthropic.*",
          "arn:aws:bedrock:*::foundation-model/amazon.nova-*"
        ]
      }
    }
  }]
}

bedrock:FoundationModel 条件キーで承認済みファミリー(Anthropic と Amazon Nova のみ)以外は組織全体で Deny する」というパターン。Inference Profile 経由でも foundation model ARN は条件キーから読めるため、CRIS を使っていてもこの SCP は効く。「あるアカウントの開発者が試しに新しい外部モデルを叩く」が起きなくなる。

SCP 2:全アカウントで Guardrail なし呼び出しを禁止

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "OrgWideDenyInvocationWithoutGuardrail",
    "Effect": "Deny",
    "Action": [
      "bedrock:InvokeModel",
      "bedrock:InvokeModelWithResponseStream",
      "bedrock:Converse",
      "bedrock:ConverseStream"
    ],
    "Resource": "*",
    "Condition": {
      "Null": { "bedrock:GuardrailIdentifier": "true" }
    }
  }]
}

層 3 で IAM ポリシー単体に書いたものと同じ Deny を 組織レベルに昇格する。各アカウントの IAM ポリシーがどう書かれていようと、Guardrail を通さない呼び出しは組織全体で物理的に不可能になる。

Cross-Account Safeguards(2026-04 GA)

2026 年 4 月に GA した Cross-Account Safeguards は、SCP のさらに上位互換的な機能だ。組織の中央アカウントが定義した Guardrail Policy を、全 OU・全アカウントの Bedrock 呼び出しに自動適用できる。

これまで「子アカウントの開発者が Guardrail を付けて InvokeModel する」のを SCP で強制してきた。Cross-Account Safeguards は **「子アカウントが Guardrail を指定しなくても、中央定義の Guardrail が必ず重なって動く」**という挙動になり、ガバナンスが一段強くなる。

helpdesk-ai が小規模な単一アカウント運用なら SCP だけで十分だが、複数事業部・複数子会社で AI 機能を横展開する段階に入ったら、Cross-Account Safeguards に乗せ替えるのが推奨パターンになる。詳細な設定方法は AWS のリリースブログと最新 docs を必ず参照してほしい(2026-06 時点ではドキュメント整備が進行中)。


コンプライアンス

Bedrock は AWS のコンプライアンスフレームワークをそのまま継承する。helpdesk-ai を金融・医療・公共系で使う想定があるなら、以下の認証状況を抑えておく。

標準対応
SOC 1 / 2 / 3対応(AWS SOC レポートに Bedrock 含まれる)
ISO 9001 / 27001 / 27017 / 27018 / 27701 / 22301 / 20000対応
CSA STAR Level 2対応
HIPAAHIPAA Eligible Service(PHI 取り扱い可、BAA 締結が前提)
GDPRAWS DPA でカバー。前述のとおり processor 関係を一本化できる
FedRAMP HighAWS GovCloud (US-West) で対応
PCI DSS / IRAP最新は AWS Compliance Programs を参照

ポイントは HIPAA Eligible Service であること。医療系で PHI(保護対象保健情報)を扱う AI を組む場合、Bedrock は BAA(Business Associate Addendum)を AWS と締結することで、OpenAI 直叩きでは詰みがちな要件にそのまま乗せられる。これは ch16 で Pfizer の事例を見るときに効いてくる前提条件でもある。


データレジデンシー ── In-Region / Geographic / Global の 3 モード

「データはどこに置かれ、推論はどこで実行されるか」── これも金融・公共で必ず聞かれる質問だ。Bedrock の Cross-Region Inference(CRIS)には 3 モードがあり、要件に応じて選ぶ。

graph TB
    A[helpdesk-ai source region: ap-northeast-1] --> M{推論モード}
    M -->|In-Region| B["同一リージョンで完結<br/>強い residency 要件向け"]
    M -->|Geographic CRIS<br/>例: Japan profile| C["日本国内リージョン群<br/>東京・大阪に分散<br/>residency 維持"]
    M -->|Global CRIS| D["全グローバル分散<br/>最安・最速だが<br/>residency 制約なし"]
    B --> L[("CloudWatch / CloudTrail /<br/>Model Invocation Logs<br/>はすべて source region に書かれる")]
    C --> L
    D --> L
モード推論実行先用途注意点
In-Regionsource region のみ強い residency 要件、金融・医療・公共スループット上限あり、スロットル多発時に逃げ場がない
Geographic CRIS同一地理圏(例:Japan = 東京 + 大阪)に分散residency 維持しつつ可用性確保「データが日本国内に留まる」を維持できる
Global CRIS全グローバル分散最安・最高スループットresidency 要件があるとアウト

ここで重要な事実:CloudWatch メトリクス・CloudTrail・Model Invocation Logs は、すべて source region(= リクエストを出したリージョン)に書かれる。Geographic / Global CRIS で推論自体は他リージョンへ飛んでも、ログは source に留まる。「推論は別リージョンに行くがログは日本に残る」という設計が成立するため、「ログだけは絶対に日本国内」という社内ガバナンスとも整合させやすい。

helpdesk-ai のデフォルトは Geographic CRIS(Japan profile) を推奨する。これが「速度・可用性・residency」の妥協点として最も無難だ。In-Region 専用は「金融機関の特定業務」など、明示的に外に出せないユースケース限定。マルチリージョン化の具体的な構築方法は 付録 A で深掘りする。


helpdesk-ai のセキュリティ実装まとめ

ここまで設計した 4 層を、helpdesk-ai のスタックに反映する。CDK の infra/cdk-stack.ts から見ると、こうレイヤー分けされる構成になる。

import { Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import { HelpdeskNetworkStack } from "./network-stack";       // VPC + Endpoint
import { HelpdeskCryptoStack } from "./crypto-stack";         // CMK + Log Bucket
import { HelpdeskIamStack } from "./iam-stack";               // 最小権限 + Guardrail 強制
import { HelpdeskAppStack } from "./app-stack";               // ECS / Lambda / Agent

export class HelpdeskRootStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const crypto = new HelpdeskCryptoStack(this, "Crypto");
    const network = new HelpdeskNetworkStack(this, "Network");
    const iam = new HelpdeskIamStack(this, "Iam", {
      approvedGuardrailArn:
        `arn:aws:bedrock:us-east-1:${this.account}:guardrail/helpdesk-prod-v3`,
      approvedAgentAliasArn:
        `arn:aws:bedrock:us-east-1:${this.account}:agent-alias/HELPDESK01/PROD`,
    });
    new HelpdeskAppStack(this, "App", {
      vpc: network.vpc,
      taskRole: iam.taskRole,
      logBucket: crypto.logBucket,
    });
  }
}
  • Crypto Stack:CMK と Log Bucket を最初に作る(他の Stack が参照する基盤)
  • Network Stack:VPC と Bedrock 用 Endpoint(policy 付き)を作る
  • IAM Stack:パターン 1〜4 を組み合わせた taskRole を作る(Guardrail 強制 + Agent 経由のみ + confused deputy 対策込み
  • App Stack:ECS / Lambda は taskRole を assume し、VPC 内で動き、Endpoint 経由で Bedrock を呼ぶ

そして組織レベルで SCP 2 種を OU に attach し、将来的に Cross-Account Safeguards へ移行する余地を残す。これが本章で目指した helpdesk-ai の Production Ready セキュリティ設計の完成形だ。


次章への接続

これで helpdesk-ai は、**機能(ch06–ch10)・安全性(ch11)・観測(ch13)・セキュリティ(ch14)**の 4 つが揃った。Production Ready の壁 1・壁 2 は、大きく崩れた。

最後に残るのが コストだ。セキュリティをここまで重ねた helpdesk-ai を、月いくらで運用できるのか。Provisioned Throughput を買うのか、On-Demand のままでいくのか。Intelligent Prompt Routing と Prompt Caching をどう組み合わせれば、想定の 10 倍コストを避けられるのか。

次章 ch15 で「コストを設計する」を扱い、Part 3「Production Ready に仕上げる」を締めくくる。そして Part 4 では、ここまで設計してきた前提を 実際の金融・医療事例ch16)で検証する。「PrivateLink 未使用」「Guardrails 後付け」「IAM 権限過大」といった本章のアンチパターンは、ch17 で 9 個のアンチパターンとして集約する。


章末まとめ

  • Bedrock の差別化は「データを学習に使わない・モデルプロバイダーに渡さない」というデータ取り扱いポリシーに出る。これが OpenAI 直叩きから移行する最大動機
  • IAM 4 パターン:(1) 特定モデルのみ許可、(2) bedrock:GuardrailIdentifier 条件キーで Guardrail 強制、(3) Agent 経由のみ・直接 InvokeModel 禁止、(4) aws:SourceAccount/aws:SourceArn で confused deputy 対策
  • VPC Endpoint(PrivateLink) で Bedrock 通信を VPC 内に閉じ、Endpoint policy で「承認モデル × 承認 Guardrail」に縛る。Internet Gateway / NAT 不要
  • KMS CMK は Guardrails / KB / Model Invocation Logs / Agent Memory に指定可能。特に Mask モードでも残る input 原文の保護に効く
  • SCP で組織レベルに「承認モデルファミリーのみ」「Guardrail なし呼び出し禁止」を昇格。Cross-Account Safeguards(2026-04 GA) が次の選択肢
  • コンプライアンスは SOC・ISO・CSA STAR・HIPAA Eligible・GDPR・FedRAMP High(GovCloud)。PCI DSS / IRAP は最新の AWS Compliance Programs を参照
  • データレジデンシーは In-Region / Geographic CRIS / Global CRIS の 3 択。CloudWatch・CloudTrail・Model Invocation Logs はすべて source region に書かれる。マルチリージョン設計の詳細は 付録 A
  • 次章は「コストを設計する」── Production Ready の最後のピース