ap-northeast-1(東京リージョン)におけるセキュアな開発環境のTerraformモジュール完全設計
はじめに
こんにちは、NKKTech Global 技術チームです。
多くの企業で以下のような「開発環境のセキュリティ負債」が見受けられます。
- SSH接続(ポート22)を開放したパブリック踏み台EC2インスタンスが存在する
- 開発者ごとに長期有効なIAMアクセスキーを発行し、ローカル端末に平文保存している
- EBSボリュームやS3バケットが暗号化されていない
- 開発環境のNAT GatewayがMulti-AZで無駄に起動し、毎月多額の待機コストが発生している
本記事では、AWSのセキュリティベストプラクティス(CIS AWS Foundations Benchmark / Well-Architected Framework)に準拠しつつ、Terraformモジュールとして抽象化・再利用可能な東京リージョン向けセキュア開発基盤の設計を解説します。
1. アーキテクチャ全体設計
設計の4大方針
- 踏み台(Bastion)の完全撤廃: AWS Systems Manager(SSM)Session Managerを採用し、インバウンドポート(Port 22)の開放をゼロにします。
- IMDSv2の強制: EC2のメタデータ取得におけるSSRF(サーバサイドリクエストフォージェリ)攻撃を防ぐため、IMDSv1を無効化します。
- KMS カスタマー管理キー (CMK) による保管時暗号化: EBS、S3、ログのすべてを自社管理のKMSキーで暗号化します。
- セキュリティとコストのバランス: 本番ではMulti-AZ必須のNAT Gatewayを、開発環境ではSingle NAT Gatewayに抑え、月額約5,000〜6,000円の固定費を削減します。
2. ディレクトリ構成
Terraformコードは、モジュールとして再利用可能な構造で設計します。
terraform-aws-secure-dev/
├── modules/
│ └── secure_environment/
│ ├── main.tf # 基本設定・プロバイダー
│ ├── vpc.tf # ネットワーク・サブネット・NAT GW
│ ├── security.tf # KMS・IAMロール・セキュリティグループ
│ ├── endpoints.tf # SSM / S3 VPCエンドポイント
│ ├── compute.tf # セキュアEC2インスタンス
│ ├── variables.tf # 入力変数定義
│ └── outputs.tf # 出力値
└── environments/
└── dev/
├── main.tf # モジュール呼び出し
└── backend.tf # S3 State管理
3. Terraformコード完全実装
① ネットワーク設計 (modules/secure_environment/vpc.tf)
東京リージョンで安定稼働する ap-northeast-1a と ap-northeast-1c を採用し、3層サブネット(Public, Private, Isolated)を構築します。
# VPC作成
resource "aws_vpc" "this" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.environment}-secure-vpc"
Environment = var.environment
}
}
# パブリックサブネット (ALB / NAT GW配置用)
resource "aws_subnet" "public" {
for_each = var.public_subnets
vpc_id = aws_vpc.this.id
cidr_block = each.value.cidr
availability_zone = each.value.az
map_public_ip_on_launch = false # デフォルトではパブリックIPを付与しない
tags = {
Name = "${var.environment}-public-${each.key}"
}
}
# プライベートサブネット (アプリケーション・開発インスタンス用)
resource "aws_subnet" "private_app" {
for_each = var.private_app_subnets
vpc_id = aws_vpc.this.id
cidr_block = each.value.cidr
availability_zone = each.value.az
tags = {
Name = "${var.environment}-private-app-${each.key}"
}
}
# アイソレーテッドサブネット (外部通信不要なDB用)
resource "aws_subnet" "isolated_db" {
for_each = var.isolated_db_subnets
vpc_id = aws_vpc.this.id
cidr_block = each.value.cidr
availability_zone = each.value.az
tags = {
Name = "${var.environment}-isolated-db-${each.key}"
}
}
# インターネットゲートウェイ
resource "aws_internet_gateway" "this" {
vpc_id = aws_vpc.this.id
tags = { Name = "${var.environment}-igw" }
}
# 開発環境用 Single NAT Gateway (コスト抑制のため1aのみに配置)
resource "aws_eip" "nat" {
domain = "vpc"
tags = { Name = "${var.environment}-nat-eip" }
}
resource "aws_nat_gateway" "this" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public["1a"].id
tags = { Name = "${var.environment}-single-nat-gw" }
depends_on = [aws_internet_gateway.this]
}
# ルーティングテーブル定義
resource "aws_route_table" "public" {
vpc_id = aws_vpc.this.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.this.id
}
tags = { Name = "${var.environment}-public-rt" }
}
resource "aws_route_table" "private" {
vpc_id = aws_vpc.this.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.this.id
}
tags = { Name = "${var.environment}-private-rt" }
}
# サブネット関連付け
resource "aws_route_table_association" "public" {
for_each = aws_subnet.public
subnet_id = each.value.id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "private" {
for_each = aws_subnet.private_app
subnet_id = each.value.id
route_table_id = aws_route_table.private.id
}
② セキュリティ・KMS・IAM設計 (modules/secure_environment/security.tf)
# 1. 開発環境専用 KMS キー (EBS/ログ暗号化用)
resource "aws_kms_key" "dev_key" {
description = "KMS Key for ${var.environment} secure resources"
deletion_window_in_days = 7
enable_key_rotation = true
tags = { Name = "${var.environment}-kms-key" }
}
# 2. リージョン全体でEBS暗号化をデフォルト有効化
resource "aws_ebs_encryption_by_default" "enabled" {
enabled = true
}
resource "aws_ebs_default_kms_key" "current" {
key_arn = aws_kms_key.dev_key.arn
}
# 3. 開発インスタンス用最小権限 IAM ロール (SSM専用)
resource "aws_iam_role" "ec2_ssm_role" {
name = "${var.environment}-ec2-ssm-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "ec2.amazonaws.com" }
}]
})
}
resource "aws_iam_role_policy_attachment" "ssm_core" {
role = aws_iam_role.ec2_ssm_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
resource "aws_iam_instance_profile" "ec2_profile" {
name = "${var.environment}-ec2-instance-profile"
role = aws_iam_role.ec2_ssm_role.name
}
# 4. セキュリティグループ (インバウンド完全遮断)
resource "aws_security_group" "private_app_sg" {
name = "${var.environment}-private-app-sg"
description = "Security group for private app instances (No Inbound)"
vpc_id = aws_vpc.this.id
# インバウンドルールなし (SSMはアウトバウンドHTTPSのみで接続可能)
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = { Name = "${var.environment}-private-app-sg" }
}
③ コンピュート設計 (modules/secure_environment/compute.tf)
IMDSv2を強制し、暗号化EBSをアタッチした開発用EC2インスタンスです。
resource "aws_instance" "dev_instance" {
ami = var.ami_id # Amazon Linux 2023 (ap-northeast-1)
instance_type = var.instance_type
subnet_id = aws_subnet.private_app["1a"].id
iam_instance_profile = aws_iam_instance_profile.ec2_profile.name
vpc_security_group_ids = [
aws_security_group.private_app_sg.id
]
# IMDSv2の強制 (必須セキュリティ設定)
metadata_options {
http_endpoint = "enabled"
http_tokens = "required" # IMDSv1を無効化
http_put_response_hop_limit = 1
}
# ルートボリュームの暗号化 (KMS CMK)
root_block_device {
volume_size = 30
volume_type = "gp3"
encrypted = true
kms_key_id = aws_kms_key.dev_key.arn
delete_on_termination = true
}
tags = {
Name = "${var.environment}-dev-app-server"
AutoStop = "true" # 夜間停止スクリプト用タグ
Environment = var.environment
}
}
4. 開発者の接続方法:SSHキー不要のセキュア接続
開発者は公開鍵(.pem / .pub)を持つ必要はありません。AWS CLIがインストールされた端末から、IAM権限に基づいてワンコマンドでログインします。
# 1. インスタンスIDの確認
aws ec2 describe-instances \
--filters "Name=tag:Name,Values=dev-dev-app-server" \
--query "Reservations[].Instances[].InstanceId" --output text
# 2. SSM セッションマネージャーで直接シェル起動 (ポート22開放不要)
aws ssm start-session --target i-0123456789abcdef0
# 3. ローカルからのポートフォワード (例: リモートのDBやWebポートを手元に転送)
aws ssm start-session \
--target i-0123456789abcdef0 \
--document-name AWS-StartPortForwardingSession \
--parameters '{"portNumber":["8080"],"localPortNumber":["8080"]}'
5. 運用コストをさらに下げる自動化(FinOps)
開発環境のクラウド破産を防ぐため、以下の自動化を推奨します。
-
夜間・休日のEC2自動停止:
AutoStop = "true"タグが付与されたインスタンスを、Amazon EventBridge + AWS Systems Manager Automation で平日19:00に自動停止し、月額コストを約60%カットします。 -
gp3ボリュームの採用:
旧来のgp2からgp3に切り替えるだけで、ストレージコストを最大20%削減しつつベーススループットを向上させます。 -
Single NAT Gatewayの維持:
本番昇格時はMulti-AZ NATに変更しますが、開発環境では1つに絞ることで、月額約45ドル(約7,000円)のNAT待機コストを削減します。
まとめ:セキュリティと開発体験(DX)は両立できる
「厳格なセキュリティ」と「使いやすい開発環境」は決して相反しません。
- VPCの3層分離とSingle NATで、安全かつ低コストな基盤を構築する。
- SSM Session Managerで、秘密鍵管理の煩わしさとポート開放リスクを同時に排除する。
- IMDSv2 & KMS暗号化をTerraformでコード化し、人手による設定漏れをゼロにする。
NKKTech Globalでは、このような「セキュア・バイ・デザイン」を徹底したクラウドインフラ構築を、日本およびグローバル企業のプロジェクトで標準採用しています。
お問い合わせ先
セキュアなAWS基盤の構築、TerraformによるIaC導入、クラウドコスト削減(FinOps)のご相談は、下記よりお気軽にお問い合わせください。
- Webサイト: https://nkktech.com/
- メール: contact@nkk.com.vn
- LinkedIn: https://www.linkedin.com/company/nkktech
著者:NKKTech Global 技術チーム
私たちは、強固なクラウドセキュリティとIaC自動化技術で、企業の安全かつ迅速なDXを支援します。
