はじめに
セキュリティ向上のため、EC2インスタンスをプライベートサブネットに配置することがあります。
この場合、インターネット通信が制限されるため、通常の手段では外部リソースに接続してパッケージをインストールしたり更新したりできません。
一般的には、パッケージの更新やインターネット通信が必要な際、パブリックサブネットにNAT Gatewayを配置し、EC2インスタンスからの通信を通過させる方法が考えられます。
しかし、Amazon LinuxのパッケージはS3にホストされているため、NAT Gatewayを設置しなくても、S3のゲートウェイ型VPCエンドポイントを活用すれば、インターネット接続なしでパッケージのインストール・更新が可能です。
実際に構築してみた
以下に、NAT Gatewayを使用せずに、プライベートサブネット内でパッケージ更新を可能にするCloudFormationテンプレート全体を示します。
このテンプレートでは以下のリソースを構築します。
VPC
プライベートサブネット
プライベートサブネット用ルートテーブル
EC2・SSM用セキュリティグループ
SSM接続用VPCエンドポイント
S3ゲートウェイ型VPCエンドポイント
EC2(Amazon Linux 2023)
EC2用IAMロール
AWSTemplateFormatVersion: 2010-09-09
Resources:
# VPC作成
MyVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
# プライベートサブネット作成
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref MyVPC
CidrBlock: 10.0.1.0/24
# プライベートサブネット用ルートテーブル作成
PrivateSubnetRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref MyVPC
# プライベートサブネットにルートテーブルをアタッチ
PrivateSubnetRoute:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet
RouteTableId: !Ref PrivateSubnetRouteTable
# SSM用セキュリティグループの作成
MySSMSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "for SSM VPC Endpoint"
VpcId: !Ref MyVPC
# EC2用セキュリティグループの作成
MyEC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Allow outbound access to S3"
VpcId: !Ref MyVPC
# SSM用セキュリティグループのインバウンドルールに、EC2からの疎通を許可するルールを追加
MySSMSecurityGroupIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref MySSMSecurityGroup
IpProtocol: tcp
FromPort: 443
ToPort: 443
SourceSecurityGroupId: !GetAtt MyEC2SecurityGroup.GroupId
# EC2用セキュリティグループのアウトバウンドルールに、SSM用VPCエンドポイントへの疎通を許可するルールを追加
MyEC2SecurityGroupEgressAllowToSSM:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref MyEC2SecurityGroup
IpProtocol: tcp
FromPort: 443
ToPort: 443
DestinationSecurityGroupId: !GetAtt MySSMSecurityGroup.GroupId
# EC2用セキュリティグループのアウトバウンドルールに、S3への疎通を許可するルールを追加
MyEC2SecurityGroupEgressAllowToS3:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref MyEC2SecurityGroup
IpProtocol: tcp
FromPort: 443
ToPort: 443
DestinationPrefixListId: pl-61a54008
# SSM用VPCエンドポイントの作成
VPCEndpointSSM:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm
VpcId: !Ref MyVPC
SubnetIds: [!Ref PrivateSubnet]
VpcEndpointType: Interface
PrivateDnsEnabled: true
SecurityGroupIds: [!Ref MySSMSecurityGroup]
# EC2メッセージング用VPCエンドポイントの作成
VPCEndpointEC2Messages:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub com.amazonaws.${AWS::Region}.ec2messages
VpcId: !Ref MyVPC
SubnetIds: [!Ref PrivateSubnet]
VpcEndpointType: Interface
PrivateDnsEnabled: true
SecurityGroupIds: [!Ref MySSMSecurityGroup]
# SSMメッセージング用VPCエンドポイントの作成
VPCEndpointSSMMessage:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages
VpcId: !Ref MyVPC
SubnetIds: [!Ref PrivateSubnet]
VpcEndpointType: Interface
PrivateDnsEnabled: true
SecurityGroupIds: [!Ref MySSMSecurityGroup]
# S3用VPCエンドポイントの作成
S3VPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !Ref MyVPC
ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
RouteTableIds:
- !Ref PrivateSubnetRouteTable # 対象サブネットのルートテーブルを紐づける
# EC2作成
MyEC2Instance:
Type: AWS::EC2::Instance
Properties:
InstanceType: t3.micro
SubnetId: !Ref PrivateSubnet
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeType: gp3
VolumeSize: 30
Encrypted: true # 暗号化を有効にする
SecurityGroupIds:
- !Ref MyEC2SecurityGroup
ImageId: resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 # Amazon Linux 2023のAMI IDを指定
IamInstanceProfile: !Ref MyInstanceProfile
UserData:
Fn::Base64: !Sub |
#!/bin/bash
sudo yum -y install http://s3-ap-northeast-1.amazonaws.com/amazon-ssm-ap-northeast-1/latest/linux_amd64/amazon-ssm-agent.rpm
# インスタンスプロファイル作成
MyInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref SSMRole
# EC2用IAMロール作成
SSMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
次に各リソースの設定方法とポイントを解説します。
1. VPCとプライベートサブネット
まず、仮想ネットワーク(VPC)とその内部にプライベートサブネット、ルートテーブルを作成します。
# VPC作成
MyVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
# プライベートサブネット作成
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref MyVPC
CidrBlock: 10.0.1.0/24
# プライベートサブネット用ルートテーブル作成
PrivateSubnetRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref MyVPC
# プライベートサブネットにルートテーブルをアタッチ
PrivateSubnetRoute:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet
RouteTableId: !Ref PrivateSubnetRouteTable
2. EC2・SSM用セキュリティグループ
EC2とSSM用VPCエンドポイントに紐づけるセキュリティグループを作成します。
EC2用セキュリティグループのアウトバウンドルールには、以下のルールを追加します。
SSM用VPCエンドポイントへのHTTPS通信を許可するルール
S3のマネージドプレフィックスへのHTTPS通信を許可するルール
※ゲートウェイ型S3用VPCエンドポイントとの疎通に必要
SSM用セキュリティグループのインバウンドルールには、以下のルールを追加します。
EC2からのHTTPS通信を許可するルール
# SSM用セキュリティグループの作成
MySSMSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "for SSM VPC Endpoint"
VpcId: !Ref MyVPC
# EC2用セキュリティグループの作成
MyEC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Allow outbound access to S3"
VpcId: !Ref MyVPC
# SSM用セキュリティグループのインバウンドルールに、EC2からの疎通を許可するルールを追加
MySSMSecurityGroupIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref MySSMSecurityGroup
IpProtocol: tcp
FromPort: 443
ToPort: 443
SourceSecurityGroupId: !GetAtt MyEC2SecurityGroup.GroupId
# EC2用セキュリティグループのアウトバウンドルールに、SSM用VPCエンドポイントへの疎通を許可するルールを追加
MyEC2SecurityGroupEgressAllowToSSM:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref MyEC2SecurityGroup
IpProtocol: tcp
FromPort: 443
ToPort: 443
DestinationSecurityGroupId: !GetAtt MySSMSecurityGroup.GroupId
# EC2用セキュリティグループのアウトバウンドルールに、S3への疎通を許可するルールを追加
MyEC2SecurityGroupEgressAllowToS3:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref MyEC2SecurityGroup
IpProtocol: tcp
FromPort: 443
ToPort: 443
DestinationPrefixListId: pl-61a54008
3. VPCエンドポイント
パッケージ更新に必要な通信先であるS3へ接続するために、S3のゲートウェイ型VPCエンドポイントを作成します。
このエンドポイントを、EC2が配置されているプライベートサブネットのルートテーブルに紐づけることが重要です。
また、マネジメントコンソール上でEC2へ接続するのに必要な以下のVPCエンドポイントも作成します。
SSM用VPCエンドポイント
EC2メッセージング用VPCエンドポイント
SSMメッセージング用VPCエンドポイント
S3用VPCエンドポイント
# SSM用VPCエンドポイントの作成
VPCEndpointSSM:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm
VpcId: !Ref MyVPC
SubnetIds: [!Ref PrivateSubnet]
VpcEndpointType: Interface
PrivateDnsEnabled: true
SecurityGroupIds: [!Ref MySSMSecurityGroup]
# EC2メッセージング用VPCエンドポイントの作成
VPCEndpointEC2Messages:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub com.amazonaws.${AWS::Region}.ec2messages
VpcId: !Ref MyVPC
SubnetIds: [!Ref PrivateSubnet]
VpcEndpointType: Interface
PrivateDnsEnabled: true
SecurityGroupIds: [!Ref MySSMSecurityGroup]
# SSMメッセージング用VPCエンドポイントの作成
VPCEndpointSSMMessage:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages
VpcId: !Ref MyVPC
SubnetIds: [!Ref PrivateSubnet]
VpcEndpointType: Interface
PrivateDnsEnabled: true
SecurityGroupIds: [!Ref MySSMSecurityGroup]
# S3用VPCエンドポイントの作成
S3VPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !Ref MyVPC
ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
RouteTableIds:
- !Ref PrivateSubnetRouteTable # 対象サブネットのルートテーブルを紐づける
4. EC2インスタンス
Amazon Linux 2023のEC2インスタンスをプライベートサブネット内に配置します。
# EC2作成
MyEC2Instance:
Type: AWS::EC2::Instance
Properties:
InstanceType: t3.micro
SubnetId: !Ref PrivateSubnet
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeType: gp3
VolumeSize: 30
Encrypted: true # 暗号化を有効にする
SecurityGroupIds:
- !Ref MyEC2SecurityGroup
ImageId: resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 # Amazon Linux 2023のAMI IDを指定
IamInstanceProfile: !Ref MyInstanceProfile
UserData:
Fn::Base64: !Sub |
#!/bin/bash
sudo yum -y install http://s3-ap-northeast-1.amazonaws.com/amazon-ssm-ap-northeast-1/latest/linux_amd64/amazon-ssm-agent.rpm
5. IAMロール
セッションマネージャー経由でEC2に接続できるよう、EC2に付与するIAMロールにAmazonSSMManagedInstanceCoreのポリシーを設定します。
# インスタンスプロファイル作成
MyInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref SSMRole
# EC2用IAMロール作成
SSMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
デプロイ
上記テンプレートを用いてCloudFormationによるリソースデプロイを実施します。
デプロイ方法は以下記事で分かりやすく記載されていますので、こちらをご参照ください。
https://blog.serverworks.co.jp/2022/02/09/073000
検証
セッションマネージャーを使ったEC2への接続
AWSマネジメントコンソールでSystems ManagerのセッションマネージャーからEC2に接続し、パッケージインストールを試みます。
接続方法は下記をご参照ください。
https://baresupport.jp/blog/2022/03/14/82/#3-1_AWS_%E3%83%9E%E3%83%8D%E3%82%B8%E3%83%A1%E3%83%B3%E3%83%88%E3%82%B3%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%AB%E3%81%8B%E3%82%89%E6%8E%A5%E7%B6%9A%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88
パッケージのインストール
接続後、以下のコマンドでパッケージを更新します。
sudo dnf install -y htop