Zum Inhalt springen

Bereitstellen

Alle Terraform-Dateien befinden sich im Verzeichnis terraform/. Klonen Sie das Repository und stellen Sie es direkt bereit:

Terminal-Fenster
git clone https://github.com/f5-sales-demo/traffic-generator.git
cd traffic-generator/terraform
cp terraform.tfvars.example terraform.tfvars
# Edit terraform.tfvars with your Azure subscription ID and target FQDN

Das Terraform-Verzeichnis enthält 9 Dateien gemäß dem Demo-Ressourcen-Standard:

  • versions.tf — Terraform- und Provider-Versionseinschränkungen (azurerm ~> 4.0, azuread ~> 3.0)
  • providers.tf — Azure RM und Azure AD Provider-Konfiguration
  • data.tf — Azure AD-Datenquellen für die automatische Auflösung des Bereitstellers
  • locals.tf — Bereitsteller-Auflösung, Azure Cloud Adoption Framework-Ressourcenbenennung, Standard-Tags
  • main.tf — Ressourcengruppe (benannt rg-traffic-generator-{environment}-{deployer})
  • variables.tf — Alle Eingabevariablen (2 erforderlich, 9 optional) mit automatischer Bereitsteller-Auflösung über Azure AD
  • network.tf — VNet (10.201.0.0/16), Subnetz, öffentliche IP, NSG (Port 22 SSH), NIC
  • vm.tf — Ubuntu 24.04 VM mit cloud-init über templatefile()
  • outputs.tf — 17 Ausgaben: 15 Standard-Ausgaben, die von allen Demo-Ressourcen gemeinsam genutzt werden (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), sowie 2 komponentenspezifische Ausgaben (target_fqdn, status_check)

main.tf konfiguriert den Azure-Provider:

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

variables.tf definiert alle konfigurierbaren Parameter. Der Standardwert für vm_size ist Standard_F16s_v2 (16 vCPU rechenoptimiert) — siehe VM-Dimensionierung für Skalierungshinweise:

# ---------------------------------------------------------
# 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 erstellt das VNet, Subnetz, die öffentliche IP, NSG (Port 22 SSH) und 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 erstellt die Ubuntu 24.04 VM und übergibt die cloud-init-Vorlage mit Ziel- und Tooling-Variablen:

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 ist die produktionsvalidierte Konfiguration, die unter 48+ Stunden kontinuierlichem Lasttests einschließlich 4 Runden Ursprungs-Belastungstests verifiziert wurde. Sie verwendet eine Hilfsbibliothek mit Wiederholungslogik, Fortschrittsprotokollierung nach /var/log/cloud-init-progress.log, 9 Bereitstellungsphasen und bedingter Statusberichterstattung (bereit/beeinträchtigt in status.json). Sie stellt Folgendes bereit:

  • Kernel-Optimierungsomaxconn=131072, tcp_max_tw_buckets=4000000, tcp_fin_timeout=5, 64 MiB Socket-Puffer, file-max=4194304
  • NIC-Optimierung — RPS/RFS über alle Kerne, maximierte Ringpuffer über ethtool, THP=always
  • APT-Pakete — 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-Pakete — mitmproxy, sslyze, dnsrecon, fierce, theHarvester, playwright, scapy, impacket, arjun
  • Node.js 20.x — Puppeteer mit Stealth-Plugin für Bot-Simulation
  • Browser-Automatisierung — Playwright Chromium für Headless-Browser-Tests
  • Git-Klone — testssl.sh, recon-ng, spiderfoot, SecLists, waf-bypass, PayloadsAllTheThings
  • Suite-Bereitstellung — 19 Datenverkehrs-Suiten aus diesem Repository nach /opt/traffic-generator/suites/ geklont
  • Konfigurationsdateiconfig.env mit TARGET_FQDN, TARGET_ORIGIN_IP und CRAPI_PORT

Cloud-init verwendet Terraform-Vorlagenvariablen: ${target_fqdn}, ${target_origin_ip} und ${tool_tier}. Shell-Variablen wie $NPROC werden in der Terraform-templatefile als $$NPROC maskiert.

#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 stellt 17 Ausgaben bereit, darunter öffentliche/private IP, SSH-Befehl, Ressourcenkennzeichner und komponentenspezifische Werte (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"
}

Kopieren Sie terraform.tfvars.example nach terraform.tfvars und tragen Sie Ihre Werte ein. Die .gitignore schließt terraform.tfvars aus, um das versehentliche Übertragen von Anmeldeinformationen zu verhindern:

# 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"

Ersetzen Sie subscription_id durch Ihre Azure-Abonnement-ID (aus az account show --query id -o tsv) und target_fqdn durch Ihre F5 XC Load-Balancer-Domain.

Terminal-Fenster
terraform init
terraform plan
terraform apply

