※3/25 17:00 内容を一部修正しました
今回のお題
Amazon DynamoDB(以下「DynamoDB」)が、リソースポリシーでのアクセス制御に対応したので、試してみた
本題
3/21(日本時間)に、DynamoDBにリソースポリシー機能が追加されたことがAWS公式より発表されました。
これにより、DynamoDBもS3やAPI Gatewayと同様に、リソースポリシーによりアクセス制御(Allow, Deny)を行うことが可能になりました。
そこで、このリソースポリシーの挙動を試してみました。
参考リンク
※「ポリシーの評価論理」については、IAMポリシーによるアクセス制御を理解する上でバイブルともいうべき資料なので、ぜひ一読することをお勧めします。(特に「アカウント内でのリクエストの許可または拒否の決定」項目内のフローチャート図)
※このフローチャートの日本語訳が見たいという方は、下記の方が日本語に訳して下さっているので、こちらを参照してください。
前提
今回は「LambdaからDynamoDBテーブルのScan(=全データ取得)を実施する」という方法でアクセスを検証します。
また、以下2つのLambdaを作成します。(ソースは下記参照)
- allowFunction:アクセス可能なことを確認するLambda
- denyFunction:アクセス不可能なことを確認するLambda
なお、DynamoDBのリソースポリシーはまだCloudFormationに対応していないので、今回はマネジメントコンソールから直接入力します。(もちろんAWS CDKも未対応)
【3/25 17:00修正】CloudFormationに対応済です。(表示言語を「English」にしないと表示されないので、勘違いしてました)
AWS::DynamoDB::Table ResourcePolicy - AWS CloudFormation
なおAWS CDKは、3/25 17:00時点で未対応です。
import { Context, APIGatewayEvent, APIGatewayProxyResult } from 'aws-lambda'; import { DynamoDBClient, DynamoDBClientConfig, GetItemCommand, GetItemCommandInput, GetItemCommandOutput } from '@aws-sdk/client-dynamodb'; import { DynamoDBDocumentClient, ScanCommand, ScanCommandInput, ScanCommandOutput } from '@aws-sdk/lib-dynamodb' const handler = async (event: APIGatewayEvent, context: Context): Promise<APIGatewayProxyResult> => { const config: DynamoDBClientConfig = {}; const client = new DynamoDBClient(config); const dcClient = DynamoDBDocumentClient.from(client); const param: ScanCommandInput = { TableName : <テーブル名>, }; const command = new ScanCommand(param); const data = await dcClient.send(command); console.info(JSON.stringify(data.Items)); return; };
検証1:Lambdaにアクセス許可がない場合
まずはLambdaにDynamoDBへのアクセス許可がない場合*1の検証です。
allowFunctionおよびdenyFunction共に、DynamoDBのアクセス許可はない状態です。(CloudWatch Logsのみ) *2
リソースポリシー未設定の場合
まず、DynamoDBのリソースポリシーが未設定の状態(=初期状態)でLambdaを実行します。
この場合、ポリシーが一切存在しない状態なので、結果「暗黙のDeny」が適用され、allowFunctionおよびdenyFunction共に「AccessDeniedException」が発生します。(これは従来通り)
リソースポリシーを設定する
次にリソースポリシーを設定します。
マネジメントコンソールの「テーブルについてのリソースベースのポリシー」-「テーブルポリシーを作成」から、以下の「allowFunctionのアクセスを許可する」リソースポリシーを作成します。(Principalは「Lambda関数のARN」ではないので注意です)
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowStatement", "Effect": "Allow", "Principal": { "AWS": "<allowFunctionのIAMロールのARN>" }, "Action": "dynamodb:Scan", "Resource": "<DynamoDBテーブルのARN>" } ] }
上記リソースベースポリシーを設定後、再度Lambda関数を実行すると、allowFunctionは正常終了し、ちゃんとテーブルの全データが取得できます。
もちろん、denyFunctionは引き続きAccessDeniedExceptionが発生します。
このことから、リソースポリシーがちゃんと機能してそうです。
検証2:Lambdaにアクセス許可がある場合
次にLambdaにDynamoDBへのアクセス許可がある場合の検証です。
allowFunctionおよびdenyFunctionに、AWS管理ポリシーの「AmazonDynamoDBFullAccess」を一時的に付与します。*3
リソースポリシー未設定の場合
まず、DynamoDBのリソースポリシーが未設定の状態(=初期状態)でLambdaを実行します。(先程のポリシーが残っている場合、一度ポリシーを削除してください)
この場合、Lambda関数の「AmazonDynamoDBFullAccess」ポリシーが適用され、allowFunctionおよびdenyFunction共に正常終了し、テーブルの全データが取得できます。(これも従来通り)
リソースポリシーを設定する
次にリソースポリシーを設定します。
先程の同様の手順で、以下の「denyFunctionのアクセスを拒否する」リソースポリシーを作成します。(今回Principalに設定するロールのARNは「denyFunction」のものです。allowFunctionではありません)
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowStatement", "Effect": "Deny", "Principal": { "AWS": "<denyFunctionのIAMロールのARN>" }, "Action": "dynamodb:Scan", "Resource": "<DynamoDBテーブルのARN>" } ] }
上記リソースベースポリシーを設定後、再度Lambda関数を実行すると、denyFunctionのみAccessDeniedExceptionが発生します。
そしてallowFunctionは引き続き正常終了し、テーブルの全データが取得できます。
このことから、Lambdaにアクセス許可がある場合もリソースポリシーがちゃんと機能してそうです。
まとめ
以上、DynamoDBのリソースポリシーの検証結果でした。
S3やAPI Gateway同様、DynamoDBにもリソースポリシーを付与できるようになり、よりアクセス制御がやりやすくなりました。
また、ポリシーを二重に付けることで、ポリシー設定ミスによる思わぬ事故の予防策になるので、有効に活用したいですね。*4
ちなみに、今回は簡単な検証のみでしたが、クラスメソッドののんピ氏がかなり突っ込んだ部分まで調査したブログを書いてますので、もし詳しく知りたい方はそちらもご参照ください。
それでは、今回はこの辺で。