本連載では、以下のイメージの構成にあるAWSリソース基盤自動化環境の構築を実践しています。
前回は、VPCおよびパブリック、プライベートサブネット、ルートテーブルおよび、インターネットゲートウェイを作成するCloudFormationテンプレートを作成しました。 今回は、作成した各パブリック・プライベートサブネットをFrontend、Backendサブネットとして位置付け、各AWSリソースへ設定するセキュリティグループ、FrontendサブネットにアタッチするNATGatewayを構築するテンプレートを実装します。 なお、セキュリティグループを作成するスタック構成は基本AWS無料枠で構成されるサービスですが、NATGatewayはスタック構築後に料金が発生するようになります。 NATGatewayはECSを実行する際に必要になってくる(NATGatewayがないと、外からコンテナイメージを取得できなくなり起動が失敗する)ので、注意が必要ですが、 不必要に費用が発生しないよう、NATGatewayの構築は別のスタックに切り出しておきましょう。 実際のソースコードは GitHub 上にコミットしています。 ソースコード中で本質的でない記述を一部省略しているので、実行コードを作成する場合は、必要に応じて適宜GitHub上のソースコードも参照してください。
クラウドネイティブ連載や本連載ではこれまで、以下のようなリソースに対するセキュリティグループを作成してきました。
連載 | 対象 | タイプ | 設定 |
クラウドネイティブ第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番ポートへの接続許可 |
基盤構築・デプロイ自動化第11回 | CodeBuildでビルド実行するコンテナ | アウトバウンド接続 | 任意のアドレスへのアウトバウンド許可 |
上記のセキュリティグループを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)〜(V)の通りです。
記述 | 説明 |
Frontendサブネットに配置するALBのセキュリティグループを定義します。 | |
セキュリティグループを使用するVPCのIDを設定します。ここでは、Fn::ImportValue関数を使って、前回作成したVPCテンプレートでOutputsとして出力したVPCの物理IDを取得します(クロススタックリファレンス)。 | |
(A)で定義したセキュリティグループに割り当てるインバウンド接続ルールを定義します。 | |
インバウンド接続ルールをどのセキュリティグループにあてるか定義します。複数の接続ルールを一つのセキュリティグループに割り当てることもできます。 | |
接続許可されるプロトコル、ポート、送信元を定義します。 | |
BackendServiceサブネットに配置するALBのセキュリティグループを定義します。設定内容は(A)と同様です。 | |
(F)で定義したセキュリティグループに割り当てるインバウンド接続ルールを定義します。 | |
送信元のCIDRを設定します。接続可能な範囲はVPC内からのアクセスですが、前回CIDRはパラメータ要素として設定しているので、今回も同様にParametersから取得するものとします。 | |
Frontendサブネットに配置するECSクラスタのセキュリティグループを定義します。設定内容は(A)と同様です。 | |
(I)で定義したセキュリティグループに割り当てるインバウンド接続ルールを定義します。 | |
送信元をFrontendサブネットに配置したALBに設定するため、SourceSecurityGroupIdにFrontendALBのセキュリティグループ(A)を設定します。 | |
(I)で定義したセキュリティグループにSSHの接続を2つ目のルールとして作成し割り当てます。 | |
Backendサブネットに配置するECSクラスタのセキュリティグループを定義します。設定内容は(A)と同様です。 | |
(M)で定義したセキュリティグループに割り当てるインバウンド接続ルールを定義します。 | |
送信元をBackendサブネットに配置したALBに設定するため、SourceSecurityGroupIdにBackendALBのセキュリティグループ(F)を設定します。 | |
RDSに設定するセキュリティグループを定義します。 | |
(P)で定義したセキュリティグループに割り当てるインバウンド接続ルールを定義します。設定内容は(A)と同様です。 | |
送信元をBackendサブネットに配置したECSクラスタに設定するため、SourceSecurityGroupIdにBackendECSクラスタのセキュリティグループ(M)を設定します。 | |
ElastiCacheに設定するセキュリティグループを定義します。設定内容は(A)と同様です。 | |
(S)で定義したセキュリティグループに割り当てるインバウンド接続ルールを定義します。 | |
送信元をFrontendサブネットに配置したECSクラスタに設定するため、SourceSecurityGroupIdにFrontendECSクラスタのセキュリティグループ(I)を設定します。 | |
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
実行が正常に終了すると、セキュリティグループが作成されます。
続いて、FrontendサブネットにNATGatewayを配置するCloudFormationテンプレートを作成します。 NATGatewayは クラウドネイティブ連載第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)〜(G)の通りです。
記述 | 説明 |
NatGatewayに割り当てるElasticIPアドレスを定義します。設定するプロパティは AWS::EC2::EIP を参照してください。Domainプロパティにはクロススタックリファレンスにより前回定義したVPCを指定します。 | |
NatGatewayリソースを定義します。設定するプロパティは AWS::EC2::NatGateway を参照して下さい。 | |
AllocationIdプロパティには、GetAtt関数を用いて、(A)で定義したElasticIPアドレスのものを設定します。 | |
SubnetIdプロパティには、前回作成したパブリックサブネットをクロススタックリファレンスを使って指定します。 | |
メインとして設定するルートテーブルを定義します。設定するプロパティは AWS::EC2::RouteTable を参照してください。 | |
メインとして設定するルートを定義します。設定するプロパティは AWS::EC2::Route を参照してください。 | |
(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等の資格を持ち、アプリケーション基盤・クラウドなど様々な開発プロジェクト支援にも携わる。