VPCアタッチメント設定が自動化されているTransit Gatewayの自動承諾の無効化手順

このブログシリーズ 「クラウドセキュリティ 実践集」 では、一般的なセキュリティ課題を取り上げ、「なぜ危険なのか?」 というリスクの解説から、「どうやって直すのか?」 という具体的な修復手順(コンソール、AWS CLI、Terraformなど)まで、分かりやすく解説します。
この記事では、自動的なVPCアタッチメント設定がされているEC2 Transit Gatewayについて、そのリスクと対策を解説します。

ポリシーの説明
まず、AWS Security Hubによるポリシーの説明は以下の通りです。
[EC2.23] Amazon EC2 Transit Gateway が VPC アタッチメントリクエストを自動的に受け付けないようにすることをお勧めします
Amazon EC2 の Security Hub コントロール – AWS Security Hub
このコントロールは、EC2 中継ゲートウェイが共有 VPC アタッチメントを自動的に受け入れているかどうかをチェックします。中継ゲートウェイが共有 VPC アタッチメントリクエストを自動的に受け入れていると、このコントロールは失敗します。
リスクとしては、Amazon EC2 Transit Gateway が VPC アタッチメントリクエストを自動的に受け付ける設定になっている場合、意図しない VPC が Transit Gateway に接続される可能性があります。これにより、予期しないトラフィックの流入やセキュリティポリシーの逸脱が発生し、ネットワーク全体のセキュリティリスクが高まる可能性があります。
そのため、EC2 Transit Gateway の設定を確認し、VPC アタッチメントリクエストの自動承諾を無効にすることが望ましいです。これにより、各アタッチメントリクエストを手動で精査し、承諾するかどうかを判断できるため、ネットワークセキュリティを強化できます。
修復方法
AWSコンソールでの修正手順
① VPC > Transit Gatewayへ移動
② 対象のTransit Gatewayを選択し、「Transit Gateway」を変更をクリック
③ クロスアカウント共有オプションの設定でチェックオフ
④ 「Transit Gatewayを変更」をクリックし設定内容を保存する

Terraformでの修復手順
Transit Gatewayの安全な設定のためのTerraformコードを作成します。
# Transit Gatewayの作成
resource "aws_ec2_transit_gateway" "main" {
description = "Main Transit Gateway for ${var.environment}"
# 重要: 自動アクセプトを無効化
auto_accept_shared_attachments = "disable"
# デフォルトルート転送を無効化(セキュリティベストプラクティス)
default_route_table_association = "disable"
default_route_table_propagation = "disable"
# DNSサポートとマルチキャストは必要に応じて有効化
dns_support = "enable"
vpn_ecmp_support = "enable"
tags = merge(
{
Name = "${var.environment}-transit-gateway"
Environment = var.environment
Managed_by = "terraform"
},
var.additional_tags
)
}
# Transit Gateway用のルートテーブル
resource "aws_ec2_transit_gateway_route_table" "main" {
transit_gateway_id = aws_ec2_transit_gateway.main.id
tags = {
Name = "${var.environment}-tgw-rt"
Environment = var.environment
}
}
# VPCアタッチメント
resource "aws_ec2_transit_gateway_vpc_attachment" "vpc_attachments" {
for_each = var.vpc_attachments
transit_gateway_id = aws_ec2_transit_gateway.main.id
vpc_id = each.value.vpc_id
subnet_ids = each.value.subnet_ids
# アタッチメントごとのルートテーブル関連付けを制御
transit_gateway_default_route_table_association = false
transit_gateway_default_route_table_propagation = false
tags = merge(
{
Name = "${var.environment}-${each.key}-attachment"
},
var.additional_tags
)
}
# ルートテーブルの関連付け
resource "aws_ec2_transit_gateway_route_table_association" "vpc_attachments" {
for_each = var.vpc_attachments
transit_gateway_attachment_id = aws_ec2_transit_gateway_vpc_attachment.vpc_attachments[each.key].id
transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.main.id
}
# RAM(Resource Access Manager)共有の設定
resource "aws_ram_resource_share" "tgw_share" {
count = var.enable_ram_sharing ? 1 : 0
name = "${var.environment}-tgw-share"
allow_external_principals = false
tags = {
Name = "${var.environment}-tgw-share"
Environment = var.environment
}
}
# RAM共有のアソシエーション
resource "aws_ram_resource_association" "tgw_share" {
count = var.enable_ram_sharing ? 1 : 0
resource_arn = aws_ec2_transit_gateway.main.arn
resource_share_arn = aws_ram_resource_share.tgw_share[0].arn
}
# RAM共有の招待(特定のAWSアカウントとの共有)
resource "aws_ram_principal_association" "tgw_share" {
count = var.enable_ram_sharing ? length(var.ram_principal_ids) : 0
principal = var.ram_principal_ids[count.index]
resource_share_arn = aws_ram_resource_share.tgw_share[0].arn
}
# CloudWatchメトリクスとアラートの設定
resource "aws_cloudwatch_metric_alarm" "tgw_attachment_count" {
alarm_name = "${var.environment}-tgw-attachment-count"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "1"
metric_name = "TransitGatewayAttachmentCount"
namespace = "AWS/TransitGateway"
period = "300"
statistic = "Maximum"
threshold = var.max_attachment_threshold
alarm_description = "Transit Gateway attachment count exceeds threshold"
alarm_actions = var.alarm_actions
dimensions = {
TransitGatewayId = aws_ec2_transit_gateway.main.id
}
}
# 変数定義
variable "environment" {
type = string
description = "Environment name (e.g., prod, dev, staging)"
}
variable "vpc_attachments" {
type = map(object({
vpc_id = string
subnet_ids = list(string)
}))
description = "Map of VPC attachments configuration"
}
variable "enable_ram_sharing" {
type = bool
description = "Enable RAM sharing for Transit Gateway"
default = false
}
variable "ram_principal_ids" {
type = list(string)
description = "List of AWS account IDs to share the Transit Gateway with"
default = []
}
variable "max_attachment_threshold" {
type = number
description = "Maximum number of attachments before alerting"
default = 100
}
variable "alarm_actions" {
type = list(string)
description = "List of ARNs to notify when attachment count exceeds threshold"
default = []
}
variable "additional_tags" {
type = map(string)
description = "Additional tags for resources"
default = {}
}
# 出力
output "transit_gateway_id" {
value = aws_ec2_transit_gateway.main.id
description = "Transit Gateway ID"
}
output "transit_gateway_arn" {
value = aws_ec2_transit_gateway.main.arn
description = "Transit Gateway ARN"
}
重要な修正ポイントと実装手順を説明します:
- 自動アクセプトの無効化(最重要):
resource "aws_ec2_transit_gateway" "main" {
auto_accept_shared_attachments = "disable" # 必ず"disable"に設定
}
2. デフォルトルート設定の制御:
resource "aws_ec2_transit_gateway" "main" {
default_route_table_association = "disable"
default_route_table_propagation = "disable"
}
3. RAMでの制御された共有:
resource "aws_ram_resource_share" "tgw_share" {
allow_external_principals = false # 外部プリンシパルを禁止
}
最後に
今回は、EC2 Transit Gateway の VPC アタッチメントリクエストの自動承諾設定に関するリスクとその対策についてご紹介しました。自動承諾が有効になっていると、意図しない VPC 接続が許可され、セキュリティリスクが高まる可能性があります。設定を確認し、必要に応じて自動承諾を無効にすることで、ネットワークの安全性を確保してください。
この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。
運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。
最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。