今S3のIaCで「AccessControlListNotSupported: The bucket does not allow ACLs」というエラーが出たならそれは2023年4月に行われたS3の仕様変更が原因かもしれない
最近、S3のIaCでAccessControlListNotSupported: The bucket does not allow ACLs
というエラーが出て困っているという方。もしかするとそれは2023年4月に行われたS3の仕様変更に拠るものかもしれません。私のケースがそうでしたので、詳細をレポートします。
私のケース
- CloudFront + S3で静的コンテンツを配信するサイトの実装を行なっていました。IaCツールはTerraformです。
- 3月にdev環境のプロビジョニングを行ないました。問題なく完了しました。
- 4月になってstaging環境のプロビジョニングを行なったところ、前述の通り
AccessControlListNotSupported: The bucket does not allow ACLs
というエラーに遭遇しました。devとstagingのコードは共通化を行なっていてほぼ同じコードだったため、何が原因かわからず、デバッグに少し手こずりました。
2023年4月に行われたS3の仕様変更とは
こちらです。
2023年4月より、全ての新しいS3バケットのデフォルト設定が
- パブリックアクセスブロックが有効
- ACLは無効
になっています。今回関係あるのは後者「ACLは無効」ですね。
これまでもマネジメントコンソールでバケットを作った時にはこの設定になっていましたが、コンソール以外の方法で作成した場合には適用されていませんでした。
この仕様変更の適用時期ですが、厳密には4月5日からこの新仕様が適用され始めて、4月28日に全リージョンで適用完了したようです。
この仕様変更がなぜエラーに繋がるのか
一部のS3バケットの設定項目は、上記ACLの設定が有効になっていることが前提になっています。そのためそういった設定を書いているとエラーになるというわけです。
前述の「私のケース」のプロジェクトでは、S3バケットを二つ定義していました。
- CloudFrontディストリビューションのログを格納するバケット
- CloudFrontのオリジンにする、静的コンテンツを格納するバケット
両方のバケットでエラーになりました。それぞれ何故エラーになったのか説明します。
CloudFrontディストリビューションのログを格納するバケット
S3バケットにCloudFrontディストリビューションのログを配信するためには、ACLにて許可をする必要があります。
awslogsdelivery
アカウントという、AWSが内部的にログ配信のために使っているアカウントからログがバケットに書き込まれるので、このawslogsdelivery
アカウントからのアクセス許可が必要なのです。
このawslogsdelivery
アカウントからのアクセス許可は、CloudFrontディストリビューションのロギングを有効化すると自動で設定されます。が、IaCで設定を明示的に残しておきたかったので、Terraformでもこの部分に関する設定を書いていました。
今回は、TerraformのS3バケットの公開モジュールを使ってS3バケット関連リソースをプロビジョニングしていたので、以下のようなコードを書いていました。
module "cdn_logging" { source = "terraform-aws-modules/s3-bucket/aws" version = "~> 3" (略) grant = [{ type = "CanonicalUser" permission = "FULL_CONTROL" id = data.aws_canonical_user_id.current.id }, { type = "CanonicalUser" permission = "FULL_CONTROL" id = data.aws_cloudfront_log_delivery_canonical_user_id.cloudfront.id } ] }
grant
リストのふたつめのオブジェクトが該当箇所です。(ひとつめのオブジェクトもデフォルトで設定されるやつです)
なのですが、この設定をするには当然ACLを有効化する必要があります。そのためAccessControlListNotSupported: The bucket does not allow ACLs
エラーが発生しました。
また蛇足ですが、先程「このawslogsdelivery
アカウントからのアクセス許可は、CloudFrontディストリビューションのロギングを有効化すると自動で設定されます。」と書いたとおり、CloudFrontディストリビューション側でも同様のACL設定をしようとします。ですのでCloudFrontディストリビューションでもError: creating CloudFront Distribution: InvalidArgument: The S3 bucket that you specified for CloudFront logs does not enable ACL access
というエラーが発生していました。
CloudFrontのオリジンにする、静的コンテンツを格納するバケット
こちらもACLの設定をprivateにするようにしていました。
module "contents" { source = "terraform-aws-modules/s3-bucket/aws" version = "~> 3" acl = "private" (略) }
これは以下のようなaws_s3_bucket_acl
Resourceを作成します。
resource "aws_s3_bucket_acl" "this" { bucket = aws_s3_bucket.this[0].id acl = "private" }
上記acl = "private"
は既定 ACLという事前定義済みの許可設定です。が、前述の通り2023年4月以降はまずACLを有効にする必要があります。無効になっていたのでAccessControlListNotSupported: The bucket does not allow ACLs
エラーが発生しました。
修正方針
そもそもACLを有効化するべきなのか今一度考える
2023年4月現在、ACL有効化が必要な要件は非常に限定的です。AWSとしてもACLを利用しないことを推奨しています。そのため、今一度ACLの必要性を精査しましょう。
今回の私のケースでいうと、「CloudFrontディストリビューションのログを格納するバケット」については上記ブログエントリにもある通り、ACL設定が必要なケースです。一方「CloudFrontのオリジンにする、静的コンテンツを格納するバケット」についてはACLの設定が不要だったことがわかりました。そのため、acl = "private"
の行を削除しました。
明示的にACLを有効にする
「CloudFrontディストリビューションのログを格納するバケット」について、明示的にACLを有効にする必要があることがわかったので、対応しました。
Terraformだと
aws_s3_bucket_ownership_controls
Resourceがこの設定を担当します。
前述のS3バケットの公開モジュールを使う場合だとcontrol_object_ownership = true
を設定することでこのresourceを作成し、さらにobject_ownership
でその内容を指定します。ObjectWriter
が仕様変更前のデフォルト値でした。
module "cdn_logging" { source = "terraform-aws-modules/s3-bucket/aws" version = "~> 3" (略) grant = [{ type = "CanonicalUser" permission = "FULL_CONTROL" id = data.aws_canonical_user_id.current.id }, { type = "CanonicalUser" permission = "FULL_CONTROL" id = data.aws_cloudfront_log_delivery_canonical_user_id.cloudfront.id } ] control_object_ownership = true object_ownership = "ObjectWriter" }
念の為明示的にACLを無効にしておく
「CloudFrontのオリジンにする、静的コンテンツを格納するバケット」については、devとstagingでACLの設定が異なっている状態です。dev作成時(3月)にはデフォルトでACL有効化、staging作成時(4月)にはデフォルトでACL無効化だったので。設定が異なっているのはあまり良くないと思うので、明示的にACL無効化にしておきました。
module "contents" { source = "terraform-aws-modules/s3-bucket/aws" version = "~> 3" (略) control_object_ownership = true object_ownership = "BucketOwnerEnforced" }