~その2~ Block public accessが自動有効、ACLが自動無効になった事でServerless Frameworkでのバケット新規作成時にハマったこと
S3バケットは23年4月以降、新たにバケットを作成する際にBlock public accessが自動有効になり、Access Control List(ACL)は自動無効になります。
Serverless frameworkを用いたS3バケットの作成の際に、この影響に対する対処が必要だったのですが、色々と予備知識が足りなく少々ハマったのでブログにどう対処したか残します。
具体的には以下2つの点で対処が必要でした。
- パブリックなアクセスが可能になるバケットポリシーを追加するには
BlockPublicPolicy
をfalseに設定する必要が有る - 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有効化の設定はされていません。
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
は以下のようになりました。
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
ObjectOwnership
をBucketOwnerEnforced
に設定した状態では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
は以下のようになりました。
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アクセス権限が付与されます。
以上。