AWS WAFの検証環境をCloudFormationで作ってみた
はじめに
こんにちは。クラウド事業本部の戸川です。
皆さんAWS WAFは使っていますか?
AWS WAFはELBやCloudFront、API Gateway、AppSyncなどのサービスの前面に立ってHTTPリクエストを監視してくれるWeb Application Firewallです。
非常に頼もしいAWS WAFですが、ELBやWebアプリなどのリソースと紐づくサービスなので、ちょっとした検証を行う為に環境を用意したり消したりするのが面倒だと感じました。
そんな私の思いに共感してくださる方も世の中にはきっといらっしゃると思い、AWS WAF検証用の環境を作成するCloudFormationテンプレートを作成しました。
ぜひ使ってやってください。
環境構成図
今回作成する環境は以下になります。
CloudFormationテンプレート
作成したテンプレートは以下になります。
AWS WAf Web AclにはAWSManagedRulesCommonRuleSetを設定しました。
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
Prefix:
Type: String
Default: test
Description: "Fill in the name of the system name."
Env:
Type: String
Default: develop
Description: "Fill in the name of the environment."
Scope:
Type: String
Default: REGIONAL
AllowedValues:
- REGIONAL
- CLOUDFRONT
Description: "Select in the scope of waf(REGIONAL or CLOUDFRONT)"
InstanceType:
Type: String
Default: t2.micro
Description: EC2 instance type
Resources:
# ------------------------------------------------------------#
# VPC
# ------------------------------------------------------------#
VPC:
Type: AWS::EC2::VPC
DeletionPolicy: Delete
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: !Sub ${Env}-${Prefix}-vpc
# Public Subnets
PublicSubnet1:
Type: AWS::EC2::Subnet
DeletionPolicy: Delete
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.1.0/24
AvailabilityZone: !Select [0, !GetAZs ""]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${Env}-${Prefix}-public-subnet-1
PublicSubnet2:
Type: AWS::EC2::Subnet
DeletionPolicy: Delete
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.2.0/24
AvailabilityZone: !Select [1, !GetAZs ""]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${Env}-${Prefix}-public-subnet-2
# Private Subnets
PrivateSubnet1:
Type: AWS::EC2::Subnet
DeletionPolicy: Delete
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.11.0/24
AvailabilityZone: !Select [0, !GetAZs ""]
Tags:
- Key: Name
Value: !Sub ${Env}-${Prefix}-private-subnet-1
# Internet Gateway
InternetGateway:
Type: AWS::EC2::InternetGateway
DeletionPolicy: Delete
Properties:
Tags:
- Key: Name
Value: !Sub ${Env}-${Prefix}-igw
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
DeletionPolicy: Delete
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
# NAT Gateway
NatGatewayEIP:
Type: AWS::EC2::EIP
DeletionPolicy: Delete
DependsOn: AttachGateway
Properties:
Domain: vpc
NatGateway:
Type: AWS::EC2::NatGateway
DeletionPolicy: Delete
Properties:
AllocationId: !GetAtt NatGatewayEIP.AllocationId
SubnetId: !Ref PublicSubnet1
Tags:
- Key: Name
Value: !Sub ${Env}-${Prefix}-nat-gw
# Route Tables
PublicRouteTable:
Type: AWS::EC2::RouteTable
DeletionPolicy: Delete
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${Env}-${Prefix}-public-rt
PrivateRouteTable:
Type: AWS::EC2::RouteTable
DeletionPolicy: Delete
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${Env}-${Prefix}-private-rt
PublicRoute:
Type: AWS::EC2::Route
DependsOn: AttachGateway
DeletionPolicy: Delete
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PrivateRoute:
Type: AWS::EC2::Route
DeletionPolicy: Delete
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway
PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
DeletionPolicy: Delete
Properties:
SubnetId: !Ref PublicSubnet1
RouteTableId: !Ref PublicRouteTable
PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
DeletionPolicy: Delete
Properties:
SubnetId: !Ref PublicSubnet2
RouteTableId: !Ref PublicRouteTable
PrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
DeletionPolicy: Delete
Properties:
SubnetId: !Ref PrivateSubnet1
RouteTableId: !Ref PrivateRouteTable
# ------------------------------------------------------------#
# Security Groups
# ------------------------------------------------------------#
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
DeletionPolicy: Delete
Properties:
GroupDescription: Security group for ALB
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: -1
FromPort: -1
ToPort: -1
CidrIp: 0.0.0.0/0
Tags:
- Key: "Name"
Value: !Sub "${Prefix}-alb-sg"
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
DeletionPolicy: Delete
Properties:
GroupDescription: Security group for EC2 instances
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !Ref ALBSecurityGroup
SecurityGroupEgress:
- IpProtocol: -1
FromPort: -1
ToPort: -1
CidrIp: 0.0.0.0/0
# ------------------------------------------------------------#
# ALB
# ------------------------------------------------------------#
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
DeletionPolicy: Delete
DependsOn:
- PublicSubnet1RouteTableAssociation
- PublicSubnet2RouteTableAssociation
- AttachGateway
Properties:
Scheme: internet-facing
LoadBalancerAttributes:
- Key: idle_timeout.timeout_seconds
Value: '60'
Subnets:
- !Ref PublicSubnet1
- !Ref PublicSubnet2
SecurityGroups:
- !Ref ALBSecurityGroup
Tags:
- Key: Name
Value: !Sub ${Env}-${Prefix}-alb
ALBTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
DeletionPolicy: Delete
DependsOn: EC2Instance
Properties:
HealthCheckIntervalSeconds: 30
HealthCheckPath: /
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 5
VpcId: !Ref VPC
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: '300'
Targets:
- Id: !Ref EC2Instance
Port: 80
Tags:
- Key: Name
Value: !Sub ${Env}-${Prefix}-tg
ALBListener:
Type: AWS::ElasticLoadBalancingV2::Listener
DeletionPolicy: Delete
DependsOn: ALBTargetGroup
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref ALBTargetGroup
LoadBalancerArn: !Ref ApplicationLoadBalancer
Port: 80
Protocol: HTTP
# ------------------------------------------------------------#
# EC2 Instance
# ------------------------------------------------------------#
EC2Instance:
Type: AWS::EC2::Instance
DeletionPolicy: Delete
Properties:
InstanceType: !Ref InstanceType
ImageId: ami-05506fa68391b4cb1
SecurityGroupIds:
- !Ref EC2SecurityGroup
SubnetId: !Ref PrivateSubnet1
UserData:
Fn::Base64: !Sub |
#!/bin/bash
dnf update -y
dnf install -y httpd
systemctl start httpd
systemctl enable httpd
systemctl restart httpd
systemctl status httpd >> /var/log/user-data.log 2>&1
Tags:
- Key: Name
Value: !Sub ${Env}-${Prefix}-ec2
# ------------------------------------------------------------#
# S3
# ------------------------------------------------------------#
S3BucketForWaflog:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
Properties:
BucketName: !Sub aws-waf-logs-${Env}-${Prefix}-${AWS::AccountId}
AccessControl: Private
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
S3BucketForAthenaQuery:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
Properties:
BucketName: !Sub athena-query-results-${Env}-${Prefix}-${AWS::AccountId}
AccessControl: Private
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
# ------------------------------------------------------------#
# WAF v2
# ------------------------------------------------------------#
WebAcl:
Type: AWS::WAFv2::WebACL
DeletionPolicy: Delete
Properties:
Name: !Sub ${Env}-${Prefix}-web-acl
Scope: !Ref Scope
DefaultAction:
Allow: {}
VisibilityConfig:
CloudWatchMetricsEnabled: true
SampledRequestsEnabled: true
MetricName: !Sub ${Env}-${Prefix}-web-acl
Rules:
-
Name: AWS-AWSManagedRulesCommonRuleSet
Priority: 1
Statement:
ManagedRuleGroupStatement:
VendorName: AWS
Name: AWSManagedRulesCommonRuleSet
ExcludedRules:
- Name: NoUserAgent_HEADER
- Name: UserAgent_BadBots_HEADER
- Name: SizeRestrictions_QUERYSTRING
- Name: SizeRestrictions_Cookie_HEADER
- Name: SizeRestrictions_BODY
- 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:
Count: {}
VisibilityConfig:
CloudWatchMetricsEnabled: true
SampledRequestsEnabled: true
MetricName: AWS-AWSManagedRulesCommonRuleSet
WAFLogConfig:
Type: AWS::WAFv2::LoggingConfiguration
DeletionPolicy: Delete
Properties:
LogDestinationConfigs:
- !GetAtt S3BucketForWaflog.Arn
ResourceArn: !GetAtt WebAcl.Arn
WebACLAssociation:
Type: AWS::WAFv2::WebACLAssociation
DeletionPolicy: Delete
Properties:
ResourceArn: !GetAtt ApplicationLoadBalancer.LoadBalancerArn
WebACLArn: !GetAtt WebAcl.Arn
動作確認
Allow
作成されたALBのDNSに向けてブラウザからアクセスしてみましたが、きちんと表示されていますね!
Block
AWSManagedRulesCommonRuleSetに引っかかりそうなリクエストを投げたところ、しっかり検知してくれています!
最後に
今回はAWS WAF検証環境を作成するCloudFormationテンプレートを作成しました。
この記事が少しでもどなたかのお役に立てば幸いです。