AWS re:Invent2013参加レポート #32 Fine-Grained Access Control for DynamoDB

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Fine-Grained Access Control for DynamoDB

  Fine-Grained Access Control(以下FGAC)、その名の通り、きめの細かいアクセス制御が出来るようになるものです。re:Inventの期間中にFine-Grained Access Control for DynamoDBが発表になりました。
概念的にはそんなに難しそうでは無いですが、つまづきポイントを探るため、実際に動かして見ました。  

FGACの設定をしてみる

テーブルを作る

[SHELL] $ aws dynamodb create-table --table-name fgac-test --attribute-definitions AttributeName=userId,AttributeType=S --key-schema AttributeName=userId,KeyType=HASH --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 [/SHELL] [JAVASCRIPT] { "TableDescription": { "AttributeDefinitions": [ { "AttributeName": "userId", "AttributeType": "S" } ], "ProvisionedThroughput": { "NumberOfDecreasesToday": 0, "WriteCapacityUnits": 1, "ReadCapacityUnits": 1 }, "TableSizeBytes": 0, "TableName": "fgac-test", "TableStatus": "CREATING", "KeySchema": [ { "KeyType": "HASH", "AttributeName": "userId" } ], "ItemCount": 0, "CreationDateTime": 1384544556.415 } } [/JAVASCRIPT]

Policy Generator

 DynamoDBでアクセス権を細かく設定するためのPolicyを作るには、ManagementConsoleにある"Amazon DynamoDB Policy Generator"が便利そうです。
 

スクリーンショット 2013-11-16 17.50.32

 
テーブルを選択して、「AccessControl」のボタンを押すと下のようなダイアログが開きます。
 スクリーンショット 2013-11-16 17.51.33  
 ポチポチと必要なActionを追加したり、後はどのAttributeを返すかを自由に追加する事が出来ます。  

スクリーンショット 2013-11-16 17.51.56  
こんな感じでPolicy のJSONが出来上がるので、policy.jsonとでもして保存しておきます。

Roleの作成

本来であれば、下図のようにRoleを作ってSTSで権限をもらって、というやり方をするのが良いのでしょうが、これで試すと、本題のDynamoDB FGAC以外のボリュームが増えてしまいます。。 fgac-sts なので、コレは別の機会にして、今回はIAMのUserを作って実験してみます。

IAM Userの作成

IAMのユーザを作成します。ユーザ名は"fgactest"とします。 [SHELL] $ aws iam create-user --user-name fgactest [/SHELL]

[JAVASCRIPT] { "User": { "UserName": "fgactest", "Path": "/", "CreateDate": "2013-11-19T04:48:12.771Z", "UserId": "AIDAJZXQ4JTWJM3WMD6RM", "Arn": "arn:aws:iam::038072554641:user/fgactest" } } [/JAVASCRIPT]

Permissionの設定

先ほどのPolicyGeneratorで作ったPolicyだとGoogleでのログインが必要になってしまうので、 dynamodb:LeadingKeysのところをIAM ユーザのusernameでやれるように${aws:username}に書き換えます。
[JAVASCRIPT] "dynamodb:LeadingKeys": [ "${aws:username}" ] [/JAVASCRIPT] [SHELL] $ aws iam put-user-policy --user-name fgactest --policy-name DYNAMODB-USER-FGAC --policy-document file://./policy.json [/SHELL] これはJSONでのレスポンスは帰ってこないようです。get-user-policyで確認します。 [SHELL] $ aws iam get-user-policy --user-name fgactest --policy-name DYNAMODB-USER-FGAC [/SHELL] [JAVASCRIPT] { "UserName": "fgactest", "PolicyName": "DYNAMODB-USER-FGAC", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": [ "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:Query", ], "Resource": [ "arn:aws:dynamodb:ap-northeast-1:000000000000:table/fgac-test" ], "Effect": "Allow", "Condition": { "StringEqualsIfExists": { "dynamodb:Select": "SPECIFIC_ATTRIBUTES", "dynamodb:ReturnValues": [ "NONE", "UPDATED_OLD", "UPDATED_NEW" ] }, "ForAllValues:StringEquals": { "dynamodb:Attributes": [ "userId", "age", "mail" ], "dynamodb:LeadingKeys": [ "${aws:username}" ] } } } ] } } [/JAVASCRIPT] ちゃんと登録されている事が確認出来ました。

AccessKey,SecretKeyの作成

