S3の権限まわりの話

記事タイトルとURLをコピーする

ディベロップメントサービス1課の三村です。

AWS の権限まわりの設定が複雑だなと思ったことはないでしょうか。 今回は Amazon S3(以下、S3)へのアクセス許可や制限についてまとめてみました。

S3 のアクセス制御について

まず、S3 へのアクセスを制御するためには大きく分けて2つの方法があります。

  • アイデンティティベースのポリシーによる制御
    • S3 へアクセスする側(IAM ユーザーや Lambda などのサービス)に権限の設定をする
  • リソースベースのポリシーによる制御
    • S3 のバケットポリシーにより、S3 側からアクセスの許可・拒否の設定をする

docs.aws.amazon.com

どちらも各 IAM ユーザーやサービスロールなどに対して、リソース単位で実行可能なアクションを定義し制御することが可能です。

基本的にはユーザーや実行ロールに対してアクセス許可を設定しますが、別アカウントからのアクセスを許可したい場合や、同一アカウント内でも特定のユーザーやロールにのみアクセスを許可したい場合はバケットポリシーによる制御も有効です。

S3 のアクセスコントロールリスト(ACL)については AWS の公式ドキュメントにて記載がある通り、あまり使用されるケースがないため今回は触れません。

Amazon S3 の最新のユースケースの大部分では ACL を使用する必要がなくなっています。オブジェクトごとに個別に制御する必要がある通常ではない状況を除き、ACL は無効にしておくことをお勧めします。

アクセスコントロールリスト (ACL) の概要 - Amazon Simple Storage Service

アイデンティティベースのポリシーによる制御

AWS Lambda(以下、Lambda)を使用し、IAM ロールのポリシーにそれぞれ明示的に許可、明示的に拒否、記述しないパターンを検証します。

  • S3 バケット名
    • bucket-policy-access-test-swx
  • ブロックパブリックアクセス
    • ON(パブリックアクセスを許可しない)
  • バケットポリシー
    • 何も設定しない

Lambda 関数にはバケット内のオブジェクトの一覧を取得し返却するよう記述します。

import boto3

def lambda_handler(event, context):
    s3 = boto3.client('s3')
    bucket_name = 'bucket-policy-access-test-swx'

    response = s3.list_objects_v2(Bucket=bucket_name)
    file_names = []
    if 'Contents' in response:
        for obj in response['Contents']:
            file_names.append(obj['Key'])
    return {
        'statusCode': 200,
        'body': file_names
    }

明示的に許可する場合

まずは Lambda から特定の S3 バケットに対して s3:ListBucket を明示的に許可するように IAM ポリシーを記述します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": "arn:aws:s3:::bucket-policy-access-test-swx"
        }
    ]
}

Lambda 関数を実行すると、問題なくオブジェクトの一覧が取得できることが確認できます。

Response:
{
  "statusCode": 200,
  "body": [
    "test.txt"
  ]
}

明示的に拒否する場合

次に、s3:ListBucket を明示的に拒否するように IAM ポリシーを記述します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": "arn:aws:s3:::bucket-policy-access-test-swx"
        }
    ]
}

Lambda 関数を実行すると、以下のようなエラーメッセージが表示されます。

An error occurred (AccessDenied) when calling the ListObjectsV2 operation: User: xxxxxxxxxxxxx/bucket-policy-access-test-role/bucket-policy-access-test-function is not authorized to perform: s3:ListBucket on resource: "arn:aws:s3:::bucket-policy-access-test-swx" with an explicit deny in an identity-based policy

アイデンティティベースのポリシーで明示的に拒否されているため、アクセスが拒否されている旨のエラーメッセージが表示され、オブジェクトの一覧が取得できないことが確認できます。

明示的な許可がない場合(暗黙的な拒否)

