#!/bin/bash # ============================================================================= # PVE-Level Firewall for Security Scanner VM # Run this on the Proxmox host # Applies firewall rules at the hypervisor level (defense in depth) # ============================================================================= set -euo pipefail VMID=200 # --- Configuration (MUST be changed before running) --- # Admin IPs allowed to access the scanner (comma-separated) ADMIN_IPS="${SCANNER_ADMIN_IPS:-}" # Internal subnets the scanner is allowed to reach INTERNAL_NETS="${SCANNER_INTERNAL_NETS:-10.0.0.0/8,172.16.0.0/12,192.168.0.0/16}" # --- Guard: require ADMIN_IPS to be set --- if [[ -z "${ADMIN_IPS}" ]]; then echo "[ERROR] SCANNER_ADMIN_IPS environment variable is not set." >&2 echo " Example: export SCANNER_ADMIN_IPS='192.168.68.100,192.168.68.101'" >&2 exit 1 fi # ============================================================================= # Enable PVE firewall for the VM # ============================================================================= echo "[+] Enabling PVE firewall for VM ${VMID}..." # Create firewall config directory mkdir -p "/etc/pve/firewall" # --- VM-level firewall rules --- cat > "/etc/pve/firewall/${VMID}.fw" << 'FWEOF' [OPTIONS] enable: 1 policy_in: DROP policy_out: ACCEPT dhcp: 0 macfilter: 0 log_level_in: info log_level_out: warning [IPSET admin_ips] # Add your admin workstation IPs here 192.168.1.100 192.168.1.101 [IPSET internal_nets] # Internal subnets the scanner can reach 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 [IPSET update_targets] # External hosts for vulnerability database updates # Greenbone feed servers feed.community.greenbone.net # Debian/Ubuntu repos deb.debian.org security.debian.org # Docker Hub registry-1.docker.io production.cloudflare.docker.com auth.docker.io # GitHub (for Nuclei templates) github.com objects.githubusercontent.com # ============================================================================= # INBOUND RULES - Only admin access # ============================================================================= [RULES] # Allow SSH from admin IPs only IN ACCEPT -source +admin_ips -p tcp -dport 22 -log info # Allow OpenVAS Web UI (9392) from admin IPs only IN ACCEPT -source +admin_ips -p tcp -dport 9392 -log info # Allow ICMP ping from internal networks IN ACCEPT -source +internal_nets -p icmp # Allow established/related connections back in IN ACCEPT -match conntrack --ctstate RELATED,ESTABLISHED # Log and drop everything else (handled by policy_in: DROP) # ============================================================================= # OUTBOUND RULES - Restricted # ============================================================================= # Allow DNS resolution OUT ACCEPT -p udp -dport 53 OUT ACCEPT -p tcp -dport 53 # Allow NTP OUT ACCEPT -p udp -dport 123 # Allow scanning to internal networks (all ports) OUT ACCEPT -dest +internal_nets # Allow HTTPS/HTTP only to update targets (vuln DB, packages, docker) OUT ACCEPT -dest +update_targets -p tcp -dport 443 -log info OUT ACCEPT -dest +update_targets -p tcp -dport 80 # Allow established connections OUT ACCEPT -match conntrack --ctstate RELATED,ESTABLISHED # Drop all other outbound OUT DROP -log warning FWEOF echo "[+] PVE firewall rules written to /etc/pve/firewall/${VMID}.fw" # --- Enable datacenter-level firewall if not already --- echo "[+] Enabling datacenter-level firewall..." pvesh set /cluster/firewall/options --enable 1 2>/dev/null || { echo "[!] pvesh failed, checking cluster.fw manually..." if ! grep -q "enable: 1" /etc/pve/firewall/cluster.fw 2>/dev/null; then mkdir -p /etc/pve/firewall if [[ -f /etc/pve/firewall/cluster.fw ]]; then sed -i 's/^enable: 0/enable: 1/' /etc/pve/firewall/cluster.fw else cat > /etc/pve/firewall/cluster.fw << 'EOF' [OPTIONS] enable: 1 policy_in: ACCEPT policy_out: ACCEPT EOF fi fi } echo "[+] PVE firewall configured. Verify with: pvesh get /nodes/$(hostname)/qemu/${VMID}/firewall/rules" echo "[!] IMPORTANT: Update admin_ips and internal_nets to match your network."