跳转到内容

部署

所有 Terraform 文件位于 terraform/ 目录中。克隆仓库并直接部署:

Terminal window
git clone https://github.com/f5-sales-demo/traffic-generator.git
cd traffic-generator/terraform
cp terraform.tfvars.example terraform.tfvars
# 使用您的 Azure 订阅 ID 和目标 FQDN 编辑 terraform.tfvars

terraform 目录包含 9 个文件,遵循演示资源标准:

  • versions.tf — Terraform 和 provider 版本约束(azurerm ~> 4.0、azuread ~> 3.0)
  • providers.tf — Azure RM 和 Azure AD provider 配置
  • data.tf — 用于部署者自动解析的 Azure AD 数据源
  • locals.tf — 部署者解析、Azure 云采用框架资源命名、标准标签
  • main.tf — 资源组(命名为 rg-traffic-generator-{environment}-{deployer}
  • variables.tf — 所有输入变量(2 个必填,9 个可选),通过 Azure AD 自动解析部署者
  • network.tf — VNet(10.201.0.0/16)、子网、公共 IP、NSG(端口 22 SSH)、NIC
  • vm.tf — 通过 templatefile() 使用 cloud-init 的 Ubuntu 24.04 VM
  • outputs.tf — 17 个输出:15 个所有演示资源共享的标准输出(deployer、public_ip、private_ip、ssh_command、resource_group_name、vm_name、nsg_name、vnet_name、subnet_id、component、environment、resource_group_id、vm_id、nsg_id、location),以及 2 个组件特定输出(target_fqdn、status_check)

main.tf 配置 Azure provider:

resource "azurerm_resource_group" "main" {
name = local.name.resource_group
location = var.location
tags = local.tags
}

variables.tf 定义所有可配置参数。vm_size 默认值为 Standard_F16s_v2(16 vCPU 计算优化型)——有关扩展指导,请参阅 VM 规格选择

# ---------------------------------------------------------
# General
# ---------------------------------------------------------
variable "subscription_id" {
description = "Azure subscription ID"
type = string
}
variable "deployer" {
description = "Override for deployer identifier (auto-resolved from Azure AD if empty). Required for service principal or managed identity authentication."
type = string
default = ""
}
variable "location" {
description = "Azure region for all resources"
type = string
default = "eastus2"
}
variable "environment" {
description = "Environment label used in resource group naming and tags"
type = string
default = "lab"
}
variable "tags" {
description = "Additional tags merged with standard tags (component, environment, deployer, managed_by)"
type = map(string)
default = {}
}
# ---------------------------------------------------------
# Compute
# ---------------------------------------------------------
variable "vm_size" {
description = "Azure VM size (F16s_v2: 16 vCPU compute-optimized, validated by benchmark)"
type = string
default = "Standard_F16s_v2"
}
variable "admin_username" {
description = "SSH admin username for the VM"
type = string
default = "azureuser"
}
variable "ssh_public_key_path" {
description = "Path to the SSH public key file"
type = string
default = "~/.ssh/id_ed25519.pub"
}
variable "disk_size_gb" {
description = "OS disk size in GB"
type = number
default = 64
}
# ---------------------------------------------------------
# Component-Specific
# ---------------------------------------------------------
variable "target_fqdn" {
description = "FQDN of the F5 XC load balancer to target"
type = string
}
variable "target_origin_ip" {
description = "Direct origin IP for bypass testing"
type = string
default = ""
}
variable "tool_tier" {
description = "Tool installation tier: standard (default) or full (includes ZAP, Metasploit)"
type = string
default = "standard"
validation {
condition = contains(["standard", "full"], var.tool_tier)
error_message = "tool_tier must be \"standard\" or \"full\"."
}
}

network.tf 创建 VNet、子网、公共 IP、NSG(端口 22 SSH)和 NIC:

resource "azurerm_virtual_network" "main" {
name = local.name.virtual_network
address_space = ["10.201.0.0/16"]
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
tags = azurerm_resource_group.main.tags
}
resource "azurerm_subnet" "main" {
name = local.name.subnet
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.201.1.0/24"]
}
resource "azurerm_public_ip" "main" {
name = local.name.public_ip
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
allocation_method = "Static"
sku = "Standard"
tags = azurerm_resource_group.main.tags
}
resource "azurerm_network_security_group" "main" {
name = local.name.nsg
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
security_rule {
name = "AllowSSH"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
}
tags = azurerm_resource_group.main.tags
}
resource "azurerm_network_interface" "main" {
name = local.name.network_interface
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
ip_configuration {
name = "internal"
subnet_id = azurerm_subnet.main.id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.main.id
}
tags = azurerm_resource_group.main.tags
}
resource "azurerm_network_interface_security_group_association" "main" {
network_interface_id = azurerm_network_interface.main.id
network_security_group_id = azurerm_network_security_group.main.id
}

vm.tf 创建 Ubuntu 24.04 VM,并传递包含目标和工具变量的 cloud-init 模板:

resource "azurerm_linux_virtual_machine" "main" {
name = local.name.virtual_machine
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
size = var.vm_size
admin_username = var.admin_username
disable_password_authentication = true
admin_ssh_key {
username = var.admin_username
public_key = file(pathexpand(var.ssh_public_key_path))
}
network_interface_ids = [azurerm_network_interface.main.id]
os_disk {
caching = "ReadWrite"
storage_account_type = "Premium_LRS"
disk_size_gb = var.disk_size_gb
}
source_image_reference {
publisher = "Canonical"
offer = "ubuntu-24_04-lts"
sku = "server"
version = "latest"
}
custom_data = base64encode(templatefile("${path.module}/cloud-init.yaml", {
target_fqdn = var.target_fqdn
target_origin_ip = var.target_origin_ip
tool_tier = var.tool_tier
}))
tags = azurerm_resource_group.main.tags
}

cloud-init.yaml 是经过生产验证的配置,已通过超过 48 小时的持续负载测试验证,包括 4 轮源服务器压力测试。它使用带有重试逻辑的辅助库、记录到 /var/log/cloud-init-progress.log 的进度日志、9 个预置阶段,以及条件状态报告(status.json 中的 ready/degraded 状态)。其预置内容包括:

  • 内核调优somaxconn=131072tcp_max_tw_buckets=4000000tcp_fin_timeout=5、64 MiB 套接字缓冲区、file-max=4194304
  • NIC 优化 — 跨所有核心的 RPS/RFS、通过 ethtool 最大化环形缓冲区、THP=always
  • APT 软件包 — nikto、nmap、masscan、sqlmap、hydra、medusa、ncrack、tshark、hping3、tcpdump、netcat、ngrep、iperf3、mtr、sslscan、socat、dirb、whatweb、wrk、hey、vegeta、ethtool
  • GitHub Releases — nuclei、dalfox、ffuf、gobuster、feroxbuster、subfinder、httpx、amass
  • Python 包 — mitmproxy、sslyze、dnsrecon、fierce、theHarvester、playwright、scapy、impacket、arjun
  • Node.js 20.x — 带隐身插件的 Puppeteer,用于机器人模拟
  • 浏览器自动化 — 用于无头浏览器测试的 Playwright Chromium
  • Git 克隆 — testssl.sh、recon-ng、spiderfoot、SecLists、waf-bypass、PayloadsAllTheThings
  • 套件部署 — 从本仓库克隆 19 个流量套件到 /opt/traffic-generator/suites/
  • 配置文件 — 包含 TARGET_FQDNTARGET_ORIGIN_IPCRAPI_PORTconfig.env

cloud-init 使用 Terraform 模板变量:${target_fqdn}${target_origin_ip}${tool_tier}。Shell 变量(如 $NPROC)在 Terraform templatefile 中转义为 $$NPROC

#cloud-config
package_update: true
package_upgrade: true
packages:
# System essentials
- ca-certificates
- curl
- gnupg
- lsb-release
- jq
- git
- unzip
- build-essential
- python3
- python3-pip
- python3-venv
- python3-dev
- libffi-dev
- libssl-dev
- libpcap-dev
# Network analysis
- nmap
- masscan
- tshark
- hping3
- socat
- tcpdump
- netcat-openbsd
- ngrep
- iperf3
- mtr-tiny
- whois
- dnsutils
# Web scanners
- nikto
- sqlmap
- dirb
- whatweb
- sslscan
# Password/credential testing
- hydra
- medusa
- ncrack
- john
- hashcat
# Forensics/analysis
- binwalk
- strace
- ltrace
# Performance monitoring
- sysstat
- htop
- iotop
- ethtool
# Load testing
- apache2-utils
- wrk
# Headless Chrome dependencies
- fonts-noto-color-emoji
- fonts-liberation
- libnss3
- libatk-bridge2.0-dev
- libdrm2
- libxkbcommon0
- libgbm1
- libasound2-dev
write_files:
- path: /usr/local/bin/ghlatest
permissions: '0755'
content: |
#!/bin/sh
set -e
REPO="$1"
if [ -z "$REPO" ]; then echo "Usage: ghlatest owner/repo" >&2; exit 1; fi
R=0; M=3
while [ "$R" -lt "$M" ]; do
RESP=$(curl -fsSL -w "\n%%{http_code}" "https://api.github.com/repos/$REPO/releases/latest" 2>/dev/null) || true
HTTP_CODE=$(echo "$RESP" | tail -1)
BODY=$(echo "$RESP" | sed '$d')
if [ "$HTTP_CODE" = "403" ] || [ "$HTTP_CODE" = "429" ]; then
echo "WARN: ghlatest rate-limited ($HTTP_CODE) for $REPO" >&2; exit 1
fi
V=$(echo "$BODY" | grep '"tag_name"' | head -1 | sed 's/.*"tag_name"[[:space:]]*:[[:space:]]*"v\{0,1\}\([^"]*\)".*/\1/')
if [ -n "$V" ]; then echo "$V"; exit 0; fi
R=$((R + 1)); sleep $((R * 3))
done
echo "ERROR: ghlatest failed for $REPO" >&2; exit 1
- path: /etc/sysctl.d/99-traffic-generator.conf
content: |
net.core.somaxconn = 131072
net.ipv4.tcp_max_syn_backlog = 131072
net.core.netdev_max_backlog = 131072
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 5
net.ipv4.tcp_max_tw_buckets = 4000000
net.ipv4.tcp_keepalive_time = 30
net.ipv4.tcp_keepalive_intvl = 5
net.ipv4.tcp_keepalive_probes = 3
net.core.rmem_max = 67108864
net.core.wmem_max = 67108864
net.ipv4.tcp_rmem = 4096 87380 67108864
net.ipv4.tcp_wmem = 4096 65536 67108864
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_no_metrics_save = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_window_scaling = 1
fs.file-max = 4194304
- path: /etc/security/limits.d/99-traffic-generator.conf
content: |
* soft nofile 524288
* hard nofile 524288
root soft nofile 524288
root hard nofile 524288
* soft nproc 65535
* hard nproc 65535
- path: /etc/profile.d/traffic-generator.sh
permissions: '0644'
content: |
export NODE_PATH=/usr/lib/node_modules
export PATH=$PATH:/usr/local/bin
- path: /usr/local/bin/tgen-profile
permissions: '0755'
content: |
#!/bin/bash
set -uo pipefail
report() {
echo "================================================================"
echo " TRAFFIC GENERATOR PLATFORM PROFILE — $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "================================================================"
echo ""
echo "--- CPU ---"
echo "Cores: $(nproc)"
uptime
echo ""
echo "--- Memory ---"
free -h
echo ""
echo "--- Disk I/O ---"
iostat -x 1 1 2>/dev/null | grep -A20 'Device' || echo "iostat not available"
echo ""
echo "--- Network connections ---"
ss -s
echo ""
echo "--- TCP state distribution ---"
ss -tan | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn
echo ""
echo "--- TIME_WAIT count ---"
ss -tan state time-wait | wc -l
echo ""
echo "--- Ephemeral port usage ---"
RANGE=$(sysctl -n net.ipv4.ip_local_port_range)
LOW=$(echo "$RANGE" | awk '{print $1}')
HIGH=$(echo "$RANGE" | awk '{print $2}')
USED=$(ss -tan | awk -v low="$LOW" -v high="$HIGH" '{split($4,a,":"); p=a[length(a)]; if(p>=low && p<=high) count++} END {print count+0}')
TOTAL=$((HIGH - LOW))
echo " Range: $LOW-$HIGH ($TOTAL ports)"
echo " Used: $USED ($((USED * 100 / TOTAL))%)"
echo ""
echo "--- Open file descriptors ---"
echo " System: $(awk '{print $1}' /proc/sys/fs/file-nr) / $(cat /proc/sys/fs/file-max)"
echo " Limit: $(ulimit -n) (soft) / $(ulimit -Hn) (hard)"
echo ""
echo "--- Kernel TCP tuning ---"
for k in net.core.somaxconn net.ipv4.tcp_max_syn_backlog net.ipv4.ip_local_port_range net.ipv4.tcp_tw_reuse net.ipv4.tcp_fin_timeout net.ipv4.tcp_max_tw_buckets net.core.rmem_max net.core.wmem_max; do
printf " %-40s %s\n" "$k" "$(sysctl -n $k 2>/dev/null || echo N/A)"
done
}
if [ "$${1:-}" = "--watch" ]; then
SECS="$${2:-10}"
while true; do clear; report; sleep "$SECS"; done
else
report
fi
- path: /opt/traffic-generator/config.env
content: |
TARGET_FQDN=${target_fqdn}
TARGET_ORIGIN_IP=${target_origin_ip}
TARGET_PROTOCOL=http
CRAPI_PORT=8888
NODE_PATH=/usr/lib/node_modules
- path: /opt/traffic-generator/status.json
content: |
{"status":"provisioning","tool_tier":"${tool_tier}"}
- path: /usr/local/lib/cloud-init-helpers.sh
permissions: "0644"
content: |
#!/bin/sh
PROGRESS_LOG="/var/log/cloud-init-progress.log"
log_phase() {
_phase="$1"; shift
_msg="$${*:-started}"
_ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
printf '[%s] [%s] %s\n' "$_ts" "$_phase" "$_msg" | tee -a "$PROGRESS_LOG" >&2
}
retry_cmd() {
_max="$1"; _base="$2"; shift 2
_attempt=1
while [ "$_attempt" -le "$_max" ]; do
if "$@"; then return 0; fi
if [ "$_attempt" -lt "$_max" ]; then
_wait=$(( _base * _attempt ))
log_phase "retry" "attempt $_attempt/$_max failed ($1) — retrying in $${_wait}s"
sleep "$_wait"
fi
_attempt=$(( _attempt + 1 ))
done
log_phase "retry" "FAILED after $_max attempts: $1"
return 1
}
fetch_url() {
_url="$1"; _out="$2"; _desc="$${3:-$1}"
log_phase "fetch" "$_desc"
retry_cmd 4 5 curl -fsSL --connect-timeout 15 --max-time 300 -o "$_out" "$_url"
}
install_packages() {
log_phase "apt" "installing: $*"
retry_cmd 3 10 apt-get install -y -o DPkg::Lock::Timeout=60 "$@"
}
clone_repo() {
_url="$1"; _dest="$2"; _depth="$${3:-1}"
log_phase "git" "cloning $_url -> $_dest"
retry_cmd 3 10 git clone --depth "$_depth" --single-branch "$_url" "$_dest"
}
wait_for_http() {
_url="$1"; _max="$2"; _desc="$${3:-$_url}"
log_phase "health" "waiting for $_desc (max $${_max}s)"
_elapsed=0
while [ "$_elapsed" -lt "$_max" ]; do
if curl -sf --max-time 5 "$_url" >/dev/null 2>&1; then
log_phase "health" "$_desc ready after $${_elapsed}s"
return 0
fi
sleep 5; _elapsed=$(( _elapsed + 5 ))
done
log_phase "health" "TIMEOUT: $_desc not ready after $${_max}s"
return 1
}
runcmd:
# --- Phase 0: Kernel tuning, NIC optimization, system limits ---
- sysctl --system
- systemctl daemon-reload
- |
. /usr/local/lib/cloud-init-helpers.sh
log_phase "phase0" "kernel tuning and NIC optimization"
# RPS/RFS: distribute NIC softirqs across all cores
NCPU=$(nproc)
RPS_MASK=$(printf '%x' $(( (1 << NCPU) - 1 )))
NIC=$(ip -o link show | awk -F': ' '/state UP/ && !/lo/{print $2; exit}')
if [ -n "$NIC" ]; then
RFS_ENTRIES=$((32768 * NCPU))
echo "$RFS_ENTRIES" > /proc/sys/net/core/rps_sock_flow_entries
for rxq in /sys/class/net/"$NIC"/queues/rx-*/rps_cpus; do
echo "$RPS_MASK" > "$rxq"
done
for rxq in /sys/class/net/"$NIC"/queues/rx-*/rps_flow_cnt; do
RXQ_COUNT=$(ls -d /sys/class/net/"$NIC"/queues/rx-* | wc -l)
echo "$((RFS_ENTRIES / RXQ_COUNT))" > "$rxq"
done
# Maximize NIC ring buffers
if command -v ethtool >/dev/null 2>&1; then
RX_MAX=$(ethtool -g "$NIC" 2>/dev/null | awk '/Pre-set.*:/,/^$/{if(/RX:/){print $2; exit}}')
TX_MAX=$(ethtool -g "$NIC" 2>/dev/null | awk '/Pre-set.*:/,/^$/{if(/TX:/){print $2; exit}}')
[ -n "$RX_MAX" ] && [ -n "$TX_MAX" ] && ethtool -G "$NIC" rx "$RX_MAX" tx "$TX_MAX" 2>/dev/null
fi
echo "NIC tuning: RPS mask=$RPS_MASK on $NIC"
fi
# Transparent Huge Pages for large connection pools
echo "always" > /sys/kernel/mm/transparent_hugepage/enabled 2>/dev/null || true
# --- Phase 1: Node.js 24 ---
- |
. /usr/local/lib/cloud-init-helpers.sh
log_phase "phase1" "installing Node.js 24"
retry_cmd 3 10 sh -c 'curl -fsSL https://deb.nodesource.com/setup_24.x | bash -'
install_packages nodejs
# --- Phase 2: Python toolchain (uv) ---
- |
. /usr/local/lib/cloud-init-helpers.sh
log_phase "phase2" "installing Python uv"
retry_cmd 4 5 sh -c 'curl -fsSL https://astral.sh/uv/install.sh | sh'
cp /root/.local/bin/uv /usr/local/bin/uv || true
cp /root/.local/bin/uvx /usr/local/bin/uvx || true
# --- Phase 3: Security binaries (Go tools via ghlatest) ---
- |
. /usr/local/lib/cloud-init-helpers.sh
log_phase "phase3" "installing security binaries"
export DPKG_ARCH=$(dpkg --print-architecture)
export PATH=/usr/local/bin:$PATH
echo "Installing nuclei..."
NUCLEI_VER=$(ghlatest projectdiscovery/nuclei)
fetch_url "https://github.com/projectdiscovery/nuclei/releases/download/v$${NUCLEI_VER}/nuclei_$${NUCLEI_VER}_linux_$${DPKG_ARCH}.zip" /tmp/nuclei.zip "nuclei"
unzip -oq /tmp/nuclei.zip nuclei -d /usr/local/bin && rm /tmp/nuclei.zip
echo "Installing subfinder..."
SUBFINDER_VER=$(ghlatest projectdiscovery/subfinder)
fetch_url "https://github.com/projectdiscovery/subfinder/releases/download/v$${SUBFINDER_VER}/subfinder_$${SUBFINDER_VER}_linux_$${DPKG_ARCH}.zip" /tmp/subfinder.zip "subfinder"
unzip -oq /tmp/subfinder.zip subfinder -d /usr/local/bin && rm /tmp/subfinder.zip
echo "Installing httpx..."
HTTPX_VER=$(ghlatest projectdiscovery/httpx)
fetch_url "https://github.com/projectdiscovery/httpx/releases/download/v$${HTTPX_VER}/httpx_$${HTTPX_VER}_linux_$${DPKG_ARCH}.zip" /tmp/httpx.zip "httpx"
unzip -oq /tmp/httpx.zip httpx -d /usr/local/bin && rm /tmp/httpx.zip
echo "Installing ffuf..."
FFUF_VER=$(ghlatest ffuf/ffuf)
retry_cmd 4 5 sh -c 'curl -fsSL "https://github.com/ffuf/ffuf/releases/download/v$${FFUF_VER}/ffuf_$${FFUF_VER}_linux_$${DPKG_ARCH}.tar.gz" | tar -xz -C /usr/local/bin ffuf'
echo "Installing gobuster..."
GOBUSTER_VER=$(ghlatest OJ/gobuster)
retry_cmd 4 5 sh -c 'curl -fsSL "https://github.com/OJ/gobuster/releases/download/v$${GOBUSTER_VER}/gobuster_Linux_$(uname -m).tar.gz" | tar -xz -C /usr/local/bin gobuster'
echo "Installing feroxbuster..."
FEROX_VER=$(ghlatest epi052/feroxbuster)
fetch_url "https://github.com/epi052/feroxbuster/releases/download/v$${FEROX_VER}/feroxbuster_$${DPKG_ARCH}.deb.zip" /tmp/ferox.zip "feroxbuster"
unzip -oq /tmp/ferox.zip -d /tmp && dpkg -i /tmp/feroxbuster_*.deb && rm -f /tmp/ferox.zip /tmp/feroxbuster_*.deb
echo "Installing dalfox..."
DALFOX_VER=$(ghlatest hahwul/dalfox)
fetch_url "https://github.com/hahwul/dalfox/releases/download/v$${DALFOX_VER}/dalfox-linux-$${DPKG_ARCH}.tar.gz" /tmp/dalfox.tar.gz "dalfox"
tar -xzf /tmp/dalfox.tar.gz -C /tmp && mv /tmp/dalfox-linux-$${DPKG_ARCH} /usr/local/bin/dalfox && chmod +x /usr/local/bin/dalfox && rm /tmp/dalfox.tar.gz
echo "Installing amass..."
AMASS_VER=$(ghlatest owasp-amass/amass)
fetch_url "https://github.com/owasp-amass/amass/releases/download/v$${AMASS_VER}/amass_linux_$${DPKG_ARCH}.tar.gz" /tmp/amass.tar.gz "amass"
mkdir -p /tmp/amass && tar -xzf /tmp/amass.tar.gz -C /tmp/amass && cp /tmp/amass/amass_linux_$${DPKG_ARCH}/amass /usr/local/bin/ && rm -rf /tmp/amass /tmp/amass.tar.gz
echo "Phase 3 complete"
# --- Phase 3b: High-performance load testing tools ---
- |
. /usr/local/lib/cloud-init-helpers.sh
log_phase "phase3b" "installing load testing tools"
export DPKG_ARCH=$(dpkg --print-architecture)
export PATH=/usr/local/bin:$PATH
echo "Installing hey (via go install — no prebuilt binaries available)..."
install_packages golang-go
mkdir -p /opt/go/bin
chown -R azureuser:azureuser /opt/go
retry_cmd 3 15 sh -c 'GOPATH=/opt/go go install github.com/rakyll/hey@latest'
cp /opt/go/bin/hey /usr/local/bin/hey
echo "Installing gotestwaf..."
retry_cmd 3 15 sh -c 'GOPATH=/opt/go go install github.com/wallarm/gotestwaf/cmd/gotestwaf@latest'
cp /opt/go/bin/gotestwaf /usr/local/bin/gotestwaf
mkdir -p /opt/gotestwaf
cp -r /opt/go/pkg/mod/github.com/wallarm/gotestwaf@*/config.yaml /opt/gotestwaf/ 2>/dev/null || true
cp -r /opt/go/pkg/mod/github.com/wallarm/gotestwaf@*/testcases /opt/gotestwaf/ 2>/dev/null || true
chown -R azureuser:azureuser /opt/gotestwaf
echo "Installing vegeta..."
VEGETA_VER=$(ghlatest tsenart/vegeta)
retry_cmd 4 5 sh -c 'curl -fsSL "https://github.com/tsenart/vegeta/releases/download/v$${VEGETA_VER}/vegeta_$${VEGETA_VER}_linux_$${DPKG_ARCH}.tar.gz" | tar -xz -C /usr/local/bin vegeta'
echo "Phase 3b complete"
# --- Phase 4: Python security tools ---
- |
. /usr/local/lib/cloud-init-helpers.sh
log_phase "phase4" "installing Python security tools"
retry_cmd 3 10 pip install --retries 3 --break-system-packages scapy impacket arjun hashid pwntools
retry_cmd 3 10 pip install --retries 3 --break-system-packages --ignore-installed typing_extensions mitmproxy sslyze
retry_cmd 3 10 pip install --retries 3 --break-system-packages wfuzz
# --- Phase 5: Browser automation ---
- |
. /usr/local/lib/cloud-init-helpers.sh
log_phase "phase5" "installing browser automation tools"
retry_cmd 3 15 npm install -g playwright puppeteer puppeteer-extra puppeteer-extra-plugin-stealth
- |
. /usr/local/lib/cloud-init-helpers.sh
retry_cmd 3 30 npx --yes playwright install chromium
retry_cmd 3 30 sh -c 'su - azureuser -c "export NODE_PATH=/usr/lib/node_modules && npx --yes playwright install chromium"'
npx --yes playwright install-deps chromium 2>/dev/null || true
# --- Phase 6: Git-cloned tools ---
- |
. /usr/local/lib/cloud-init-helpers.sh
log_phase "phase6" "cloning tool repositories"
clone_repo "https://github.com/drwetter/testssl.sh.git" /opt/testssl.sh
ln -sf /opt/testssl.sh/testssl.sh /usr/local/bin/testssl
clone_repo "https://github.com/danielmiessler/SecLists.git" /opt/SecLists
clone_repo "https://github.com/lanmaster53/recon-ng.git" /opt/recon-ng
clone_repo "https://github.com/smicallef/spiderfoot.git" /opt/spiderfoot
clone_repo "https://github.com/nemesida-waf/waf-bypass.git" /opt/waf-bypass
clone_repo "https://github.com/swisskyrepo/PayloadsAllTheThings.git" /opt/PayloadsAllTheThings
# --- Phase 7: ZAP (standard tier) + heavy frameworks (full tier) ---
- |
. /usr/local/lib/cloud-init-helpers.sh
log_phase "phase7" "installing ZAP and optional tools"
export PATH=/usr/local/bin:$PATH
echo "Installing ZAP..."
ZAP_VER=$(ghlatest zaproxy/zaproxy)
retry_cmd 4 5 sh -c 'curl -fsSL "https://github.com/zaproxy/zaproxy/releases/download/v$${ZAP_VER}/ZAP_$${ZAP_VER}_Linux.tar.gz" | tar -xz -C /opt'
mv /opt/ZAP_$${ZAP_VER} /opt/zaproxy 2>/dev/null || true
printf '#!/bin/sh\nexec /opt/zaproxy/zap.sh "$@"\n' > /usr/local/bin/zap && chmod +x /usr/local/bin/zap
if [ "${tool_tier}" = "full" ]; then
echo "Installing Metasploit (full tier)..."
curl -fsSL https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > /tmp/msfinstall
chmod +x /tmp/msfinstall && /tmp/msfinstall
fi
# --- Phase 8: Clone traffic-generator suites ---
- mkdir -p /opt/traffic-generator/suites /opt/traffic-generator/results
- |
. /usr/local/lib/cloud-init-helpers.sh
log_phase "phase8" "cloning traffic generator suites"
clone_repo "https://github.com/f5-sales-demo/traffic-generator.git" /opt/traffic-generator/repo 1 || log_phase "warning" "traffic-generator repo clone failed — suites will be from cloud-init"
- |
if [ -d /opt/traffic-generator/repo/suites ]; then
cp -r /opt/traffic-generator/repo/suites/* /opt/traffic-generator/suites/
chmod -R +x /opt/traffic-generator/suites/
fi
- chown -R azureuser:azureuser /opt/traffic-generator
# --- Phase 9: Smoke test and status update ---
- |
. /usr/local/lib/cloud-init-helpers.sh
log_phase "phase9" "running smoke test"
export NODE_PATH=/usr/lib/node_modules
export PATH=/usr/local/bin:$PATH
PASS=0; FAIL=0
for tool in nmap nikto sqlmap nuclei dalfox ffuf gobuster feroxbuster subfinder httpx sslscan hydra john hashcat masscan hping3 tshark wrk hey vegeta ab zap testssl arjun wfuzz gotestwaf node npx playwright; do
if command -v "$tool" >/dev/null 2>&1; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); echo "MISSING: $tool"; fi
done
if [ "$FAIL" -eq 0 ]; then STATUS="ready"; else STATUS="degraded"; fi
log_phase "phase9" "smoke test complete: $PASS passed, $FAIL failed"
printf '{"status":"%s","tool_tier":"${tool_tier}","tools_pass":%d,"tools_fail":%d,"timestamp":"%s"}\n' \
"$STATUS" "$PASS" "$FAIL" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > /opt/traffic-generator/status.json
# --- Completion ---
- |
. /usr/local/lib/cloud-init-helpers.sh
log_phase "complete" "traffic-generator provisioned"

outputs.tf 公开 17 个输出,包括公共/私有 IP、SSH 命令、资源标识符以及组件特定值(target_fqdn、status_check):

# ---------------------------------------------------------
# Standard Outputs (present in every demo resource)
# ---------------------------------------------------------
output "deployer" {
description = "Resolved deployer identifier"
value = local.deployer
}
output "resource_group_name" {
description = "Name of the resource group"
value = azurerm_resource_group.main.name
}
output "resource_group_id" {
description = "Resource ID of the resource group"
value = azurerm_resource_group.main.id
}
output "location" {
description = "Azure region"
value = azurerm_resource_group.main.location
}
output "public_ip" {
description = "Public IP address of the VM"
value = azurerm_public_ip.main.ip_address
}
output "private_ip" {
description = "Private IP address of the VM"
value = azurerm_network_interface.main.private_ip_address
}
output "ssh_command" {
description = "SSH command to connect to the VM"
value = "ssh ${var.admin_username}@${azurerm_public_ip.main.ip_address}"
}
output "vm_name" {
description = "Name of the virtual machine"
value = azurerm_linux_virtual_machine.main.name
}
output "vm_id" {
description = "Resource ID of the virtual machine"
value = azurerm_linux_virtual_machine.main.id
}
output "nsg_name" {
description = "Name of the network security group"
value = azurerm_network_security_group.main.name
}
output "nsg_id" {
description = "Resource ID of the network security group"
value = azurerm_network_security_group.main.id
}
output "vnet_name" {
description = "Name of the virtual network"
value = azurerm_virtual_network.main.name
}
output "subnet_id" {
description = "Resource ID of the subnet"
value = azurerm_subnet.main.id
}
output "component" {
description = "Component name"
value = local.component
}
output "environment" {
description = "Environment label"
value = var.environment
}
# ---------------------------------------------------------
# Component-Specific Outputs
# ---------------------------------------------------------
output "target_fqdn" {
description = "Target FQDN the traffic generator is configured to attack"
value = var.target_fqdn
}
output "status_check" {
description = "SSH command to check provisioning status"
value = "ssh ${var.admin_username}@${azurerm_public_ip.main.ip_address} cat /opt/traffic-generator/status.json"
}

terraform.tfvars.example 复制到 terraform.tfvars 并填入您的值。.gitignore 排除了 terraform.tfvars 以防止提交凭据:

# Copy this file to terraform.tfvars and fill in your values.
# terraform.tfvars is gitignored — never commit real credentials.
# --- Required ---
subscription_id = "00000000-0000-0000-0000-000000000000"
target_fqdn = "demo.example.com"
# --- Optional overrides (defaults shown) ---
# deployer = "" # auto-resolved from Azure AD
# location = "eastus2"
# environment = "lab"
# vm_size = "Standard_F16s_v2"
# disk_size_gb = 64
# admin_username = "azureuser"
# ssh_public_key_path = "~/.ssh/id_ed25519.pub"
# tags = {}
# target_origin_ip = ""
# tool_tier = "standard"

subscription_id 替换为您的 Azure 订阅 ID(通过 az account show --query id -o tsv 获取),将 target_fqdn 替换为您的 F5 XC 负载均衡器域名。

Terminal window
terraform init
terraform plan
terraform apply

部署成功后,Terraform 将输出公共 IP、SSH 命令和目标 FQDN。

建议对此 CPU 密集型流量生成工作负载使用 F 系列计算优化型 VM。cloud-init 内核调优和工具并发数会随 vCPU 数量自动扩展。

VM SKUvCPU内存网络使用场景
Standard_F4s_v248 GiB10 Gbps轻量实验室/演示
Standard_F8s_v2816 GiB12.5 Gbps标准演示
Standard_F16s_v21632 GiB12.5 Gbps负载测试(默认)
Standard_F32s_v23264 GiB16 Gbps重度基准测试
Standard_D16s_v31664 GiB12.5 Gbps内存密集型工作负载

Cloud-init 预置标准层需要 15-20 分钟完整层约需 25 分钟。VM 可立即通过 SSH 访问,但在 cloud-init 完成之前,工具仍在安装中。

SSH 进入 VM 并等待预置完成:

Terminal window
ssh azureuser@$(terraform output -raw public_ip)
cloud-init status --wait

完成后的预期输出:

status: done

cloud-init 脚本在完成时写入状态文件:

Terminal window
cat /opt/traffic-generator/status.json

预期输出:

{
"status": "ready",
"tier": "standard",
"target_fqdn": "demo.example.com",
"completed": "2026-04-25T14:30:00Z",
"tools_installed": true
}

运行验证循环以确认每个关键工具在 PATH 中可用:

Terminal window
for tool in nikto sqlmap nuclei dalfox ffuf gobuster feroxbuster nmap masscan \
hydra medusa ncrack sslscan tshark hping3 mitmproxy sslyze subfinder httpx \
amass arjun mtr whatweb dirb socat netcat ngrep iperf3 whois dig wrk hey; do
if command -v "$tool" > /dev/null 2>&1; then
echo "OK: $tool"
else
echo "MISSING: $tool"
fi
done

确认运行器和套件已安装:

Terminal window
ls /opt/traffic-generator/suites/runner.sh
ls /opt/traffic-generator/suites/*/
/opt/traffic-generator/suites/runner.sh web-app-attacks --dry-run
输出描述示例
deployer已解析的部署者标识符jsmith
public_ip流量生成器 VM 的公共 IP 地址20.12.78.200
private_ipVM 的私有 IP 地址10.201.0.4
ssh_command连接到 VM 的 SSH 命令ssh azureuser@20.12.78.200
resource_group_name资源组名称rg-traffic-generator-dev-jsmith
resource_group_id资源组 Azure 资源 ID/subscriptions/.../resourceGroups/rg-traffic-generator-dev-jsmith
vm_name虚拟机名称vm-traffic-generator-dev-jsmith
vm_id虚拟机 Azure 资源 ID/subscriptions/.../virtualMachines/vm-traffic-generator-dev-jsmith
nsg_name网络安全组名称nsg-traffic-generator-dev-jsmith
nsg_id网络安全组 Azure 资源 ID/subscriptions/.../networkSecurityGroups/nsg-traffic-generator-dev-jsmith
vnet_name虚拟网络名称vnet-traffic-generator-dev-jsmith
subnet_id子网 Azure 资源 ID/subscriptions/.../subnets/snet-traffic-generator-dev-jsmith
component组件标识符traffic-generator
environment部署环境dev
locationAzure 区域eastus2
target_fqdn为流量套件配置的目标 FQDNdemo.example.com
status_check检查预置状态的命令ssh azureuser@20.12.78.200 cat /opt/traffic-generator/status.json

检索任意输出:

Terminal window
terraform output -raw public_ip
terraform output -raw ssh_command

流量生成器以 F5 XC HTTP 负载均衡器为目标,该负载均衡器位于源服务器前端。请先部署源服务器,配置 F5 XC,然后将 target_fqdn 设置为负载均衡器的 FQDN:

Terminal window
# 部署 origin-server 并创建 F5 XC HTTP 负载均衡器后:
cat > terraform.tfvars <<EOF
subscription_id = "your-subscription-id"
target_fqdn = "your-xc-load-balancer.example.com"
target_origin_ip = "$(cd ../../origin-server/terraform && terraform output -raw public_ip)"
EOF
必填变量来源描述
target_fqdnF5 XC 控制台保护源服务器的 HTTP 负载均衡器 FQDN
target_origin_ip源服务器 public_ip 输出用于旁路测试的直接源 IP(可选)

继续前往工具目录查看完整工具参考,或前往套件开始生成流量。