[SHELL] $ aws iam create-access-key --user-name fgactest [/SHELL] [JAVASCRIPT] { "AccessKey": { "UserName": "fgactest", "Status": "Active", "CreateDate": "2013-11-19T04:57:50.692Z", "SecretAccessKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "AccessKeyId": "xXXXXXXXXXXXXXXXXXXXXXXXX" } }

[/JAVASCRIPT]

権限の確認

ここまで出来たところで、AWS CLIで適切に見えるもの見えないものを確かめて行きます。
コンフィグファイルに新しくprofileを追加します。

list-tablesを試す

[SHELL] $ aws --profile fgac dynamodb list-tables A client error (AccessDeniedException) occurred: Unknown [/SHELL] いい感じにはじいてもらえました。

put-itemを試す

[SHELL] $ aws --profile fgac dynamodb put-item --table-name fgac-test --item '{"userId":{"S": "test"}, "age":{"N":"23"}}' A client error (AccessDeniedException) occurred: Unknown [/SHELL]

ユーザ名が"fgactest"なので、HashKey testに対してはput-itemが出来ません。

次はハッシュキーを"fgactest"にして書き込みしてみます。 [SHELL] $ aws --profile fgac dynamodb put-item --table-name fgac-test --item '{"userId":{"S": "fgactest"}, "age":{"N":"23"}}' [/SHELL] 無事に書き込みが出来たようです。

次はageとmailというattributeを増やしてputItemをしてみます。 [SHELL] $ aws --profile fgac dynamodb put-item --table-name fgac-test --item '{"userId":{"S": "fgactest"}, "age":{"N":"23"},"mail":{"S":"fgac@example.com"}}' [/SHELL] これも無事に成功しました。

最後にaddressというattributeも増やしてputItemをしてみます。 [SHELL] $ aws --profile fgac dynamodb put-item --table-name fgac-test --item '{"userId":{"S": "fgactest"}, "age":{"N":"23"},"mail":{"S":"fgac@example.com"},"address":{"S":"addr1"}}' A client error (AccessDeniedException) occurred: Unknown [/SHELL] キチンと失敗しました。 policyで以下のように設定しているので、アクセス出来るのはuserId,age,mailだけになります。 [JAVASCRIPT] "ForAllValues:StringEquals": { "dynamodb:Attributes": [ "userId", "age", "mail" ], "dynamodb:LeadingKeys": [ "${aws:username}" ] } [/JAVASCRIPT] 失敗しましたが、getItemで取得できないでいるかを確認するために、制限されていないprofileで何かしら書き込んでおきましょう。ついでに別のハッシュキーのアイテムも入れておきます。 [SHELL] $ aws dynamodb put-item --table-name fgac-test --item '{"userId":{"S": "fgactest"}, "age":{"N":"23"},"mail":{"S":"fgac@example.com"},"address":{"S":"addr1"}}' $ aws dynamodb put-item --table-name fgac-test --item '{"userId":{"S": "test"}, "age":{"N":"33"},"mail":{"S":"test@example.com"},"address":{"S":"addrtest"}}'

[/SHELL]

今テーブルはこんな感じです。

userId age mail address
fgactest 23 fgac@example.com addr1
test 33 test@example.com addrtest

getItemを試す

先ほどputしたアイテムを取得します。 [SHELL] $ aws --profile fgac dynamodb get-item --table-name fgac-test --key '{"userId":{"S":"fgactest"}}' A client error (AccessDeniedException) occurred: Unknown [/SHELL] なんと失敗しました。
予想外です。

もしや、addressに対してアクセス出来ないから出ているのでは?と思い、 --attribute-to-getを付けて再度挑戦してみます。 [SHELL] $ aws --profile fgac dynamodb get-item --table-name fgac-test --key '{"userId":{"S":"fgactest"}}' --attributes-to-get "userId" "age" [/SHELL] [JAVASCRIPT] { "Item": { "age": { "N": "23" }, "userId": { "S": "fgactest" } } } [/JAVASCRIPT] 今回は取得出来ました。
ということは、アクセス権がないものは自動的にフィルターされて出てこないのではなく、問合せの時に自分で設定しないといけないという事が分かりました。
これはすごく大事です。
帰ってくるAttributeを指定する癖を付けたほうが良いのかなと思わされる出来事でした。

次に失敗するであろう、userId がtestのアイテムに対してやってみます。 [SHELL] $ aws --profile fgac dynamodb get-item --table-name fgac-test --key '{"userId":{"S":"test"}}' --attributes-to-get "userId" "age"A client error (AccessDeniedException) occurred: Unknown [/SHELL] こちらは予想通りの失敗をしてくれています。
キチンと自分のusernameがハッシュキーのアイテムにしかアクセス出来ないことが確認出来ました。

さいごに

今回のFGACのおかげでかなりいい感じにDynamoDBにアクセスコントロールをかける事が出来るようになりました。
実際に試してみてわかったことですが、このFGACで制御されたユーザはScanを使う事が出来ないようです。
PolicyGeneratorになかったので、手で加えてみましたが、やはりダメでした。
フルスキャンしてしまうScanが使えないのは当然かもしません

もし、FGACを使う場合はScanは使わないデータベース設計をする必要がありそうです。
また、今回はひとつのポリシーしか使いませんでしたが、書き込みようと読み込みようとに分けることで、readonlyなattributeを作る事も出来そうです。
get-itemのところで分かりましたが、attributes-to-getで、権限のあるところだけを取ってくるようにしないと、QueryもgetItemも弾かれます。
自動的にフィルタされて出てくると思っていたので、ここは実際に確認してよかったなと思うところです。

このあたりとS3とを組み合わせて、本当にEC2を使わないでWebアプリが作れるようになる日は近い気がします。そうなった時にはBaasとしてかなり破格で堅牢なサービスになるんじゃないでしょうか。楽しみです。