👻

SAM template で public に公開する S3 bucket を書く

2023/12/20に公開

AWS のサンプルコードを動かしていたんだけど、コードが S3 の ACL まわりのアップデート以前のもので、 CloudFormation Stack がデプロイできてなかった

S3 のアプデ
https://dev.classmethod.jp/articles/s3-bucket-owner-enforced/

サンプルコード
https://github.com/aws-samples/aws-cognito-quicksight-auth

エラー

エラーメッセージは

Resource handler returned message: "Bucket cannot have ACLs set with ObjectOwnership's BucketOwnerEnforced setting

で、ウェブサイトの静的ファイルを入れる bucket を作るところでエラーになってた

エラー文の意味は

  • S3 bucket の ACL (access control list) の設定で、
  • ObjectOwnership の項目が BucketOwnerEnforced になってるけどそれはだめだよ

調査

ここで template のコードで S3 bucket を設定しているところを見てみると、

  S3WebSite:
    Type: "AWS::S3::Bucket"
    Properties:
      AccessControl: PublicRead
      WebsiteConfiguration:
        IndexDocument: index.html

とあって、どこにも ObjectOwnership の話が書いてなくてハテナになった。S3 のアップデートはというと、

by default have ...(中略)... access control lists (ACLs) disabled

https://aws.amazon.com/jp/blogs/aws/heads-up-amazon-s3-security-changes-are-coming-in-april-of-2023/

で、つまりこのアップデート以降はデフォルトで S3 の ACL が無効化された状態になる。で、ACL 無効化と BucketOwnerEnforced の関係はなにかというと、

ACL が無効 
= bucket 内の object の所有者は bucket の所有者 
= ObjectOwnerShip (object の所有者) が 
  BucketOwnerEnforced (bucket の所有者に強制される)

というふうなので、 テンプレートに何も指定していないと bucket の ObjectOwnerShip が BucketOwnerEnforced になる。ACL が有効だと、たとえば bucket 内にファイルがあったとしても、bucket の所有者 A さんと ファイル (=object) の所有者 B さんが異なり、自分が所有している bucket の中にあるファイルでもアクセス権がない、ということが起こりうる。

https://docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html
https://dev.classmethod.jp/articles/s3-bucket-owner-enforced/#toc-13

対処

この回答の通りにやるとデプロイできるようになる

https://repost.aws/questions/QUWqGG74w_R_6h9RWDXQNiOQ/cloud-formation-always-returns-cannot-have-public-acls-set-with-blockpublicaccess-enabled-service-amazon-s3-status-code-400-error-code-invalidbucketaclwithblockpublicaccesserror#ANujwOPYgURFixQ2CO63DrMg

いちおう貼り付け

  S3WebSite:
    Type: "AWS::S3::Bucket"
    Properties:
      PublicAccessBlockConfiguration:
        BlockPublicAcls: false
        BlockPublicPolicy: false
        IgnorePublicAcls: false
        RestrictPublicBuckets: false
      OwnershipControls:
        Rules:
          - ObjectOwnership: ObjectWriter
      WebsiteConfiguration:
        IndexDocument: index.html
  S3WebSiteBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref S3WebSite
      PolicyDocument:
        Statement:
          - Sid: PublicReadGetObject
            Effect: Allow
            Principal: "*"
            Action:
              - s3:GetObject
            Resource: !Sub ${S3WebSite.Arn}/*

diff はこう

<       AccessControl: PublicRead
---
>       PublicAccessBlockConfiguration:
>         BlockPublicAcls: false
>         BlockPublicPolicy: false
>         IgnorePublicAcls: false
>         RestrictPublicBuckets: false
>       OwnershipControls:
>         Rules:
>           - ObjectOwnership: ObjectWriter
172a180,191
>   S3WebSiteBucketPolicy:
>     Type: AWS::S3::BucketPolicy
>     Properties:
>       Bucket: !Ref S3WebSite
>       PolicyDocument:
>         Statement:
>           - Sid: PublicReadGetObject
>             Effect: Allow
>             Principal: "*"
>             Action:
>               - s3:GetObject
>             Resource: !Sub ${S3WebSite.Arn}/*

Discussion