~その2~ Block public accessが自動有効、ACLが自動無効になった事でServerless Frameworkでのバケット新規作成時にハマったこと

2023.06.07

S3バケットは23年4月以降、新たにバケットを作成する際にBlock public accessが自動有効になり、Access Control List(ACL)は自動無効になります。

Serverless frameworkを用いたS3バケットの作成の際に、この影響に対する対処が必要だったのですが、色々と予備知識が足りなく少々ハマったのでブログにどう対処したか残します。

具体的には以下2つの点で対処が必要でした。

  1. パブリックなアクセスが可能になるバケットポリシーを追加するにはBlockPublicPolicyをfalseに設定する必要が有る
  2. CloudFrontの標準ログ用のバケットはACLを有効化する必要があるので、serverless.ymlに明示的に有効化の記述が必要

先日公開したブログで1.のパブリックなアクセスが可能になるバケットポリシーを追加するにはBlockPublicPolicyをfalseに設定する必要が有るについて書きました。

こちらのブログでは2.のCloudFrontの標準ログ用のバケットはACLを有効化する必要があるので、serverless.ymlに明示的に有効化の記述が必要について書きたいと思います。

CloudFrontの標準ログ用のバケットはACLを有効化する必要があるので、serverless.ymlに明示的に有効化の記述が必要

やりたかったこと

やりたかったことはCrondFront作成時にCloudFrontのアクセスログ保存先のS3バケットを作成することです。

2023年4月からのACLの自動無効化に伴って対処が必要だったのですが、当時把握していなかったため対処せずにデプロイを実行しました。
この結果、デプロイで新規バケットの作成を行う段階でエラーが発生してデプロイが失敗しました。

以下2点について対処が必要でした。 - アクセスログ保存先のバケットのACLの有効化 - ACLを有効化するに伴いOwnershipControlsの設定

対処前のserverless.yml

対処前は以下のような形でresources:配下にアクセスログ用のバケットCloudFrontのDistributionに関して記述し、serverless.ymlを作成しました。 ACL有効化の設定はされていません。

serverless.yml

resources:
  Resources:

    PbaAclTestDistributionLogBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: cf-log-PbaAclTest

    PbaAclTestDistribution:
      Type: AWS::CloudFront::Distribution
      Properties:
        DistributionConfig:
          Enabled: true
          Comment:
            Ref: AWS::StackName
          DefaultRootObject: /index
          Logging:
            Bucket:
              Fn::GetAtt: [ PbaAclTestDistributionLogBucket, DomainName ]
          PriceClass: PriceClass_All
          Origins:
            - DomainName:
                Fn::GetAtt: [ PbaAclTestBucket, DomainName ]
              Id:
                Fn::Sub: S3-${PbaAclTestBucket}
              S3OriginConfig:
                OriginAccessIdentity: ""
          DefaultCacheBehavior:
            TargetOriginId:
              Fn::Sub: S3-${PbaAclTestBucket}
            AllowedMethods: [ "GET", "HEAD" ]
            ForwardedValues:
              QueryString: false
              Cookies:
                Forward: none
            ViewerProtocolPolicy: redirect-to-https
            DefaultTTL: 86400
            MaxTTL: 31536000
            MinTTL: 0
          ViewerCertificate:
            SslSupportMethod: sni-only
            AcmCertificateArn:
              Fn::Sub: arn:aws:acm:us-east-1:${AWS::AccountId}:certificate/${PbaAclTestDistributionAcmCertificateIdentifier}
          CustomErrorResponses:
            - ErrorCode: 403
              ResponsePagePath: /error/message
              ResponseCode: 404
              ErrorCachingMinTTL: 300
            - ErrorCode: 404
              ResponsePagePath: /error/message
              ResponseCode: 404
              ErrorCachingMinTTL: 300

発生したエラー

このserverless.ymlでデプロイすると以下のエラーが発生します。

Error:
CREATE_FAILED: MillDistribution (AWS::CloudFront::Distribution)
Resource handler returned message: "Invalid request provided: AWS::CloudFront::Distribution: The S3 bucket that you specified for CloudFront logs does not enable ACL access

こちらのエラーは内容もわかりやすく、すぐにACLの有効化が必要な事が抜けていたのが原因だと分かりました。

ACLの有効化

ACLの有効化はAccessControlというプロパティに設定を記述することで対応できました。 このプロパティ以下の複数の設定可能な項目があります。

  • Private
  • PublicRead
  • PublicReadWrite
  • AwsExecRead
  • AuthenticatedRead
  • BucketOwnerRead
  • BucketOwnerFullControl
  • LogDeliveryWrite

AWSのドキュメントの説明は以下のリンク先にあります。

これらの項目は既定ACLと呼ばれる 事前定義済みのACLセットとなります。

LogDeliveryWrite

アクセスログに対するACLの適応には、この中のLogDeliveryWriteを設定するのが適当です。

LogDeliveryWriteについて、AWSのドキュメントでの説明は以下の通りです。

