# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License is located at # # http://www.apache.org/licenses/LICENSE-2.0 # # or in the "license" file accompanying this file. This file is distributed # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either # express or implied. See the License for the specific language governing # permissions and limitations under the License. AWSTemplateFormatVersion: 2010-09-09 Description: >- (SO0006-FA) - Security Automations for AWS WAF - FA %VERSION%: This AWS CloudFormation template helps you provision the Security Automations for AWS WAF stack without worrying about creating and configuring the underlying AWS infrastructure. **WARNING** This template creates an AWS Lambda function, an AWS WAF Web ACL, an Amazon S3 bucket, and an Amazon CloudWatch custom metric. You will be billed for the AWS resources used if you create a stack from this template. Parameters: ActivateHttpFloodProtectionParam: Type: String ActivateScannersProbesProtectionParam: Type: String EndpointType: Type: String AppAccessLogBucket: Type: String ParentStackName: Type: String WafLogBucket: Type: String WafLogBucketArn: Type: String RequestThreshold: Type: String ErrorThreshold: Type: String WAFBlockPeriod: Type: String GlueDatabaseName: Type: String DeliveryStreamName: Type: String UUID: Type: String Conditions: AlbEndpoint: !Equals - !Ref EndpointType - 'ALB' CloudFrontEndpoint: !Not [Condition: AlbEndpoint] HttpFloodLambdaLogParser: !Equals - !Ref ActivateHttpFloodProtectionParam - 'yes - AWS Lambda log parser' HttpFloodAthenaLogParser: !Equals - !Ref ActivateHttpFloodProtectionParam - 'yes - Amazon Athena log parser' HttpFloodProtectionLogParserActivated: !Or - Condition: HttpFloodLambdaLogParser - Condition: HttpFloodAthenaLogParser ScannersProbesAthenaLogParser: !Equals - !Ref ActivateScannersProbesProtectionParam - 'yes - Amazon Athena log parser' ALBScannersProbesAthenaLogParser: !And - Condition: ScannersProbesAthenaLogParser - Condition: AlbEndpoint CloudFrontScannersProbesAthenaLogParser: !And - Condition: ScannersProbesAthenaLogParser - Condition: CloudFrontEndpoint AthenaLogParser: !Or - Condition: HttpFloodAthenaLogParser - Condition: ScannersProbesAthenaLogParser Resources: # Ref: https://amzn.to/2GX48Pr FirehoseWAFLogsDeliveryStreamRole: Type: AWS::IAM::Role Condition: HttpFloodProtectionLogParserActivated Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: firehose.amazonaws.com Action: sts:AssumeRole Condition: StringEquals: sts:ExternalId: !Ref 'AWS::AccountId' Policies: - PolicyName: S3Access PolicyDocument: Statement: - Effect: Allow Action: - s3:AbortMultipartUpload - s3:GetBucketLocation - s3:GetObject - s3:ListBucket - s3:ListBucketMultipartUploads - s3:PutObject Resource: - !Sub 'arn:${AWS::Partition}:s3:::${WafLogBucket}' - !Sub 'arn:${AWS::Partition}:s3:::${WafLogBucket}/*' - PolicyName: KinesisAccess PolicyDocument: Statement: - Effect: Allow Action: - kinesis:DescribeStream - kinesis:GetShardIterator - kinesis:GetRecords Resource: - !Sub 'arn:${AWS::Partition}:kinesis:${AWS::Region}:${AWS::AccountId}:stream/${DeliveryStreamName}' - PolicyName: CloudWatchAccess PolicyDocument: Statement: - Effect: Allow Action: - 'logs:PutLogEvents' Resource: - !Sub 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/kinesisfirehose/${DeliveryStreamName}:*' Metadata: cfn_nag: rules_to_suppress: - id: W11 reason: "S3Access restricted to WafLogBucket and CloudWatchAccess to DeliveryStreamName." FirehoseWAFLogsDeliveryStream: Type: AWS::KinesisFirehose::DeliveryStream Condition: HttpFloodProtectionLogParserActivated Properties: DeliveryStreamName: !Ref DeliveryStreamName DeliveryStreamType: DirectPut DeliveryStreamEncryptionConfigurationInput: KeyType: AWS_OWNED_CMK ExtendedS3DestinationConfiguration: BucketARN: !Ref WafLogBucketArn BufferingHints: IntervalInSeconds: 300 SizeInMBs: 5 CompressionFormat: GZIP Prefix: 'AWSLogs/year=!{timestamp:yyyy}/month=!{timestamp:MM}/day=!{timestamp:dd}/hour=!{timestamp:HH}/' ErrorOutputPrefix: 'AWSErrorLogs/result=!{firehose:error-output-type}/year=!{timestamp:yyyy}/month=!{timestamp:MM}/day=!{timestamp:dd}/hour=!{timestamp:HH}/' RoleARN: !GetAtt FirehoseWAFLogsDeliveryStreamRole.Arn GlueAccessLogsDatabase: Type: AWS::Glue::Database Condition: AthenaLogParser Properties: DatabaseInput: Name: !Ref GlueDatabaseName Description: !Sub '${ParentStackName} - Access Logs' CatalogId: !Ref AWS::AccountId GlueWafAccessLogsTable: Type: AWS::Glue::Table Condition: HttpFloodAthenaLogParser Properties: DatabaseName: !Ref GlueAccessLogsDatabase CatalogId: !Ref AWS::AccountId TableInput: Name: waf_access_logs Parameters: {"EXTERNAL": "TRUE"} PartitionKeys: - Name: year Type: int - Name: month Type: int - Name: day Type: int - Name: hour Type: int StorageDescriptor: Location: Fn::Sub: "s3://${WafLogBucket}/AWSLogs/" InputFormat: "org.apache.hadoop.mapred.TextInputFormat" OutputFormat: "org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat" SerdeInfo: Parameters: { "paths": "action,formatVersion,httpRequest,httpSourceId,httpSourceName,nonTerminatingMatchingRules,rateBasedRuleList,ruleGroupList,terminatingRuleId,terminatingRuleType,timestamp,webaclId" } SerializationLibrary: "org.openx.data.jsonserde.JsonSerDe" Compressed : true StoredAsSubDirectories: false Columns: - Name: timestamp Type: bigint - Name: formatversion Type: int - Name: webaclid Type: string - Name: terminatingruleid Type: string - Name: terminatingruletype Type: string - Name: action Type: string - Name: httpsourcename Type: string - Name: httpsourceid Type: string - Name: rulegrouplist Type: array - Name: ratebasedrulelist Type: array - Name: nonterminatingmatchingrules Type: array - Name: httprequest Type: struct>,uri:string,args:string,httpversion:string,httpmethod:string,requestid:string> ALBGlueAppAccessLogsTable: Type: AWS::Glue::Table Condition: ALBScannersProbesAthenaLogParser Properties: DatabaseName: !Ref GlueAccessLogsDatabase CatalogId: !Ref AWS::AccountId TableInput: Name: app_access_logs Description: !Sub '${ParentStackName} - APP Access Logs' Parameters: {"EXTERNAL": "TRUE"} TableType: EXTERNAL_TABLE PartitionKeys: - Name: year Type: int - Name: month Type: int - Name: day Type: int - Name: hour Type: int StorageDescriptor: Location: !Sub - "s3://${AppAccessLogBucket}/AWSLogs-Partitioned/" - {"AppAccessLogBucket" : !Ref AppAccessLogBucket} InputFormat: "org.apache.hadoop.mapred.TextInputFormat" OutputFormat: "org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat" SerdeInfo: Parameters: { "serialization.format": "1", "input.regex": "([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*):([0-9]*) ([^ ]*)[:-]([0-9]*) ([-.0-9]*) ([-.0-9]*) ([-.0-9]*) (|[-0-9]*) (-|[-0-9]*) ([-0-9]*) ([-0-9]*) \"([^ ]*) ([^ ]*) (- |[^ ]*)\" \"([^\"]*)\" ([A-Z0-9-]+) ([A-Za-z0-9.-]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" ([-.0-9]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\"($| \"[^ ]*\")(.*)" } SerializationLibrary: "org.apache.hadoop.hive.serde2.RegexSerDe" Compressed : true StoredAsSubDirectories: false Columns: - Name: type Type: string - Name: time Type: string - Name: elb Type: string - Name: client_ip Type: string - Name: client_port Type: int - Name: target_ip Type: string - Name: target_port Type: int - Name: request_processing_time Type: double - Name: target_processing_time Type: double - Name: response_processing_time Type: double - Name: elb_status_code Type: string - Name: target_status_code Type: string - Name: received_bytes Type: bigint - Name: sent_bytes Type: bigint - Name: request_verb Type: string - Name: request_url Type: string - Name: request_proto Type: string - Name: user_agent Type: string - Name: ssl_cipher Type: string - Name: ssl_protocol Type: string - Name: target_group_arn Type: string - Name: trace_id Type: string - Name: domain_name Type: string - Name: chosen_cert_arn Type: string - Name: matched_rule_priority Type: string - Name: request_creation_time Type: string - Name: actions_executed Type: string - Name: redirect_url Type: string - Name: lambda_error_reason Type: string - Name: new_field Type: string CloudFrontGlueAppAccessLogsTable: Type: AWS::Glue::Table Condition: CloudFrontScannersProbesAthenaLogParser Properties: DatabaseName: !Ref GlueAccessLogsDatabase CatalogId: !Ref AWS::AccountId TableInput: Name: app_access_logs Description: !Sub '${ParentStackName} - APP Access Logs' Parameters: {"skip.header.line.count": "2", "EXTERNAL": "TRUE"} TableType: EXTERNAL_TABLE PartitionKeys: - Name: year Type: int - Name: month Type: int - Name: day Type: int - Name: hour Type: int StorageDescriptor: Location: !Sub - "s3://${AppAccessLogBucket}/AWSLogs-Partitioned/" - {"AppAccessLogBucket" : !Ref AppAccessLogBucket} InputFormat: "org.apache.hadoop.mapred.TextInputFormat" OutputFormat: "org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat" SerdeInfo: Parameters: {"field.delim": "\t", "serialization.format": "\t"} SerializationLibrary: "org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe" Compressed : true StoredAsSubDirectories: true Columns: - Name: date Type: date - Name: time Type: string - Name: location Type: string - Name: bytes Type: bigint - Name: requestip Type: string - Name: method Type: string - Name: host Type: string - Name: uri Type: string - Name: status Type: int - Name: referrer Type: string - Name: useragent Type: string - Name: querystring Type: string - Name: cookie Type: string - Name: resulttype Type: string - Name: requestid Type: string - Name: hostheader Type: string - Name: requestprotocol Type: string - Name: requestbytes Type: bigint - Name: timetaken Type: float - Name: xforwardedfor Type: string - Name: sslprotocol Type: string - Name: sslcipher Type: string - Name: responseresulttype Type: string - Name: httpversion Type: string - Name: filestatus Type: string - Name: encryptedfields Type: int WAFAddPartitionAthenaQueryWorkGroup: Type: AWS::Athena::WorkGroup Condition: AthenaLogParser Properties: Name: !Join ['-', ['WAFAddPartitionAthenaQueryWorkGroup', !Ref UUID]] Description: Athena WorkGroup for adding Athena partition queries used by Security Automations for AWS WAF Solution State: ENABLED RecursiveDeleteOption: true WorkGroupConfiguration: PublishCloudWatchMetricsEnabled: true WAFLogAthenaQueryWorkGroup: Type: AWS::Athena::WorkGroup Condition: HttpFloodAthenaLogParser Properties: Name: !Join ['-', ['WAFLogAthenaQueryWorkGroup', !Ref UUID]] Description: Athena WorkGroup for WAF log queries used by Security Automations for AWS WAF Solution State: ENABLED RecursiveDeleteOption: true WorkGroupConfiguration: PublishCloudWatchMetricsEnabled: true WAFAppAccessLogAthenaQueryWorkGroup: Type: AWS::Athena::WorkGroup Condition: ScannersProbesAthenaLogParser Properties: Name: !Join ['-', ['WAFAppAccessLogAthenaQueryWorkGroup', !Ref UUID]] Description: Athena WorkGroup for CloudFront or ALB application access log queries used by Security Automations for AWS WAF Solution State: ENABLED RecursiveDeleteOption: true WorkGroupConfiguration: PublishCloudWatchMetricsEnabled: true Outputs: FirehoseWAFLogsDeliveryStreamArn: Value: !GetAtt FirehoseWAFLogsDeliveryStream.Arn Condition: HttpFloodProtectionLogParserActivated GlueAccessLogsDatabase: Value: !Ref GlueAccessLogsDatabase Condition: AthenaLogParser GlueWafAccessLogsTable: Value: !Ref GlueWafAccessLogsTable Condition: HttpFloodAthenaLogParser GlueAppAccessLogsTable: Condition: ScannersProbesAthenaLogParser Value: !If [AlbEndpoint, !Ref ALBGlueAppAccessLogsTable, !Ref CloudFrontGlueAppAccessLogsTable] WAFAddPartitionAthenaQueryWorkGroup: Description: Athena WorkGroup for adding Athena partition queries used by Security Automations for AWS WAF Solution Value: !Ref WAFAddPartitionAthenaQueryWorkGroup Condition: AthenaLogParser WAFLogAthenaQueryWorkGroup: Description: Athena WorkGroup for WAF log queries used by Security Automations for AWS WAF Solution Value: !Ref WAFLogAthenaQueryWorkGroup Condition: HttpFloodAthenaLogParser WAFAppAccessLogAthenaQueryWorkGroup: Description: Athena WorkGroup for CloudFront or ALB application access log queries used by Security Automations for AWS WAF Solution Value: !Ref WAFAppAccessLogAthenaQueryWorkGroup Condition: ScannersProbesAthenaLogParser Version: Value: "%VERSION%"