Suricata NAT Instance ベース IPS/IDS 実習ラボ構築(第1編)

Terraform で AWS リソース作成

Terraform で最小コストの実習インフラを作る


⚠️ 事前案内

運用環境では
GSLB + 複数センサー(複数 AZ / 複数インスタンス)構成を推奨する。

Terraform に不慣れな場合は、
AWS Console でリソースを手動作成し、
本編(第1編)はスキップして、
次の編の Suricata / ELK インストールおよびシグネチャテストガイド から
直接参照しても問題ない。


インスタンス仕様に関する補足

本実習では
Suricata(NAT Instance)ターゲットサーバー の両方を
t3.medium で構成した。

これは
初期構成および性能検証を単純化するための選択である。
ただしコストを考慮すると、両インスタンスを同一仕様で維持する必要はない。

  • Suricata NAT Instance
    パケット処理、NFQUEUE、ELK(Logstash / Elasticsearch)まで同時に行うため、
    最低でも t3.medium 以上を推奨する。
  • Web サーバー / PBX ターゲットインスタンス
    単純なトラフィック生成および攻撃テスト用途なので、
    t3.micro または t3.small に下げても問題ない。
    ただし、その場合ターゲットインスタンスの仕様変更は Terraform コードで直接修正する必要がある。
    (本実習では単純化のため同一仕様を維持する)

1. 目的

  • NAT Instance 上に Suricata + ELK を構築
    (コストの問題で OpenSearch は使用しない)
  • Target サーバー(Private Subnet)を作成し、
    すべてのトラフィックが NAT を必ず経由するようにルーティングを構成
  • EIP を付与して NAT の Public IP を固定
  • Terraform で 一括作成 / 一括削除

📌 Terraform コード適用範囲に関する案内(重要)

本シリーズで提供する Terraform コードは、
**Suricata Inline IPS 構成を説明するための 핵심例(コア例)**のみを含む。

実際の運用環境では以下のリソースが
すでに別構成・別コードで管理されている可能性が高い。

  • VPC
  • Subnet(Public / Private)
  • Route Table
  • Internet Gateway
  • 既存 EC2 / Security Group
  • その他ネットワークモジュール

環境差異により混乱を招く恐れがあるため、
本シリーズでは 運用中の Terraform 全コードは公開しない。

代わりに以下の原則で説明する。

本シリーズの Terraform ガイド原則

  • Suricata に直接関連するリソースのみをコードとして提示
  • 既存インフラは
    • data リソースで参照するか
    • 上位モジュールから変数として受け取る前提
  • 読者は自分の Terraform 構成に合わせて応用すること

つまり本シリーズのコードは、
コピー&ペースト用ではなく、構造理解と応用のための基準例である。


2. アーキテクチャ

(要点)は 2 つだけ。

  1. NAT Instance は source/dest check を OFF
  2. Private Subnet のルーティングは
    0.0.0.0/0 → NAT Instance ENI に強制

3. フォルダ構成

画像のようにモジュールは 3 ファイルで分割する。

my-site/
├─ envs/
│  └─ prod/
│     ├─ main.tf
│     ├─ variables.tf
│     └─ terraform.tfvars
└─ modules/
   └─ suricata_lab/
      ├─ main.tf
      ├─ variables.tf
      └─ outputs.tf
  • modules/suricata_lab/main.tf にリソースを全て集約
  • variables.tf / outputs.tf は定義のみ最小限
  • envs/prod はモジュール呼び出し + 変数注入のみ

4. envs/prod 側で追加する内容

4.1 envs/prod/variables.tf

variable "key_name" {
  type        = string
  description = "EC2 key pair name"
}

4.2 envs/prod/main.tf

(既存モジュールがあっても問題ない。以下のモジュールだけ追加すればよい)

module "suricata_lab" {
  source = "../../modules/suricata_lab"
  name          = "sec-lab"
  key_name      = var.key_name
  my_ip         = "162.120.XXX.XXX/32"  # 実際に接続する自分の IP を適用
  instance_type = "t3.medium"
}

5. modules/suricata_lab コード

✅ modules/suricata_lab/main.tf

############################################################
# modules/suricata_lab/main.tf
# - Ubuntu 24.04
# - NAT Instance (Suricata IPS + ELK) in Public Subnet
# - Target EC2 in Private Subnet
# - Private RT: 0.0.0.0/0 -> NAT Instance
# - NAT Instance: source_dest_check = false (必須)
############################################################

############################
# VPC / Subnet / IGW
############################
resource "aws_vpc" "lab" {
  cidr_block           = "10.20.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "${var.name}-vpc"
  }
}

resource "aws_internet_gateway" "lab" {
  vpc_id = aws_vpc.lab.id

  tags = {
    Name = "${var.name}-igw"
  }
}

resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.lab.id
  cidr_block              = "10.20.0.0/24"
  map_public_ip_on_launch = true

  tags = {
    Name = "${var.name}-public"
  }
}

resource "aws_subnet" "private" {
  vpc_id     = aws_vpc.lab.id
  cidr_block = "10.20.1.0/24"

  tags = {
    Name = "${var.name}-private"
  }
}

############################
# Route Table - Public
############################
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.lab.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.lab.id
  }

  tags = {
    Name = "${var.name}-rt-public"
  }
}

resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}

