本記事について
-
AWS CloudFormation (yamlテンプレート) を用いて、色々なアーキテクチャを構築していきます。テンプレートのコピペ元としてご活用いただければ幸いです
- 01. 仮想ネットワークの構築
- 02. アプリケーションサーバーの構築と踏み台サーバー経由でのアクセス方法
- 03. S3バケットの作成とポリシー・アクセス許可の設定
- 04. S3 (+ CloudFront + OAC) による静的ウェブサイトのホスティング
- 05. S3 + CloudFront + Cognito + Lambda@Edge による認証機能付き静的ウェブサイトのホスティング
- 06. Lambda関数を作成して、S3/EventBridge/SQS から呼び出す
- 07. EC2インスタンスの構築とアクセス方法 (Session Manager / EC2 Instance Connect Endpoint)
-
以前の02. アプリケーションサーバーの構築と踏み台サーバー経由でのアクセス方法 の記事では踏み台サーバー経由でPrivate SubnetにあるEC2 Instanceにアクセスする方法を説明しました。しかしこの方法だと、わざわざ踏み台サーバーを用意したりポート開放をする必要がありました。また、一般的にはキーペアの管理も必要となってしまいます(この記事ではEC2 Instance Connect (EIC) を使って、キーペア管理をしないで済むようにしています)
-
また、多くの記事ではサーバーをPrivate Subnetに配置する構成が取られています。実際に会社で構築する環境等ではセキュリティを担保するためにはそうすべきだと思います。しかし、Private Subnetに配置されたEC2 Instanceをインターネットに接続するためには通常はNAT Gatewayが必要となります。NAT Gatewayは個人で使うには少々コストが高いという問題もあります
-
本記事では、EC2 Instanceへの接続を以下の2パターンで紹介します。どちらも外部に対してのポート開放は不要です
- AWS System Manager (SSM) Session Manager を使う
- EC2 Instance Connect (EIC) Endpoint を使う
-
また、key pairのやり取りのために
ec2-instance-connect send-ssh-public-key
を使っています。これによってローカルPCの公開鍵をEC2 Instanceに一時的に登録します- Session ManagerもEIC Endpointも、あくまでもSSH Sessionを開始したりトンネルを作成するものになります。SSHのためにkey pairは結局必要となります (自分は最初なぜこれが必要なのか理解していなかったです)
-
また、EC2 InstanceをPrivate Subnetだけなく、Public Subnetに配置するパターンも挙げて個人でもお金の面で使いやすくしています
- サーバーをPublic Subnetに配置する際はセキュリティリスク等について各自の責任で十分にご確認をお願いいたします
- Public Subnetに配置してもポートは閉じているし問題ないと思うかもしれません。が、ミドルウェアやOSのセキュリティホールを突いた攻撃を受けるリスクはあります
-
おまけでリモートデスクトップ環境の構築方法も記載します
-
本記事で紹介するEC2インスタンスの構築とアクセス方法のパターン
key pair 管理 | 接続方法 | 配置場所 | |
---|---|---|---|
パターン1 | ec2-instance-connect send-ssh-public-key | Session Manager | Public |
パターン2 | ec2-instance-connect send-ssh-public-key | Session Manager | Private |
パターン3 | ec2-instance-connect send-ssh-public-key | EIC Endpoint | Public |
パターン4 | ec2-instance-connect send-ssh-public-key | EIC Endpoint | Private |
AWS System Manager (SSM) Session Manager を使ってEC2 Instance にアクセスする
EC2 InstanceをPublic Subnetに配置する場合
-
Network
- VPCとPublic Subnetを作成します
-
EC2
- Public Subnetに配置します
- OSはUbuntu Server 22.04を使います。これにはssm-agentがデフォルトでインストールされています。ssm-agentがインストールされていないAMIを使う場合は自分でインストールする必要があります
- Session Managerを使えるように
arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
ポリシーを割り当てます - 常時起動するような使い方はしないため、Elastic IP アドレス (静的 IPv4 アドレス) は割り当てずにパブリックIPを自動割当しています
- 設定は、
AssociatePublicIpAddress: true
- パブリックIPv4アドレスが割り当てられると、0.005USD/hourの料金がかかります (24/2/1以降)
- 一方、EIPを使うとインスタンスが起動しているときは無料ですが、インスタンスが起動していない場合は料金がかかります
- インスタンスの使い方によってどちらにするか決めると良いかと思います
- 設定は、
- Security Groupはデフォルトのままで大丈夫です
- ポート開放は不要です
-
テンプレート
ec2_ssm_eic_public.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: |
Create an EC2 and connect using SSM and EC2 Instance Connect (EIC)
Parameters:
SystemName:
Description: System Name
Type: String
AvailabilityZone:
Description: Availability Zone
Type: AWS::EC2::AvailabilityZone::Name
Resources:
#-----------------------------------------------------------------------------
# VPC
#-----------------------------------------------------------------------------
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
Tags:
- Key: Name
Value: !Sub ${SystemName}-vpc
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${SystemName}-igw
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
#-----------------------------------------------------------------------------
# Public Subnet
#-----------------------------------------------------------------------------
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Ref AvailabilityZone
VpcId: !Ref VPC
CidrBlock: 10.0.0.0/20
Tags:
- Key: Name
Value: !Sub ${SystemName}-public-subnet
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-public-rtb
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref PublicRouteTable
PublicSubnetToInternetRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
#-----------------------------------------------------------------------------
# EC2
#-----------------------------------------------------------------------------
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: 'Security Group for an EC2 server'
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-ec2-sg
EC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0595d6e81396a9efb # Ubuntu Server 22.04
InstanceType: t2.micro
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeType: gp3
VolumeSize: 64
IamInstanceProfile:
!Ref EC2InstanceProfile
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: 0
GroupSet:
- !Ref EC2SecurityGroup
SubnetId: !Ref PublicSubnet
Tags:
- Key: Name
Value: !Sub ${SystemName}-ec2
#-----------------------------------------------------------------------------
# IAM Role for EC2 server
#-----------------------------------------------------------------------------
EC2Role:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${SystemName}-ec2-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore # To connect to an instance using Session Manager
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref EC2Role
EC2 InstanceをPrivate Subnetに配置する場合
-
Network
- VPCとPublic Subnet、Private Subnetを作成します
-
NAT Gateway
- Public Subnetに配置します
-
EC2
- Private Subnetに配置します。Private SubnetのRouteTableではdefaultの宛先をNAT Gatewayに設定しています
- OSはUbuntu Server 22.04を使います。これにはssm-agentがデフォルトでインストールされています。ssm-agentがインストールされていないAMIを使う場合は自分でインストールする必要があります
- Session Managerを使えるように
arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
ポリシーを割り当てます - Security Groupはデフォルトのままで大丈夫です
- ポート開放は不要です
-
ノート
- 今回、EC2 InstanceはNAT Gateway経由でインターネットに接続されています。インターネット接続がないVPCの場合にはSession Manager接続用にVPC Endpointが必要となります
-
テンプレート
ec2_ssm_eic_private.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: |
Create an EC2 and connect using SSM and EC2 Instance Connect (EIC)
Parameters:
SystemName:
Description: System Name
Type: String
AvailabilityZone:
Description: Availability Zone
Type: AWS::EC2::AvailabilityZone::Name
Resources:
#-----------------------------------------------------------------------------
# VPC
#-----------------------------------------------------------------------------
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
Tags:
- Key: Name
Value: !Sub ${SystemName}-vpc
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${SystemName}-igw
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
#-----------------------------------------------------------------------------
# Public Subnet
#-----------------------------------------------------------------------------
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Ref AvailabilityZone
VpcId: !Ref VPC
CidrBlock: 10.0.0.0/20
Tags:
- Key: Name
Value: !Sub ${SystemName}-public-subnet
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-public-rtb
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref PublicRouteTable
PublicSubnetToInternetRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
#-----------------------------------------------------------------------------
# Private Subnet
#-----------------------------------------------------------------------------
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Ref AvailabilityZone
VpcId: !Ref VPC
CidrBlock: 10.0.16.0/20
Tags:
- Key: Name
Value: !Sub ${SystemName}-private-subnet
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-private-rtb
PrivateSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet
RouteTableId: !Ref PrivateRouteTable
PrivateSubnetToInternetRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway
#-----------------------------------------------------------------------------
# Nat Gateway
#-----------------------------------------------------------------------------
NatGatewayEIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub ${SystemName}-ngw-eip
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
SubnetId: !Ref PublicSubnet
AllocationId: !GetAtt NatGatewayEIP.AllocationId
Tags:
- Key: Name
Value: !Sub ${SystemName}-ngw
#-----------------------------------------------------------------------------
# EC2
#-----------------------------------------------------------------------------
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: 'Security Group for an EC2 server'
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-ec2-sg
EC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0595d6e81396a9efb # Ubuntu Server 22.04
InstanceType: t2.micro
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeType: gp3
VolumeSize: 64
IamInstanceProfile:
!Ref EC2InstanceProfile
SecurityGroupIds:
- !Ref EC2SecurityGroup
SubnetId: !Ref PrivateSubnet
Tags:
- Key: Name
Value: !Sub ${SystemName}-ec2
#-----------------------------------------------------------------------------
# IAM Role for EC2 server
#-----------------------------------------------------------------------------
EC2Role:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${SystemName}-ec2-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore # To connect to an instance using Session Manager
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref EC2Role
デプロイ方法
EC2 Instanceをどちらに配置したいかに応じて、以下コマンド内で使うテンプレートファイルを切り替えてください
Region=ap-northeast-1
AvailabilityZone=ap-northeast-1a
SystemName=sample-ec2-ssm-eic-public
TemplateFileName=./ec2_ssm_eic_public.yaml
# SystemName=sample-ec2-ssm-eic-private
# TemplateFileName=./ec2_ssm_eic_private.yaml
aws cloudformation deploy \
--region "${Region}" \
--stack-name "${SystemName}" \
--template-file ${TemplateFileName} \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
SystemName="${SystemName}" \
AvailabilityZone="${AvailabilityZone}"
EC2へのアクセス方法
EC2 Instanceの配置場所に関わらずアクセス方法は同じです
準備
- ローカルPC (Windows/Linux/Macなど) で、以下を用意します
- sshコマンド
- AWS CLI
- AWS CLI 用の Session Manager プラグイン
- AWSコンソール -> EC2 -> Isntanceから、作成したインスタンス名 (i-00000000000000000) を確認しておきます
- 以後、記事内の i-00000000000000000 は適宜置き換えをしてください
簡単な接続確認
- 下記コマンドでインスタンスにアクセスできることを確認します
aws ssm start-session --target i-00000000000000000
sshの設定
-
~/.ssh/config
を開き、以下を追記します- Windowsの場合
- 場所は、
C:\Users\ooo\.ssh
-
sh -c
をC:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
に置き換えます - 改行区切りを && から ;(セミコロン) に変えます。Linuxでのエラー処理を気にしなければどちらもセミコロンで大丈夫です
- 場所は、
- Windowsの場合
-
設定の説明
-
i-00000000000000000
というホスト名にsshアクセスしようとしたら記載したコマンドが動きます - まず、ローカルPCの公開鍵をEC2 Instanceに一時的に登録します
- その後、SSH sessionを開始しています
-
-
ssh接続のたびに毎回インスタンス名を入力するのが面倒な場合は、configにあらかじめ記載して、普段は
ec2-server
という名前のホストを使うと便利です (名前は任意)
Host i-* mi-*
ProxyCommand sh -c "aws ec2-instance-connect send-ssh-public-key --instance-id %h --instance-os-user %r --ssh-public-key 'file://~/.ssh/id_rsa.pub' && aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"
# (Optional) To specify host id
Host ec2-server
HostName i-00000000000000000
User ubuntu
ProxyCommand sh -c "aws ec2-instance-connect send-ssh-public-key --instance-id %h --instance-os-user %r --ssh-public-key 'file://~/.ssh/id_rsa.pub' && aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"
Host i-* mi-*
ProxyCommand C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe "aws ec2-instance-connect send-ssh-public-key --instance-id %h --instance-os-user %r --ssh-public-key 'file://~/.ssh/id_rsa.pub' ; aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"
# (Optional) To specify host id
Host ec2-server
HostName i-0b723aba7cd8e8183
User ubuntu
ProxyCommand C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe "aws ec2-instance-connect send-ssh-public-key --instance-id %h --instance-os-user %r --ssh-public-key 'file://~/.ssh/id_rsa.pub' ; aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"
sshで接続する
- 以下のいずれかのコマンドで接続できるはずです
- また、~/.ssh/config に設定があるため、VSCodeからもRemote Explorerで接続できるはずです
ssh ubuntu@i-00000000000000000
ssh ec2-server
- もしも以下のようなエラーが発生したら、known_hosts ファイルを削除するか、sshに
-o 'StrictHostKeyChecking no'
オプションをつけてくださいkex_exchange_identification: Connection closed by remote host
EC2 Instance Connect Endpoint (EIC Endpoint) を使ってEC2 Instance にアクセスする
EC2 InstanceをPublic Subnetに配置する場合
-
Network
- VPCとPublic Subnetを作成します
-
EIC Endpoint
- Public Subnetに配置します
- Securiy Groupはデフォルトのままとしました
-
EC2
- Public Subnetに配置します
- OSはUbuntu Server 22.04を使います
- 常時起動するような使い方はしないため、Elastic IP アドレス (静的 IPv4 アドレス) は割り当てずにパブリックIPを自動割当しています
- 設定は、
AssociatePublicIpAddress: true
- パブリックIPv4アドレスが割り当てられると、0.005USD/hourの料金がかかります (24/2/1以降)
- 一方、EIPを使うとインスタンスが起動しているときは無料ですが、インスタンスが起動していない場合は料金がかかります
- インスタンスの使い方によってどちらにするか決めると良いかと思います
- 設定は、
- Security Group
- EIC Endpointからのsshのみ受け付けるように設定します
-
テンプレート
ec2_eic_public.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: |
Create an EC2 and connect using SSM and EC2 Instance Connect (EIC) Endpoint
Parameters:
SystemName:
Description: System Name
Type: String
AvailabilityZone:
Description: Availability Zone
Type: AWS::EC2::AvailabilityZone::Name
Resources:
#-----------------------------------------------------------------------------
# VPC
#-----------------------------------------------------------------------------
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
Tags:
- Key: Name
Value: !Sub ${SystemName}-vpc
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${SystemName}-igw
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
#-----------------------------------------------------------------------------
# Public Subnet
#-----------------------------------------------------------------------------
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Ref AvailabilityZone
VpcId: !Ref VPC
CidrBlock: 10.0.0.0/20
Tags:
- Key: Name
Value: !Sub ${SystemName}-public-subnet
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-public-rtb
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref PublicRouteTable
PublicSubnetToInternetRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
#-----------------------------------------------------------------------------
# EC2 Instance Connect Endpoint
#-----------------------------------------------------------------------------
EICSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: 'Security Group for an EC2 Instance Connect Endpoint'
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-eic-sg
EICEndpoint:
Type: AWS::EC2::InstanceConnectEndpoint
Properties:
SecurityGroupIds:
- !Ref EICSecurityGroup
SubnetId: !Ref PublicSubnet
#-----------------------------------------------------------------------------
# EC2
#-----------------------------------------------------------------------------
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: 'Security Group for an EC2 server'
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
SourceSecurityGroupId: !Ref EICSecurityGroup
# - IpProtocol: tcp
# FromPort: 3389
# ToPort: 3389
# SourceSecurityGroupId: !Ref EICSecurityGroup
Tags:
- Key: Name
Value: !Sub ${SystemName}-ec2-sg
EC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0595d6e81396a9efb # Ubuntu Server 22.04
InstanceType: t2.micro
# InstanceType: m7i.xlarge
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeType: gp3
VolumeSize: 64
IamInstanceProfile:
!Ref EC2InstanceProfile
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: 0
GroupSet:
- !Ref EC2SecurityGroup
SubnetId: !Ref PublicSubnet
Tags:
- Key: Name
Value: !Sub ${SystemName}-ec2
#-----------------------------------------------------------------------------
# IAM Role for EC2 server
#-----------------------------------------------------------------------------
EC2Role:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${SystemName}-ec2-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref EC2Role
EC2 InstanceをPrivate Subnetに配置する場合
-
Network
- VPCとPublic Subnet、Private Subnetを作成します
-
NAT Gateway
- Public Subnetに配置します
-
EIC Endpoint
- Public Subnetに配置します
- Securiy Groupはデフォルトのままとしました
-
EC2
- Private Subnetに配置します。Private SubnetのRouteTableではdefaultの宛先をNAT Gatewayに設定しています
- OSはUbuntu Server 22.04を使います
- Security Group
- EIC Endpointからのsshのみ受け付けるように設定します
-
テンプレート
ec2_eic_private.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: |
Create an EC2 and connect using SSM and EC2 Instance Connect (EIC) Endpoint
Parameters:
SystemName:
Description: System Name
Type: String
AvailabilityZone:
Description: Availability Zone
Type: AWS::EC2::AvailabilityZone::Name
Resources:
#-----------------------------------------------------------------------------
# VPC
#-----------------------------------------------------------------------------
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
Tags:
- Key: Name
Value: !Sub ${SystemName}-vpc
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${SystemName}-igw
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
#-----------------------------------------------------------------------------
# Public Subnet
#-----------------------------------------------------------------------------
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Ref AvailabilityZone
VpcId: !Ref VPC
CidrBlock: 10.0.0.0/20
Tags:
- Key: Name
Value: !Sub ${SystemName}-public-subnet
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-public-rtb
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref PublicRouteTable
PublicSubnetToInternetRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
#-----------------------------------------------------------------------------
# Private Subnet
#-----------------------------------------------------------------------------
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Ref AvailabilityZone
VpcId: !Ref VPC
CidrBlock: 10.0.16.0/20
Tags:
- Key: Name
Value: !Sub ${SystemName}-private-subnet
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-private-rtb
PrivateSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet
RouteTableId: !Ref PrivateRouteTable
PrivateSubnetToInternetRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway
#-----------------------------------------------------------------------------
# Nat Gateway
#-----------------------------------------------------------------------------
NatGatewayEIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub ${SystemName}-ngw-eip
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
SubnetId: !Ref PublicSubnet
AllocationId: !GetAtt NatGatewayEIP.AllocationId
Tags:
- Key: Name
Value: !Sub ${SystemName}-ngw
#-----------------------------------------------------------------------------
# EC2 Instance Connect Endpoint
#-----------------------------------------------------------------------------
EICSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: 'Security Group for an EC2 Instance Connect Endpoint'
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-eic-sg
EICEndpoint:
Type: AWS::EC2::InstanceConnectEndpoint
Properties:
SecurityGroupIds:
- !Ref EICSecurityGroup
SubnetId: !Ref PublicSubnet
#-----------------------------------------------------------------------------
# EC2
#-----------------------------------------------------------------------------
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: 'Security Group for an EC2 server'
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
SourceSecurityGroupId: !Ref EICSecurityGroup
# - IpProtocol: tcp
# FromPort: 3389
# ToPort: 3389
# SourceSecurityGroupId: !Ref EICSecurityGroup
Tags:
- Key: Name
Value: !Sub ${SystemName}-ec2-sg
EC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0595d6e81396a9efb # Ubuntu Server 22.04
# ImageId: ami-09b74ffed30c40197 # Deep Learning Base OSS Nvidia Driver GPU AMI (Ubuntu 22.04) 20240610
InstanceType: t2.micro
# InstanceType: m7i.xlarge
# InstanceType: g5.4xlarge
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeType: gp3
VolumeSize: 64
IamInstanceProfile:
!Ref EC2InstanceProfile
SecurityGroupIds:
- !Ref EC2SecurityGroup
SubnetId: !Ref PrivateSubnet
Tags:
- Key: Name
Value: !Sub ${SystemName}-ec2
#-----------------------------------------------------------------------------
# IAM Role for EC2 server
#-----------------------------------------------------------------------------
EC2Role:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${SystemName}-ec2-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref EC2Role
デプロイ方法
EC2 Instanceをどちらに配置したいかに応じて、以下コマンド内で使うテンプレートファイルを切り替えてください
Region=ap-northeast-1
AvailabilityZone=ap-northeast-1a
SystemName=sample-ec2-eic-public
TemplateFileName=./ec2_eic_public.yaml
# SystemName=sample-ec2-eic-private
# TemplateFileName=./ec2_eic_private.yaml
aws cloudformation deploy \
--region "${Region}" \
--stack-name "${SystemName}" \
--template-file ${TemplateFileName} \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
SystemName="${SystemName}" \
AvailabilityZone="${AvailabilityZone}"
EC2へのアクセス方法
EC2 Instanceの配置場所に関わらずアクセス方法は同じです
準備
- ローカルPC (Windows/Linux/Macなど) で、以下を用意します
- sshコマンド
- AWS CLI
- AWSコンソール -> EC2 -> Isntanceから、作成したインスタンス名 (i-00000000000000000) を確認しておきます
- 以後、記事内の i-00000000000000000 は適宜置き換えをしてください
簡単な接続確認
- 下記コマンドでインスタンスにアクセスできることを確認します
aws ec2-instance-connect ssh --instance-id i-00000000000000000 --os-user ubuntu --connection-type eice
sshの設定
-
~/.ssh/config
を開き、以下を追記します- Windowsの場合
- 場所は、
C:\Users\ooo\.ssh
-
sh -c
をC:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
に置き換えます - 改行区切りを && から ;(セミコロン) に変えます。Linuxでのエラー処理を気にしなければどちらもセミコロンで大丈夫です
- 場所は、
- Windowsの場合
- 設定の説明
-
i-00000000000000000
というホスト名にsshアクセスしようとしたら記載したコマンドが動きます - まず、ローカルPCの公開鍵をEC2 Instanceに一時的に登録します
- その後、open-tunnelコマンドによってインスタンスへのプライベートトンネルを作成します
-
- ssh接続のたびに毎回インスタンス名を入力するのが面倒な場合は、configにあらかじめ記載して、普段は
ec2-server
という名前のホストを使うと便利です - 参考としてトンネル作成だけを行うコマンドも記載しています。
ec2-server-tunnel
- 後述するリモートデスクトップ接続のための設定もここで記載します。
ec2-server-tunnel-rdp
Host i-* mi-*
ProxyCommand sh -c "aws ec2-instance-connect send-ssh-public-key --instance-id %h --instance-os-user %r --ssh-public-key 'file://~/.ssh/id_rsa.pub' && aws ec2-instance-connect open-tunnel --instance-id %h"
# (Optional) To specify host id
Host ec2-server
HostName i-00000000000000000
User ubuntu
ProxyCommand sh -c "aws ec2-instance-connect send-ssh-public-key --instance-id %h --instance-os-user %r --ssh-public-key 'file://~/.ssh/id_rsa.pub' && aws ec2-instance-connect open-tunnel --instance-id %h"
# (Optional) Sample to create tunnel
Host ec2-server-tunnel
HostName i-00000000000000000
User ubuntu
ProxyCommand sh -c "aws ec2-instance-connect send-ssh-public-key --instance-id %h --instance-os-user %r --ssh-public-key 'file://~/.ssh/id_rsa.pub' && aws ec2-instance-connect open-tunnel --instance-id %h --remote-port 22 --local-port 2222"
# (Optional) Sample to create tunnel for rdp
Host ec2-server-tunnel-rdp
HostName i-00000000000000000
User ubuntu
ProxyCommand sh -c "aws ec2-instance-connect send-ssh-public-key --instance-id %h --instance-os-user %r --ssh-public-key 'file://~/.ssh/id_rsa.pub' && aws ec2-instance-connect open-tunnel --instance-id %h --remote-port 3389 --local-port 3389"
Host i-* mi-*
ProxyCommand C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe "aws ec2-instance-connect send-ssh-public-key --instance-id %h --instance-os-user %r --ssh-public-key 'file://~/.ssh/id_rsa.pub' ; aws ec2-instance-connect open-tunnel --instance-id %h"
# (Optional) To specify host id
Host ec2-server
HostName i-00000000000000000
User ubuntu
ProxyCommand C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe "aws ec2-instance-connect send-ssh-public-key --instance-id %h --instance-os-user %r --ssh-public-key 'file://~/.ssh/id_rsa.pub' ; aws ec2-instance-connect open-tunnel --instance-id %h"
# (Optional) Sample to create tunnel
Host ec2-server-tunnel
HostName i-00000000000000000
User ubuntu
ProxyCommand C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe "aws ec2-instance-connect send-ssh-public-key --instance-id %h --instance-os-user %r --ssh-public-key 'file://~/.ssh/id_rsa.pub' ; aws ec2-instance-connect open-tunnel --instance-id %h --remote-port 22 --local-port 2222"
# (Optional) Sample to create tunnel for rdp
Host ec2-server-tunnel-rdp
HostName i-00000000000000000
User ubuntu
ProxyCommand C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe "aws ec2-instance-connect send-ssh-public-key --instance-id %h --instance-os-user %r --ssh-public-key 'file://~/.ssh/id_rsa.pub' ; aws ec2-instance-connect open-tunnel --instance-id %h --remote-port 3389 --local-port 3389"
sshで接続する
- 以下のいずれかのコマンドで接続できるはずです
- また、~/.ssh/config に設定があるため、VSCodeからもRemote Explorerで接続できるはずです
ssh ubuntu@i-00000000000000000
ssh ec2-server
- トンネル作成とssh接続をあえて分けると以下のようになります
ssh ec2-server-tunnel
ssh ubuntu@localhost -p 2222
リモートデスクトップ (xrdp) 環境を構築する
- 最後に構築したパターン4 (以下の環境) に対して、リモートデスクトップ (xrdp) 追加します
- key pairはec2-instance-connect send-ssh-public-key で管理
- 接続にはEC2 Instance Connect Endpoint (EIC Endpoint) を使用
- EC2 InstanceはPrivate Subnetに配置
- ここまで使用したテンプレートだと、インスタンスタイプは
t2.micro
とデスクトップ環境には貧弱なものとなっています。適当にm7i.xlarge
あたりに変更しておいてください
xrdp用のポートを開放する
- EIC Endpoint -> EC2 Instance に対してRDP用のポート (3389) を開放します
- 外部に対するポート開放ではないです。送信元はEIC Endpointに限定します
- 以下のいずれかの方法で開放します
- なお、EIC Endpointにおいて使用可能なポートはSSH用の22とRDP用の3389のみになります。もしも他のポートを使おうとしたら以下のようなエラーが発生します
awscli.customizations.ec2instanceconnect.websocket - ERROR - {"ErrorCode":"InvalidParameter","Message":"The specified RemotePort is not valid. Specify either 22 or 3389 as the RemotePort and retry your request."}
- 手段1: テンプレートにInboundルールを追加する
- テンプレート内の
EC2SecurityGroup
に以下を追加しますSecurityGroupIngress: - IpProtocol: tcp FromPort: 3389 ToPort: 3389 SourceSecurityGroupId: !Ref EICSecurityGroup
- テンプレート内の
- 手段2: AWSコンソール上で手動でInboundルールを追加する
- Type: RDP (3389)
- Source: Custom (EICSecurityGroup)
EC2の設定を行う
- EC2 Instance内にユーザーを追加します
- ここでは
user0
というユーザーを作りsudo権限を付与しています - デフォルトの
ubuntu
ユーザーをそのまま使用する場合はパスワード設定だけ行います
- ここでは
- デスクトップ環境とxrdpをインストールします
- 上記を行うために、EC2にSSHでログイン後下記コマンドを実行します
- デスクトップ環境はお好みで
# sudo passwd ubuntu
sudo adduser user0
sudo usermod -aG sudo user0
sudo apt update
sudo apt install -y ubuntu-desktop xrdp
sudo systemctl enable xrdp
sudo reboot
リモートデスクトップ接続する
- まず、ローカルPCから下記コマンドでRDP用のトンネルを作成します。 (設定は前述のconfig参照)
ssh ec2-server-tunnel-rdp
- 以下のホストに対してリモートデスクトップ接続します
localhost:3389
- Windowsだと標準の「リモートデスクトップ接続」が使えます
(おまけ) VNCを使いたい場合
- リモートデスクトップだと、キーボード入力など局所的な画面変化への応答性は高いのですが、画面全体が変わるようなシーンで遅延が大きいように見えます
- 負荷は高くなりますが、VNCを使いたい場合もあるかと思います
- 一応以下で出来ました
- 適当にtigervncあたりをインストールする
- 3389ポートを使うように設定する