0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【AWS CDK】Bedrock Knowledge Base + OpenSearch Serverless でハマった7つの落とし穴

0
Posted at

はじめに

Amazon Bedrock Knowledge Base と OpenSearch Serverless を CDK (TypeScript) で構築したとき、正直かなり詰まりました。

公式ドキュメントを読んでも「なんかエラーが出る」「IAM を直したはずなのに動かない」という状態が続きます。原因の多くは CDK 特有の落とし穴や、OpenSearch Serverless 固有の仕様で、コンソール操作ベースの記事では拾えない情報です。

この記事では、実装を通じて実際に踏んだ7つのハマりポイントを、解決コード付きでまとめます。

構築全体の解説は以下記事を参照してください。

①: JSON.stringify は CFn トークンを壊す

DataAccessPolicy の policy に IAM ロール ARN を埋め込む際、JSON.stringify を使うと CDK の CFn トークンが解決されません。

// NG: JSON.stringify が CDK トークンを文字列化してしまう
policy: JSON.stringify([{
  Principal: [props.knowledgeBaseRole.roleArn],
  // → "${Token[TOKEN.123]}" という文字列になる
}])

CloudFormation に渡る JSON がこうなります:

"Principal": ["${Token[TOKEN.123]}"]

OpenSearch Serverless はこのトークン文字列を ARN として認識できないため 403 になります。

CDK 公式が推奨する方法は stack.toJsonString() です。トークンを含むオブジェクトを JSON 文字列化する際に使います。

// OK: stack.toJsonString() はトークンを正しく処理する
const dataAccessPolicy = Stack.of(this).toJsonString([{
  Principal: [props.knowledgeBaseRole.roleArn, indexCreatorRole.roleArn],
}]);

cdk.Fn.sub でも回避できます(プレースホルダーを明示的に置換する方法):

// OK: CFn デプロイ時に ARN が解決される
const dataAccessPolicy = cdk.Fn.sub(
  JSON.stringify([{
    Principal: ['${KnowledgeBaseRoleArn}', '${IndexCreatorRoleArn}'],
  }]),
  {
    KnowledgeBaseRoleArn: props.knowledgeBaseRole.roleArn,
    IndexCreatorRoleArn: indexCreatorRole.roleArn,
  }
);

②: OpenSearch Serverless のアクセス制御は2層構造

OpenSearch Serverless のアクセス制御は IAM ポリシーと DataAccessPolicy の 両方が必要 です。片方だけでは 403 になります。

Being granted permissions within a data access policy is not sufficient to access data in your OpenSearch Serverless collection. An associated principal must also be granted access to the IAM permissions aoss:APIAccessAll and aoss:DashboardsAccessAll.

参照: Data access control for Amazon OpenSearch Serverless

レイヤー 役割 設定場所
IAM ポリシー コントロールプレーン API へのアクセス制御 IAM ロールのインラインポリシー
DataAccessPolicy インデックス操作(データプレーン)へのアクセス制御 CfnAccessPolicy

なお aoss:APIAccessAll のリソースにはコレクション ARN を指定できます(* でも可)。

参照: Identity and Access Management for Amazon OpenSearch Serverless


③: Custom Resource が DataAccessPolicy より先に実行される

CloudFormation はリソースを並列で作成します。IndexCreator(Custom Resource)と DataAccessPolicy の間に依存関係がないと、DataAccessPolicy の作成完了前に Lambda が実行されて 403 になります。

const indexCreator = new cdk.CustomResource(this, 'IndexCreator', {
  serviceToken: provider.serviceToken,
});

// DataAccessPolicy 作成完了後に実行されるよう依存関係を明示する
indexCreator.node.addDependency(cfnDataAccessPolicy);

④: botocore.SigV4Auth + urllib は IAM ロールで 403 になる

Custom Resource Lambda でインデックスを作成する際、botocore.SigV4Auth + urllib の組み合わせでは IAM ロールの AssumedRole セッションが 403 で弾かれます。

切り分けの結果:

実行者 ライブラリ 結果
IAM ユーザー requests-aws4auth ✅ 200
IAM ロール(AssumedRole) botocore.SigV4Auth + urllib ❌ 403
IAM ロール(AssumedRole) requests-aws4auth ✅ 200

requests + requests-aws4auth を Lambda Layer としてバンドルして解決しました。

# NG
from botocore.auth import SigV4Auth
# → IAM ロールの AssumedRole セッションで 403

# OK
from requests_aws4auth import AWS4Auth
creds = session.get_credentials()
auth = AWS4Auth(creds.access_key, creds.secret_key, region, 'aoss',
                session_token=creds.token)