############################
# Security Group - NAT/Suricata/ELK
############################
resource "aws_security_group" "nat" {
  name        = "${var.name}-sg-nat"
  description = "NAT Instance (Suricata+ELK) SG"
  vpc_id      = aws_vpc.lab.id

  # SSH (管理)
  ingress {
    description = "SSH from my IP"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = [var.my_ip]
  }

  # HTTP 接続許可 (only my ip)
  ingress {
    description = "HTTP from my IP"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = [var.my_ip]
  }

  # Kibana
  ingress {
    description = "Kibana from my IP"
    from_port   = 5601
    to_port     = 5601
    protocol    = "tcp"
    cidr_blocks = [var.my_ip]
  }

  # private target 許可
  ingress {
    description = "All from target ip"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["10.20.1.0/24"]
  }

  # Outbound all
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "${var.name}-sg-nat"
  }
}

############################
# Security Group - Target (Private)
############################
resource "aws_security_group" "target" {
  name        = "${var.name}-sg-target"
  description = "Target EC2 (private) SG"
  vpc_id      = aws_vpc.lab.id

  # SSH (管理)
  ingress {
    description     = "SSH from NAT Instance only"
    from_port       = 22
    to_port         = 22
    protocol        = "tcp"
    security_groups = [aws_security_group.nat.id]
  }

  # HTTP: NAT SG のみ許可(必要なら)
  ingress {
    description     = "HTTP 80 from NAT SG"
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    security_groups = [aws_security_group.nat.id]
  }

  # Outbound all
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "${var.name}-sg-target"
  }
}

############################
# EC2 - NAT/Suricata/ELK (Public)
############################
resource "aws_instance" "nat" {
  ami                         = "ami-0a71e3eb8b23101ed"
  instance_type               = var.instance_type
  subnet_id                   = aws_subnet.public.id
  vpc_security_group_ids      = [aws_security_group.nat.id]
  key_name                    = var.key_name
  associate_public_ip_address = true

  root_block_device {
    volume_size           = 20
    volume_type           = "gp3"
    delete_on_termination = true
  }

  # NAT Instance 必須
  source_dest_check = false

  tags = {
    Name = "${var.name}-nat-suricata-elk"
  }
}

############################
# EC2 - Target (Private)
############################
resource "aws_instance" "target" {
  ami                    = "ami-0a71e3eb8b23101ed"
  instance_type          = var.instance_type
  subnet_id              = aws_subnet.private.id
  vpc_security_group_ids = [aws_security_group.target.id]
  key_name               = var.key_name

  tags = {
    Name = "${var.name}-target"
  }
}

############################
# Route Table - Private
#  - 0.0.0.0/0 -> NAT Instance
############################
resource "aws_route_table" "private" {
  vpc_id = aws_vpc.lab.id

  route {
    cidr_block           = "0.0.0.0/0"
    network_interface_id = aws_instance.nat.primary_network_interface_id
  }

  tags = {
    Name = "${var.name}-rt-private"
  }
}

resource "aws_route_table_association" "private" {
  subnet_id      = aws_subnet.private.id
  route_table_id = aws_route_table.private.id
}

# NAT Instance 用 EIP
resource "aws_eip" "nat" {
  domain = "vpc"

  tags = {
    Name = "${var.name}-eip-nat"
  }
}

# NAT Instance に EIP を関連付け
resource "aws_eip_association" "nat" {
  allocation_id = aws_eip.nat.id
  instance_id   = aws_instance.nat.id
}

6. modules/suricata_lab/variables.tf

リソースは main.tf に全て入り、ここは変数定義のみ置く。

✅ modules/suricata_lab/variables.tf

variable "name" {
  type        = string
  description = "Lab name prefix"
}

variable "region" {
  type    = string
  default = "ap-northeast-2"
}

variable "key_name" {
  type = string
}

variable "my_ip" {
  type        = string
  description = "162.120.xxx.xxx/32" # 実際に接続する IP を適用
}

variable "instance_type" {
  type    = string
  default = "t3.medium"
}

7. modules/suricata_lab/outputs.tf

outputs は最小限にする。
実習で NAT 接続 / ターゲット確認のためだけ出力する。

✅ modules/suricata_lab/outputs.tf

output "nat_public_ip" {
  value = aws_eip.nat.public_ip
}

output "target_private_ip" {
  value = aws_instance.target.private_ip
}

8. Terraform 実行時の変数注入(Windows 環境)

Key Pair は Terraform で作る必要はない。
AWS Console で作成済みのものを名前だけ指定する。

cd C:\projects\my-site\envs\prod
PS C:\projects\my-site\envs\prod> terraform init

key pair 確認

PS C:\projects\my-site\envs\prod> aws ec2 describe-key-pairs --region ap-northeast-2 --query "KeyPairs[].KeyName" --output table

key pair 変数注入 + terraform 実行

PS C:\projects\my-site\envs\prod>terraform apply -var="key_name=blog"

Step-by-Step: 実際の terraform 実行とインフラ作成プロセス


9. 本編(第1編)の到達点

ここまでで以下が完了する。

  • NAT Instance(Suricata + ELK 用)作成
  • EIP 関連付け完了(固定 Public IP)
  • Private Subnet のトラフィックを NAT 強制経由にするルーティング完了
  • Target サーバー作成完了

次の編でやることは明確。

  • NAT インスタンスに Suricata + ELK をインストール
  • ip_forward + iptables (NFQUEUE) 構成
  • ルール更新 + SIP シグネチャ検証

〆(最後に)

Terraform に不慣れな場合は、

  • Console で VPC / Subnet / IGW / RT / EC2 / EIP を手動作成して
  • 本編(第1編)はスキップして
  • 第2編から進めればよい。

🛠 마지막 수정일: 2025.12.22

💡 お困りですか?
Zabbix、Kubernetes、各種オープンソースインフラの構築・運用・最適化・障害解析が必要であれば、いつでもご連絡ください。

📧 メール: jikimy75@gmail.com
💼 サービス: 導入支援 | 性能チューニング | 障害解析コンサルティング


코멘트

답글 남기기