背景となったお客様からの問い合わせ
あるお客様から、次の記事を読んでお問い合わせがありました。
記事では、Session Manager を使ったシェルセッションのログを、異なる AWS アカウントに属する S3 バケットに保存する方法について説明しています。
記事の中では、S3 バケットに保存するためのバケットポリシーに s3:PutObjectAcl という権限が必要だと記載されています。
s3:PutObject および s3:PutObjectAcl アクセス許可は、暗号化されたオブジェクトを S3 バケットに配置します。
お客様からの問い合わせ内容は、「s3:PutObjectAcl の権限は本当に必要なのか?」というものでした。どうやら、社内で使っているセキュリティ製品のチェックで、そのバケットポリシーが外部の AWS アカウントに対して過剰な権限を与えていると判断されたようです。
記事内に記載のバケットポリシーです。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": "s3:GetEncryptionConfiguration", "Resource": "arn:aws:s3:::customer_session_manager_logging_bucket", "Condition": { "StringEquals": { "aws:PrincipalOrgID": "o-xxxxxxxxxxxxxx" } } }, { "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": [ "s3:PutObject", "s3:PutObjectAcl" ], "Resource": "arn:aws:s3:::customer_session_manager_logging_bucket/*", "Condition": { "StringEquals": { "aws:PrincipalOrgID": "o-xxxxxxxxxxxxx" } } } ] }
考察
s3:PutObjectAcl は、オブジェクトに対してアクセスコントロールリスト(ACL)を設定するための権限です。ACL では「オブジェクトをパブリック公開する」といったアクセス権限を設定することができます。
- 参考: 詳細は PutObjectAcl をご覧ください。
「オブジェクトをパブリック公開する」といったアクセス権限を設定する場合、オブジェクトの ACL を使うのではなく、バケットポリシーを使用することが推奨されています。
- 参考: 詳細は すべての新しいバケットの ACL を無効にし、オブジェクト所有権を執行します。 をご覧ください。
- 「Amazon S3 バケットで ACL を無効にすることをお勧めします。」と記載があります。
また、2023年4月以降に作成された新しい S3 バケットでは、アクセスコントロールリスト(ACL)がデフォルトで無効になっています。
- 参考: 詳細は 事前のお知らせ: 2023 年 4 月より、Amazon S3 で、すべての新しいバケットに対して自動的に S3 パブリックアクセスブロックが有効化、アクセスコントロールリストが無効化 をご覧ください。
- 参考: 詳細は 2023年 4月から順次、S3 の ブロックパブリックアクセスが デフォルトで有効になります。 サーバーワークスエンジニアブログ をご覧ください。
アクセスコントロールリスト(ACL)が無効になっている S3 バケットで、s3:PutObjectAcl アクションを使用すると、当然、例外が発生します。
2024/10/28 追記:
ACLが無効になっている S3 バケットにオブジェクトをアップロードする際には、ACL を指定しないか、ACL にbucket-owner-full-control を指定するしかないようです。 空にするか、ACL が無効の時と同じ設定をする、ということですね。
仮説・検証
Session Manager のログは、アクセスコントロールリスト(ACL)がデフォルトで無効になっている S3 バケットにも出力できると考えられます。
検証してみましょう。
以下、構成図です。

セッションマネージャーのログを EC2 上からインスタンスプロファイルの IAM 権限を利用して、他の AWS アカウントの S3 バケットに配置 (PutObject) します。
クロスアカウントでのアクセス許可を行うために、EC2 のインスタンスプロファイルと、S3 のバケットポリシーの双方にアクセス許可が必要と考えられます。
EC2 のインスタンスプロファイル
EC2 のインスタンスプロファイルには、Session Manager を利用するためのポリシーである AmazonSSMManagedInstanceCoreの他に、S3 バケットにログを保管する権限が必要と考えます。
以下のインラインポリシーを作成し付与しました。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:GetEncryptionConfiguration", "Resource": "arn:aws:s3:::1025102510251025" }, { "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::1025102510251025/*" } ] }
S3 バケットは 1025102510251025 という名前です。
S3 バケットが暗号化されていることを確認する権限s3:GetEncryptionConfiguration"と、S3 バケットにログを保管する権限 s3:PutObject を付与しました。
これが最小限な権限だと思います。

S3 側のバケットポリシー
EC2 に与えた権限2つを、S3 側でも EC2 に対して許可しました。
EC2 にs3:GetEncryptionConfiguration"、s3:PutObject を許可しました。
Principal には EC2 の インスタンスプロファイルを入れています。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111111111111:role/ssm-managed-instance-core" }, "Action": "s3:GetEncryptionConfiguration", "Resource": "arn:aws:s3:::1025102510251025" }, { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::11111111111:role/ssm-managed-instance-core" }, "Action": "s3:PutObject", "Resource": "arn:aws:s3:::1025102510251025/*" } ] }
S3 側の他の設定はデフォルトの設定です。
ACL 関連の設定である「ブロックパブリックアクセス (バケット設定)」は 「オン」です。

また、「オブジェクト所有者」は「バケット所有者の強制」です。
ACL がデフォルトで無効な状態です。

結果:無事、S3 バケットにログを配置することができました。
シェルセッション:

S3 バケット上のオブジェクト:

S3 バケットに配置したログに対して、EC2 から、PutObjectACL アクションを実行し、パブリック公開しようとしたものの、想定通りエラーになりました。
aws s3api put-object-acl --bucket 1025102510251025 --key administrator-gyfqzqzbpx6h2ao3anpcsxrk5a.log --acl public-read

PutObjectACL権限がなくてもログは S3 に配置でき、実行不要なPutObjectACLアクションも実行できない状態になっています。
結論として、デフォルトの設定すなわち ACL が無効の S3 バケットに対しては、EC2 にPutObjectACLの権限を与える必要がないことがわかりました。
例外
S3 バケット側で、ACL を有効にする場合には、EC2 に、PutObjectACL アクションを許可していないと、ログを配置できませんでした。

おまけ:PutObjectACL を試す。
試験的に、アクセスログをパブリックに公開してみましょう。
セキュリティ的にも AWS 的にも推奨されないことを試しているので、環境によってはアラート等が出ると思います。
もし、お試しの際は慎重にお願いします。
S3 バケットのACL 設定を有効にします。

ブロックパブリックアクセス を「オフ」にします。

S3 のバケットポリシーに、EC2 からの s3:PutObjectAcl" を許可します。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111111111111:role/ssm-managed-instance-core" }, "Action": "s3:GetEncryptionConfiguration", "Resource": "arn:aws:s3:::1025102510251025" }, { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111111111111:role/ssm-managed-instance-core" }, "Action": [ "s3:PutObject", "s3:PutObjectAcl" ], "Resource": "arn:aws:s3:::1025102510251025/*" } ] }

EC2 のインスタンスプロファイルに付与したインラインポリシーに s3:PutObjectAcl" を許可します。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:GetEncryptionConfiguration", "Resource": "arn:aws:s3:::1025102510251025" }, { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:PutObjectAcl" ], "Resource": "arn:aws:s3:::1025102510251025/*" } ] }

Session Manager を利用し、S3 にログを配置しました。

EC2 から、PutObjectACL アクションを実行し、パブリック公開します。
aws s3api put-object-acl --bucket 1025102510251025 --key administrator-38uuhdb8nfchshnvs4gzan4ii8.log --acl public-read
今回は成功しました。

S3 バケットで該当のオブジェクトを選択し、「オブジェクト URL」をクリックしてみます。

パブリック公開されていました。

"s3:PutObjectACL" 権限を与えると、オブジェクトを公開してしまうリスクがあるということが、よくわかります。
おまけ2:同一の AWS アカウント(1つのアカウント内で処理が完結する)の場合には、 ACL が有効でも、s3:PutObjectAcl 権限が不要
異なる AWS アカウント(クロスアカウント)の場合と、同一の AWS アカウント(1つのアカウント内で処理が完結する)の場合で、ACL 有効時の結果が異なることがわかりました。 Session Manager から S3 へのファイルアップロードは以下の結果になりました。
異なる AWS アカウント(クロスアカウント)の場合
- Session Manager から S3 へのファイルアップロード結果
| S3 側の設定 (行)/EC2 の持つ IAM 権限 (列) | PutObjectACL権限あり | PutObjectACL権限なし |
|---|---|---|
| ACL 無効(推奨。デフォルト) | ○ | ○ |
| ACL 有効 | ○ | × |
同一の AWS アカウント(1つのアカウント内で処理が完結する)の場合
- Session Manager から S3 へのファイルアップロード結果
| S3 側の設定 (行)/EC2 の持つ IAM 権限 (列) | PutObjectACL権限あり | PutObjectACL権限なし |
|---|---|---|
| ACL 無効(推奨。デフォルト) | ○ | ○ |
| ACL 有効 | ○ | ○ |
同一の AWS アカウントでは、ACL が有効であっても、PutObjectACL 権限が不要でした。
結果が異なる理由
Session Manager が S3 にログを保存するとき、ACL に bucket-owner-full-control を指定しているようです。これを確認する方法として、AWS CLI を使って同じアカウント内と異なるアカウント間で試してみるとよくわかります。
- S3 の ACL を有効にします。
- S3 バケットの設定で、EC2 からの PutObject リクエストを許可します。
- EC2 のインスタンスプロファイルから PutObjectAcl 権限を外し、
PutObjectの権限だけを与えてファイルをアップロードします。
以下のコマンドを試します:
- 同じ AWS アカウント内でファイルをアップロードする場合:
aws s3 cp アップロードするファイル s3://バケット名(同一の AWS アカウント) --acl bucket-owner-full-control
- 異なる AWS アカウントにファイルをアップロードする場合:
aws s3 cp アップロードするファイル s3://バケット名(異なる AWS アカウント) --acl bucket-owner-full-control
バケット名以外、同じ内容のコマンドです。

結果として、同じアカウント内での操作は正常に完了しますが、異なるアカウントの場合は失敗します。
S3 バケットで ACL が有効な場合は、上記のような動作になります。
ACL 有効で ACL に何も指定しない場合、かつクロスアカウントの場合には、バケット所有者側の権限がかなり制限されるので、bucket-owner-full-control を指定するようにしているのだと思います。
おまけ2の参考ドキュメント
以下、公式ドキュメントより抜粋します。
新しいオブジェクトを追加するときに、ヘッダーを使用して、個々の AWS アカウントまたは Amazon S3 によって定義された定義済みグループに ACL ベースのアクセス許可を付与できます。これらのアクセス許可は、オブジェクトの ACL に追加されます。 デフォルトでは、すべてのオブジェクトはプライベートです 。 所有者のみが完全なアクセス制御を持ちます 。詳細については、Amazon S3 ユーザーガイドの「アクセス制御リスト (ACL) の概要」 および「REST API を使用した ACL の管理」を参照してください。
オブジェクトのアップロード先のバケットが、 S3 オブジェクト所有権のバケット所有者強制設定を使用している場合、ACL は無効になり、アクセス許可に影響を与えなくなります 。 この設定を使用するバケットは、ACL を指定しない PUT リクエスト、またはバケット所有者のフルコントロール ACL (既定bucket-owner-full-control ACL や XML 形式で表現されたこの ACL と同等の形式など) を指定する PUT リクエストのみを受け入れます 。 他の ACL (特定の AWS アカウントへのカスタム許可など) を含む PUT リクエストは失敗し、 400エラーコード でエラーを返します AccessControlListNotSupported。詳細については、 『 Amazon S3 ユーザーガイド』の「オブジェクトの所有権の制御と ACL の無効化」を参照してください。
- 参考:PutObject
まとめ
デフォルトの設定すなわち ACL が無効の S3 バケットに対しては、EC2 にPutObjectACLの権限を与える必要がないことがわかりました。
s3:PutObjectACL 権限を与えると、オブジェクトを公開してしまうリスクがあるので要注意です。
s3:PutObjectACL 権限が必要と書いてあっても、不要な場合もあります。
s3:PutObjectACL 権限を外して、確認してみるのも良いかと思います。
AWS にもフィードバックを送ろうと思います。
余談
富士山の6合目あたりまでジョギングしてみたら、眼下に雲海が広がっていて感動しました。

