S3アクセスポイントのうれしい点を自分なりの理解で解説してみる #reinvent

アクセスポリシーを持ったS3のアクセスポイントがGAしました! ただ、これがどういった機能でどんなうれしさがあるのか私にはいまいちピンと来なかったんですね。 この機能のドキュメントをじっくり読んだので、自分なりの理解でS3アクセスポイントについて解説します。
2019.12.04

アクセスポリシーを持ったS3のアクセスポイントがGAしました!

と、速報を聞いたのはいいのですが、これがどういった機能どんなうれしさがあるのか私にはいまいちピンと来なかったんですね。

この機能のドキュメントをじっくり読んだので、自分なりの理解でS3アクセスポイントについて解説します。

jsonばかりの前説

S3アクセスポイントの話へ入る前に、S3のアクセス制限の要であるバケットポリシーについて語りましょう。

Policyドキュメントについては以前ブログでじっくり語っているのでこちらをご参照ください。

バケットポリシーを利用することで、柔軟にS3バケットの接続へ制限をかけることができます。

具体例をあげましょう。

「IAMユーザーAliceに、S3バケットのaliceフォルダを読み書きできるようにしてください。」

そしたら、バケットポリシーはこんな感じですね。

※便宜上、S3バケット名を ${S3_BUCKET_NAME} に、AWSアカウントIDを ${ACCOUNT_ID} に変更しています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::${ACCOUNT_ID}:user/Alice"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::${S3_BUCKET_NAME}/alice/*"
        }
    ]
}

参考: AWS JSON ポリシーの要素:Principal - AWS Identity and Access Management

「IAMユーザーBobに、S3バケットのbobフォルダを読み書きできるようにしてください。」

それじゃ、バケットポリシーはこんな感じですね。

※便宜上、S3バケット名を ${S3_BUCKET_NAME} に、AWSアカウントIDを ${ACCOUNT_ID} に変更しています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::${ACCOUNT_ID}:user/Alice"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::${S3_BUCKET_NAME}/alice/*"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::${ACCOUNT_ID}:user/Bob"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::${S3_BUCKET_NAME}/bob/*"
        }
    ]
}

「外部のAシステムからS3のデータを読み書きすることになったので、IP 192.0.2.2 からの接続を許可してください。」

そうですか。じゃあバケットポリシーはこんな感じにします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::${ACCOUNT_ID}:user/Alice"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::${S3_BUCKET_NAME}/alice/*"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::${ACCOUNT_ID}:user/Bob"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::${S3_BUCKET_NAME}/bob/*"
        },
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Condition": {"aws:SourceIp": "192.0.2.2/32"}
            "Resource": "arn:aws:s3:::${S3_BUCKET_NAME}/*"
        },
        
    ]
}

参考: 特定の IP アドレスへのアクセスの制限 | バケットポリシーの例 - Amazon Simple Storage Service

…JSONばかりになってきたのでそろそろやめましょうか。

こんな感じで今まではS3に対する複数のアクセス可否を1つのバケットポリシーで管理していました。これが3つ程度ならよいのですが、10、100とルールが増えていくとバケットポリシーへ追記するのに時間がかかるし、事故を起こしかねません。

そこで、1つの接続可否は1つのポリシーで管理しようぜ!というコンセプトで、 S3アクセスポイント という機能ができたのだと想像しています。

それでは、実際にS3アクセスポイントを作ってみます。

S3アクセスポイント接続用にAliceユーザーを作る

プログラムによるアクセスを許可して、S3にフルアクセスできる Alice ユーザーを作ります。

アクセスキーとシークレットキーを利用して、CLI用に aliceプロファイル を作っておきます。

S3アクセスポイントを作ってみる

マネジメントコンソールのアクセスポイントタブからS3アクセスポイントを作成します。

適当なアクセスポイント名をつけて、ネットワークのアクセスタイプはインターネットを選択します。

その下のアクセスポイントポリシーを設定します。

このポリシーの例では、IAMユーザーAliceがS3アクセスポイントを設定しているS3のオブジェクトすべてに対して、GetObject,PutObjectできる権限を設定しています。

設定の記述の詳細は公式ドキュメントを御覧ください。

S3からGetObjectできることを確認するため、S3に Alice/hello_world.html ファイルをアップロードしておきます。

CLIでS3アクセスポイント経由でファイル取得してみる

先ほど作った Aliceユーザー のアクセスキーとシークレットキーを設定したaliceプロファイル を利用してCLIでS3アクセスポイント経由でファイル取得します。

まず、AWS CLIでS3アクセスポイントにアクセスするには、バージョンを1.16.295以上に上げてください。それ以前のバージョンでは、S3アクセスポイントに対応していません。

aws s3api コマンドの --bucket オプションにS3アクセスポイントのARNを設定することで、S3アクセスポイントを利用してS3オブジェクトの取得できます。

詳しくは公式ドキュメントを御覧ください。

$ ACCOUNT_ID=123456789012
$ aws s3api get-object \
  --key Alice/hello_world.html \
  --bucket arn:aws:s3:us-east-1:${ACCOUNT_ID}:accesspoint/alice-access-point \
  hello_world.html \
  --profile alice

S3アクセスポイントを経由しないアクセスを禁止する

S3アクセスポイントを利用して、ユーザーごとのアクセスポリシーが管理できそうな気がしてきました。

しかし、このままではこんな感じでS3へ直接アクセスできてしまいます。

$ BUCKET_NAME=<<BUCKET_NAME>>
$ aws s3api get-object \
  --key Alice/hello_world.html \
  --bucket ${BUCKET_NAME} \
  hello_world.html \
  --profile alice

S3アクセスポイントでユーザーごとのアクセスポリシーを管理しても他の経路から接続されてはうれしくありません。S3アクセスポイントを経由しないアクセスを禁止してみます。

バケットポリシーのConditionに次の3つのキーを設定できるようになっています。

  • s3:DataAccessPointArn
  • s3:DataAccessPointAccount
  • s3:AccessPointNetworkOrigin

このうち、 s3:DataAccessPointArn は、経由したS3アクセスポイントARNの照合に利用できます。

詳しくは公式ドキュメントを御覧ください。

よって、S3アクセスポイントを経由していないアクセスを拒否するバケットポリシーを、こんな感じで書けます。

※便宜上、S3バケット名を ${S3_BUCKET_NAME} に、AWSアカウントIDを ${ACCOUNT_ID} に変更しています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::${S3_BUCKET_NAME}/*",
            "Condition": {
                "StringNotEquals": {
                    "s3:DataAccessPointArn": "arn:aws:s3:us-east-1:${ACCOUNT_ID}:accesspoint/alice-access-point"
                }
            }
        }
    ]
}

これで、S3アクセスポイントを使ってユーザー(またはシステム)毎にポリシー管理できそうです。

終わりに

S3のバケットポリシーは柔軟に接続可否を設定できてとても便利なのですが、その接続可否を1つのバケットポリシーで管理するため、ポリシーが複雑になりがちでした。

それをS3アクセスポイントを利用して個別に管理できるようになったことで、ポリシーの見通しがよくなりそうです。

S3アクセスポイントは自分もまだ全容が見えていないので、さらに検証を進めていきたいと思います。