- หน้าแรก
- ตัวสร้างทราฟฟิก
- ปรับใช้งาน
ปรับใช้งาน
ไฟล์ Terraform ทั้งหมดอยู่ในไดเรกทอรี terraform/ โคลน repository และปรับใช้งานโดยตรง:
git clone https://github.com/f5-sales-demo/traffic-generator.gitcd traffic-generator/terraformcp terraform.tfvars.example terraform.tfvars# Edit terraform.tfvars with your Azure subscription ID and target FQDNการกำหนดค่า Terraform
หัวข้อที่มีชื่อว่า “การกำหนดค่า Terraform”โครงสร้างไฟล์ Terraform
หัวข้อที่มีชื่อว่า “โครงสร้างไฟล์ Terraform”ไดเรกทอรี terraform ประกอบด้วย 9 ไฟล์ตามมาตรฐาน Demo Resource Standard:
versions.tf— ข้อจำกัดเวอร์ชัน Terraform และ provider (azurerm ~> 4.0, azuread ~> 3.0)providers.tf— การกำหนดค่า provider สำหรับ Azure RM และ Azure ADdata.tf— แหล่งข้อมูล Azure AD สำหรับการแก้ไข deployer อัตโนมัติlocals.tf— การแก้ไข deployer, การตั้งชื่อทรัพยากรตาม Azure Cloud Adoption Framework, แท็กมาตรฐานmain.tf— Resource group (ตั้งชื่อเป็นrg-traffic-generator-{environment}-{deployer})variables.tf— ตัวแปรนำเข้าทั้งหมด (2 ที่จำเป็น, 9 ที่เป็นตัวเลือก) พร้อมการแก้ไข deployer อัตโนมัติผ่าน Azure ADnetwork.tf— VNet (10.201.0.0/16), subnet, public IP, NSG (พอร์ต 22 SSH), NICvm.tf— Ubuntu 24.04 VM พร้อม cloud-init ผ่าน templatefile()outputs.tf— 17 outputs: 15 outputs มาตรฐานที่ใช้ร่วมกันโดยทรัพยากรสาธิตทั้งหมด (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 outputs เฉพาะส่วนประกอบ (target_fqdn, status_check)
Provider และตัวแปร
หัวข้อที่มีชื่อว่า “Provider และตัวแปร”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 compute-optimized) — ดู การกำหนดขนาด 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, subnet, public 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}Virtual Machine พร้อม Cloud-Init
หัวข้อที่มีชื่อว่า “Virtual Machine พร้อม Cloud-Init”vm.tf สร้าง Ubuntu 24.04 VM และส่งต่อ cloud-init template พร้อมตัวแปร target และ tooling:
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
หัวข้อที่มีชื่อว่า “การจัดเตรียมด้วย Cloud-Init”cloud-init.yaml คือการกำหนดค่าที่ผ่านการตรวจสอบในการใช้งานจริง ซึ่งได้รับการยืนยันภายใต้การทดสอบโหลดอย่างต่อเนื่องมากกว่า 48 ชั่วโมง รวมถึงการทดสอบ origin torture 4 รอบ ใช้ไลบรารีช่วยเหลือพร้อมตรรกะการลองใหม่, การบันทึกความคืบหน้าไปยัง /var/log/cloud-init-progress.log, 9 ขั้นตอนการจัดเตรียม และการรายงานสถานะแบบมีเงื่อนไข (ready/degraded ใน status.json) โดยจัดเตรียม:
- การปรับแต่ง Kernel —
somaxconn=131072,tcp_max_tw_buckets=4000000,tcp_fin_timeout=5, socket buffers ขนาด 64 MiB,file-max=4194304 - การปรับแต่ง NIC — RPS/RFS ทั่วทุก core, ring buffers สูงสุดผ่าน 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 พร้อม stealth plugin สำหรับการจำลอง bot
- การทำงานอัตโนมัติของเบราว์เซอร์ — Playwright Chromium สำหรับการทดสอบ headless browser
- Git clones — testssl.sh, recon-ng, spiderfoot, SecLists, waf-bypass, PayloadsAllTheThings
- การปรับใช้งาน Suite — ชุดทราฟฟิก 19 ชุด โคลนจาก repository นี้ไปยัง
/opt/traffic-generator/suites/ - ไฟล์กำหนดค่า —
config.envพร้อมTARGET_FQDN,TARGET_ORIGIN_IPและCRAPI_PORT
cloud-init ใช้ตัวแปร Terraform template: ${target_fqdn}, ${target_origin_ip} และ ${tool_tier} ตัวแปร Shell เช่น $NPROC ถูก escape เป็น $$NPROC ใน Terraform templatefile
#cloud-configpackage_update: truepackage_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
หัวข้อที่มีชื่อว่า “Outputs”outputs.tf แสดง 17 outputs รวมถึง public/private 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 เพื่อป้องกันการ commit credentials:
# 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 subscription ID ของคุณ (จาก az account show --query id -o tsv) และ target_fqdn ด้วยโดเมน F5 XC load balancer ของคุณ
ปรับใช้งาน
หัวข้อที่มีชื่อว่า “ปรับใช้งาน”terraform init
terraform plan
terraform applyTerraform แสดงผล public IP, คำสั่ง SSH และ target FQDN หลังจากการปรับใช้งานสำเร็จ
การกำหนดขนาด VM
หัวข้อที่มีชื่อว่า “การกำหนดขนาด VM”แนะนำให้ใช้ F-series compute-optimized VMs สำหรับ workload การสร้างทราฟฟิกที่ใช้ CPU เป็นหลัก การปรับแต่ง kernel ของ cloud-init และการทำงานพร้อมกันของเครื่องมือจะปรับขนาดโดยอัตโนมัติตามจำนวน vCPU
| VM SKU | vCPU | RAM | เครือข่าย | กรณีการใช้งาน |
|---|---|---|---|---|
| Standard_F4s_v2 | 4 | 8 GiB | 10 Gbps | Lab/demo ขนาดเล็ก |
| Standard_F8s_v2 | 8 | 16 GiB | 12.5 Gbps | Demo มาตรฐาน |
| Standard_F16s_v2 | 16 | 32 GiB | 12.5 Gbps | การทดสอบโหลด (ค่าเริ่มต้น) |
| Standard_F32s_v2 | 32 | 64 GiB | 16 Gbps | การทดสอบประสิทธิภาพสูง |
| Standard_D16s_v3 | 16 | 64 GiB | 12.5 Gbps | Workload ที่ต้องการ RAM มาก |
การตรวจสอบหลังปรับใช้งาน
หัวข้อที่มีชื่อว่า “การตรวจสอบหลังปรับใช้งาน”การจัดเตรียมด้วย cloud-init ใช้เวลา 15-20 นาทีสำหรับ standard tier และ ประมาณ 25 นาทีสำหรับ full tier VM สามารถเข้าถึงผ่าน SSH ได้ทันที แต่เครื่องมือยังคงถูกติดตั้งอยู่จนกว่า cloud-init จะเสร็จสมบูรณ์
รอ Cloud-Init
หัวข้อที่มีชื่อว่า “รอ Cloud-Init”SSH เข้าสู่ VM และรอให้การจัดเตรียมเสร็จสมบูรณ์:
ssh azureuser@$(terraform output -raw public_ip)
cloud-init status --waitผลลัพธ์ที่คาดหวังเมื่อเสร็จสมบูรณ์:
status: doneตรวจสอบไฟล์สถานะ
หัวข้อที่มีชื่อว่า “ตรวจสอบไฟล์สถานะ”สคริปต์ cloud-init เขียนไฟล์สถานะเมื่อเสร็จสมบูรณ์:
cat /opt/traffic-generator/status.jsonผลลัพธ์ที่คาดหวัง:
{ "status": "ready", "tier": "standard", "target_fqdn": "demo.example.com", "completed": "2026-04-25T14:30:00Z", "tools_installed": true}ตรวจสอบเครื่องมือสำคัญ
หัวข้อที่มีชื่อว่า “ตรวจสอบเครื่องมือสำคัญ”รัน verification loop เพื่อยืนยันว่าเครื่องมือสำคัญแต่ละรายการพร้อมใช้งานบน PATH:
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" fidoneตรวจสอบ Suite Runner
หัวข้อที่มีชื่อว่า “ตรวจสอบ Suite Runner”ยืนยันว่า runner และ suites ถูกติดตั้งแล้ว:
ls /opt/traffic-generator/suites/runner.shls /opt/traffic-generator/suites/*/
/opt/traffic-generator/suites/runner.sh web-app-attacks --dry-runTerraform Outputs
หัวข้อที่มีชื่อว่า “Terraform Outputs”| Output | คำอธิบาย | ตัวอย่าง |
|---|---|---|
deployer | ตัวระบุ deployer ที่แก้ไขแล้ว | jsmith |
public_ip | ที่อยู่ IP สาธารณะของ VM ตัวสร้างทราฟฟิก | 20.12.78.200 |
private_ip | ที่อยู่ IP ส่วนตัวของ VM | 10.201.0.4 |
ssh_command | คำสั่ง SSH เพื่อเชื่อมต่อกับ VM | ssh azureuser@20.12.78.200 |
resource_group_name | ชื่อ resource group | rg-traffic-generator-dev-jsmith |
resource_group_id | Azure resource ID ของ resource group | /subscriptions/.../resourceGroups/rg-traffic-generator-dev-jsmith |
vm_name | ชื่อ virtual machine | vm-traffic-generator-dev-jsmith |
vm_id | Azure resource ID ของ virtual machine | /subscriptions/.../virtualMachines/vm-traffic-generator-dev-jsmith |
nsg_name | ชื่อ network security group | nsg-traffic-generator-dev-jsmith |
nsg_id | Azure resource ID ของ network security group | /subscriptions/.../networkSecurityGroups/nsg-traffic-generator-dev-jsmith |
vnet_name | ชื่อ virtual network | vnet-traffic-generator-dev-jsmith |
subnet_id | Azure resource ID ของ subnet | /subscriptions/.../subnets/snet-traffic-generator-dev-jsmith |
component | ตัวระบุส่วนประกอบ | traffic-generator |
environment | สภาพแวดล้อมการปรับใช้งาน | dev |
location | Azure region | eastus2 |
target_fqdn | FQDN เป้าหมายที่กำหนดค่าสำหรับชุดทราฟฟิก | demo.example.com |
status_check | คำสั่งตรวจสอบสถานะการจัดเตรียม | ssh azureuser@20.12.78.200 cat /opt/traffic-generator/status.json |
ดึงค่า output ใดก็ได้:
terraform output -raw public_ipterraform output -raw ssh_commandการเชื่อมต่อจากส่วนประกอบต้นน้ำ
หัวข้อที่มีชื่อว่า “การเชื่อมต่อจากส่วนประกอบต้นน้ำ”ตัวสร้างทราฟฟิกกำหนดเป้าหมายที่ F5 XC HTTP load balancer ที่อยู่หน้าเซิร์ฟเวอร์ต้นทาง ปรับใช้งานเซิร์ฟเวอร์ต้นทางก่อน จากนั้นกำหนดค่า F5 XC แล้วตั้งค่า target_fqdn เป็น FQDN ของ load balancer:
# After deploying origin-server and creating an F5 XC HTTP load balancer:cat > terraform.tfvars <<EOFsubscription_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_fqdn | F5 XC คอนโซล | FQDN ของ HTTP load balancer ที่ปกป้องเซิร์ฟเวอร์ต้นทาง |
target_origin_ip | output public_ip ของเซิร์ฟเวอร์ต้นทาง | IP ต้นทางโดยตรงสำหรับการทดสอบ bypass (ตัวเลือก) |
ดำเนินการต่อไปยัง Tool Catalog สำหรับข้อมูลอ้างอิงเครื่องมือฉบับสมบูรณ์ หรือ Suites เพื่อเริ่มสร้างทราฟฟิก