AWS CloudFormationでセキュリティグループを作成する場合、通信許可の設定は、以下のどちらでも書ける。
-
AWS::EC2::SecurityGroup リソースの
SecurityGroupIngress
またはSecurityGroupEgress
プロパティ - AWS::EC2::SecurityGroupIngress リソースまたは AWS::EC2::SecurityGroupEgress リソース
セキュリティグループをつくろうとしているのだから、どちらにしてもAWS::EC2::SecurityGroupリソースは作るので、そこのプロパティに纏めてしまう方が手軽。ただし、インバウンド通信(Ingress)の通信元やアウトバウンド通信(Egress)の通信先にセキュリティグループを指定するときは、AWS::EC2::SecurityGroupIngressリソースやAWS::EC2::SecurityGroupEgressリソースとして外だしした方がよいかもしれない。
SecurityGroupプロパティに書くことの問題点
次のようなケースで、問題が発生する。
-
セキュリティグループAに、セキュリティグループA自身からのアクセスを許可する。接続元に
!Ref セキュリティグループA
のように自分自身を指定することになり、自己参照になる。この構成は、例えばAWS GlueやAmazon MWAAの設定でも出てくる。 -
セキュリティグループAはセキュリティグループBはからのアクセスを、BはAからのアクセスを許可する。セキュリティグループAでは接続元に
!Ref セキュリティグループB
を、 セキュリティグループBは接続元に!Ref セキュリティグループA
を指定することになり、循環参照になる。例えばWebサーバーAと監視兼ファイルサーバーBがあり、サーバーAはファイル取得のためにサーバーBにアクセスし、サーバーBはプロセスやリソース監視のためにサーバAにアクセスする、といった構成がある。
実際にCloudFormationスタックを作成してみると、セキュリティグループAの作成中にどちらのケースでも、通信許可対象のリソースの作成完了を待機する。前者では、通信許可対象のリソースが自分自身だから、自分自身が作成完了するまで自分自身が作成完了しないというパラドックスめいたことになる。後者では、通信許可対象のセキュリティグループBが作成完了するまで待機するけど、セキュリティグループBも通信元に設定するセキュリティグループAが作成完了するのを待機してしまうので、デッドロックのような形になってしまう。
最終的に、CloudFormationスタック作成はタイムアウトするまで待たされた上に、失敗する。
AWS::EC2::SecurityGroupIngressに外だし
上記の問題を回避するため、セキュリティグループからの通信許可はAWS::EC2::SecurityGroupリソース内にプロパティとして書くのではなく、AWS::EC2::SecurityGroupIngressリソースとして外だしする。これにより、自己参照や循環参照がなくなり、またリソースの依存関係も適切に処理されて、CloudFormationスタックの問題が起きなくなる。
自分自身からのアクセスを許可
次のようにAWS::EC2::SecurityGroupリソース内にプロパティとして書きたくなるケース。最後の行で自分自身を参照(自己参照)している。
Resources:
SecurityGroupA:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Self Reference Test
VpcId: vpc-06e4ab6c6cEXAMPLE
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
SourceSecurityGroupId: !Ref SecurityGroupA
SecurityGroupIngress
プロパティの指定をやめ AWS::EC2::SecurityGroupIngress
リソースを追加する。
Resources:
SecurityGroupA:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Self Reference Test
VpcId: vpc-06e4ab6c6cEXAMPLE
SecurityGroupIgressAtoA22:
Type: AWS::EC2::SecurityGroupIngress
Properties:
IpProtocol: tcp
FromPort: 22
ToPort: 22
GroupId: !Ref SecurityGroupA
SourceSecurityGroupId: !Ref SecurityGroupA
相互にアクセスを許可
次のようにAWS::EC2::SecurityGroupリソース内にプロパティとして書きたくなるケース。それぞれのリソースの最後の行で、お互いを参照(循環参照している。
Resources:
SecurityGroupA:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Circular Reference Test - Web Server
VpcId: vpc-06e4ab6c6cEXAMPLE
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
SourceSecurityGroupId: !Ref SecurityGroupB
SecurityGroupB:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Circular Reference Test - File and Management Server
VpcId: vpc-06e4ab6c6cEXAMPLE
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
SourceSecurityGroupId: !Ref SecurityGroupA
SecurityGroupIngress
プロパティの指定をやめ AWS::EC2::SecurityGroupIngress
リソースを追加する。
Resources:
SecurityGroupA:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Circular Reference Test - Web Server
VpcId: vpc-06e4ab6c6cEXAMPLE
SecurityGroupB:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Circular Reference Test - File and Management Server
VpcId: vpc-06e4ab6c6cEXAMPLE
SecurityGroupIgressAtoB22:
Type: AWS::EC2::SecurityGroupIngress
Properties:
IpProtocol: tcp
FromPort: 22
ToPort: 22
GroupId: !Ref SecurityGroupB
SourceSecurityGroupId: !Ref SecurityGroupA
SecurityGroupIgressBtoA22:
Type: AWS::EC2::SecurityGroupIngress
Properties:
IpProtocol: tcp
FromPort: 22
ToPort: 22
GroupId: !Ref SecurityGroupA
SourceSecurityGroupId: !Ref SecurityGroupB
プロパティとAWS::EC2::SecurityGroupIngressの使い分け
通信元や通信先の指定対象が他のセキュリティグループで、お互いに指定しあっているのでなければ、セキュリティグループをAWS::EC2::SecurityGroupリソースのプロパティとして指定しても問題は起きなかった。どのような場合に、プロパティとへの記述をやめてAWS::EC2::SecurityGroupIngressに外だしするか、方針を決めておくといいと思う。例えば以下が考えられる。
- 常にAWS::EC2::SecurityGroupIngressリソースとして外だしで定義する。プロパティは使わない。
- 通信元や通信先の指定対象がセキュリティグループの時は、常にAWS::EC2::SecurityGroupIngressリソースとして外だしで定義する。セキュリティグループ以外であれば、プロパティで指定する。
- 通信元や通信先の指定対象がセキュリティグループで自己参照や循環参照になった時だけ、AWS::EC2::SecurityGroupIngressリソースとして外だしで定義する。基本的にはプロパティで指定する。
個人的には、プロパティのまとまりの良さをすべて捨てるのは惜しいし、外だしの判断をできるだけ簡単にしたいので、2番目の方針を取りたい。