resp = requests.put(url, json=body, auth=auth)

CDK 側では Lambda Layer を追加します:

const requestsLayer = new lambda.LayerVersion(this, 'RequestsLayer', {
  code: lambda.Code.fromAsset(path.join(__dirname, '../lambda-layer')),
  compatibleRuntimes: [lambda.Runtime.PYTHON_3_12],
});

Layer のセットアップ(初回のみ):

mkdir -p cdk/lambda-layer/python
pip install requests requests-aws4auth -t cdk/lambda-layer/python

⑤: ロール名を固定しないとスタック再作成で ARN が変わる

CDK はロール名を指定しない場合、StackName-LogicalId-RandomSuffix 形式で自動生成します。スタックを削除して再作成するたびにサフィックスが変わるため、DataAccessPolicy に登録済みの ARN と実際のロール ARN が一致しなくなります。

const indexCreatorRole = new iam.Role(this, 'IndexCreatorRole', {
  roleName: 'rag-index-creator-role', // 固定する
  // ...
});

⑥: bedrock:RetrieveAndGenerate は KB ARN にスコープできない

bedrock:RetrieveAndGenerateresource: '*' 固定 にする必要があります。

// RetrieveAndGenerate は resource: '*' 固定(KB ARN にスコープ不可)
new iam.PolicyStatement({
  actions: ['bedrock:RetrieveAndGenerate'],
  resources: ['*'],
});

// Retrieve は KB ARN にスコープ可能
new iam.PolicyStatement({
  actions: ['bedrock:Retrieve'],
  resources: [knowledgeBaseArn],
});

// InvokeModel はモデル ARN にスコープ可能
new iam.PolicyStatement({
  actions: ['bedrock:InvokeModel'],
  resources: ['arn:aws:bedrock:ap-northeast-1::foundation-model/amazon.nova-lite-v1:0'],
});

bedrock:RetrieveAndGenerate だけリソース指定のルールが違います。ここを knowledgeBaseArn にしてしまうと AccessDenied が発生します。

Service Authorization Reference の Actions テーブルで RetrieveAndGenerate の Resource types 列が空欄になっており、リソースレベルの権限をサポートしていないことが確認できます。

参照: Actions defined by Amazon Bedrock

⑦: クロスリージョン推論プロファイルは使わない

us.amazon.nova-micro-v1:0 のような us. プレフィックスのモデル ID を使うと、2つの問題が起きます。

  • データが別リージョンに飛ぶ(データレジデンシーの問題)
  • IAM ポリシーが3ステートメント必要になり複雑(推論プロファイル ARN・リージョン FM ARN・グローバル FM ARN の3つを個別に Allow する必要がある)

特に2点目は気づきにくいです。クロスリージョン推論プロファイルには arn:aws:bedrock:REGION:ACCOUNT:inference-profile/global.MODEL-NAME という ARN が存在しますが、それに加えてリージョン FM ARN とグローバル FM ARN の計3つの IAM ステートメントが必要です。

参照: IAM policy requirements for global cross-Region inference

ap-northeast-1 でシングルリージョン固定できるモデルを使うのが正解です。学習・検証用途であれば Nova Lite がおすすめです。

// parameter.ts で一元管理
export const parameter = {
  models: {
    embedding: "arn:aws:bedrock:ap-northeast-1::foundation-model/amazon.titan-embed-text-v2:0",
    generation: "arn:aws:bedrock:ap-northeast-1::foundation-model/amazon.nova-lite-v1:0",
  },
};

まとめ

CDK × Bedrock KB + OpenSearch Serverless 実装で踏みやすい落とし穴は次の7つです。

  1. JSON.stringify に CDK トークンを渡さない。動的な値を JSON 文字列に埋め込む場合は stack.toJsonString() または cdk.Fn.sub を使う
  2. OpenSearch Serverless のアクセス制御は IAM ポリシーと DataAccessPolicy の 2層が両方必要
  3. Custom Resource の実行順序は addDependency で明示的に制御する
  4. OpenSearch Serverless への署名は requests-aws4auth を使う(botocore.SigV4Auth + urllib は IAM ロールで動作しない)
  5. DataAccessPolicy の Principal に使うロールは roleName を固定する
  6. bedrock:RetrieveAndGenerate の IAM リソースは '*' 固定
  7. クロスリージョン推論プロファイルは使わず、ap-northeast-1 シングルリージョンのモデルを使う

どれも「ドキュメントに書いてあるけど見落としやすい」類のミスで、再現性のあるパターンです。

同じところで詰まっている方の参考になれば嬉しいです。

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?