cloudpack の 磯辺(@muramasa64)です。
あるバケットに、別のアカウントから読み書きできるようにバケットポリシーを定義したい。普通に考えると、下記のような感じになるだろう。
{ "Version": "2012-10-17", "Id": "Policy1406782030997", "Statement": [ { "Sid": "Stmt1406782027567", "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListBucket", ], "Effect": "Allow", "Resource": [ "arn:aws:s3:::s3-acl-test", "arn:aws:s3:::s3-acl-test/*" ], "Principal": { "AWS": [ "024665792058" ] } } ] }
このように設定されたバケットに、許可した別アカウントから書き込みをする。
% aws --profile s3test s3api put-object --bucket s3-acl-test --key file --body file { "ETag": ""8a9c538c7f848d97d9d45736c4f709f3"" }
書き込んだオブジェクトを取得してみる。中身はどうでもいいので、/dev/nullに捨てる。
% aws --profile s3test s3api get-object --bucket s3-acl-test --key file /dev/null { "AcceptRanges": "bytes", "ContentType": "binary/octet-stream", "LastModified": "Thu, 31 Jul 2014 04:56:08 GMT", "ContentLength": "14", "ETag": ""8a9c538c7f848d97d9d45736c4f709f3"" }
さて、これをバケットを所有しているアカウントで取得してみよう。
% aws --profile default s3api get-object --bucket s3-acl-test --key file /dev/null A client error (AccessDenied) occurred when calling the GetObject operation: Access Denied
アクセス出来ない。これは、ACLが下記のように、書き込んだユーザのみ権限が付与されているからである。
% aws --profile s3test s3api get-object-acl --bucket s3-acl-test --key file { "Owner": { "DisplayName": "s3test", "ID": "4682c00752c7f406f24e7dda04700a324b2179670ee5ab2a610352226a47dc73" }, "Grants": [ { "Grantee": { "DisplayName": "s3test", "ID": "4682c00752c7f406f24e7dda04700a324b2179670ee5ab2a610352226a47dc73" }, "Permission": "FULL_CONTROL" } ] }
バケットの所有者は、このオブジェクトを誰でも読み取り可能にしたい。バケットポリシーを下記のものに変更する。
{ "Version":"2012-10-17", "Id": "Policy1406782030997", "Statement": [ { "Sid": "Stmt1406782027567", "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListBucket" ], "Effect": "Allow", "Resource": [ "arn:aws:s3:::s3-acl-test", "arn:aws:s3:::s3-acl-test/*" ], "Principal": { "AWS": [ "024665792058" ] } }, { "Sid": "Stmt1406782027568", "Action": [ "s3:GetObject" ], "Effect": "Allow", "Resource": [ "arn:aws:s3:::s3-acl-test", "arn:aws:s3:::s3-acl-test/*" ], "Principal": { "AWS": [ "*" ] } ] }
自分でオブジェクトを作成して、テストしてみる。
% aws --profile default s3api put-object --bucket s3-acl-test --key file2 --body file { "ETag": ""8a9c538c7f848d97d9d45736c4f709f3"" }
curlで取得してみる。
% curl http://s3-acl-test.s3.amazonaws.com/file2 Hello, World.
% curl --head http://s3-acl-test.s3.amazonaws.com/file2 HTTP/1.1 200 OK x-amz-id-2: 8Bb1L6uwe497S7CEHzFTtx4qI2rye2c+ilZoALtDqJ9xzk+LXVvZmcbk7/HB+yy/ x-amz-request-id: 1696D56CCBF01BE5 Date: Thu, 31 Jul 2014 05:08:17 GMT Last-Modified: Thu, 31 Jul 2014 05:06:04 GMT ETag: "8a9c538c7f848d97d9d45736c4f709f3" Accept-Ranges: bytes Content-Type: binary/octet-stream Content-Length: 14 Server: AmazonS3
問題なく取得できる。
他のアカウントが作成したオブジェクトも同様に取得してみる。
% curl http://s3-acl-test.s3.amazonaws.com/fileAccessDenied
Access Denied 8A2F7FE7C668B21C ZMgwnn6fAvDCB3bP8pLIXu7Ar3zQ0jeMfTXP6Wn1fW9y9dKPsgkBaMc8l+esYiQ+
% curl --head http://s3-acl-test.s3.amazonaws.com/file HTTP/1.1 403 Forbidden x-amz-request-id: 7FF68FDDDC40A0A7 x-amz-id-2: 4RN6yGv2TUrEXSwvaTGsaOANf5rEW0j82eOyh4hPdOGuMbRLtZ4+Kuw45BtSzZOckPMVIGUbahBY+8MXv0YM1A== Content-Type: application/xml Transfer-Encoding: chunked Date: Thu, 31 Jul 2014 05:12:23 GMT Server: AmazonS3
エラーになってしまった。これはどういう事だろうか。バケットポリシーが適用されていないように見える。
実は、バケットポリシーは、バケットの所有者とオブジェクトの所有者が同じでないと、ポリシーが適用されないという仕様になっている*1。
ということで、バケットの所有者で改めてオブジェクトを作成しなおしてみる。オブジェクトに権限がなくてもオブジェクトを作成・削除する権限があるので、普通にputすればよい。
% aws --profile default s3api put-object --bucket s3-acl-test --key file --body file { "ETag": ""8a9c538c7f848d97d9d45736c4f709f3"" }
所有者が変更されている。
% aws --profile default s3api get-object-acl --bucket s3-acl-test --key file { "Owner": { "DisplayName": "default", "ID": "2b70304c677e207198b076a1436d3f0fdf8c4cd34a1d6a77c95924268e52dc27" }, "Grants": [ { "Grantee": { "DisplayName": "default", "ID": "2b70304c677e207198b076a1436d3f0fdf8c4cd34a1d6a77c95924268e52dc27" }, "Permission": "FULL_CONTROL" } ] }
この状態であれば、バケットポリシーが適用されるので、匿名ユーザでもGETできるようになる。
% curl http://s3-acl-test.s3.amazonaws.com/file Hello, World.
% curl --head http://s3-acl-test.s3.amazonaws.com/file HTTP/1.1 200 OK x-amz-id-2: /Hy86Fcmqf6t5ICqdycw4GvqI9DX12cPtTfE6OOXYJZ0JRMZPK6WTc2vOU/+Ot88 x-amz-request-id: 3388276F03F90125 Date: Thu, 31 Jul 2014 11:07:17 GMT Last-Modified: Thu, 31 Jul 2014 10:56:18 GMT ETag: "8a9c538c7f848d97d9d45736c4f709f3" Accept-Ranges: bytes Content-Type: binary/octet-stream Content-Length: 14 Server: AmazonS3
問題が起こらないようにするには
ということで、他のアカウントのユーザに自分のバケットを開放する際には、気をつけなければいけないことがわかった。では、誰が書き込んだとしても、匿名ユーザからのアクセスを許可したい場合には、どうすればいいのか。
下記のようなバケットポリシーを設定すればよい。
{ "Version": "2012-10-17", "Id": "Policy1406782030997", "Statement": [ { "Sid": "Stmt1406782027567", "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListBucket" ], "Effect": "Allow", "Resource": [ "arn:aws:s3:::s3-acl-test", "arn:aws:s3:::s3-acl-test/*" ], "Principal": { "AWS": [ "024665792058" ] } }, { "Sid": "Stmt1406782027569", "Effect": "Deny", "Principal": { "AWS": [ "024665792058" ] }, "Action": "s3:PutObject", "Resource": "arn:aws:s3:::s3-acl-test/*", "Condition": { "StringNotEquals": { "s3:x-amz-grant-read": [ "uri=http://acs.amazonaws.com/groups/global/AllUsers" ] } } }, { "Sid": "Stmt1406782027568", "Action": [ "s3:GetObject" ], "Effect": "Allow", "Resource": [ "arn:aws:s3:::s3-acl-test", "arn:aws:s3:::s3-acl-test/*" ], "Principal": { "AWS": [ "*" ] } } ] }
追加したポリシー(真ん中のブロック)は、PutObjectを実行する際には、AllUsersに対して読み込みを許可する権限が付与されていなければ拒否する、というものである*2。つまり、書き込むオブジェクトの権限を強制するもの、ということになる。
それでは、このバケットポリシーが設定されている状態で、先ほどと同じように、オブジェクトを作成してみる。
% aws --profile s3test s3api put-object --bucket s3-acl-test --key file3 A client error (AccessDenied) occurred when calling the PutObject operation: Access Denied
エラーとなった。すべてのユーザーがREADできるようにACLを付与するように変更してみる。
% aws --profile s3test s3api put-object --bucket s3-acl-test --key file3 --body file --grant-read "uri=http://acs.amazonaws.com/groups/global/AllUsers" { "ETag": ""8a9c538c7f848d97d9d45736c4f709f3"" }
今度は書き込めたので、ACLを確認する。
% aws --profile s3test s3api get-object-acl --bucket s3-acl-test --key file3 { "Owner": { "DisplayName": "s3test", "ID": "4682c00752c7f406f24e7dda04700a324b2179670ee5ab2a610352226a47dc73" }, "Grants": [ { "Grantee": { "URI": "http://acs.amazonaws.com/groups/global/AllUsers" }, "Permission": "READ" } ] }
curlでGETしてみる。
% curl http://s3-acl-test.s3.amazonaws.com/file3 Hello, World.
% curl --head http://s3-acl-test.s3.amazonaws.com/file3 HTTP/1.1 200 OK x-amz-id-2: sZY8vgy+haVfV8Rji1w8bePa8kjFkyxms6mMZvu18vHgIStEB4RUcS7Y+stiHrra x-amz-request-id: C58EB280DFE46C5D Date: Thu, 31 Jul 2014 11:45:07 GMT Last-Modified: Thu, 31 Jul 2014 11:44:39 GMT ETag: "8a9c538c7f848d97d9d45736c4f709f3" Accept-Ranges: bytes Content-Type: binary/octet-stream Content-Length: 14 Server: AmazonS3
今度は取得できるようになった。
今回はACLにて権限を設定したが、実際はバケットポリシーで制限をしたい場合の方が多いのではないかと思う。その場合は、バケットとオブジェクトの所有者をあわせなければならない。どうやら、オブジェクトの所有者を変更するには、そのユーザでオブジェクトを作成するしか方法がないようである。従って、次のようにすれば良い。
- バケットの所有者にフルコントロールを与える権限を付与しないとPUTできないようにする(前述のバケットポリシーの応用)
- バケットの所有者が、書き込まれたオブジェクトを同じ場所にコピーする(コピー元とコピー先を同じにする)。
こうすることで、バケットの所有者とオブジェクトの所有者を同じにする事ができる。書き込まれたオブジェクト毎に実行しなければならないので、書き込まれたことの通知を受けて処理するようにする必要があるなど、実現するにはハードルが高いように思える。そもそも同じアカウントで書き込むのが手っ取り早いだろう。
これを知らなかったせいで結構ハマったのであった…
AWSのドキュメントを参考にした。
http://docs.aws.amazon.com/AmazonS3/latest/dev/AccessPolicyLanguage_UseCases_s3_a.html
元記事は、こちらです。