CloudFormationを使用してネットワークACLを作成する
本ページでは、ネットワークACLを取り上げます。
ネットワークアクセスコントロールリスト (ACL) は、サブネットレベルで特定のインバウンドまたはアウトバウンドのトラフィックを許可または拒否します。
ネットワーク ACL を使用してサブネットへのトラフィックを制御する
今回はCloudFormationを使用して、ネットワークACLを作成し、挙動を確認します。
構築する環境
VPC内に5つのサブネットを作成し、それぞれにEC2インスタンスを配置します。
インスタンスはAmazon Linux 2です。
インスタンス5が配置されているサブネットにネットワークACLを作成します。
このインスタンスにはApacheをインストールして、Webサーバとします。
インスタンス1~4はインスタンス5にアクセスするクライアントとして使用します。
curlコマンドを使用して、それぞれのインスタンスからインスタンス5へアクセスを試みます。
ネットワークACLの設定による影響を確認します。
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。
https://github.com/awstut-an-r/awstut-saa/tree/main/03/005
テンプレートファイルのポイント解説
ネットワークACL
ネットワークACLを作成するためには、以下の3種類のリソースを作成する必要があります。
- ネットワークACL本体
- ネットワークACL関連付け
- ネットワークACLエントリー
ネットワークACL本体
Resources:
NetworkAcl:
Type: AWS::EC2::NetworkAcl
Properties:
VpcId: !Ref VPC
Code language: YAML (yaml)
ネットワークACL本体です。
ネットワークACLの作成先のVPCを指定します。
ネットワークACL関連付け
Resources:
NetworkAclAssociation:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
NetworkAclId: !Ref NetworkAcl
SubnetId: !Ref PrivateSubnet5
Code language: YAML (yaml)
ネットワークACLとサブネットを関連付けます。
今回はWebサーバインスタンスを配置するサブネットに、ネットワークACLを関連付けます。
ネットワークACLエントリー
エントリーはネットワークACLの具体的なルールに相当するリソースです。
ルールの内容を見る前に、今回の構成で扱う通信を確認しておきます。
今回は各クライアントインスタンスからWebサーバインスタンスへのHTTP通信(80/tcp)を扱います。
この通信をまとめた表が以下となります。
送信元アドレス | 送信元ポート | 宛先アドレス | 宛先ポート | プロトコル |
10.0.1.0/2410.0.2.0/2410.0.3.0/2410.0.4.0/24 | 1024-65535 | 10.0.5.0/24 | 80 | TCP |
インスタンス1用エントリー
Resources:
NetworkAclEntry1Ingress:
Type: AWS::EC2::NetworkAclEntry
Properties:
CidrBlock: !Ref CidrIp1
Egress: false
NetworkAclId: !Ref NetworkAcl
PortRange:
From: !Ref HTTPPort
To: !Ref HTTPPort
Protocol: !Ref TCPProtocolNumber
RuleAction: allow
RuleNumber: 10
NetworkAclEntry1Egress:
Type: AWS::EC2::NetworkAclEntry
Properties:
CidrBlock: !Ref CidrIp1
Egress: true
NetworkAclId: !Ref NetworkAcl
PortRange:
From: !Ref RandomPortFrom
To: !Ref RandomPortTo
Protocol: !Ref TCPProtocolNumber
RuleAction: allow
RuleNumber: 20
Code language: YAML (yaml)
1つ目のインスタンス用のエントリーです。
ネットワークACLを設定する上でのポイントですが、エントリーはインバウンド用とアウトバウンド用の2つ必要になります。
これはネットワークACLがステートであるためです。
NACL はステートレスです。つまり、以前に送受信されたトラフィックに関する情報は保存されません。例えば、サブネットへの特定のインバウンドトラフィックを許可する NACL ルールを作成しても、そのトラフィックへの応答は自動的には許可されません。
ネットワーク ACL を使用してサブネットへのトラフィックを制御する
上記の例ですと、1つ目のリソースがインスタンス1が設置されているサブネットから、インスタンス5が設置されているサブネットへの通信に関するルールです。
2つ目のリソースはその逆の通信用です。
Egressプロパティで通信の向きを定義できます。
falseの場合はインバウンド、trueの場合はアウトバウンドを指します。
CidrBlockプロパティで対象のCIDRを指定します。
インバウンドの場合は送信元、アウトバウンドの場合は宛先を指します。
PortRangeプロパティで対象とする通信のポート番号を指定します。
今回はクライアントインスタンスからWebサーバインスタンスへのHTTP通信(80/tcp)を扱います。
そのためこの通信における宛先のポート番号は80、送信元のポート番号は1024~65535の範囲の中からランダムに決定されます。
ですからインバウンドエントリーにおける本プロパティには、両方とも80を指定します。
そしてアウトバウンドエントリーでは、1024と65535を指定します。
Protocolプロパティには、プロトコル番号を指定します。
HTTPはTCPを使用しますから、TCPのプロトコル番号である6を指定します。
RuleActionプロパティで、扱う通信の許可/拒否を指定します。
インスタンス1に関する通信は、インバウンド/アウトバウンドともに許可とします。
RuleNumberプロパティでルールの番号を指定できます。
今回は10, 20を指定します。
設定をまとめたものが、以下の表になります。
ルール番号 | 方向 | CIDR | ポート番号 | プロトコル | アクション |
10 | インバウンド | 10.0.1.0/24 | 80 | 6(TCP) | allow |
20 | アウトバウンド | 10.0.1.0/24 | 1024-65535 | 6(TCP) | allow |
インスタンス2用エントリー
Resources:
NetworkAclEntry2Ingress:
Type: AWS::EC2::NetworkAclEntry
Properties:
CidrBlock: !Ref CidrIp2
Egress: false
NetworkAclId: !Ref NetworkAcl
PortRange:
From: !Ref HTTPPort
To: !Ref HTTPPort
Protocol: !Ref TCPProtocolNumber
RuleAction: allow
RuleNumber: 30
NetworkAclEntry2Egress:
Type: AWS::EC2::NetworkAclEntry
Properties:
CidrBlock: !Ref CidrIp2
Egress: true
NetworkAclId: !Ref NetworkAcl
PortRange:
From: !Ref RandomPortFrom
To: !Ref RandomPortTo
Protocol: !Ref TCPProtocolNumber
RuleAction: deny
RuleNumber: 40
Code language: YAML (yaml)
2つ目のインスタンス用のエントリーです。
考え方は先ほどと同様です。
こちらはインバウンド通信は許可しますが、アウトバウンド通信は拒否する内容です。
設定をまとめたものが、以下の表になります。
ルール番号 | 方向 | CIDR | ポート番号 | プロトコル | アクション |
30 | インバウンド | 10.0.1.0/24 | 80 | 6(TCP) | allow |
40 | アウトバウンド | 10.0.1.0/24 | 1024-65535 | 6(TCP) | deny |
インスタンス3用エントリー
Resources:
NetworkAclEntry3Ingress:
Type: AWS::EC2::NetworkAclEntry
Properties:
CidrBlock: !Ref CidrIp3
Egress: false
NetworkAclId: !Ref NetworkAcl
PortRange:
From: !Ref HTTPPort
To: !Ref HTTPPort
Protocol: !Ref TCPProtocolNumber
RuleAction: deny
RuleNumber: 50
NetworkAclEntry3Egress:
Type: AWS::EC2::NetworkAclEntry
Properties:
CidrBlock: !Ref CidrIp3
Egress: true
NetworkAclId: !Ref NetworkAcl
PortRange:
From: !Ref RandomPortFrom
To: !Ref RandomPortTo
Protocol: !Ref TCPProtocolNumber
RuleAction: allow
RuleNumber: 60
Code language: YAML (yaml)
3つ目のインスタンス用のエントリーです。
こちらはインバウンド通信は拒否しますが、アウトバウンド通信は許可する内容です。
設定をまとめたものが、以下の表になります。
ルール番号 | 方向 | CIDR | ポート番号 | プロトコル | アクション |
50 | インバウンド | 10.0.1.0/24 | 80 | 6(TCP) | deny |
60 | アウトバウンド | 10.0.1.0/24 | 1024-65535 | 6(TCP) | allow |
インスタンス4用エントリー
インスタンス4用にはエントリーを作成しません。
つまりネットワークACLには、インスタンス4からの通信用の設定が存在しない状態ということです。
設定がない場合のネットワークACLの挙動を確認するためのものです。
(参考) セキュリティグループ
Resources:
InstanceSecurityGroup1:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${Prefix}-InstanceSecurityGroup1"
GroupDescription: Deny All.
VpcId: !Ref VPC
InstanceSecurityGroup2:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${Prefix}-InstanceSecurityGroup2"
GroupDescription: Allow HTTP from InstanceSecurityGroup1.
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: !Ref HTTPPort
ToPort: !Ref HTTPPort
SourceSecurityGroupId: !Ref InstanceSecurityGroup1
Code language: YAML (yaml)
インスタンス1~4は1つ目のセキュリティグループを、インスタンス5には2つ目のセキュリティグループを関連付けます。
ご覧の通り、Webサーバの設定は、クライアントインスタンスからのHTTP通信を許可する内容です。
(参考) EC2
Resources:
Instance1:
Type: AWS::EC2::Instance
Properties:
IamInstanceProfile: !Ref InstanceProfile
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
NetworkInterfaces:
- DeviceIndex: 0
SubnetId: !Ref PrivateSubnet1
GroupSet:
- !Ref InstanceSecurityGroup1
Instance5:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
NetworkInterfaces:
- DeviceIndex: 0
SubnetId: !Ref PrivateSubnet5
GroupSet:
- !Ref InstanceSecurityGroup2
UserData: !Base64 |
#!/bin/bash -xe
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
ec2-metadata -i > /var/www/html/index.html
Code language: YAML (yaml)
インスタンス1およびインスタンス5用の設定です。
インスタンス1~4はサブネット以外は同一のため、1つだけ表示しています。
インスタンス1~4は特別な設定は行いません。
インスタンス5はユーザデータを使用して、Apacheをインストールして、Webサーバとして動作させます。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- ネットワークACL:acl-08b8d8a5e5cb97e90
- インスタンス1:i-0ffa7081a96da4a95
- インスタンス2:i-054bb5c83794ee9a0
- インスタンス3:i-04c9ee40fe26feb62
- インスタンス4:i-0832c58a73d109af6
- インスタンス5のプライベートDNS:ip-10-0-5-44.ap-northeast-1.compute.internal
AWS Management ConsoleからネットワークACLを確認します。
正常にネットワークACLが作成されています。
各クライアントインスタンス用のインバウンド/アウトバウンドルールが作成されています。
それぞれの最後の行にDenyのルールが追加されていることに気が付きます。
これはいわゆる「暗黙のDeny」というもので、この行よりも上で許可された通信以外は拒否するという意味です。
動作確認
準備が整いましたので、実際に挙動を確認します。
インバウンド・アウトバウンドを許可
まずインスタンス1にアクセスします。
インスタンス1は、ネットワークACLでインバウンド/アウトバウンドともに通信が許可されています。
インスタンスへのアクセスはSSM Session Managerを使用します。
% aws ssm start-session --target i-0ffa7081a96da4a95
...
sh-4.2$
Code language: Bash (bash)
SSM Session Managerに関する詳細は、以下のページをご確認ください。
curlコマンドを使用して、インスタンス5にアクセスします。
sh-4.2$ curl http://ip-10-0-5-44.ap-northeast-1.compute.internal
instance-id: i-0f918893101c6d102
Code language: Bash (bash)
正常に応答が返ってきました。
このようにネットワークACLでインバウンド/アウトバウンドともに許可されている場合、正常に通信することができます。
インバウンドのみ許可
先ほどと同様にインスタンス2にアクセスし、試験を行います。
インスタンス2は、ネットワークACLでインバウンドのみ通信が許可されています。
% aws ssm start-session --target i-054bb5c83794ee9a0
...
sh-4.2$
sh-4.2$ curl http://ip-10-0-5-44.ap-northeast-1.compute.internal
curl: (28) Failed to connect to ip-10-0-5-44.ap-northeast-1.compute.internal port 80 after 132026 ms: Couldn't connect to server
Code language: Bash (bash)
通信に失敗しました。
このようにネットワークACLでインバウンドだけ許可した場合、正常に通信することはできません。
アウトバウンドのみ許可
インスタンス3にアクセスし、試験を行います。
インスタンス3は、ネットワークACLでアウトバウンドのみ通信が許可されています。
% aws ssm start-session --target i-04c9ee40fe26feb62
...
sh-4.2$
sh-4.2$ curl http://ip-10-0-5-44.ap-northeast-1.compute.internal
curl: (28) Failed to connect to ip-10-0-5-44.ap-northeast-1.compute.internal port 80 after 132513 ms: Couldn't connect to server
Code language: Bash (bash)
通信に失敗しました。
このようにネットワークACLでアウトバウンドだけ許可した場合、正常に通信することはできません。
どちらも許可しない場合
最後にインスタンス4にアクセスし、試験を行います。
インスタンス4は、ネットワークACLでどちらの通信が許可していません。
% aws ssm start-session --target i-0832c58a73d109af6
...
sh-4.2$
sh-4.2$ curl http://ip-10-0-5-44.ap-northeast-1.compute.internal
curl: (28) Failed to connect to ip-10-0-5-44.ap-northeast-1.compute.internal port 80 after 132510 ms: Couldn't connect to server
Code language: Bash (bash)
通信に失敗しました。
このようにネットワークACLで何も設定しなかった場合、やはり正常に通信することはできません。
まとめ
CloudFormationを使用して、ネットワークACLを作成し、挙動を確認しました。
ネットワークACLを使用する場合、インバウンド/アウトバウンド通信を許可する必要があります。