次に、s3:ListBucket については許可も拒否も記述せず、バケット内のオブジェクトに対して s3:GetObject を許可するよう記述します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::bucket-policy-access-test-swx/*"
        }
    ]
}

Lambda 関数を実行すると、以下のようなエラーメッセージが表示されます。

An error occurred (AccessDenied) when calling the ListObjectsV2 operation: User: xxxxxxxxxxxxx/bucket-policy-access-test-role/bucket-policy-access-test-function is not authorized to perform: s3:ListBucket on resource: "arn:aws:s3:::bucket-policy-access-test-swx" because no identity-based policy allows the s3:ListBucket action

エラーメッセージが少し変わり、アイデンティティベースのポリシーで s3:ListBucket が許可されていないというメッセージが表示されました。

これは、明示的に許可されたアクション以外を暗黙的に拒否するという IAM 側の仕様によるもので、ユーザーガイドにも記載があります。

docs.aws.amazon.com

上記の例では s3:GetObject を許可する設定としましたが、 s3:GetObject を拒否する設定のみ記述した場合も同様の結果となり、アイデンティティベースのポリシーで制御する際、なんらかのアクションをする場合には IAM ポリシーに明示的な許可が必要となります。

リソースベースのポリシーによる制御

次にアイデンティティベースのポリシーで検証した Lambda 関数を使用し、s3:ListBucket を明示的に許可した状態でバケットポリシーを以下条件に設定します。

  • 明示的に許可(実行側許可 かつ リソース側許可)
  • 明示的に拒否(実行側許可 かつ リソース側拒否)
  • 記述しない(実行側許可 かつ リソース側に別のアクションの許可のみ記述)

明示的に許可する場合

S3 バケットのバケットポリシーに Lambda に設定している IAM ロールからのアクセスを明示的に許可するよう記述します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::xxxxxxxxxxxxx/bucket-policy-access-test-role"
            },
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::bucket-policy-access-test-swx"
        }
    ]
}

Lambda 関数を実行すると、問題なくオブジェクトの一覧が取得できることが確認できます。

Response:
{
  "statusCode": 200,
  "body": [
    "test.txt"
  ]
}

明示的に拒否する場合

バケットポリシーに以下を設定します。 この場合、実行側の権限には s3:ListBucket を許可する設定がされており、リソース側のバケットポリシーでは s3:ListBucket を拒否している状態です。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Deny",
            "Principal": {
                "AWS": "arn:aws:iam::xxxxxxxxxxxxx/bucket-policy-access-test-role"
            },
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::bucket-policy-access-test-swx"
        }
    ]
}

Lambda 関数を実行すると、以下のようなエラーメッセージが表示されます。

An error occurred (AccessDenied) when calling the ListObjectsV2 operation: User: xxxxxxxxxxxxx/bucket-policy-access-test-role/bucket-policy-access-test-function is not authorized to perform: s3:ListBucket on resource: "arn:aws:s3:::bucket-policy-access-test-swx" with an explicit deny in a resource-based policy

リソースベースのポリシーで明示的に拒否されているためにエラーとなっていることが確認できます。

これは、権限の設定において評価される順序が以下となるためです。

  1. 明示的な拒否
  2. 明示的な許可
  3. 暗黙的な拒否

docs.aws.amazon.com

明示的な拒否が強いことにはセキュリティ上の大きなメリットがあります。例えば、絶対に削除されては困るようなデータを保管している S3 バケットに対して管理者など特定のユーザー以外からの削除アクションを拒否するような設定を記述しておくことで、実行側のロールに誤って許可の権限を設定してしまった場合にも削除されることを防ぐことができます。

バケットポリシーに明示的な許可がない場合

アイデンティティベースのポリシーと同様に明示的な許可を設定したアクション以外は暗黙的に拒否のような挙動となるでしょうか。

バケットポリシーを以下に設定します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::xxxxxxxxxxxxx/bucket-policy-access-test-role"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::bucket-policy-access-test-swx/*"
        }
    ]
}

Lambda 関数を実行すると、問題なくオブジェクトの一覧が取得できることが確認できます。

Response:
{
  "statusCode": 200,
  "body": [
    "test.txt"
  ]
}

これは、明示的な拒否のセクションで記載した通り、アイデンティティベースのポリシーとリソースベースのポリシーのどちらにも明示的な拒否の設定がなく、どちらかで明示的に許可されている場合はアクセスが許可されるためです。(この例ではアイデンティティベースのポリシー側(Lambda の実行ロール)で明示的に許可されているため、アクセスが可能となります)

クロスアカウントアクセスの場合

では、クロスアカウントアクセスの場合にも同じことが言えるでしょうか。

別のアカウントに中身が同じ Lambda 関数(bucket-policy-cross-account-access-test-function)を作成し、アタッチしている IAM ポリシーにも同様にs3:ListBucket を許可するよう設定します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": "arn:aws:s3:::bucket-policy-access-test-swx"
        }
    ]
}

クロスアカウントアクセスを検証するため、まずS3バケット(bucket-policy-access-test-swx)から既存のバケットポリシーを削除し、何も設定されていない状態に戻します。

Lambda 側に s3:ListBucket を許可する設定をしているため同じルールであれば取得できそうですよね。

Lambda 関数を実行すると、以下のようなエラーメッセージが表示されます。

An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied

これは、公式ドキュメントに以下の記載がある通り、アイデンティティベースのポリシーとリソースベースのポリシーのどちらにも明示的な許可が必要であるためです。

クロスアカウントアクセスでは、プリンシパルは ID ポリシーおよびリソースベースのポリシー内に Allow を必要とします。

docs.aws.amazon.com

そこで、バケットポリシーにも s3:ListBucket の許可設定します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::xxxxxxxxxxxxx/bucket-policy-cross-account-access-test-role"
            },
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::bucket-policy-access-test-swx"
        }
    ]
}

Lambda 関数を実行すると、問題なくオブジェクトの一覧が取得できることが確認できます。

Response:
{
  "statusCode": 200,
  "body": [
    "test.txt"
  ]
}

まとめ

同一アカウントの場合とクロスアカウントの場合に分けて表にしました。

○:(明示的に許可)  ×:(明示的に拒否)  - :(記述しない)

  • 同一アカウントの場合
アイデンティティベースのポリシー リソースベースのポリシー 権限
- 許可
× - 拒否
- - 拒否
許可
× 拒否
  • クロスアカウントの場合
アイデンティティベースのポリシー リソースベースのポリシー 権限
- 拒否
× - 拒否
- - 拒否
許可
× 拒否

まとめ

  • S3 へのアクセスはアイデンティティベースのポリシー(実行ロールの権限)とリソースベースのポリシー(バケットポリシー)で制御することができる
  • 明示的な拒否 > 明示的な許可 > 暗黙的な拒否 の順で評価されるため、どちらかで明示的に拒否されているアクションは実行できない
    • どちらにも明示的な許可がない場合も同様にアクションは実行できない
  • クロスアカウントアクセスの場合は両方に許可が必要

最後に

どなたかのお役に立てば幸いです。

三村 菜穂 (記事一覧)

アプリケーションサービス部 ディベロップメントサービス1課

2024年5月入社