こしあん
2025-03-09

VPCのトラフィックミラーリングを試す


2{icon} {views}


ALB経由のWebサーバー通信をVPCのトラフィックミラーリング機能で複製し、別EC2でパケットをキャプチャする手順をTerraformで組み上げる方法を紹介。フィルターやセッション設定の具体例、キャプチャ結果の確認を通じて、効率的な通信解析とトラブルシューティングのポイントを解説する。

はじめに

  • EC2へのトラフィックを分散(クローン)できるトラフィックミラーリングを試してみた
  • これはVPCの機能で、例えばWebサーバーのEC2への通信をミラーリングして別のEC2に転送するもの。負荷分散のロードバランサーとは違う
  • ミラーリングした側からの結果の取得は期待できないので、パケットキャプチャやファイアーウォール向けの使い方

アーキテクチャー図

  • ALBには1個のWebサーバーを紐づける
  • VPCの内部でトラフィックミラーリングを実装し、トラフィックダンプ用のEC2に振り分ける

トラフィックミラーリングのやり方

以下のTerraformでできる。ミラーリング用のフィルタールール、セッションを設定する。フィルタールールではプロトコルの指定が必要。

# トラフィックミラーリング
# ミラーリング先となるターゲットの作成(対象のネットワークインターフェイスIDを指定)
resource "aws_ec2_traffic_mirror_target" "target" {
  network_interface_id = aws_instance.ec2_instance2.primary_network_interface_id
  description          = "Traffic mirror target for EC2"
  tags = {
    Name = "TrafficMirrorTarget"
  }
}

# ミラーリング用フィルターの作成
resource "aws_ec2_traffic_mirror_filter" "filter" {
  description = "Traffic mirror filter"
  tags = {
    Name = "TrafficMirrorFilter"
  }
}

# ミラーリングフィルタールールの作成(ここでは全トラフィックを許可)
resource "aws_ec2_traffic_mirror_filter_rule" "rule_ingress" {
  traffic_direction        = "ingress"
  rule_number              = 1
  rule_action              = "accept"
  destination_cidr_block   = "0.0.0.0/0"
  source_cidr_block        = "0.0.0.0/0"
  protocol                 = 6 # TCP
  traffic_mirror_filter_id = aws_ec2_traffic_mirror_filter.filter.id
}

# トラフィックミラーリングセッションの作成
resource "aws_ec2_traffic_mirror_session" "session" {
  network_interface_id     = aws_instance.ec2_instance1.primary_network_interface_id
  traffic_mirror_target_id = aws_ec2_traffic_mirror_target.target.id
  traffic_mirror_filter_id = aws_ec2_traffic_mirror_filter.filter.id
  session_number           = 1
  description              = "EC2 Traffic Mirroring Session"
  tags = {
    Name = "TrafficMirrorSession"
  }
}

ユーザーデータ

確認用に、

  • 左側のEC2には、Webサーバー(httpd)+トラフィックキャプチャ(tcpdump)
  • 右側のEC2には、トラフィックキャプチャ(tcpdump)

ユーザーデータは以下の通り

Webサーバー+トラフィックキャプチャ

#!/bin/bash
dnf update -y
dnf install -y httpd tcpdump
# Webサーバーの起動設定
systemctl enable httpd
systemctl start httpd
# 簡単なHTMLページの作成
echo "<html><body><h1>Hello from Web Server</h1></body></html>" > /var/www/html/index.html
# tcpdumpでトラフィックをキャプチャ(同一の設定を利用)
nohup tcpdump -i ens5 -w /var/log/traffic_mirror.pcap &

トラフィックキャプチャ

#!/bin/bash
dnf update -y
dnf install -y tcpdump
# tcpdumpでトラフィックをキャプチャ(同一の設定を利用)
nohup tcpdump -i ens5 -w /var/log/traffic_mirror.pcap &

結果

2つのEC2に対してキャプチャログが記録されているのを確認する

ALBなし側

ALBあり側(Webサーバー)

Session Managerが結構ノイズになっちゃってるけど、まあ多分取れてると思うからよしw!

全体コード

# ALB用のセキュリティグループ
resource "aws_security_group" "alb_sg" {
  name        = "alb-security-group"
  description = "ALB Security Group"
  vpc_id      = var.vpc_id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = [var.my_ip]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  tags = {
    Name = "alb-sg"
  }
}

# ALBからアクセス可能なEC2用セキュリティグループ
resource "aws_security_group" "ec2_sg_with_alb" {
  name        = "ec2-sg-with-alb"
  description = "Security group for EC2 with ALB access"
  vpc_id      = var.vpc_id

  ingress {
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    security_groups = [aws_security_group.alb_sg.id]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  tags = {
    Name = "ec2-sg-with-alb"
  }
}

# 2つ目のEC2用セキュリティグループ
resource "aws_security_group" "ec2_sg" {
  name        = "ec2-sg"
  description = "General EC2 Security Group"
  vpc_id      = var.vpc_id

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  tags = {
    Name = "ec2-sg"
  }
}

# Session Manager用のIAMロール
resource "aws_iam_role" "ssm_role" {
  name = "SSMRoleForEC2"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "ec2.amazonaws.com"
        }
      },
    ]
  })
}

