複数の否定条件を使ったS3バケットポリシーを正しく理解してますか?
S3 へのアクセス元を限定する場合に、複数の否定条件を使った Deny でバケットポリシーを書くことがありますが、これは AND 条件なの? OR 条件なの? と混乱することがあるので、わかりやすく図解で考えてみました。
想定環境
今回、想定する環境は下図のとおりです。前提条件として「同一アカウントの IAM ポリシーで、S3へのアクセス権限は与えられている」(明示的な Allow は不要)とします。
- "192.228.xx.xx/32" はインターネット経由のアクセス
- "10.0.0.0/24" は、VPC エンドポイント経由のアクセス
バケットポリシーのおさらい
バケットポリシーについては、弊社 北野の記事がとても参考になります。
押さえておきたいのは以下の図です。
つまり複数条件を書く場合、以下のいずれかになります。
- "Effect" ブロックを分ける場合は OR 条件
- "Effect" ブロック内で "Condition" または "Key" を分ける場合は AND 条件
問題
それでは先の環境において、以下のようなバケットポリシーがあるとします。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Deny", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::${S3_BUCKET_NAME}/*", "Condition": { "NotIpAddress": { "aws:SourceIp": [ "10.0.0.0/24", "192.228.xx.xx/32" ] }, "StringNotEquals": { "aws:sourceVpc": [ "vpc-xxxxxxxxxxxxxxxxx" ] } } } ] }
Effect ブロック内の Key で Condition を分けているので、このバケットポリシーは AND 条件です。「特定 IP以外」かつ「特定VPC以外」はアクセスできません。
この場合、アクセス可能な範囲を表している図は (A),(B) のどちらになるでしょうか?(赤い部分がアクセス可能範囲とします)
正解は・・・
このバケットポリシーの場合、AND 条件ということからパッと見で(B)と勘違いしそうですよね。でも、OR を表す(A)が正解です。 ここが複数の否定条件のややこしいところかと思います。
それでは間違って (B) を選んだ方は図を見ながら理解を深めていきましょう。
図解
まず、それぞれの条件を図で確認しましょう。1つ目は「特定 IP 以外を Deny」になりますので、青色で塗られた部分がブロックされる範囲となります。
2つ目は「特定 VPC 以外を Deny」になりますので、黄色で塗られば部分がブロックされる範囲となります。
そして今回はこれらの AND 条件です。AND とは、すなわち共通部分です。「青色」「黄色」が共通している箇所である緑色で塗られた部分が今回のバケットポリシーでブロックされる範囲となります。
緑色ではない部分(白塗り部分)は拒否されないわけですから、アクセス可能な範囲であり先の回答として OR を表す (A) が正しいことになります。
言い換えると
- 『"特定 IP アドレス以外" かつ "特定 VPC 以外"からのアクセスを拒否』 は、
- 『"特定 IP アドレス" または "特定 VPC"からはアクセス可能』
と言い換えることができることが出来ます。
中学・高校あたりの数学を真面目にされていた方はピンと来ているかと思いますが、これは「ド・モルガンの法則」です。(私は真面目に勉強してなかったので、同僚から「ド・モルガンの法則だよ」と言われて、「・・・は?」となりました)
特定 IP アドレス かつ 特定 VPC のみをアクセスさせたい場合
先の問題では特定 IP アドレス、特定 VPC のいずれかであればアクセスできるポリシーでしたが、特定 IP アドレスかつ特定 VPCのみをアクセスさせたい場合、どのようにすれば良いでしょうか?
ド・モルガンの法則にならうと、以下のような解釈ができますね。
- 『"特定 IP アドレス" かつ "特定 VPC"からはアクセス可能』 は、
- 『"特定 IP アドレス以外" または "特定 VPC 以外"からのアクセスを拒否』
と言い換えることが出来ますので、OR 条件を使って制限することが可能です。バケットポリシーに落とし込むと以下のようになります。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Deny", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::${S3_BUCKET_NAME}/*", "Condition": { "NotIpAddress": { "aws:VpcSourceIp": [ "10.0.0.0/24" ] } } }, { "Effect": "Deny", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::${S3_BUCKET_NAME}/*", "Condition": { "StringNotEquals": { "aws:sourceVpc": [ "vpc-xxxxxxxxxxxxxxxxx" ] } } } ] }
これで複数の否定条件もバッチリですね!
余談: SourceIp と VpcSourceIp
前のバケットポリシー(AND条件)では aws:SourceIp
で VPC エンドポイントからアクセスする特定プライベートアドレスを指定しているかのように見せていますが、これは(A)(B)選択で悩ませるように入れたフェイクです。実質的には意味がありません。というのも、VPC エンドポイントからのアクセスに対して、aws:SourceIp
は使えないからです。
VPC エンドポイントを介した Amazon S3 へのリクエストに、バケットポリシーの aws:SourceIp 条件を使用することはできません。条件が、指定した IP アドレスまたは IP アドレス範囲のいずれにも一致しない場合、Amazon S3 バケットに対しリクエストを作成するときに望ましくない影響が生じる可能性があります。
(引用: Amazon S3 におけるエンドポイント)
VPC エンドポイント経由での特定 IP アドレスを指定する場合は aws:VpcSourceIp
を使えば可能ですので、間違えないようにしましょう。
さいごに
みなさんは(A)と(B)の選択に正解できたでしょうか?
もし間違った方が居られましたら、自身の環境のバケットポリシーを見直してみましょう!「特定IP かつ 特定 VPC」のみにアクセスを限定させたいはずなのに、「特定IP または 特定 VPC」でアクセス可能になっているかもしれませんね。(今回、IP と VPC を対象とした条件でしたが、その他の条件についても同じです)
以上!大阪オフィスの丸毛(@marumo1981)でした!