NEWS
執筆活動:2024.08.16
CloudFormationを用いたAWS Network Firewallのデプロイについて
初めに
S&J株式会社コアテクノロジーグループの新井です。
普段は社内AWS環境のセキュリティ向上の対応やIaC化など担当しています。
今回は当社のプロジェクトでAWS Network Firewallを利用した際のナレッジとして、インフラ構成やデプロイ方法の当社事例をご紹介させていただきます。
当社事例紹介
前提
・プライベートな EC2 インスタンスから外部ネットワーク通信を行う際にIPSを通過させる・IPSでは許可リスト形式で *.google.comのドメインへのアクセスのみ許可する
・構築はIaCのCloudFormationで行う
・検証のためシングルAZでの構築を行う
構成図
以下のインフラ構成で作成します。PrivateサブネットのEC2インスタンスから外部ネットワーク通信を行います。「IPアドレスとドメイン名でアウトバウント通信を制限したい要件」・「許可リスト形式でドメイン名を指定したい要件」はよくある構成ではないでしょうか。
デプロイ方法
以下CloudFormationのテンプレートです。テンプレート
AWSTemplateFormatVersion: '2010-09-09'
Parameters :
ProjectName :
Type: String
Default: network-firewall-poc
VpcCidr:
Type: String
Default: 192.170.2.0/24
PrivateSubnet1aCidr :
Type: String
Default: 192.170.2.0/28
PublicSubnet1aCidr :
Type: String
Default: 192.170.2.32/28
FirewallSubnet1aCidr :
Type: String
Default: 192.170.2.64/28
Resources :
VPC :
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCidr
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: !Sub ${ProjectName}-vpc
- Key: Project
Value: !Ref ProjectName
PrivateSubnet1a :
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1a
CidrBlock: !Ref PrivateSubnet1aCidr
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${ProjectName}-private-subnet-1a
- Key: Project
Value: !Ref ProjectName
PublicSubnet1a:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1a
CidrBlock: !Ref PublicSubnet1aCidr
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${ProjectName}-public-subnet-1a
- Key: Project
Value: !Ref ProjectName
FirewallSubnet1a :
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1a
CidrBlock: !Ref FirewallSubnet1aCidr
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${ProjectName}-firewall-subnet-1a
- Key: Project
Value: !Ref ProjectName
InternetGateway :
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${ProjectName}-internet-gateway
- Key: Project
Value: !Ref ProjectName
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
NatGateway1a:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NATGatewayEIP1a.AllocationId
ConnectivityType: public
SubnetId: !Ref PublicSubnet1a
Tags:
- Key: Name
Value: !Sub ${ProjectName}-nat-1a
- Key: Project
Value: !Ref ProjectName
NATGatewayEIP1a:
Type: "AWS::EC2::EIP"
Properties:
Domain: vpc
PrivateRouteTable1a:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${ProjectName}-private-route-table-1a
- Key: Project
Value: !Ref ProjectName
PublicRouteTable1a:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${ProjectName}-public-route-table-1a
- Key: Project
Value: !Ref ProjectName
FirewallRouteTable1a:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${ProjectName}-firewall-route-table-1a
- Key: Project
Value: !Ref ProjectName
PublicRoute1aIGW:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable1a
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
FirewallRouteNat1a:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref FirewallRouteTable1a
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway1a
RouteTableAssocPublic1a:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet1a
RouteTableId: !Ref PublicRouteTable1a
RouteTableAssocPrivate1a:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1a
RouteTableId: !Ref PrivateRouteTable1a
RouteTableAssocFirewall1a:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref FirewallSubnet1a
RouteTableId: !Ref FirewallRouteTable1a
VPCSsmEndpoint:
Type: "AWS::EC2::VPCEndpoint"
Properties:
SubnetIds:
- !Ref PrivateSubnet1a
SecurityGroupIds:
- !Ref SecurityGroup
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm
VpcId: !Ref VPC
VpcEndpointType: Interface
PrivateDnsEnabled: true
VPCSsmmessagesEndpoint:
Type: "AWS::EC2::VPCEndpoint"
Properties:
SubnetIds:
- !Ref PrivateSubnet1a
SecurityGroupIds:
- !Ref SecurityGroup
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages
VpcId: !Ref VPC
VpcEndpointType: Interface
PrivateDnsEnabled: true
Ec2messagesEndpoint:
Type: "AWS::EC2::VPCEndpoint"
Properties:
SubnetIds:
- !Ref PrivateSubnet1a
SecurityGroupIds:
- !Ref SecurityGroup
ServiceName: !Sub com.amazonaws.${AWS::Region}.ec2messages
VpcId: !Ref VPC
VpcEndpointType: Interface
PrivateDnsEnabled: true
SecurityGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
VpcId: !Ref VPC
GroupName: "vpc-endpoint-sg"
GroupDescription: "-"
Tags:
- Key: "Name"
Value: "vpcendpoint-sg"
- Key: Project
Value: !Ref ProjectName
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: !GetAtt VPC.CidrBlock
NetworkFirewall:
Type: AWS::NetworkFirewall::Firewall
Properties:
FirewallName: !Sub ${ProjectName}-network-firewall
FirewallPolicyArn: !Ref NetworkFirewallPolicy
VpcId: !Ref VPC
SubnetMappings:
- SubnetId: !Ref FirewallSubnet1a
Tags:
- Key: Project
Value: !Ref ProjectName
NetworkFirewallPolicy:
Type: AWS::NetworkFirewall::FirewallPolicy
Properties:
FirewallPolicyName: !Sub ${ProjectName}-network-firewall-policy
FirewallPolicy:
StatelessDefaultActions:
- aws:forward_to_sfe
StatelessFragmentDefaultActions:
- aws:forward_to_sfe
StatefulDefaultActions:
- aws:drop_established
- aws:alert_established
StatefulEngineOptions:
RuleOrder: STRICT_ORDER
StatefulRuleGroupReferences:
- ResourceArn: !Ref RuleGroup
Priority: 1
Tags:
- Key: Project
Value: !Ref ProjectName
RuleGroup:
Type: "AWS::NetworkFirewall::RuleGroup"
Properties:
RuleGroupName: !Sub ${ProjectName}-rule-group
Type: "STATEFUL"
Capacity: 50
RuleGroup:
RulesSource:
RulesSourceList:
Targets:
- ".ap-northeast-1.amazonaws.com"
- ".google.com"
TargetTypes:
- "HTTP_HOST"
- "TLS_SNI"
GeneratedRulesType: "ALLOWLIST"
StatefulRuleOptions:
RuleOrder: "STRICT_ORDER"
Tags:
- Key: Project
Value: !Ref ProjectName
Instance:
Type: 'AWS::EC2::Instance'
Properties:
InstanceType: t3.micro
ImageId: ami-05a56ce08feadf9c4
IamInstanceProfile: !Ref InstanceProfile
NetworkInterfaces:
- AssociatePublicIpAddress: false
DeviceIndex: '0'
SubnetId: !Ref PrivateSubnet1a
Tags:
- Key: Name
Value: !Sub ${ProjectName}-01
- Key: Project
Value: !Ref ProjectName
InstanceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${ProjectName}-role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
InstanceProfile:
Type: 'AWS::IAM::InstanceProfile'
Properties:
Path: /
InstanceProfileName: !Sub ${ProjectName}-profile
Roles:
- !Ref InstanceRole
Role:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${ProjectName}-fix-route-role
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: root
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- network-firewall:DescribeFirewall
Resource: "*"
- Effect: Allow
Action:
- ec2:ReplaceRoute
- ec2:CreateRoute
- ec2:DescribeVpcs
Resource: "*"
Function:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${ProjectName}-fix-route
Handler: "index.handler"
Role: !GetAtt Role.Arn
Code:
ZipFile: |
import boto3
import cfnresponse
def handler(event, context):
responseData = {}
responseStatus = cfnresponse.FAILED
if event["RequestType"] == "Delete":
responseStatus = cfnresponse.SUCCESS
cfnresponse.send(event, context, responseStatus, responseData)
if event["RequestType"] == "Create":
try:
responseStatus = cfnresponse.SUCCESS
cfnresponse.send(event, context, responseStatus, responseData)
PrivateRouteTable1a = event["ResourceProperties"]["PrivateRouteTable1a"]
PublicRouteTable1a = event["ResourceProperties"]["PublicRouteTable1a"]
VpcCidr = event["ResourceProperties"]["VpcCidr"]
NetworkFirewallArn = event["ResourceProperties"]["NetworkFirewallArn"]
ec2 = boto3.client('ec2')
nfw = boto3.client('network-firewall')
NfwResponse=nfw.describe_firewall(FirewallArn=NetworkFirewallArn)
VpceId = NfwResponse['FirewallStatus']['SyncStates']['ap-northeast-1a']['Attachment']['EndpointId']
ec2.create_route(
DestinationCidrBlock='0.0.0.0/0',
RouteTableId=PrivateRouteTable1a,
VpcEndpointId=VpceId
)
ec2.replace_route(
DestinationCidrBlock=VpcCidr,
RouteTableId=PublicRouteTable1a,
VpcEndpointId=VpceId
)
except Exception as e:
responseStatus = cfnresponse.FAILED
cfnresponse.send(event, context, responseStatus, responseData)
Runtime: python3.12
Timeout: 30
FixRoutes:
Type: Custom::FixRoutes
Properties:
ServiceToken: !GetAtt Function.Arn
PrivateRouteTable1a: !Ref PrivateRouteTable1a
PublicRouteTable1a: !Ref PublicRouteTable1a
NetworkFirewallArn: !Ref NetworkFirewall
VpcCidr: !GetAtt VPC.CidrBlock
Parameters :
ProjectName :
Type: String
Default: network-firewall-poc
VpcCidr:
Type: String
Default: 192.170.2.0/24
PrivateSubnet1aCidr :
Type: String
Default: 192.170.2.0/28
PublicSubnet1aCidr :
Type: String
Default: 192.170.2.32/28
FirewallSubnet1aCidr :
Type: String
Default: 192.170.2.64/28
Resources :
VPC :
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCidr
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: !Sub ${ProjectName}-vpc
- Key: Project
Value: !Ref ProjectName
PrivateSubnet1a :
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1a
CidrBlock: !Ref PrivateSubnet1aCidr
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${ProjectName}-private-subnet-1a
- Key: Project
Value: !Ref ProjectName
PublicSubnet1a:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1a
CidrBlock: !Ref PublicSubnet1aCidr
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${ProjectName}-public-subnet-1a
- Key: Project
Value: !Ref ProjectName
FirewallSubnet1a :
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1a
CidrBlock: !Ref FirewallSubnet1aCidr
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${ProjectName}-firewall-subnet-1a
- Key: Project
Value: !Ref ProjectName
InternetGateway :
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${ProjectName}-internet-gateway
- Key: Project
Value: !Ref ProjectName
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
NatGateway1a:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NATGatewayEIP1a.AllocationId
ConnectivityType: public
SubnetId: !Ref PublicSubnet1a
Tags:
- Key: Name
Value: !Sub ${ProjectName}-nat-1a
- Key: Project
Value: !Ref ProjectName
NATGatewayEIP1a:
Type: "AWS::EC2::EIP"
Properties:
Domain: vpc
PrivateRouteTable1a:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${ProjectName}-private-route-table-1a
- Key: Project
Value: !Ref ProjectName
PublicRouteTable1a:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${ProjectName}-public-route-table-1a
- Key: Project
Value: !Ref ProjectName
FirewallRouteTable1a:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${ProjectName}-firewall-route-table-1a
- Key: Project
Value: !Ref ProjectName
PublicRoute1aIGW:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable1a
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
FirewallRouteNat1a:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref FirewallRouteTable1a
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway1a
RouteTableAssocPublic1a:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet1a
RouteTableId: !Ref PublicRouteTable1a
RouteTableAssocPrivate1a:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1a
RouteTableId: !Ref PrivateRouteTable1a
RouteTableAssocFirewall1a:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref FirewallSubnet1a
RouteTableId: !Ref FirewallRouteTable1a
VPCSsmEndpoint:
Type: "AWS::EC2::VPCEndpoint"
Properties:
SubnetIds:
- !Ref PrivateSubnet1a
SecurityGroupIds:
- !Ref SecurityGroup
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm
VpcId: !Ref VPC
VpcEndpointType: Interface
PrivateDnsEnabled: true
VPCSsmmessagesEndpoint:
Type: "AWS::EC2::VPCEndpoint"
Properties:
SubnetIds:
- !Ref PrivateSubnet1a
SecurityGroupIds:
- !Ref SecurityGroup
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages
VpcId: !Ref VPC
VpcEndpointType: Interface
PrivateDnsEnabled: true
Ec2messagesEndpoint:
Type: "AWS::EC2::VPCEndpoint"
Properties:
SubnetIds:
- !Ref PrivateSubnet1a
SecurityGroupIds:
- !Ref SecurityGroup
ServiceName: !Sub com.amazonaws.${AWS::Region}.ec2messages
VpcId: !Ref VPC
VpcEndpointType: Interface
PrivateDnsEnabled: true
SecurityGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
VpcId: !Ref VPC
GroupName: "vpc-endpoint-sg"
GroupDescription: "-"
Tags:
- Key: "Name"
Value: "vpcendpoint-sg"
- Key: Project
Value: !Ref ProjectName
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: !GetAtt VPC.CidrBlock
NetworkFirewall:
Type: AWS::NetworkFirewall::Firewall
Properties:
FirewallName: !Sub ${ProjectName}-network-firewall
FirewallPolicyArn: !Ref NetworkFirewallPolicy
VpcId: !Ref VPC
SubnetMappings:
- SubnetId: !Ref FirewallSubnet1a
Tags:
- Key: Project
Value: !Ref ProjectName
NetworkFirewallPolicy:
Type: AWS::NetworkFirewall::FirewallPolicy
Properties:
FirewallPolicyName: !Sub ${ProjectName}-network-firewall-policy
FirewallPolicy:
StatelessDefaultActions:
- aws:forward_to_sfe
StatelessFragmentDefaultActions:
- aws:forward_to_sfe
StatefulDefaultActions:
- aws:drop_established
- aws:alert_established
StatefulEngineOptions:
RuleOrder: STRICT_ORDER
StatefulRuleGroupReferences:
- ResourceArn: !Ref RuleGroup
Priority: 1
Tags:
- Key: Project
Value: !Ref ProjectName
RuleGroup:
Type: "AWS::NetworkFirewall::RuleGroup"
Properties:
RuleGroupName: !Sub ${ProjectName}-rule-group
Type: "STATEFUL"
Capacity: 50
RuleGroup:
RulesSource:
RulesSourceList:
Targets:
- ".ap-northeast-1.amazonaws.com"
- ".google.com"
TargetTypes:
- "HTTP_HOST"
- "TLS_SNI"
GeneratedRulesType: "ALLOWLIST"
StatefulRuleOptions:
RuleOrder: "STRICT_ORDER"
Tags:
- Key: Project
Value: !Ref ProjectName
Instance:
Type: 'AWS::EC2::Instance'
Properties:
InstanceType: t3.micro
ImageId: ami-05a56ce08feadf9c4
IamInstanceProfile: !Ref InstanceProfile
NetworkInterfaces:
- AssociatePublicIpAddress: false
DeviceIndex: '0'
SubnetId: !Ref PrivateSubnet1a
Tags:
- Key: Name
Value: !Sub ${ProjectName}-01
- Key: Project
Value: !Ref ProjectName
InstanceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${ProjectName}-role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
InstanceProfile:
Type: 'AWS::IAM::InstanceProfile'
Properties:
Path: /
InstanceProfileName: !Sub ${ProjectName}-profile
Roles:
- !Ref InstanceRole
Role:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${ProjectName}-fix-route-role
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: root
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- network-firewall:DescribeFirewall
Resource: "*"
- Effect: Allow
Action:
- ec2:ReplaceRoute
- ec2:CreateRoute
- ec2:DescribeVpcs
Resource: "*"
Function:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${ProjectName}-fix-route
Handler: "index.handler"
Role: !GetAtt Role.Arn
Code:
ZipFile: |
import boto3
import cfnresponse
def handler(event, context):
responseData = {}
responseStatus = cfnresponse.FAILED
if event["RequestType"] == "Delete":
responseStatus = cfnresponse.SUCCESS
cfnresponse.send(event, context, responseStatus, responseData)
if event["RequestType"] == "Create":
try:
responseStatus = cfnresponse.SUCCESS
cfnresponse.send(event, context, responseStatus, responseData)
PrivateRouteTable1a = event["ResourceProperties"]["PrivateRouteTable1a"]
PublicRouteTable1a = event["ResourceProperties"]["PublicRouteTable1a"]
VpcCidr = event["ResourceProperties"]["VpcCidr"]
NetworkFirewallArn = event["ResourceProperties"]["NetworkFirewallArn"]
ec2 = boto3.client('ec2')
nfw = boto3.client('network-firewall')
NfwResponse=nfw.describe_firewall(FirewallArn=NetworkFirewallArn)
VpceId = NfwResponse['FirewallStatus']['SyncStates']['ap-northeast-1a']['Attachment']['EndpointId']
ec2.create_route(
DestinationCidrBlock='0.0.0.0/0',
RouteTableId=PrivateRouteTable1a,
VpcEndpointId=VpceId
)
ec2.replace_route(
DestinationCidrBlock=VpcCidr,
RouteTableId=PublicRouteTable1a,
VpcEndpointId=VpceId
)
except Exception as e:
responseStatus = cfnresponse.FAILED
cfnresponse.send(event, context, responseStatus, responseData)
Runtime: python3.12
Timeout: 30
FixRoutes:
Type: Custom::FixRoutes
Properties:
ServiceToken: !GetAtt Function.Arn
PrivateRouteTable1a: !Ref PrivateRouteTable1a
PublicRouteTable1a: !Ref PublicRouteTable1a
NetworkFirewallArn: !Ref NetworkFirewall
VpcCidr: !GetAtt VPC.CidrBlock
デプロイ方法
以下のコマンドでデプロイします。
$ aws cloudformation deploy --template-file <前セクションのCloudFormationテンプレート>.yaml --stack-name NetworkFirewallPoc --capabilities CAPABILITY_NAMED_IAM
実行後、以下の出力結果が表示されればデプロイ成功となります。
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack – NetworkFirewallPoc
解説
CloudFormationのコードの中で重要な点をいくつかピックアップしてご紹介します。
・FirewallPolicyでステートフルデフォルトアクションをdrop_establishedに設定します。これによって暗黙のDenyを実現できます。
・RuleGroupで許可したいドメイン名のルールグループをALLOWLISTに設定することで、そのルールを許可リストとして扱うことができます。
NetworkFirewallPolicy:
Type: AWS::NetworkFirewall::FirewallPolicy
Properties:
FirewallPolicyName: !Sub ${ProjectName}-network-firewall-policy
FirewallPolicy:
StatelessDefaultActions:
- aws:forward_to_sfe
StatelessFragmentDefaultActions:
- aws:forward_to_sfe
StatefulDefaultActions:
- aws:drop_established
- aws:alert_established
StatefulEngineOptions:
RuleOrder: STRICT_ORDER
StatefulRuleGroupReferences:
- ResourceArn: !Ref RuleGroup
Priority: 1
Tags:
- Key: Project
Value: !Ref ProjectName
RuleGroup:
Type: "AWS::NetworkFirewall::RuleGroup"
Properties:
RuleGroupName: !Sub ${ProjectName}-rule-group
Type: "STATEFUL"
Capacity: 50
RuleGroup:
RulesSource:
RulesSourceList:
Targets:
- ".ap-northeast-1.amazonaws.com"
- ".google.com"
TargetTypes:
- "HTTP_HOST"
- "TLS_SNI"
GeneratedRulesType: "ALLOWLIST"
StatefulRuleOptions:
RuleOrder: "STRICT_ORDER"
Tags:
- Key: Project
Value: !Ref ProjectName
Type: AWS::NetworkFirewall::FirewallPolicy
Properties:
FirewallPolicyName: !Sub ${ProjectName}-network-firewall-policy
FirewallPolicy:
StatelessDefaultActions:
- aws:forward_to_sfe
StatelessFragmentDefaultActions:
- aws:forward_to_sfe
StatefulDefaultActions:
- aws:drop_established
- aws:alert_established
StatefulEngineOptions:
RuleOrder: STRICT_ORDER
StatefulRuleGroupReferences:
- ResourceArn: !Ref RuleGroup
Priority: 1
Tags:
- Key: Project
Value: !Ref ProjectName
RuleGroup:
Type: "AWS::NetworkFirewall::RuleGroup"
Properties:
RuleGroupName: !Sub ${ProjectName}-rule-group
Type: "STATEFUL"
Capacity: 50
RuleGroup:
RulesSource:
RulesSourceList:
Targets:
- ".ap-northeast-1.amazonaws.com"
- ".google.com"
TargetTypes:
- "HTTP_HOST"
- "TLS_SNI"
GeneratedRulesType: "ALLOWLIST"
StatefulRuleOptions:
RuleOrder: "STRICT_ORDER"
Tags:
- Key: Project
Value: !Ref ProjectName
・CloudFormation の仕様では NetworkFirewall リソースの戻り値に AWS Network Firewall のエンドポイント ID が渡されます。マルチ AZ で構築した場合はその戻り値が配列形式で渡されるのですが、配列内のエンドポイント ID の順番は AZ 順などではなくランダムです。
そのため構築を CloudFormation で完結するには下記のようにカスタムリソースを作成し、スクリプトを実行する必要があります。今回はシングル AZ なため不要ではありますが、本番環境などでマルチ AZ にしたい時があるので、カスタムリソースを用いるのが良いかと思います。
FixRoutes:
Type: Custom::FixRoutes
Properties:
ServiceToken: !GetAtt Function.Arn
PrivateRouteTable1a: !Ref PrivateRouteTable1a
PublicRouteTable1a: !Ref PublicRouteTable1a
NetworkFirewallArn: !Ref NetworkFirewall
VpcCidr: !GetAtt VPC.CidrBlock
Type: Custom::FixRoutes
Properties:
ServiceToken: !GetAtt Function.Arn
PrivateRouteTable1a: !Ref PrivateRouteTable1a
PublicRouteTable1a: !Ref PublicRouteTable1a
NetworkFirewallArn: !Ref NetworkFirewall
VpcCidr: !GetAtt VPC.CidrBlock
動作確認
EC2 インスタンスに Session Manager で接続し curl コマンドを実行します。
aws ssm start-session –target <<今回作成したインスタンス ID>>
sh-5.2$ curl www.google.com -o /dev/null -w '%{http_code}\n' -s #HTTPステータスコードのみ標準出力
200
sh-5.2$ curl www.sandj.co.jp
curl: (52) Empty reply from server
以上の結果から、許可リストによるドメイン名の外部ネットワーク通信の制限ができていると判断できます。
まとめ
今回はAWS Network Firewallの構成図・デプロイ方法の当社事例ついてご紹介しました。
AWS Network Firewallはルーティングの設定やFirewallPolicyの設定項目の難しさがあるサービスです。そのため必要な要件を満たすよう検証が必要になると思います。
しかし、要件に対して適切な設定をすることでマネージドでIDS/IPSを実現できる強みを持っています。
また今回Network FirewallのデプロイにCloudFormarmationを利用しました。
そのため、当記事を見てくださった方も容易にNetwork Firewallのデプロイおよび検証を行うことができるため、ぜひ試していただければと思います。
詳しいAWS Network Firewallのマネージドルールについて次回の記事で記載いたします。
ここまでご覧いただき、ありがとうございました。