VPCのトラフィックミラーリングを試す
Posted On 2025-03-09
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の中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内
技術書コーナー
北海道の駅巡りコーナー