# SSMマネージドポリシーをロールにアタッチ
resource "aws_iam_role_policy_attachment" "ssm_policy" {
  role       = aws_iam_role.ssm_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

# インスタンスプロファイル
resource "aws_iam_instance_profile" "ec2_profile" {
  name = "EC2InstanceProfile"
  role = aws_iam_role.ssm_role.name
}

data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["137112412989"] # AmazonのAMI所有者ID

  filter {
    name = "name"
    # Amazon Linux 2023 AMIの名前パターン。minimumを除外する
    values = ["al2023-ami-2023*-kernel-*-x86_64"]
  }

  filter {
    name   = "architecture"
    values = ["x86_64"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

# ALBに接続されるEC2インスタンス
resource "aws_instance" "ec2_instance1" {
  ami                    = data.aws_ami.amazon_linux.id
  instance_type          = "t3.micro"
  subnet_id              = var.private_subnet_ids[0]
  vpc_security_group_ids = [aws_security_group.ec2_sg_with_alb.id]
  iam_instance_profile   = aws_iam_instance_profile.ec2_profile.name

  user_data = <<-EOF
#!/bin/bash
dnf update -y
dnf install -y httpd tcpdump
# Webサーバーの起動設定
systemctl enable httpd
systemctl start httpd
# 簡単なHTMLページの作成
echo "<html><body><h1>Hello from Web Server</h1></body></html>" > /var/www/html/index.html
# tcpdumpでトラフィックをキャプチャ(同一の設定を利用)
nohup tcpdump -i ens5 -w /var/log/traffic_mirror.pcap &
EOF

  tags = {
    Name = "EC2Instance1-ALB"
  }
}

# ALBに接続されないEC2インスタンス
resource "aws_instance" "ec2_instance2" {
  ami                    = data.aws_ami.amazon_linux.id
  instance_type          = "t3.micro"
  subnet_id              = length(var.private_subnet_ids) > 1 ? var.private_subnet_ids[1] : var.private_subnet_ids[0]
  vpc_security_group_ids = [aws_security_group.ec2_sg.id]
  iam_instance_profile   = aws_iam_instance_profile.ec2_profile.name

  user_data = <<-EOF
#!/bin/bash
dnf update -y
dnf install -y tcpdump
# tcpdumpでトラフィックをキャプチャ(同一の設定を利用)
nohup tcpdump -i ens5 -w /var/log/traffic_mirror.pcap &
EOF

  tags = {
    Name = "EC2Instance2-NoALB"
  }
}

# トラフィックミラーリング
# ミラーリング先となるターゲットの作成(対象のネットワークインターフェイスIDを指定)
resource "aws_ec2_traffic_mirror_target" "target" {
  network_interface_id = aws_instance.ec2_instance2.primary_network_interface_id
  description          = "Traffic mirror target for EC2"
  tags = {
    Name = "TrafficMirrorTarget"
  }
}

# ミラーリング用フィルターの作成
resource "aws_ec2_traffic_mirror_filter" "filter" {
  description = "Traffic mirror filter"
  tags = {
    Name = "TrafficMirrorFilter"
  }
}

# ミラーリングフィルタールールの作成(ここでは全トラフィックを許可)
resource "aws_ec2_traffic_mirror_filter_rule" "rule_ingress" {
  traffic_direction        = "ingress"
  rule_number              = 1
  rule_action              = "accept"
  destination_cidr_block   = "0.0.0.0/0"
  source_cidr_block        = "0.0.0.0/0"
  protocol                 = 6 # TCP
  traffic_mirror_filter_id = aws_ec2_traffic_mirror_filter.filter.id
}

# トラフィックミラーリングセッションの作成
resource "aws_ec2_traffic_mirror_session" "session" {
  network_interface_id     = aws_instance.ec2_instance1.primary_network_interface_id
  traffic_mirror_target_id = aws_ec2_traffic_mirror_target.target.id
  traffic_mirror_filter_id = aws_ec2_traffic_mirror_filter.filter.id
  session_number           = 1
  description              = "EC2 Traffic Mirroring Session"
  tags = {
    Name = "TrafficMirrorSession"
  }
}

# ALB
resource "aws_lb" "application_lb" {
  name               = "application-lb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb_sg.id]
  subnets            = var.public_subnet_ids

  enable_deletion_protection = false

  tags = {
    Name = "application-lb"
  }
}

# ALBターゲットグループ
resource "aws_lb_target_group" "alb_tg" {
  name     = "alb-target-group"
  port     = 80
  protocol = "HTTP"
  vpc_id   = var.vpc_id

  health_check {
    path                = "/"
    protocol            = "HTTP"
    matcher             = "200"
    interval            = 30
    timeout             = 5
    healthy_threshold   = 2
    unhealthy_threshold = 2
  }
}

# EC2インスタンス1をターゲットグループに追加
resource "aws_lb_target_group_attachment" "tg_attachment" {
  target_group_arn = aws_lb_target_group.alb_tg.arn
  target_id        = aws_instance.ec2_instance1.id
  port             = 80
}

# ALBリスナー
resource "aws_lb_listener" "front_end" {
  load_balancer_arn = aws_lb.application_lb.arn
  port              = 80
  protocol          = "HTTP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.alb_tg.arn
  }
}

# 出力
output "alb_dns_name" {
  value = aws_lb.application_lb.dns_name
}


Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内

技術書コーナー

北海道の駅巡りコーナー


Add a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です