この記事は個人ブログと同じ内容です
Session Managerについて
Session ManagerはAWS Systems Managerの一部であり、安全にEC2インスタンスなどに接続するためのツールです。Session Managerを使用すれば、SSHキーやパブリックIPを割り当てることなく、AWSのプライベートサブネットに配置されたEC2インスタンスに接続することができます。
VPCエンドポイントについて
VPCエンドポイントは、VPC内のリソース(例:EC2 インスタンス)がインターネットを経由せずに、他のAWSサービスに接続できるようにするための仮想デバイスです。
VPCエンドポイントを使ったSession Managerでのアクセスに必要なもの
・IAMポリシーのAmazonSSMManagedInstanceCore
を持ったIAMロールを持つEC2
・SSM エージェントがインストールされているEC2
・以下の4つのインターフェース型のVPCエンドポイント
・com.amazonaws.region.ec2.
・com.amazonaws.region.ec2messages.
・com.amazonaws.region.ssm.
・com.amazonaws.region.ssmmessages.
・VPCエンドポイントへの443通信の許可.
今回作るもの
今回はプライベートサブネットにあるEC2にVPCエンドポイント経由でSession Managerを使ってセキュアにアクセスできるようにします。
作るのは、以下の通りです。
・VPCとサブネット.
・EC2インスタンス.
・IAMロール.
・VPCエンドポイント.
図にするとこんな感じです。
実際に作っていく
ステップ1 VPCとサブネットを作る
VPCとプライベートサブネットをEC2用とVPCエンドポイント用で2つ作っていきます。
VPCとサブネットのcidr_blockと設計は以下の通りです。
10.0.0.0/16 は全体のアドレス範囲、
10.0.0.0/18 はリージョン、AZ用(4個)、
10.0.0.0/20 はサブネット用(4個)、
アドレス部は12ビット(=4096-5個)
になるような設計をしました。
name | cidr_block |
---|---|
VPC | 10.0.0.0/16 |
EC2のサブネット | 10.0.16.0/20 |
VPCエンドポイントのサブネット | 10.0.48.0/20 |
locals { aws_region = "ap-northeast-1" name = "sample" cidr_blocks = { vpc = "10.0.0.0/16" private = "10.0.16.0/20" endpoint = "10.0.48.0/20" } } # VPC # 10.0.0.0/16 は全体のアドレス範囲 # 10.0.0.0/18 はリージョン、AZ用(4個) # 10.0.0.0/20 はサブネット用(4個) # アドレス部は12ビット(=4096-5個) resource "aws_vpc" "main" { cidr_block = local.cidr_blocks.vpc enable_dns_hostnames = true enable_dns_support = true tags = { Name = local.name } }
# EC2用のプライベートサブネット resource "aws_subnet" "private" { availability_zone = "ap-northeast-1a" cidr_block = local.cidr_blocks.private vpc_id = aws_vpc.main.id } resource "aws_network_acl" "private" { vpc_id = aws_vpc.main.id subnet_ids = [aws_subnet.main.id] } resource "aws_route_table" "private" { vpc_id = aws_vpc.main.id } resource "aws_route_table_association" "private" { subnet_id = aws_subnet.private.id route_table_id = aws_route_table.private.id }
# VPCエンドポイント用のプライベートサブネット resource "aws_subnet" "endpoint" { availability_zone = "ap-northeast-1a" cidr_block = local.cidr_blocks.endpoint vpc_id = aws_vpc.main.id } resource "aws_network_acl" "endpoint" { vpc_id = aws_vpc.main.id subnet_ids = [aws_subnet.main.id] } resource "aws_route_table" "endpoint" { vpc_id = aws_vpc.main.id } resource "aws_route_table_association" "endpoint" { subnet_id = aws_subnet.endpoint.id route_table_id = aws_route_table.endpoint.id }
ステップ2 IAMロールの作成
IAMポリシーのAmazonSSMManagedInstanceCore
を持ったIAMロールを作ります。
data "aws_iam_policy_document" "assume_role" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["ec2.amazonaws.com"] } } } resource "aws_iam_role" "main" { name = "ssm-iam-role" assume_role_policy = data.aws_iam_policy_document.assume_role.json } data "aws_iam_policy" "systems_manager" { arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" } resource "aws_iam_role_policy_attachment" "main" { role = aws_iam_role.main.name policy_arn = data.aws_iam_policy.systems_manager.arn } resource "aws_iam_instance_profile" "systems_manager" { name = "ssm-instance-profile" role = aws_iam_role.main.name }
ステップ3 EC2インスタンスの作成
EC2用のプライベートサブネットに、先ほど作ったIAMロールを付与したEC2インスタンスを作っていきます。 AMIは、SSM エージェントがすでにインストールされているAmazon Linux2023を使います。
resource "aws_instance" "main" { ami = "ami-05779067e4eff0b9d" instance_type = "t4g.nano" subnet_id = local.cidr_blocks.private associate_public_ip_address = false vpc_security_group_ids = [aws_security_group.ec2.id] availability_zone = "ap-northeast-1a" iam_instance_profile = aws_iam_instance_profile.systems_manager.name } resource "aws_security_group" "ec2" { name = "ec2-sg" vpc_id = aws_vpc.main.id description = "ec2 security group" }
ステップ4 VPCエンドポイントの作成
以下の4つのインターフェースエンドポイントを作成します。
・com.amazonaws.region.ec2.
・com.amazonaws.region.ec2messages.
・com.amazonaws.region.ssm.
・com.amazonaws.region.ssmmessages.
resource "aws_vpc_endpoint" "main" { for_each = { ec2 = "com.amazonaws.ap-northeast-1.ec2" ec2messages = "com.amazonaws.ap-northeast-1.ec2messages" ssm = "com.amazonaws.ap-northeast-1.ssm" ssmmessages = "com.amazonaws.ap-northeast-1.ssmmessages" } service_name = each.value vpc_endpoint_type = "Interface" vpc_id = var.vpc_id security_group_ids = [aws_security_group.vpc_endpoint.id] subnet_ids = [aws_subnet.private.id] } resource "aws_security_group" "vpc_endpoint" { description = "vpc-endpoint" name = "vpc-endpoint" vpc_id = aws_vpc.main.id }
ステップ5 セキュリティグループとNACLの調整
先ほど作成したエンドポイントに対して、EC2からポート443のアクセスができるようにセキュリティグループとNACLを調整します。
注意ポイント:
・行きの通信だけではなく、帰りの通信も通れるようにしよう
・NACLは帰りの通信も記載する必要がありますが、セキュリティグループはステートレスなので、行きの通信のみで良いです
NACLの調整
# ステップ1で作成した、EC2用のプライベートサブネットのファイルの続き resource "aws_network_acl_rule" "subnet_private1_ingress" { for_each = { ephemeral = { action = "allow", cidr_block = local.cidr_blocks.endpoint, from_port = 1024, to_port = 65535, protocol = "tcp", rule_no = 100 }, #ssmの443通信の帰り } network_acl_id = aws_network_acl.private.id rule_action = each.value.action cidr_block = each.value.cidr_block from_port = each.value.from_port to_port = each.value.to_port protocol = each.value.protocol rule_number = each.value.rule_no egress = false } resource "aws_network_acl_rule" "subnet_private1_egress" { for_each = { https = { action = "allow", cidr_block = local.cidr_blocks.endpoint, from_port = 443, to_port = 443, protocol = "tcp", rule_no = 100 }, #ec2からインターネット通信するため } network_acl_id = aws_network_acl.private.id rule_action = each.value.action cidr_block = each.value.cidr_block from_port = each.value.from_port to_port = each.value.to_port protocol = each.value.protocol rule_number = each.value.rule_no egress = true }
# ステップ1で作成した、VPCエンドポイント用のプライベートサブネットのファイルの続き resource "aws_network_acl_rule" "subnet_endpoint1_ingress" { for_each = { https_from_private = { action = "allow", cidr_block = local.cidr_blocks.private, from_port = 443, to_port = 443, protocol = "tcp", rule_no = 100 }, } network_acl_id = aws_network_acl.endpoint.id rule_action = each.value.action cidr_block = each.value.cidr_block from_port = each.value.from_port to_port = each.value.to_port protocol = each.value.protocol rule_number = each.value.rule_no egress = false } resource "aws_network_acl_rule" "subnet_endpoint1_egress" { for_each = { ephemeral_to_private1 = { action = "allow", cidr_block = local.cidr_blocks.private, from_port = 1024, to_port = 65535, protocol = "tcp", rule_no = 100 }, } network_acl_id = aws_network_acl.endpoint.id rule_action = each.value.action cidr_block = each.value.cidr_block from_port = each.value.from_port to_port = each.value.to_port protocol = each.value.protocol rule_number = each.value.rule_no egress = true }
セキュリティグループの調整
# ステップ3で作ったEC2のファイルの続き resource "aws_vpc_security_group_egress_rule" "ec2_egress" { from_port = 443 to_port = 443 ip_protocol = "tcp" security_group_id = aws_security_group.ec2.id cidr_ipv4 = local.cidr_blocks.private }
#ステップ4で作ったVPCエンドポイントのファイルの続き resource "aws_vpc_security_group_ingress_rule" "vpc_endpoint_ingress" { from_port = 443 to_port = 443 ip_protocol = "tcp" security_group_id = aws_security_group.vpc_endpoint.id cidr_ipv4 = local.cidr_blocks.private }
ステップ6 Session Managerでアクセスする
まとめ
今回SSMでの接続をしましたが、個人的にネットワーク周りにかなり苦戦したので、うまくいかない時は入念に見直しましょう。
あと、公式ドキュメント読みにくくて、個人で書いたブログに行きがちですが、公式は間違いがないので最優先で読みましょう。
不明点や問題点などありましたらコメントお願いします。
参考記事.
https://zenn.dev/kazutech/articles/3559db9605d198
https://dev.classmethod.jp/articles/terraform-session-manager-linux-ec2-vpcendpoint/