privateサブネットに配置したAWS EC2インスタンスにssmでログインする
はじめに
前回はpublicサブネットにEC2インスタンスを配置してssmでログインできるようにしました。
しかし、実際の仕事ではEC2インスタンスをpublicサブネットに置くことは稀だと思っています。
そのため今回は、privateサブネットにEC2インスタンスを配置してSSM経由でセッションを開始できるようにします。
目標
- AWSマネジメントコンソールからEC2インスタンスへセッションを開始できる
- ローカルPCから
aws ssm session-start
コマンドを実行できるようにする
前提
- ローカルでterraformを実行できる
- ローカルでaws cliを実行できる
~/.aws/credentials
にAWSアクセスキーを設定している
バージョン
ツール | バージョン |
---|---|
terraform | v1.4.6 |
aws cli | aws-cli/2.9.19 |
準備
環境変数
環境変数をエクスポートします。
筆者はdirenvを使っているため.env
ファイルを定義します
AWS_PROFILE
の値は、~/.aws/credentials
に合わせて設定してください
$ cat ~/.aws/credentials[profile_name]aws_access_key_id = xxxxxxxxxxaws_secret_access_key = xxxxxxxxxx
AWS_PROFILE=profile_nameAWS_REGION=ap-northeast-1
terraform
使用するTerraformのバージョンは2023年5月現在最新の1.4.6を使用します。
$ asdf list all terraform | tail1.4.11.4.21.4.31.4.41.4.51.4.61.5.0-alpha202304051.5.0-alpha202305041.5.0-beta11.5.0-beta2
$ asdf local terraform 1.4.6 # .tool-versions作成$ terraform versionTerraform v1.4.6on darwin_arm64
terraform init
aws providerのバージョンも2023年5月現在、最新のバージョンにします。
terraform { required_version = "1.4.6"
required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0.1" } }}
provider "aws" { region = "ap-northeast-1"}
tfstateはあくまで勉強用なのでs3ではなくローカルに置くことにします。
terraform { backend "local" { path = "./terraform.tfstate" }}
terraform initを実行してみます。
$ terraform init
Initializing the backend...
Initializing provider plugins...- Reusing previous version of hashicorp/aws from the dependency lock file- Using previously-installed hashicorp/aws v5.0.1
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to seeany changes that are required for your infrastructure. All Terraform commandsshould now work.
If you ever set or change modules or backend configuration for Terraform,rerun this command to reinitialize your working directory. If you forget, othercommands will detect it and remind you to do so if necessary.
とりあえず準備は完了しました。この段階でmain.tfには何も書いていませんが、ここまで以下のようなディレクトリ構成になっています。
$ tree -a -L 1.├── .env├── .gitignore├── .terraform├── .terraform.lock.hcl├── .tool-versions├── backend.tf├── main.tf└── providers.tf
privateサブネットに配置したEC2インスタンスがアクセスできるよう設定する
EC2インスタンスを作成する
VPCを含め、EC2インスタンスまで一気に作成します。
resource "aws_vpc" "ssm_vpc" { cidr_block = "172.16.0.0/16"
tags = { Name = "aws-ssm-example" }}
resource "aws_subnet" "ssm_private_subnet" { vpc_id = aws_vpc.ssm_vpc.id cidr_block = "172.16.11.0/24" availability_zone = "ap-northeast-1c"
tags = { Name = "ssm-private-subnet" }}
resource "aws_security_group" "ssm_private_sg" { name = "ssm-private-sg" description = "ssm private security group" vpc_id = aws_vpc.ssm_vpc.id
egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] ipv6_cidr_blocks = ["::/0"] }
tags = { Name = "ssm-private-sg" }}
data "aws_ami" "amzn-linux-2023-ami" { most_recent = true owners = ["amazon"]
filter { name = "name" values = ["al2023-ami-2023.*-x86_64"] }}
resource "aws_instance" "ssm" { ami = data.aws_ami.amzn-linux-2023-ami.id instance_type = "t2.micro" subnet_id = aws_subnet.ssm_private_subnet.id iam_instance_profile = aws_iam_instance_profile.ssm_instance_core.name tags = { Name = "aws-ssm-private-example" }}
data "aws_iam_policy_document" "instance_assume_role_policy" { statement { actions = ["sts:AssumeRole"]
principals { type = "Service" identifiers = ["ec2.amazonaws.com"] } }}
data "aws_iam_policy" "ssm_managed_instance_core" { arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"}
resource "aws_iam_role" "ssm_instance_core" { name = "SSMInstanceCoreRole" assume_role_policy = data.aws_iam_policy_document.instance_assume_role_policy.json managed_policy_arns = [data.aws_iam_policy.ssm_managed_instance_core.arn]}
resource "aws_iam_instance_profile" "ssm_instance_core" { name = "ssm" role = aws_iam_role.ssm_instance_core.name}
terraform applyを実行します。
$ terraform plan$ terraform apply --auto-approve
EC2インスタンス作成後、接続
ボタンを押します。
しかし・・・
まだインスタンスに接続することはできません。
エラーには、以下のことが書いてあります。
- インスタンスに SSM エージェントがインストールされていません。エージェントは Windows インスタンスと Linux インスタンスの両方にインストールできます。
- 必須の IAM インスタンスプロファイルがインスタンスにアタッチされていません。プロファイルは AWS Systems Manager 高速セットアップを使ってアタッチできます。
EC2インスタンスに使っているAMIはAmazon Linux 2023です。このAMIはデフォルトでSSMエージェントが入っているので、原因ではありません。 SSM Agent がプリインストールされた Amazon Machine Images (AMIs) - AWS Docs
IAM インスタンスプロファイルについては、先ほどapplyしたTerraformのコードをにしっかり書いています。インスタンスプロファイルも原因ではありません。
data "aws_iam_policy_document" "instance_assume_role_policy" { statement { actions = ["sts:AssumeRole"]
principals { type = "Service" identifiers = ["ec2.amazonaws.com"] } }}
data "aws_iam_policy" "ssm_managed_instance_core" { arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"}
resource "aws_iam_role" "ssm_instance_core" { name = "SSMInstanceCoreRole" assume_role_policy = data.aws_iam_policy_document.instance_assume_role_policy.json managed_policy_arns = [data.aws_iam_policy.ssm_managed_instance_core.arn]}
resource "aws_iam_instance_profile" "ssm_instance_core" { name = "ssm" role = aws_iam_role.ssm_instance_core.name}
原因を調べる
Systems Manager を使用してインターネットアクセスなしでプライベート EC2 インスタンスを管理できるように、VPC エンドポイントを作成するにはどうすればよいですか?
という記事によると、やっていないことがわかりました。
-
VPCエンドポイントを追加する 最低以下3つのサービスにエンドポイントを追加する必要があります。
- com.amazonaws.ap-northeast-1.ec2messages
- com.amazonaws.ap-northeast-1.ssm
- com.amazonaws.ap-northeast-1.ssmmessages
-
VPCエンドポイントにセキュリティグループに設定する VPCエンドポイントにセキュリティグループを設定し、インバウンドルールとしてHTTP(ポート443)のトラフィックを許可する必要があります。
-
VPCエンドポイントのDNSを有効にする VPCエンドポイントにはDNSを有効にする設定があるので、これを有効にします。 プライベート DNS 名を有効にする - AWS Docs
-
VPCにDNSホスト名とDNS解決を有効にする VPCエンドポイントのDNSを有効にするには、VPCのDNSホスト名とDNS解決を有効にする必要があります。 インターフェイス VPC エンドポイントを使用して AWS のサービス にアクセスする - AWS Docs
Terraformのコードを修正 & 追記します。
resource "aws_vpc" "ssm_vpc" { cidr_block = "172.16.0.0/16"
enable_dns_hostnames = true enable_dns_support = true tags = { Name = "aws-ssm-example" }}
# 省略...
# == 以下を追加 ====
resource "aws_security_group" "ssm" { name = "ssm-endpoint-sg" description = "ssm-endpoint-sg" vpc_id = aws_vpc.ssm_vpc.id ingress { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = [ "172.16.0.0/16" ] }
tags = { Name = "ssm-endpoint-sg" }}
resource "aws_vpc_endpoint" "ec2_messages" { vpc_id = aws_vpc.ssm_vpc.id service_name = "com.amazonaws.ap-northeast-1.ec2messages" vpc_endpoint_type = "Interface" private_dns_enabled = true
security_group_ids = [ aws_security_group.ssm.id ] subnet_ids = [ aws_subnet.ssm_private_subnet.id ]}
resource "aws_vpc_endpoint" "ssm" { vpc_id = aws_vpc.ssm_vpc.id service_name = "com.amazonaws.ap-northeast-1.ssm" vpc_endpoint_type = "Interface" private_dns_enabled = true security_group_ids = [ aws_security_group.ssm.id ] subnet_ids = [ aws_subnet.ssm_private_subnet.id ]}
resource "aws_vpc_endpoint" "ssm_messages" { vpc_id = aws_vpc.ssm_vpc.id service_name = "com.amazonaws.ap-northeast-1.ssmmessages" vpc_endpoint_type = "Interface" private_dns_enabled = true security_group_ids = [ aws_security_group.ssm.id ] subnet_ids = [ aws_subnet.ssm_private_subnet.id ]}
接続
terraform applyを実行した後、接続できるか確認しましょう。
AWSマネジメントコンソールからアクセスする
EC2のページからインスタンスを選択して接続
ボタンをクリックしてみます。
privateサブネットに配置した EC2インスタンスは接続
ボタンが押せるようになっています!
(一度EC2インスタンスを作り直したので、インスタンスIDが変わっています)
ボタンをクリックするとAWSマネジメントコンソールからセッションを開始することができました!
これで1つ目の目標 “AWSマネジメントコンソールからEC2インスタンスへセッションを開始できる” が完了しました。
ターミナルからセッションを開始する
aws ssm start-session
コマンドでセッションを開始するには、Session Manegerプラグインをインストール必要があります。
$ aws ssm start-session --target i-00b32e54399466f12SessionManagerPlugin is not found. Please refer to SessionManager Documentation here: http://docs.aws.amazon.com/console/systems-manager/session-manager-plugin-not-found
$ curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/mac_arm64/sessionmanager-bundle.zip" -o "sessionmanager-bundle.zip"$ unzip sessionmanager-bundle.zip$ sudo ./sessionmanager-bundle/install -i /usr/local/sessionmanagerplugin -b /usr/local/bin/session-manager-plugin
参考:AWS CLI 用の Session Manager プラグインをインストールする
無事にセッションをスタートさせることができました。
これで2つ目の目標 “ローカルPCからaws ssm session-start
コマンドを実行できるようにする” が完了しました。
$ aws ssm start-session --target i-07d3006a86659ee30
Starting session with SessionId: xxxxx-088961133074ed89dsh-5.2$
その他学んだこと
- VPCインターフェイスエンドポイントからAWSのサービスを使用するとき、サービスエンドポイントを名前解決できないため、VPCのDNSを有効にないといけない
- VPCインターフェイスエンドポイントを使用するときは、VPCに関連しているセキュリティグループのインバウンドルールにHTTPSを許可しないといけない
さいごに
無事にprivateサブネットに配置したEC2インスタンスもSSMを使ってアクセスできるようになりました。
しかしこの記事では、IAMでアクセス制御したり、ログについて考慮しませんでした。
なので、実務で使用する際はもう少しTerraformのコードを修正した方が良いと思いました。
参考
セッションマネージャーを使って鍵ストレスの無いEC2アクセス! - Classsmethod セッションマネージャーを使用してプライベートサブネットのLinux用EC2にアクセス(VPCエンドポイント編) - Classmethod