← 技術情報一覧へ戻る
技術解説

TerraformによるSecurity Group一括管理 ─ Security Group ID参照による通信許可設計

  • AWS
  • Security Group
  • VPC
  • Terraform
  • ネットワーク

プロジェクトで使用するSecurity Groupを一括管理し、リソース間の通信許可をSecurity Group ID参照で設計するパターンを解説する。 IPアドレスに依存しないネットワークアクセス制御の実践を紹介する。

ページ構成

  1. Security Group一括管理の動機
  2. 設計パターン:箱の一括作成 + リソース側でのルール追加
  3. IPアドレスではなくSecurity Group IDで許可する理由
  4. リソース別の通信許可パターン
  5. まとめ

1. Security Group一括管理の動機

Security Groupの散逸問題

Security Groupもリソースごとに個別作成すると以下の問題が生じる。

  • 全体像が見えない: VPC内にどのSecurity Groupが存在し、何を許可しているか把握しづらい
  • 命名規則の不統一: 作成者・タイミングによって命名がばらつく
  • 不要Security Groupの残存: リソース削除後もSecurity Groupだけが残り、棚卸しが困難
  • ルールの重複: 似たルールが複数Security Groupに散在する

設計方針:一箇所で「箱」を一括作成

全Security Groupを1箇所のTerraformファイルに集約する。

ここで定義するのは:

  • Security Groupの箱(名前、説明、VPC紐付け)
  • 汎用的なルールだけ(ALBの443受信、全Egress許可など)

リソース固有の通信許可(RDSへのECSからのアクセス等)は、ここでは定義しない。

2. 設計パターン:箱の一括作成 + リソース側でのルール追加

一括管理で作るもの

環境内で必要なSecurity Groupを宣言的に定義する。

Security Group一括管理で定義するルール
ALB用Ingress: HTTPS(443) from anywhere / Egress: 全許可
ECSタスク用Ingress: なし(リソース側で追加) / Egress: 全許可
RDS用Ingress: なし(リソース側で追加) / Egress: 全許可
Lambda用Ingress: なし / Egress: 全許可
Bastion用Ingress: SSH(22) from オフィスCIDR / Egress: 全許可

ECSタスク用やRDS用のSecurity Groupは、Ingressルールを空で作成する。 「誰からの通信を許可するか」は、リソース側で決定する。

リソース側で追加するもの

各リソースのTerraformが、自身のSecurity Groupに対してインバウンドルールを追加する。

# RDS構築時に、ECSタスクからの通信を許可するルールを追加
resource "aws_security_group_rule" "allow_from_ecs" {
  security_group_id        = module.sgs.sgs["rds"].id      # RDSのSG
  source_security_group_id = module.sgs.sgs["ecs"].id      # 接続元: ECSタスクのSG
  type                     = "ingress"
  from_port                = 3306
  to_port                  = 3306
  protocol                 = "tcp"
  description              = "Allow MySQL from ECS tasks"
}

この設計により:

  • RDSのTerraformを見れば「どこから通信できるか」がわかる
  • Security Group一括管理側を見に行く必要がない
  • リソースの追加・削除に伴い、通信許可も自然に追加・削除される

3. IPアドレスではなくSecurity Group IDで許可する理由

CIDRベースの許可の問題

# CIDRベース(非推奨パターン)
cidr_blocks = ["10.0.1.0/24"]  # ECSタスクが配置されるサブネット
  • ECSタスク(Fargate)のIPアドレスはタスク起動ごとに変わる
  • サブネットCIDR全体を許可すると、そのサブネット内の全リソースからアクセス可能になる
  • サブネット構成を変更するとルールの修正が必要

Security Group ID参照の利点

# Security Group IDベース(推奨パターン)
source_security_group_id = module.sgs.sgs["ecs"].id
  • IPアドレスに依存しない: タスクのIPが変わっても通信許可は維持される
  • 最小権限: 同じサブネット内でも、指定したSecurity Groupがアタッチされたリソースからのみ許可
  • 可読性: 「ECSタスクからの通信を許可」という意図がコードから読み取れる
  • サブネット変更に強い: ネットワーク構成を変更してもルール修正不要

CIDRを使う例外ケース

ケース理由
ALBへのCloudFrontアクセスAWSマネージドプレフィックスリストを使用(IPレンジがAWSにより動的に管理される)
Bastionへのオフィスからのアクセス接続元がVPC外のため、Security Group IDで参照できない
サブネット全体からの許可が意図的な場合設計上の判断として明示的に選択する場合のみ

4. リソース別の通信許可パターン

各リソースが「許可する接続元のSecurity Group IDリスト」を受け取り、自身のSecurity Groupにインバウンドルールを追加する。

リソース許可する接続元ポート説明
RDS AuroraECSタスクSG、Lambda SG3306 / 5432アプリケーションとバッチ処理からのDB接続
ElastiCache (Redis)ECSタスクSG、Lambda SG6379セッション・キャッシュへのアクセス
EFSECSタスクSG2049 (NFS)共有ファイルシステムへのマウント
OpenSearchECSタスクSG、Lambda SG443 (HTTPS)検索・分析エンジンへのアクセス
ALBCloudFront Managed Prefix List、特定SG443 / 80ロードバランサーへのトラフィック受信
EC2 (外部接続先)Bastion SG22踏み台からのSSH接続

通信フローの可視化

各矢印が aws_security_group_rule リソースに対応する。 ルールはリソース側(矢印の先)のTerraformに定義されるため、「このリソースに誰がアクセスできるか」がリソースのコードを見れば完結する。

ALBの特殊なパターン

ALBへのアクセス許可は、他のリソースとは異なるパターンを取る。

  • CloudFrontからのアクセス: AWSが管理するManaged Prefix List(com.amazonaws.global.cloudfront.origin-facing)を使用する。CloudFrontのIPレンジはAWSが動的に更新するため、CIDRの手動管理が不要
  • 特定Security Groupからのアクセス: 内部ALBの場合、特定のSecurity Groupからのアクセスのみを許可する

5. まとめ

設計判断内容
Security Groupの集約管理全Security Groupを1ファイルに集約し、一覧性を確保
汎用ルールのみ一括定義ALBの443受信や全Egressなど、リソース非依存のルールだけを一括管理で定義
リソース固有の許可はリソース側で追加RDS、ElastiCache、EFS等がSecurity Group Ruleで自身のSGにインバウンドルールを追加
Security Group ID参照による許可IPアドレスではなくSecurity Group IDで接続元を指定し、IP変更に強い構成を実現
可読性の確保リソースのTerraformを見れば「どこから通信できるか」が完結する