ディストリビューションを作成または更新してロギングを有効にすると、CloudFront はこれらのアクセス許可を使用してバケットの ACL を更新し、awslogsdelivery アカウントに FULL_CONTROL のアクセス許可を付与します。awslogsdelivery アカウントはログファイルをバケットに書き込みます。

アクセスログはawslogsdeliveryアカウントを経由してバケットに書き込まれるとのことで、このアカウントに対してのみアクセス権限を付与する、という事が行われるようです。

バケットの作成が成功するとACLの設定に外部アカウントに対しての許可が追加されていることが確認できます。これがawslogsdeliveryのアカウントになります。

serverless.ymlの修正

この設定を盛り込んだserverless.ymlは以下のようになりました。

serverless.yml

resources:
  Resources:

    PbaAclTestDistributionLogBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: cf-log-PbaAclTest
    AccessControl: LogDeliveryWrite  # 追加

再度デプロイ

この設定を追加して再度デプロイすると、今度は以下のエラーが発生しました。

Error:
CREATE_FAILED: MillDistributionLogBucket (AWS::S3::Bucket)
Bucket cannot have ACLs set with ObjectOwnership's BucketOwnerEnforced setting

ObjectOwnershipBucketOwnerEnforcedに設定した状態ではACLを設定できないようです。 ObjectOwnershipとはなんでしょうか?この時点では初耳でした。

ObjectOwnershipの設定

調べてみるとObjectOwnershipとは、ACLを有効に設定する際に必須の項目で、オブジェクトの所有者を設定する項目となっています。 公式情報の説明はこちらになります。↓

この項目には以下の3つの設定が可能です。

  • BucketOwnerEnforced(バケット所有者強制)
  • BucketOwnerPreferred(バケット所有者推奨)
  • ObjectWriter(オブジェクトライター)

BucketOwnerEnforced

デフォルト設定がBucketOwnerEnforcedとなっているので、何も設定しない = BucketOwnerEnforced が設定されるということになります。 BucketOwnerEnforcedが設定されている場合ACLが無効となります。今回のエラーはこの設定が原因でした。 ACLを有効にするにはその他の2つの項目に設定変更する必要があります。

BucketOwnerPreferred

先程、AccessControlの設定のところで出てきたBucketOwnerFullControlを指定した場合、バケットを所有しているアカウントがオブジェクトの所有者となります。 それ以外のAccessControlを設定した場合は次項目で説明するObjectWriterと同じ挙動となります。

ObjectWriter

オブジェクトをアップロードしたアカウントがオブジェクトの所有者となります。

結論

今回,AccessControlはLogDeliveryWriteを設定しているのでObjectWriterを設定しました。

serverless.ymlの修正

この設定を盛り込んだserverless.ymlは以下のようになりました。

serverless.yml

resources:
  Resources:

    PbaAclTestDistributionLogBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: cf-log-PbaAclTest
    OwnershipControls:                  # 追加
      Rules:                            # 追加
        - ObjectOwnership: ObjectWriter # 追加
    AccessControl: LogDeliveryWrite

このserverless.ymlをデプロイすると、今度はエラー無しでデプロイが成功しました。

その他

AccessControlのその他の設定

今回の対処の中でAccessControlの設定項目について調べましたので、先程説明したLogDeliveryWrite以外のプロパティについても解説致します。 再掲しますが、以下の既定ACLが設定可能となっています。

  • Private
  • PublicRead
  • PublicReadWrite
  • AwsExecRead
  • AuthenticatedRead
  • BucketOwnerRead
  • BucketOwnerFullControl
  • LogDeliveryWrite

こちらの弊社ブログ記事にまとめ情報もありますので、合わせてご参照ください。

それぞれの既定ACLを設定した時に与えられる権限は以下の通りです。

Private

所有者に FULL_CONTROL のみ付与。デフォルト設定はこれになります。
これはACLが無効な状態と実質同じとなります。

PublicRead

誰でもREADアクセスが可能となります。
上記の権限とPrivateで与えられる権限のセットとなります。

PublicReadWrite

誰でもREAD/WRITEアクセスが可能となります。
上記の権限とPrivateで与えられる権限のセットとなります。

AwsExecRead

この規定ACLが付与されたEC2はS3からAMI用のバンドルをGETするためのREADアクセスが可能となります。
上記の権限とPrivateで与えられる権限のセットとなります。

AuthenticatedRead

Authenticated Usersグループに属するAWSユーザーはだれでもREADアクセスが可能となります。
Authenticated UsersとはAWSにログインし、有効な認証情報を持っているユーザーの事です。
上記の権限とPrivateで与えられる権限のセットとなります。

BucketOwnerRead

オブジェクト所有者にFULL_CONTROLを、バケット所有者には READ が付与されます。

BucketOwnerFullControl

オブジェクト所有者とバケット所有者にFULL_CONTROLが付与されます。

LogDeliveryWrite

今回のエラー解決の際に設定した既定ACLです。 アクセスログのために作られる外部ユーザー(awslogsdelivery)にバケットへのREAD/WRITEアクセス権限が付与されます。

以上。