この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは!コンサル部のinomaso(@inomasosan)です。
プライベートサブネットのEC2にアクセスしたい場合は、これまでパブリックサブネットに踏み台サーバを作成してアクセスしていましたが、AWS Systems Manager Session Manager(以下、セッションマネージャー)を利用すれば、踏み台サーバの構築・運用が不要になるので検証してみました。
検証にあたり、以下のAWS公式ナレッジが分かりやすかったので、こちらをベースにした検証記事となります。
この記事で学べること
- セッションマネージャーとVPCエンドポイントの知識
- セッションマネージャーを使用したプライベートサブネットのLinux用EC2への接続方法
- Terraformでのコードの書き方
前提知識
1. AWS Systems Manager Session Managerとは
セッションマネージャはAWS Systems Managerの機能の一つです。
AWSマネージメントコンソールやAWS CLI経由で、EC2に接続することができます。
2. VPCエンドポイントとは
VPC内のリソースと、サポートされているAWSサービスやVPCエンドポイントサービス間で、プライベートに接続可能になります。
VPCエンドポイントを利用する場合は、該当のAWSサービスへインターネット経由でのアクセスが不要になるため、インターネットゲートウェイ(以下、IGWという)やNatGateway等を作成する必要はありません。
システム構成図
今回、検証で構築するシステム構成図です。
必要なAWSリソースや、ネットワーク要件を記載しています。
セッションマネージャーやS3へアクセスするためにVPCエンドポイントを作成しますが、IGWやNatGateway等は作成しておりません。
必要な手順
AWSマネージメントコンソールからEC2へアクセスする場合は、1. セッションマネージャー関連のリソース作成
のみの手順となります。
AWS CLIやSSHでアクセスしたい場合は2. AWS CLIやSSHでアクセスする準備
も必要となります。
1. セッションマネージャー関連のリソース作成
- EC2にSSM Agentのバージョン
2.3.68.0
以降をインストール - EC2に
AmazonSSMManagedInstanceCore
ポリシーを含むIAMロールをアタッチ - プライベートサブネットにセッションマネージャー接続用に、以下のVPCエンドポイントを作成
com.amazonaws.ap-northeast-1.ssm
com.amazonaws.ap-northeast-1.ssmmessages
com.amazonaws.ap-northeast-1.ec2messages
※1. セキュリティグループは、VPC内からHTTPSのインバウンド通信を許可
※2. EC2とVPCエンドポイントのサブネットを分ける分けないに関わらずプライベートDNS名
を有効にする
- SSMはAmazon S3 出力ログをアップロードし、SSM エージェントを更新
com.amazonaws.ap-northeast-1.s3
2. AWS CLIやSSHでアクセスする準備
- クライアントPCからAWS CLIで接続するために、以下のポリシーをもつIAMユーザかロールを作成
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ssm:StartSession",
"Resource": [
"arn:aws:ec2:*:*:instance/(インスタンスID)",
"arn:aws:ssm:*:*:document/AWS-StartSSHSession"
]
}
]
}
- クライアントPCにAWS CLI 用の Session Manager plugin をインストール
- SSH 設定ファイル(
~/.ssh/config
)を更新して、Session Manager セッションを開始し、接続を介してすべてのデータを転送するプロキシコマンドを実行できるようにします。
今回はAWS CLIでセッションマネージャー接続用にssmsession
プロファイルを作成しています。プロファイルを分けていないdafault
の場合は--profile ssmsession
は不要となります。
# SSH over Session Manager
host i-* mi-*
ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p' --profile ssmsession"
やってみた
1. 環境
今回実行した環境は以下の通りです。
- macOS Catalina 10.15.7
- Terraform 0.14.8
- AWSプロバイダー 3.35.0
2. コード
・VPC関連
VPCやサブネット、ルートテーブルは作成しますが、IGWやNatGateway等は不要なため作成しません。
また、今回はEC2とVPCエンドポイントのサブネットを分けていますが、同じサブネットでも問題ありません。
####################
# VPC
####################
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
instance_tenancy = "default"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "inomaso-dev-vpc"
}
}
####################
# Subnet
####################
resource "aws_subnet" "sub_privatelink_1a" {
vpc_id = aws_vpc.vpc.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-northeast-1a"
tags = {
Name = "inomaso-dev-privatelink-subnet-a"
}
}
resource "aws_subnet" "sub_app_1a" {
vpc_id = aws_vpc.vpc.id
cidr_block = "10.0.11.0/24"
availability_zone = "ap-northeast-1a"
tags = {
Name = "inomaso-dev-app-subnet-a"
}
}
resource "aws_route_table" "privatelink_rt" {
vpc_id = aws_vpc.vpc.id
tags = {
Name = "inomaso-dev-privatelink-rtb"
}
}
resource "aws_route_table" "app_rt" {
vpc_id = aws_vpc.vpc.id
tags = {
Name = "inomaso-dev-app-rtb"
}
}
resource "aws_route_table_association" "sub_privatelink_1a_rt_assocication" {
subnet_id = aws_subnet.sub_privatelink_1a.id
route_table_id = aws_route_table.privatelink_rt.id
}
resource "aws_route_table_association" "sub_app_1a_rt_assocication" {
subnet_id = aws_subnet.sub_app_1a.id
route_table_id = aws_route_table.app_rt.id
}
・EC2関連
今回はSSM Agentがインストール済みである、最新のAmazon Linux 2のAMIを使用してEC2を起動します。
セキュリティグループは、今回のネットワーク要件だとEC2へのインバウンドルールは不要なため、アウトバウンドルールのみの設定となります。
EC2のIAMロールには、AWS Systems Managerのサービスコア機能を使用できるようにAmazonSSMManagedInstanceCore
ポリシーを指定します。
####################
# EC2
####################
resource "aws_instance" "ec2" {
# Amazon Linux 2
ami = "ami-06098fd00463352b6"
instance_type = "t3.micro"
vpc_security_group_ids = [aws_security_group.ec2.id]
key_name = "aws-ssh-key"
subnet_id = aws_subnet.sub_app_1a.id
# EBS最適化を有効
ebs_optimized = "true"
# IAM Role
iam_instance_profile = "EC2RoleforSSM"
# EBSのルートボリューム設定
root_block_device {
# ボリュームサイズ(GiB)
volume_size = 8
# ボリュームタイプ
volume_type = "gp3"
# GP3のIOPS
iops = 3000
# GP3のスループット
throughput = 125
# EC2終了時に削除
delete_on_termination = true
# EBSのNameタグ
tags = {
Name = "inomaso-dev-ec2"
}
}
# EC2のNameタグ
tags = {
Name = "inomaso-dev-ec2"
}
}
####################
# EC2 Security Group
####################
resource "aws_security_group" "ec2" {
name = "inomaso-dev-ec2-sg"
description = "inomaso-dev-ec2-sg"
vpc_id = aws_vpc.vpc.id
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "inomaso-dev-ec2-sg"
}
}
####################
# EC2 IAM Role
####################
data "aws_iam_policy_document" "ssm_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ec2.amazonaws.com"]
}
}
}
resource "aws_iam_instance_profile" "ssm_role" {
name = "EC2RoleforSSM"
role = aws_iam_role.ssm_role.name
}
resource "aws_iam_role" "ssm_role" {
name = "EC2RoleforSSM"
assume_role_policy = data.aws_iam_policy_document.ssm_role.json
}
resource "aws_iam_role_policy_attachment" "ssm_role" {
role = aws_iam_role.ssm_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
尚、Amazon Linux 2へ明示的にSSM Agentの最新バージョンをインストールしたい場合は、以下のユーザデータを設定しEC2起動時に実行させてください。
#!/bin/bash
sudo yum install -y https://s3.ap-northeast-1.amazonaws.com/amazon-ssm-ap-northeast-1/latest/linux_amd64/amazon-ssm-agent.rpm
・VPCエンドポイント関連
VPCエンドポイントのIAMポリシーは、今回はフルアクセスで作成しています。
また、SSMAgent更新用にS3のGateway側VPCエンドポイント作成時にroute_table_ids
を設定することで、ルートテーブルのS3へのルートが追加されます。
セッションマネージャ接続用のInterface型VPCエンドポイントに設定するセキュリティグループは、VPC内部からのHTTPSアクセスのインバウンドルールを追加します。
尚、アウトバウンドを許可していなくても通信可能なため、今回はインバウンドルールのみ定義します。
####################
# VPC Endpoint
####################
data "aws_iam_policy_document" "vpc_endpoint" {
statement {
effect = "Allow"
actions = [ "*" ]
resources = [ "*" ]
principals {
type = "AWS"
identifiers = [ "*" ]
}
}
}
resource "aws_vpc_endpoint" "ssm" {
vpc_endpoint_type = "Interface"
vpc_id = aws_vpc.vpc.id
service_name = "com.amazonaws.ap-northeast-1.ssm"
policy = data.aws_iam_policy_document.vpc_endpoint.json
subnet_ids = [
aws_subnet.sub_privatelink_1a.id
]
private_dns_enabled = true
security_group_ids = [
aws_security_group.ssm.id
]
}
resource "aws_vpc_endpoint" "ssmmessages" {
vpc_endpoint_type = "Interface"
vpc_id = aws_vpc.vpc.id
service_name = "com.amazonaws.ap-northeast-1.ssmmessages"
policy = data.aws_iam_policy_document.vpc_endpoint.json
subnet_ids = [
aws_subnet.sub_privatelink_1a.id
]
private_dns_enabled = true
security_group_ids = [
aws_security_group.ssm.id
]
}
resource "aws_vpc_endpoint" "ec2messages" {
vpc_endpoint_type = "Interface"
vpc_id = aws_vpc.vpc.id
service_name = "com.amazonaws.ap-northeast-1.ec2messages"
policy = data.aws_iam_policy_document.vpc_endpoint.json
subnet_ids = [
aws_subnet.sub_privatelink_1a.id
]
private_dns_enabled = true
security_group_ids = [
aws_security_group.ssm.id
]
}
resource "aws_vpc_endpoint" "s3" {
vpc_endpoint_type = "Gateway"
vpc_id = aws_vpc.vpc.id
service_name = "com.amazonaws.ap-northeast-1.s3"
policy = data.aws_iam_policy_document.vpc_endpoint.json
route_table_ids = [
aws_route_table.app_rt.id
]
}
####################
# VPC Endpoint Security Group
####################
resource "aws_security_group" "ssm" {
name = "inomaso-dev-ssm-sg"
description = "inomaso-dev-ssm-sg"
vpc_id = aws_vpc.vpc.id
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [
"10.0.0.0/16"
]
}
tags = {
Name = "inomaso-dev-ssm-sg"
}
}
・クライアントPC関連
クライアントPCからAWS CLI使用してセッションを開始するためのIAM作成です。
今回使用する環境は、AssumeRole+MFAが必要なためIAMロールを作成します。
また、IAMポリシーでは、任意のEC2へのセッション開始を許可しました。
リージョンやアカウントID、インスタンスIDを指定することで制限を厳しくすることもできます。
####################
# VPC Endpoint IAM Role
####################
data "aws_iam_policy_document" "assume_role" {
statement {
effect = "Allow"
actions = [ "sts:AssumeRole" ]
principals {
type = "AWS"
identifiers = [ "arn:aws:iam::1234567890:user/hogehoge" ]
}
condition {
test = "Bool"
variable = "aws:MultiFactorAuthPresent"
values = ["true"]
}
}
}
resource "aws_iam_role" "session_role" {
name = "SessionRoleforSSM"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}
data "aws_iam_policy_document" "ssm_startsesstion" {
statement {
effect = "Allow"
actions = [ "ssm:StartSession" ]
resources = [
"arn:aws:ec2:*:*:instance/*",
"arn:aws:ssm:*:*:document/AWS-StartSSHSession"
]
}
}
resource "aws_iam_role_policy" "session_role" {
name = "SSMSession_policy"
role = aws_iam_role.session_role.id
policy = data.aws_iam_policy_document.ssm_startsesstion.json
}
3. 接続確認
下記いずれかの方法でEC2とのセッションを開始します。
・AWSマネジメントコンソール(EC2コンソール)
- AWSマネジメントコンソールにログインします。
- EC2コンソールを開き、接続したいEC2にチェックを入れ、接続をクリックします。
- セッションセッションマネージャータブを選択し、接続をクリックします。
※環境作成してすぐの場合は、接続できない場合があるので十数分ほどお待ちください。 - 接続が確立され、下記のような画面が表示されればbashコマンドを実行可能です。
・AWS CLI
--target
にEC2のinstance-id
指定し、セッションを開始します。
特にプロファイルを分けていない場合は、--profile
の指定は不要です。
% aws ssm start-session --target instance-id --profile ssmsession
Starting session with SessionId: hoge.hoge-instance-id
sh-4.2$
・SSH
セッションマネージャ経由でSSH接続します。
/path/my-key-pair.pem
にキーペアを、username
にはEC2のOSユーザ名(ec2-user等)を指定してください。
% ssh -i /path/my-key-pair.pem username@instance-id
The authenticity of host 'instance-id ()' can't be established.
ECDSA key fingerprint is SHA256:123456789ABCDEFGHIJKLMN.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'instance-id' (ECDSA) to the list of known hosts.
__| __|_ )
_| ( / Amazon Linux 2 AMI
___|\___|___|
https://aws.amazon.com/amazon-linux-2/
No packages needed for security; 2 packages available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-10-0-11-200 ~]$
・その他
上記以外にも接続方法はあります。
詳細は公式ドキュメントをご参照願います。
検証中に感じたQA
1. システム構成図のセッションマネージャとEC2間で通信の向き逆じゃね?
EC2のSSMAgentから、セッションマネージャのエンドポイントにポーリングしているので、システム構成図の通信の向きは正しいです。
2. EC2のSSMAgentは自動更新できないの?
AWS Systems Managerコンソールのマネージドインスタンスページで、Agent auto update (エージェントの自動更新)を選択で設定可能です。
3. クライアントPCからセッションマネージャへのアクセス制限はどうするの?
VPCエンドポイントのセキュリティグループでは、クライアントPCからのアクセス制御を設定することはできません。
アクセス制御するためには、クライアントPC用のIAMポリシーで実施する必要があります。
詳しくは、弊社の以下ブログに詳細な内容がまとめられておりますので、かなりオススメです。
4. VPCエンドポイントやプライベートDNSの仕組みがいまいちよく分からんタスケテ?
弊社の以下ブログに、技術同人誌が作れるくらいの濃い内容がまとまっています。
参考
まとめ
セッションマネージャでプライベートサブネットのEC2へのアクセスするための手順について、一つにまとまっている記事を見つけられなかったので検証がてらに書いてみました。
設定や考慮点がいくつかあるものの、VPCの設定変更が軽微で、EC2のインバウンドルール変更が不要なので、既存環境への一時的なアクセス経路追加等でも有効な手段だと感じました。
この記事が、どなたかのお役に立てば幸いです。それでは!