この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
Amazon S3のアクセスコントロールがわけわからない
いくつもあってわけがわかりませんね。それぞれ何が出来て何ができないのか、どうゆう時にどれを使うのか、組み合わせて使うとどうなるのか、いい機会なので整理してみたいと思います。
ACL
これはおそらくいちばん古くからある機能です。
ManagementConsoleではPermissionsというところで設定ができます。
ACLの設定はバケット、もしくはオブジェクト単位で設定する事が出来るのが特徴になっています。
APIを見てみても、putBucketACLとputObjectACLとあることから、Bucket単位でのACLとObject単位でのACLがあることが分かります。
別のアカウントにまたがって設定することも出来ます。
BucketPolicy
S3のBucketに対して、PolicyDocumentを使ってアクセスコントロールを設定出来ます。
{
"Id": "bucketpolicy-${oacl}-${acl}-${EFFECT}",
"Statement": [
{
"Sid": "Stmt${UNIQ}-1",
"Action": [
"s3:GetObject"
],
"Effect": "${EFFECT}",
"Resource": "arn:aws:s3:::${BUCKET}/*",
"Principal": {
"AWS": [
"${TARGET_PRINCIPAL}"
]
}
},
{
"Sid": "Stmt${UNIQ}-2",
"Action": [
"s3:ListBucket"
],
"Effect": "${EFFECT}",
"Resource": "arn:aws:s3:::${BUCKET}",
"Principal": {
"AWS": [
"${TARGET_PRINCIPAL}"
]
}
}
]
}
こんな感じで、Action, Effect, Principal, Resourceと、これらを組み合わせて柔軟なアクセスコントロールが実現出来ます。
この一個のファイルだけを公開したいとかそういった用途に使うのには向いていないように思います。ObjectACLを使いましょう。
個人的な感覚ですが、bucketACLはBucketPolicyに置き換えたほうが良いなと思っています。
ACLとBucketPolicyどちらが強いのか
気になってしまったので、ACLとBucketPolicyどちらが強いのかを検証したいと思います。
検証の方法は簡単です。
ACLは許可するか、何もしないかの2値です。BucketPolicyは許可する、拒絶する、何もしないの3値です。
ACLはBucketとObjectがありますので、この3つの設定を全通りの組み合わせでやってみます。
BucketACL | ObjcetACL | BucketPolicy | 結果 |
---|---|---|---|
許可 | 許可 | 拒否 | 失敗 |
許可 | 未設定 | 拒否 | 失敗 |
許可 | 許可 | 許可 | 成功 |
許可 | 未設定 | 許可 | 成功 |
許可 | 許可 | 未設定 | 成功 |
許可 | 未設定 | 未設定 | 失敗 |
未設定 | 許可 | 拒否 | 失敗 |
未設定 | 未設定 | 拒否 | 失敗 |
未設定 | 許可 | 許可 | 成功 |
未設定 | 未設定 | 許可 | 成功 |
未設定 | 許可 | 未設定 | 成功 |
未設定 | 未設定 | 未設定 | 失敗 |
この結果からもわかるように、[Bucket|Object]ACLよりもBucketPolicyの方が強くなります。
BucketPolicyで設定されていない場合は、ObjcetACL , BucketACLによって制御されています。
これについては、公式にドキュメントでここやここにも書いて有ります。
さらにBucketPolicyではIPアドレスで制御やRefererで制御も出来ます。
直リン禁止とか出来たりします。
IAM(IAMRole)
IAMではPolicyDocumentを使用してアクセスをコントロールします。
IAMとBucketPolicyの違いはユーザ(クライアント)側に紐付いて権限が与えられるということです。
また、現在はPolicyに変数を利用する事が出来るため、ユーザ名に応じたアクセスのコントロールが可能になっています。
例えば、こんな感じです。
policyDocumentに
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Effect": "Allow",
"Resource": ["arn:aws:s3:::BUCKET/${aws:username}/*"]
}
]
}
と記述されていたら、
IAMuser名: akeri の時には s3://BUCKET/akeri/ で始まるオブジェクトにアクセス出来る事になります。
BucketPolicy同様にconditionによって、条件を指定する事も出来ます。
IPアドレスで制御したり、
@see AWS Identity and Access Management
で、IAMとBucketPolicyはどっちが強いの?
先ほどACLとBucketPolicyを試してみたら、BucketPolicyが強かったのですが、今度はIAMとBucketPolicyどっちが強いのかを試してみたいと思います。
さっきと同様に総当りで調べてみます。
今回ACLは何も設定していない状態です。
BucketPolicy | IAM | 結果 |
---|---|---|
拒否 | 拒否 | 失敗 |
拒否 | 許可 | 失敗 |
許可 | 拒否 | 失敗 |
許可 | 許可 | 成功 |
未設定 | 拒否 | 失敗 |
未設定 | 許可 | 成功 |
結果はこんな感じです。
とりあえず、IAMで許可されていないと確実に失敗します。
当たり前といえば当たり前な気がします。
しかし、IAMで許可されていたとしてもBucketPolicyで拒否されているとアクセス出来ませんでした。
とりあえず、ここから わかることは拒否は絶対ということです。
IAMで拒否されていてもBucketPolicyで拒否されていても、失敗します。
成功するのは、IAMで許可されているかつBucketPolicyで拒否されていない(Allowもしくは未設定)の場合でした。
さっきのACLとBucketPolicyではわかりませんでしたが、BucketPolicyが優先されているわけではなくて、Denyが優先されているようです。
まとめ
上記の結果から、S3のACLに関するルールがなんとなく見えてきました。
ACLの基本として、どこかしらでDenyとなっていたら、ほかでAllowだろうとDenyになってしまいます。
Denyに対して、Allowで上書きは出来ません。
それとは逆にBucketPolicyやACLでAllowを設定していても、IAMでDenyであった場合は当然アクセス出来ません。
ここから考えるベストプラクティスというのはパットは出てきませんが、ACLはあるオブジェクト一つだけを公開したい時や、publicにする時に使う以外では使わないのかもしれません。
まとめるとタイトルで言ったものの、あまりまとめられてる感じがしないですね。ごめんなさい。
バケットとして不変的なアクセスコントロールはBucketPolicyで定義し、IAMはユーザ毎に変更になるようなものを設定するのが良いのではと思います。この時にBucketPolicyでDenyを付けてしまうと、IAMでいくら設定してもDenyなので、今の段階で私の考えられるベターな方法は、まずは必要なところだけBucketPolicyで公開します。その公開している範囲ないで更に限定的に拒否したいところにはDenyで上書いてあげる方法が良さそうです。
さらにIAMでは変数が使えるので、グループのPolicyで一括設定もできるので、複数のユーザがいて、あるルールに基づいてアクセスをコントロールしたい場合にはIAMを使いたいですね。
用途をまとめるとこんな感じかなと思います。◯はいいじゃんで、☓は出来ない。△はやってやれないことは無いけど、めんどくさすぎてやりたくない位なゆるい判断基準で付けてます。
ユースケース | [Bucket|Object]ACL | BucketPolicy | IAM |
---|---|---|---|
一個のObjectを公開したい | ◯ | △ | △ |
バケット全体を公開したい | △ | ◯ | ◯ |
特定のユーザにだけ公開したい | △ | △ | ◯ |
IPアドレス制限をかけたい | ☓ | ◯ | ◯ |
Bucketの中のPrefixを指定して公開したい | △ | ◯ | ◯ |
prefixがユーザ毎に可変 | △ | △ | ◯ |
おまけ
今回調査に使ったシェルスクリプト載せておきます。
権限毎にバケット何個も作って、オブジェクトも作って、アクセスしてみて、終わったら削除してという感じのものです。
#!/bin/bash
UNIQ=$(date +'%Y%m%d%I%M%S')
alias aws='aws --region ap-northeast-1'
BUCKET_PREFIX=akeri-acl-test-$UNIQ-
TARGET_PRINCIPAL="arn:aws:iam::123456789012:user/s3acltest"
TARGET_MAIL="example@example.com"
# create Bucket Policy Document
UPLOADFILE=akeridayo.txt
IAM_USERNAME=s3acltest
echo "akeridayo" > $UPLOADFILE
echo "created upload file"
echo "<table>" > result.html
# bucket create
# 0 deny
# 1 allow
# 2 none
for acl in `seq 2 2`
do
for policy in `seq 0 2`
do
BUCKET=${BUCKET_PREFIX}${acl}${policy}
# create bucket
aws s3api create-bucket --bucket $BUCKET
if [ $acl -eq 1 ];
then
aws s3api put-bucket-acl --bucket $BUCKET --grant-read emailaddress="$TARGET_MAIL"
echo "putted bucket acl"
fi
echo "created bucket $BUCKET"
# set Bucket Policy
if [ $policy -ne 2 ];
then
EFFECT="Allow"
if [ $policy -eq 0 ];
then
EFFECT="Deny"
fi
#========================================= policy.json
cat << _EOT_ > bucketPolicy-${UNIQ}-${acl}-${EFFECT}.json
{
"Id": "bucketpolicy-${oacl}-${acl}-${EFFECT}", "Statement": [ {
"Sid": "Stmt${UNIQ}-1",
"Action": ["s3:GetObject"],
"Effect": "${EFFECT}",
"Resource": "arn:aws:s3:::${BUCKET}/*",
"Principal": {"AWS": ["${TARGET_PRINCIPAL}" ]}},
{
"Sid": "Stmt${UNIQ}-2",
"Action": ["s3:ListBucket"],
"Effect": "${EFFECT}",
"Resource": "arn:aws:s3:::${BUCKET}",
"Principal": {"AWS": ["${TARGET_PRINCIPAL}"]}
}
]
}
_EOT_
#==========================================================
# set Bucket ACL Allow
aws s3api put-bucket-policy --bucket $BUCKET --policy file://$(pwd)/bucketPolicy-${UNIQ}-${acl}-${EFFECT}.json
echo "putted bucket policy"
rm bucketPolicy-${UNIQ}-${acl}-${EFFECT}.json
fi
# set Bucket Policy END
#___Object ACL Loop Start
for oacl in `seq 2 2`
do
key=${acl}${oacl}
aws s3api put-object --bucket $BUCKET --key $key --body $UPLOADFILE
echo "file uploaded "
if [ $oacl -eq 1 ];
then
aws s3api put-object-acl --bucket $BUCKET --key $key --grant-read emailaddress="$TARGET_MAIL",emailaddress="magcot.com@gmail.com"
echo "putted object acl "
fi
aws s3api get-object --profile iamuser --bucket $BUCKET --key $key /dev/null
if [ $? -eq 0 ];
then
resultOther="成功"
else
resultOther="失敗"
fi
echo "getted Object other account $resultOther"
for iam in `seq 0 1`
do
IAMEFFECT="Allow"
if [ $iam -eq 0 ];
then
IAMEFFECT="Deny"
fi
POLICYNAME=POLICY-${UNIQ}-${acl}-${oacl}-${policy}-${iam}
POLICYFILE=file://$(pwd)/$POLICYNAME.json
cat << _EOT_ > $POLICYNAME.json
{"Statement": [
{
"Sid": "Stmt${UNIQ}2",
"Action": ["s3:GetObject"],
"Effect": "${IAMEFFECT}",
"Resource": "arn:aws:s3:::${BUCKET}/*"
}]}
_EOT_
echo "created policy document"
aws iam put-user-policy --policy-name $POLICYNAME --user-name $IAM_USERNAME --policy-document $POLICYFILE
if [ $? -eq 0 ];
then
echo "put user policy OK"
fi
echo "putted user policy"
rm $POLICYNAME.json
sleep 5
aws s3api get-object --profile iamuser --bucket $BUCKET --key $key out
if [ $? -eq 0 ];
then
resultIAM="成功"
else
resultIAM="失敗"
fi
echo "get object IAM User $resultIAM"
aws iam delete-user-policy --policy-name $POLICYNAME --user-name $IAM_USERNAME
#output html
cat << _EOT_ >> result.html
<tr>
<td>${acl}</td>
<td>${oacl}</td>
<td>${policy}</td>
<td>${resultOther}</td>
<td>${iam}</td>
<td>${resultIAM}</td>
</tr>
_EOT_
done
aws s3api delete-object --bucket $BUCKET --key $key
done
aws s3api delete-bucket --bucket $BUCKET
done
done
echo "</table>" >> result.html
cat result.html |perl -pe "s/>1</>許可</g" |perl -pe "s/>2</>未設定</g" |perl -pe "s/>0</>拒否</g" > index.html
rm result.html