Terraform gibt nach erfolgreicher Bereitstellung die öffentliche IP, den SSH-Befehl und den Ziel-FQDN aus.

Die rechenoptimierten VMs der F-Series werden für diese CPU-gebundene Datenverkehrsgenerierungs-Arbeitslast empfohlen. Die cloud-init-Kernel-Optimierung und Tool-Parallelität skalieren automatisch mit der Anzahl der vCPUs.

VM-SKUvCPURAMNetzwerkAnwendungsfall
Standard_F4s_v248 GiB10 GbpsLeichtes Labor/Demo
Standard_F8s_v2816 GiB12.5 GbpsStandard-Demos
Standard_F16s_v21632 GiB12.5 GbpsLasttests (Standard)
Standard_F32s_v23264 GiB16 GbpsUmfangreiches Benchmarking
Standard_D16s_v31664 GiB12.5 GbpsRAM-intensive Arbeitslasten

Die cloud-init-Bereitstellung dauert 15–20 Minuten für den Standard-Tier und ca. 25 Minuten für den Full-Tier. Die VM ist sofort über SSH erreichbar, aber die Werkzeuge werden noch installiert, bis cloud-init abgeschlossen ist.

Verbinden Sie sich per SSH mit der VM und warten Sie, bis die Bereitstellung abgeschlossen ist:

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

Erwartete Ausgabe nach Abschluss:

status: done

Das cloud-init-Skript schreibt nach Abschluss eine Statusdatei:

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

Erwartete Ausgabe:

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

Führen Sie eine Überprüfungsschleife aus, um zu bestätigen, dass jedes kritische Werkzeug im PATH verfügbar ist:

Terminal-Fenster
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

Bestätigen Sie, dass der Runner und die Suiten installiert sind:

Terminal-Fenster
ls /opt/traffic-generator/suites/runner.sh
ls /opt/traffic-generator/suites/*/
/opt/traffic-generator/suites/runner.sh web-app-attacks --dry-run
AusgabeBeschreibungBeispiel
deployerAufgelöste Bereitsteller-Kennungjsmith
public_ipÖffentliche IP-Adresse der Datenverkehrsgenerator-VM20.12.78.200
private_ipPrivate IP-Adresse der VM10.201.0.4
ssh_commandSSH-Befehl zur Verbindung mit der VMssh azureuser@20.12.78.200
resource_group_nameName der Ressourcengrupperg-traffic-generator-dev-jsmith
resource_group_idAzure-Ressourcen-ID der Ressourcengruppe/subscriptions/.../resourceGroups/rg-traffic-generator-dev-jsmith
vm_nameName der virtuellen Maschinevm-traffic-generator-dev-jsmith
vm_idAzure-Ressourcen-ID der virtuellen Maschine/subscriptions/.../virtualMachines/vm-traffic-generator-dev-jsmith
nsg_nameName der Netzwerksicherheitsgruppensg-traffic-generator-dev-jsmith
nsg_idAzure-Ressourcen-ID der Netzwerksicherheitsgruppe/subscriptions/.../networkSecurityGroups/nsg-traffic-generator-dev-jsmith
vnet_nameName des virtuellen Netzwerksvnet-traffic-generator-dev-jsmith
subnet_idAzure-Ressourcen-ID des Subnetzes/subscriptions/.../subnets/snet-traffic-generator-dev-jsmith
componentKomponentenkennungtraffic-generator
environmentBereitstellungsumgebungdev
locationAzure-Regioneastus2
target_fqdnKonfigurierter Ziel-FQDN für Datenverkehrs-Suitendemo.example.com
status_checkBefehl zur Prüfung des Bereitstellungsstatusssh azureuser@20.12.78.200 cat /opt/traffic-generator/status.json

Beliebige Ausgabe abrufen:

Terminal-Fenster
terraform output -raw public_ip
terraform output -raw ssh_command

Der Datenverkehrsgenerator zielt auf einen F5 XC HTTP-Load-Balancer, der den Ursprungsserver vorschaltet. Stellen Sie zuerst den Ursprungsserver bereit, konfigurieren Sie F5 XC, und setzen Sie dann target_fqdn auf den FQDN des Load-Balancers:

Terminal-Fenster
# After deploying origin-server and creating an F5 XC HTTP load balancer:
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
Erforderliche VariableQuelleBeschreibung
target_fqdnF5 XC KonsoleFQDN des HTTP-Load-Balancers, der den Ursprungsserver schützt
target_origin_ippublic_ip-Ausgabe des UrsprungsserversDirekte Ursprungs-IP für Bypass-Tests (optional)

Fahren Sie fort mit dem Werkzeugkatalog für die vollständige Werkzeugreferenz oder mit Suiten, um mit der Datenverkehrsgenerierung zu beginnen.