feat: initial setup for PVE security scanner VM
Scripts for deploying a hardened internal network security scanner on Proxmox VE: - PVE-level firewall and VM creation - System hardening (sysctl, auditd, AIDE) - nftables firewall with dynamic IP blocking - SSH hardening with fail2ban - Security tools (OpenVAS, Nmap, Nuclei, httpx, Nikto, testssl, NetExec) - Monitoring, logging, and Docker autostart
This commit is contained in:
48
pve/create-vm.sh
Normal file
48
pve/create-vm.sh
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# PVE Security Scanner VM - Creation Script
|
||||
# Run this on the Proxmox host
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
# --- Configuration (modify these) ---
|
||||
VMID=200
|
||||
VM_NAME="security-scanner"
|
||||
STORAGE="local-lvm" # PVE storage pool
|
||||
ISO_PATH="local:iso/debian-12-amd64-netinst.iso" # Debian 12 ISO
|
||||
BRIDGE="vmbr0" # Network bridge
|
||||
CORES=4
|
||||
MEMORY=8192 # MB
|
||||
DISK_SIZE="80G"
|
||||
VLAN_TAG="" # Set VLAN tag if needed, e.g., "10"
|
||||
|
||||
# --- Guard: check if VM already exists ---
|
||||
if qm status "${VMID}" &>/dev/null; then
|
||||
echo "[!] VM ${VMID} already exists. Skipping creation."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# --- Create VM ---
|
||||
echo "[+] Creating VM ${VMID} (${VM_NAME})..."
|
||||
|
||||
# VLAN_TAG conditional: appends ,tag=<N> only when VLAN_TAG is set
|
||||
qm create "${VMID}" \
|
||||
--name "${VM_NAME}" \
|
||||
--ostype l26 \
|
||||
--cores "${CORES}" \
|
||||
--memory "${MEMORY}" \
|
||||
--cpu cputype=host \
|
||||
--scsihw virtio-scsi-single \
|
||||
--scsi0 "${STORAGE}:${DISK_SIZE}" \
|
||||
--ide2 "${ISO_PATH},media=cdrom" \
|
||||
--net0 "virtio,bridge=${BRIDGE}${VLAN_TAG:+,tag=${VLAN_TAG}}" \
|
||||
--boot "order=ide2;scsi0" \
|
||||
--agent enabled=1 \
|
||||
--onboot 1 \
|
||||
--protection 0 \
|
||||
--description "Internal network security scanner. Restricted network access."
|
||||
|
||||
echo "[+] VM ${VMID} created successfully."
|
||||
echo "[+] Start the VM and install Debian 12, then run the vm/ scripts."
|
||||
echo ""
|
||||
echo " qm start ${VMID}"
|
||||
135
pve/firewall.sh
Normal file
135
pve/firewall.sh
Normal file
@@ -0,0 +1,135 @@
|
||||
#!/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."
|
||||
Reference in New Issue
Block a user