本連載では、以下のイメージの構成にあるAWSリソース基盤自動化環境の構築を実践しています。
前回は、VPCおよびパブリック、プライベートサブネット、ルートテーブル、インターネットゲートウェイを作成するCloudFormationテンプレートを作成しました。今回は、作成した各パブリック/プライベートサブネットをFrontend/Backendサブネットに位置付け、各AWSリソースへ設定するセキュリティグループ、FrontendサブネットにアタッチするNATGatewayを構築するテンプレートを実装します。
なお、セキュリティグループを作成するスタック構成は基本AWS無料枠で構成されるサービスですが、NATGatewayはスタック構築後に料金が発生するので注意が必要です。とはいえ、NATGatewayはECSを実行する際に必要になる(NATGatewayがないと、外からコンテナイメージを取得できずに起動が失敗する)ので、無駄な費用が発生しないよう、NATGatewayの構築は別のスタックに切り出しておきましょう。
なお、実際のソースコードはGitHub上にコミットしています。以降のソースコードでは本質的でない記述を一部省略しているので、実行コードを作成する場合は、必要に応じて適宜GitHub上のソースコードも参照してください。
セキュリティグループスタック構築テンプレート
筆者が執筆する本連載および連載「クラウドネイティブアプリケーションの基本」では、これまで、以下のようなリソースに対するセキュリティグループを作成してきました。
連載 | 対象となるリソース | タイプ | 設定 |
---|---|---|---|
本連載第11回 | CodeBuildでビルド実行するコンテナ | アウトバウンド接続 | 任意のアドレスへのアウトバウンド許可 |
クラウドネイティブ第5回 | FrontendALB | インバウンド接続 | 任意のアドレスからの80番ポートの接続許可 |
クラウドネイティブ第5回 | BackendALB | インバウンド接続 | VPC内からの80番ポートの接続許可 |
クラウドネイティブ第8回 | Frontendサブネットに配置するECSクラスタ | インバウンド接続 | FrontendALBから32768-61000ポートの接続許可 |
クラウドネイティブ第8回 | Frontendサブネットに配置するECSクラスタ | インバウンド接続 | 任意のアドレスからのSSH接続 |
クラウドネイティブ第8回 | Backendサブネットに配置するECSクラスタ | インバウンド接続 | BackendALBから32768-61000ポートの接続許可 |
クラウドネイティブ第11回 | BackendサブネットからアクセスされるRDS | インバウンド接続 | Backendサブネットからの5432番ポートへの接続許可 |
クラウドネイティブ第22回 | FrontendサブネットからアクセスされるElastiCache | インバウンド接続 | Frontendサブネットに配置されたECSクラスタからの6379番ポートへの接続許可 |
上記のセキュリティグループをCloudFormationで構築する場合、リソースタイプが「AWS::EC2::SecurityGroup」となるセキュリティグループ自体の定義と、接続可能な条件を設定する「AWS::EC2::SecurityGroupIngress(インバウンド)」、もしくは「AWS::EC2::SecurityGroupEgress(アウトバウンド接続)」が必要になります。プロパティとして設定可能な属性は各リンク先の通りですが、上記の表のセキュリティグループを作成する「sample-sg-cfn.yml」は以下の通りです。
AWSTemplateFormatVersion: '2010-09-09'
// omit
Resources:
SecurityGroupFrontendALB: #(A)
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: SecurityGroupFrontendALB
GroupDescription: http access
VpcId:
Fn::ImportValue: !Sub ${VPCName}-VPCID #(B)
Tags:
- Key : Name
Value: !Sub ${VPCName}-SecurityGroupFrontendALB
SecurityGroupInggressFrontendALB: #(C)
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref SecurityGroupFrontendALB #(D)
IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0 #(E)
SecurityGroupBackendALB: #(F)
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: SecurityGroupBackendALB
GroupDescription: http access
VpcId:
Fn::ImportValue: !Sub ${VPCName}-VPCID
Tags:
- Key : Name
Value: !Sub ${VPCName}-SecurityGroupBackendALB
SecurityGroupIngressBackendALB: #(G)
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref SecurityGroupBackendALB
IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: !Ref VPCCiderBlock #(H)
SecurityGroupFrontendEcsCluster: #(I)
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: SecurityGroupFrontendEcsCluster
GroupDescription: http access only alb
VpcId:
Fn::ImportValue: !Sub ${VPCName}-VPCID
Tags:
- Key : Name
Value: !Sub ${VPCName}-SecurityGroupFrontendEcsCluster
SecurityGroupIngressFrontendEcsCluster: #(J)
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref SecurityGroupFrontendEcsCluster
IpProtocol: tcp
FromPort: 32768
ToPort: 61000
SourceSecurityGroupId: !Ref SecurityGroupFrontendALB #(K)
SecurityGroupIngressForSSHFrontendEcsCluster: #(L)
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref SecurityGroupFrontendEcsCluster
IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
SecurityGroupBackendEcsCluster: #(M)
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: SecurityGroupBackendEcsCluster
GroupDescription: http access only alb
VpcId:
Fn::ImportValue: !Sub ${VPCName}-VPCID
Tags:
- Key : Name
Value: !Sub ${VPCName}-SecurityGroupBackendEcsCluster
SecurityGroupIngressBackendEcsCluster: #(N)
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref SecurityGroupBackendEcsCluster
IpProtocol: tcp
FromPort: 32768
ToPort: 61000
SourceSecurityGroupId: !Ref SecurityGroupBackendALB #(O)
SecurityGroupRdsPostgres: #(P)
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: SecurityGroupRdsPostgres
GroupDescription: db access only backend subnet
VpcId:
Fn::ImportValue: !Sub ${VPCName}-VPCID
Tags:
- Key: Name
Value: !Sub ${VPCName}-SecurityGroupRdsPostgres
SecurityGroupIngressRdsPostgres: #(Q)
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref SecurityGroupRdsPostgres
IpProtocol: tcp
FromPort: 5432
ToPort: 5432
SourceSecurityGroupId: !Ref SecurityGroupBackendEcsCluster #(R)
SecurityGroupElastiCacheRedis: #(S)
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: SecurityGroupElastiCacheRedis
GroupDescription: redis access only frontend ecs cluster
VpcId:
Fn::ImportValue: !Sub ${VPCName}-VPCID
Tags:
- Key: Name
Value: !Sub ${VPCName}-SecurityGroupElastiCacheRedis
SecurityGroupIngressElastiCacheRedis: #(T)
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref SecurityGroupElastiCacheRedis
IpProtocol: tcp
FromPort: 6379
ToPort: 6379
SourceSecurityGroupId: !Ref SecurityGroupFrontendEcsCluster #(U)
SecurityGroupCodeBuild: #(V)
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: SecurityGroupCodeBuild
GroupDescription: CodeBuild environments
VpcId:
Fn::ImportValue: !Sub ${VPCName}-VPCID
Tags:
- Key: Name
Value: !Sub ${VPCName}-SecurityGroupCodeBuild
// omit
セキュリティグループのテンプレートの記述の基本となるポイントは下表の通りです。
記述 | 説明 |
---|---|
A | Frontendサブネットに配置するALBのセキュリティグループを定義します |
B | セキュリティグループを使用するVPCのIDを設定します。ここでは、Fn::ImportValue関数を使って、前回作成したVPCテンプレートでOutputsとして出力したVPCの物理IDを取得します(クロススタックリファレンス) |
C | Aで定義したセキュリティグループに割り当てるインバウンド接続ルールを定義します |
D | インバウンド接続ルールをどのセキュリティグループに当てるか定義します。複数の接続ルールを1つのセキュリティグループに割り当てることもできます |
E | 接続許可されるプロトコル、ポート、送信元を定義します |
F | BackendServiceサブネットに配置するALBのセキュリティグループを定義します。設定内容はAと同様です |
G | Fで定義したセキュリティグループに割り当てるインバウンド接続ルールを定義します |
H | 送信元のCIDRを設定します。接続可能な範囲はVPC内からのアクセスですが、前回CIDRはパラメータ要素として設定しているので、今回も同様にParametersから取得するものとします |
I | Frontendサブネットに配置するECSクラスタのセキュリティグループを定義します。設定内容はAと同様です |
J | Iで定義したセキュリティグループに割り当てるインバウンド接続ルールを定義しま。 |
K | 送信元をFrontendサブネットに配置したALBに設定するため、SourceSecurityGroupIdにFrontendALBのセキュリティグループAを設定します。 |
L | Iで定義したセキュリティグループにSSHの接続を2つ目のルールとして作成し割り当てます |
M | Backendサブネットに配置するECSクラスタのセキュリティグループを定義します。設定内容はAと同様です |
N | Mで定義したセキュリティグループに割り当てるインバウンド接続ルールを定義します |
O | 送信元をBackendサブネットに配置したALBに設定するため、SourceSecurityGroupIdにBackendALBのセキュリティグループFを設定します |
P | RDSに設定するセキュリティグループを定義します |
Q | Pで定義したセキュリティグループに割り当てるインバウンド接続ルールを定義します。設定内容はAと同様です |
R | 送信元をBackendサブネットに配置したECSクラスタに設定するため、SourceSecurityGroupIdにBackendECSクラスタのセキュリティグループMを設定します |
S | ElastiCacheに設定するセキュリティグループを定義します。設定内容はAと同様です |
T | Sで定義したセキュリティグループに割り当てるインバウンド接続ルールを定義します |
U | 送信元をFrontendサブネットに配置したECSクラスタに設定するため、SourceSecurityGroupIdにFrontendECSクラスタのセキュリティグループIを設定します |
V | CodeBuildに設定するセキュリティグループを定義します。アウトバウンド接続はデフォルトで全ての通信が許可されるため、SecurityGroupEgressの設定は必要ありません |
作成したテンプレートに対して、以下のようにスタック名とテンプレートパスを変更してヘルパースクリプトを実行します(パラメータはデフォルト値を利用するので省略します)。
#!/usr/bin/env bash
stack_name="mynavi-sample-sg"
template_path="sample-sg-cfn.yml"
aws cloudformation deploy --stack-name ${stack_name} --template-file ${template_path} --capabilities CAPABILITY_IAM
実行が正常に終了すると、セキュリティグループが作成されます。
NATGatewayスタック構築テンプレート
続いて、FrontendサブネットにNATGatewayを配置するCloudFormationテンプレートを作成します。NATGatewayは、連載「AWSで作るクラウドネイティブアプリケーションの基本」の第4回でVPC構築をする際に作成したものですが、時間単位で費用が発生するのでNATGatwayだけ切り出し、いつでも配置から外せるようにしておきましょう。
テンプレートではNATGatewayのリソース定義に加えて、ElasticIPアドレスやメインルートテーブル、パブリックサブネットのアタッチ定義なども必要です。
AWSTemplateFormatVersion: '2010-09-09'
// omit
Resources:
NatGWEIP: #(A)
Type: AWS::EC2::EIP
Properties:
Domain:
Fn::ImportValue: !Sub ${VPCName}-VPCID
NatGW: #(B)
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGWEIP.AllocationId #(C)
SubnetId:
Fn::ImportValue: !Sub ${VPCName}-PublicSubnet1 #(D)
Tags:
- Key: Name
Value: !Sub ${VPCName}-NatGW
MainRouteTable: #(E)
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Fn::ImportValue: !Sub ${VPCName}-VPCID
Tags:
- Key: Name
Value: !Sub ${VPCName}-PrivateRoute
MainRoute: #(F)
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref MainRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGW
PrivateSubnet1Association: #(G)
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId:
Fn::ImportValue: !Sub ${VPCName}-PrivateSubnet1
RouteTableId: !Ref MainRouteTable
// omit
NatGatewayのテンプレートの記述の基本となるポイントは、下表の通りです。
記述 | 説明 |
---|---|
A | NatGatewayに割り当てるElasticIPアドレスを定義します。設定するプロパティは「AWS::EC2::EIP」を参照してください。Domainプロパティにはクロススタックリファレンスにより前回定義したVPCを指定します |
B | NatGatewayリソースを定義します。設定するプロパティは「AWS::EC2::NatGateway」を参照して下さい |
C | AllocationIdプロパティには、GetAtt関数を用いて、Aで定義したElasticIPアドレスのものを設定します |
D | SubnetIdプロパティには、前回作成したパブリックサブネットをクロススタックリファレンスを使って指定します |
E | メインとして設定するルートテーブルを定義します。設定するプロパティは「AWS::EC2::RouteTable」を参照してください |
F | メインとして設定するルートを定義します。設定するプロパティは「AWS::EC2::Route」を参照してください |
G | Eで設定したルートテーブルのプライベートサブネットへの関連付けを定義します。設定するプロパティは「AWS::EC2::SubnetRouteTableAssociation」を参照してください |
作成したテンプレートに対して、ヘルパースクリプトを以下のように、スタック名とテンプレートパスを変更して実行します。
#!/usr/bin/env bash
stack_name="mynavi-sample-ng"
template_path="sample-ng-cfn.yml"
aws cloudformation deploy --stack-name ${stack_name} --template-file ${template_path} --capabilities CAPABILITY_IAM
実行が正常に終了すると、NATGatewayが作成/アタッチされます。
以上、今回はセキュリティグループとNATGatewayをCloudFormationテンプレートで構築しました。次回は、アプリケーションロードバランサーを構築するスタックテンプレートを作成します。
著者紹介
川畑 光平(KAWABATA Kohei) - NTTデータ 課長代理
金融機関システム業務アプリケーション開発・システム基盤担当を経て、現在はソフトウェア開発自動化関連の研究開発・推進に従事。
Red Hat Certified Engineer、Pivotal Certified Spring Professional、AWS Certified Solutions Architect Professional等の資格を持ち、アプリケーション基盤・クラウドなどさまざまな開発プロジェクト支援にも携わる。2019 APN AWS Top Engineers & Ambassadors選出。
本連載の内容に対するご意見・ご質問は Facebook まで。