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:
Yaojia Wang
2026-03-08 20:21:29 +01:00
commit 5e49b977ab
10 changed files with 1511 additions and 0 deletions

32
README.md Normal file
View File

@@ -0,0 +1,32 @@
# PVE Security Scanner
Internal network security scanning VM for Proxmox VE.
## Structure
```
pve-security-scanner/
├── pve/ # Proxmox host-level config
│ ├── create-vm.sh # One-click VM creation
│ └── firewall.sh # PVE firewall rules
├── vm/ # VM internal config
│ ├── 01-system-harden.sh # OS hardening
│ ├── 02-firewall.sh # nftables firewall
│ ├── 03-ssh-harden.sh # SSH hardening
│ ├── 04-install-tools.sh # Security tools
│ └── 05-monitoring.sh # Logging and monitoring
└── README.md
```
## Deployment
1. On PVE host: run `pve/create-vm.sh` to create the VM
2. On PVE host: run `pve/firewall.sh` to apply PVE-level firewall
3. SSH into VM, run scripts in `vm/` directory in order (01 -> 05)
## Network Design
- Scanner VM sits on the management VLAN / main bridge
- Allowed to reach all internal subnets for scanning
- Outbound internet restricted (only for vuln DB updates)
- Inbound restricted to SSH + Web UI from admin IPs only

48
pve/create-vm.sh Normal file
View 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
View 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."

207
vm/01-system-harden.sh Normal file
View File

@@ -0,0 +1,207 @@
#!/bin/bash
# =============================================================================
# System Hardening for Security Scanner VM
# Run this inside the VM as root
# =============================================================================
set -euo pipefail
echo "============================================"
echo " System Hardening - Security Scanner VM"
echo "============================================"
# --- 1. Update system ---
echo "[+] Updating system packages..."
apt update && apt upgrade -y
apt install -y unattended-upgrades apt-listchanges
# Enable automatic security updates
cat > /etc/apt/apt.conf.d/50unattended-upgrades << 'EOF'
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
};
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false";
EOF
dpkg-reconfigure -f noninteractive unattended-upgrades
# --- 2. Kernel hardening (sysctl) ---
echo "[+] Applying kernel hardening..."
cat > /etc/sysctl.d/99-security-scanner.conf << 'EOF'
# --- Network hardening ---
# Disable IP forwarding (scanner should not route traffic)
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
# Don't send ICMP redirects
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# Enable reverse path filtering (anti-spoofing)
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Ignore broadcast pings
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Enable SYN flood protection
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
# Log suspicious packets
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
# Disable source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# --- Memory protection ---
# Restrict dmesg access
kernel.dmesg_restrict = 1
# Restrict kernel pointer exposure
kernel.kptr_restrict = 2
# Enable ASLR
kernel.randomize_va_space = 2
# Restrict ptrace
kernel.yama.ptrace_scope = 2
# --- File system ---
# Restrict core dumps
fs.suid_dumpable = 0
EOF
sysctl -p /etc/sysctl.d/99-security-scanner.conf
# --- 3. Restrict core dumps ---
echo "[+] Disabling core dumps..."
cat > /etc/security/limits.d/99-no-core.conf << 'EOF'
* hard core 0
* soft core 0
EOF
# --- 4. Secure shared memory ---
echo "[+] Securing shared memory..."
if ! grep -q "tmpfs /dev/shm" /etc/fstab; then
echo "tmpfs /dev/shm tmpfs defaults,noexec,nosuid,nodev 0 0" >> /etc/fstab
fi
# --- 5. Set file permissions ---
echo "[+] Hardening file permissions..."
chmod 700 /root
chmod 600 /etc/crontab
chmod 700 /etc/cron.d
chmod 700 /etc/cron.daily
chmod 700 /etc/cron.hourly
chmod 700 /etc/cron.weekly
chmod 700 /etc/cron.monthly
# --- 6. Disable unnecessary services ---
echo "[+] Disabling unnecessary services..."
DISABLE_SERVICES=(
"avahi-daemon"
"cups"
"rpcbind"
"bluetooth"
)
for svc in "${DISABLE_SERVICES[@]}"; do
if systemctl is-enabled "${svc}" 2>/dev/null; then
systemctl disable --now "${svc}"
echo " Disabled: ${svc}"
fi
done
# --- 7. Install security tools ---
echo "[+] Installing security audit tools..."
apt install -y \
aide \
rkhunter \
lynis \
auditd \
audispd-plugins \
fail2ban \
logwatch
# --- 8. Initialize AIDE (file integrity monitoring) ---
echo "[!] Initializing AIDE database - this may take 10-20 minutes..."
aideinit
cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db
# --- 9. Configure auditd ---
echo "[+] Configuring audit rules..."
cat > /etc/audit/rules.d/scanner-audit.rules << 'EOF'
# Delete all existing rules
-D
# Buffer size
-b 8192
# Failure mode (1=printk, 2=panic)
-f 1
# Monitor /etc changes
-w /etc/ -p wa -k etc_changes
# Monitor authentication
-w /var/log/auth.log -p wa -k auth_log
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/gshadow -p wa -k identity
# Monitor sudo usage
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d/ -p wa -k sudoers
# Monitor network config changes
-w /etc/hosts -p wa -k network
-w /etc/network/ -p wa -k network
-w /etc/nftables.conf -p wa -k firewall
# Monitor cron changes
-w /etc/crontab -p wa -k cron
-w /etc/cron.d/ -p wa -k cron
# Monitor scanner tool configs
-w /opt/greenbone/ -p wa -k scanner_config
# Lock audit rules (requires reboot to change)
-e 2
EOF
systemctl restart auditd
# --- 10. Password policy ---
echo "[+] Setting password policy..."
apt install -y libpam-pwquality
sed -i 's/^#\s*minlen.*/minlen = 12/' /etc/security/pwquality.conf
sed -i 's/^#\s*minclass.*/minclass = 3/' /etc/security/pwquality.conf
sed -i 's/^#\s*maxrepeat.*/maxrepeat = 3/' /etc/security/pwquality.conf
# Verify settings were applied
for setting in minlen minclass maxrepeat; do
if ! grep -q "^${setting}" /etc/security/pwquality.conf; then
echo "[!] WARNING: ${setting} was not set - appending to config"
case "${setting}" in
minlen) echo "minlen = 12" >> /etc/security/pwquality.conf ;;
minclass) echo "minclass = 3" >> /etc/security/pwquality.conf ;;
maxrepeat) echo "maxrepeat = 3" >> /etc/security/pwquality.conf ;;
esac
fi
done
echo ""
echo "[+] System hardening complete."
echo "[!] Reboot recommended: shutdown -r now"

201
vm/02-firewall.sh Normal file
View File

@@ -0,0 +1,201 @@
#!/bin/bash
# =============================================================================
# nftables Firewall for Security Scanner VM
# Run this inside the VM as root
# Defense in depth: works alongside PVE-level firewall
# =============================================================================
set -euo pipefail
echo "============================================"
echo " nftables Firewall - Security Scanner VM"
echo "============================================"
# --- Configuration (MUST be set via environment variables) ---
ADMIN_IPS="${SCANNER_ADMIN_IPS:-192.168.68.0/24}"
INTERNAL_NETS="${SCANNER_INTERNAL_NETS:-10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16}"
DNS_SERVERS="${SCANNER_DNS_SERVERS:-192.168.68.1}"
# --- Guard: require ADMIN_IPS ---
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
# --- Install nftables ---
apt install -y nftables
systemctl enable nftables
# --- Write nftables config ---
cat > /etc/nftables.conf << EOF
#!/usr/sbin/nft -f
flush ruleset
# =============================================================================
# Definitions
# =============================================================================
define ADMIN_IPS = { ${ADMIN_IPS} }
define INTERNAL_NETS = { ${INTERNAL_NETS} }
define DNS_SERVERS = { ${DNS_SERVERS} }
# Ports for scanner services
define SCANNER_WEB_PORTS = { 443, 9392 }
define SCANNER_SSH_PORT = 22
# =============================================================================
# Main table
# =============================================================================
table inet firewall {
# -------------------------------------------------------------------------
# Sets for dynamic blocking
# -------------------------------------------------------------------------
set blocked_ips {
type ipv4_addr
flags timeout
comment "Dynamically blocked IPs"
}
set ssh_bruteforce {
type ipv4_addr
flags dynamic, timeout
timeout 15m
comment "SSH brute force tracking"
}
# -------------------------------------------------------------------------
# INPUT chain - What can reach the scanner
# -------------------------------------------------------------------------
chain input {
type filter hook input priority 0; policy drop;
# Drop blocked IPs immediately
ip saddr @blocked_ips drop
# Allow loopback
iif "lo" accept
# Allow established/related connections
ct state established,related accept
# Drop invalid packets
ct state invalid counter drop
# --- ICMP (rate limited) ---
ip protocol icmp icmp type {
echo-request,
echo-reply,
destination-unreachable,
time-exceeded
} limit rate 10/second accept
# --- SSH from admin IPs only (with brute-force protection) ---
tcp dport \$SCANNER_SSH_PORT ip saddr \$ADMIN_IPS \
ct state new \
limit rate 5/minute \
accept
# Track SSH brute force attempts
tcp dport \$SCANNER_SSH_PORT ct state new \
add @ssh_bruteforce { ip saddr limit rate 3/minute } \
counter drop
# --- OpenVAS Web UI from admin IPs only ---
tcp dport \$SCANNER_WEB_PORTS ip saddr \$ADMIN_IPS accept
# --- Docker internal communication (use iifname for wildcard) ---
iifname "docker*" accept
iifname "br-*" accept
# --- Log dropped packets (rate limited) ---
limit rate 10/second \
log prefix "[NFT-INPUT-DROP] " level warn
# Default: drop (set by policy)
}
# -------------------------------------------------------------------------
# FORWARD chain - Docker container traffic
# -------------------------------------------------------------------------
chain forward {
type filter hook forward priority 0; policy accept;
# Allow Docker container communication
iifname "docker*" accept
oifname "docker*" accept
iifname "br-*" accept
oifname "br-*" accept
}
# -------------------------------------------------------------------------
# OUTPUT chain - What the scanner can reach
# -------------------------------------------------------------------------
chain output {
type filter hook output priority 0; policy drop;
# Allow loopback
oif "lo" accept
# Allow established/related
ct state established,related accept
# --- DNS ---
udp dport 53 accept
tcp dport 53 accept
# --- NTP ---
udp dport 123 accept
# --- Scanning: allow ALL traffic to internal networks ---
# This is required for port scanning, service detection, vuln scanning
ip daddr \$INTERNAL_NETS accept
# --- Package updates and vuln DB sync (HTTPS/HTTP) ---
# Outbound HTTP/HTTPS is allowed for vuln DB updates, package management,
# and Docker image pulls. Restricted at PVE layer to update_targets IPSET.
tcp dport { 80, 443 } accept
# --- ICMP out ---
ip protocol icmp accept
# --- Log dropped outbound (rate limited) ---
limit rate 10/second \
log prefix "[NFT-OUTPUT-DROP] " level warn
# Default: drop (set by policy)
}
}
# =============================================================================
# Separate table for connection tracking and logging
# =============================================================================
table inet logging {
chain prerouting {
type filter hook prerouting priority -200; policy accept;
# Log new inbound connections (rate limited, for audit trail)
ct state new \
limit rate 5/second \
log prefix "[NFT-NEW-CONN] " level info
}
}
EOF
# --- Apply rules ---
echo "[+] Applying nftables rules..."
nft -f /etc/nftables.conf
# --- Verify ---
echo "[+] Current ruleset:"
nft list ruleset
echo ""
echo "[+] nftables firewall configured and active."
echo ""
echo "Useful commands:"
echo " nft list ruleset # Show all rules"
echo " nft list set inet firewall blocked_ips # Show blocked IPs"
echo " nft add element inet firewall blocked_ips { 1.2.3.4 timeout 1h } # Block IP"
echo " systemctl restart nftables # Reload rules"

150
vm/03-ssh-harden.sh Normal file
View File

@@ -0,0 +1,150 @@
#!/bin/bash
# =============================================================================
# SSH Hardening for Security Scanner VM
# Run this inside the VM as root
# =============================================================================
set -euo pipefail
echo "============================================"
echo " SSH Hardening - Security Scanner VM"
echo "============================================"
SSHD_CONFIG="/etc/ssh/sshd_config"
SSHD_CUSTOM="/etc/ssh/sshd_config.d/99-scanner-hardening.conf"
SSHD_BACKUP="${SSHD_CONFIG}.bak.$(date +%Y%m%d_%H%M%S)"
# --- 1. Backup original config ---
cp "${SSHD_CONFIG}" "${SSHD_BACKUP}"
# --- 2. Write hardened SSH config ---
echo "[+] Writing hardened SSH configuration..."
cat > "${SSHD_CUSTOM}" << 'EOF'
# =============================================================================
# SSH Hardening - Security Scanner VM
# =============================================================================
# --- Authentication ---
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
PermitEmptyPasswords no
MaxAuthTries 10
MaxSessions 3
LoginGraceTime 30
# --- Ciphers ---
KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
# --- Network ---
Port 22
AddressFamily inet
ListenAddress 0.0.0.0
TCPKeepAlive yes
ClientAliveInterval 300
ClientAliveCountMax 2
# --- Access control ---
# Uncomment and set your admin user
# AllowUsers scanner-admin
# AllowGroups ssh-users
# --- Restrictions ---
# DisableForwarding covers X11, agent, TCP forwarding, and tunneling
DisableForwarding yes
GatewayPorts no
# --- Logging ---
SyslogFacility AUTH
LogLevel VERBOSE
# --- Misc ---
PrintMotd no
PrintLastLog yes
UseDNS no
Compression no
StrictModes yes
IgnoreRhosts yes
HostbasedAuthentication no
# --- Banner ---
Banner /etc/issue.net
EOF
# --- 3. Create scanner admin user ---
ADMIN_USER="scanner-admin"
# Also allow the cloud-init default user
CLOUDINIT_USER="${SCANNER_SSH_USER:-kai}"
echo "[+] Creating admin user: ${ADMIN_USER}..."
if ! id "${ADMIN_USER}" &>/dev/null; then
useradd -m -s /bin/bash -G sudo,docker "${ADMIN_USER}"
mkdir -p "/home/${ADMIN_USER}/.ssh"
chmod 700 "/home/${ADMIN_USER}/.ssh"
touch "/home/${ADMIN_USER}/.ssh/authorized_keys"
chmod 600 "/home/${ADMIN_USER}/.ssh/authorized_keys"
chown -R "${ADMIN_USER}:${ADMIN_USER}" "/home/${ADMIN_USER}/.ssh"
echo "[!] Add your SSH public key to: /home/${ADMIN_USER}/.ssh/authorized_keys"
else
echo " User ${ADMIN_USER} already exists."
fi
# Set AllowUsers (idempotent: replace commented or active line)
ALLOW_USERS="${ADMIN_USER} ${CLOUDINIT_USER}"
if grep -q "^AllowUsers" "${SSHD_CUSTOM}"; then
sed -i "s/^AllowUsers.*/AllowUsers ${ALLOW_USERS}/" "${SSHD_CUSTOM}"
elif grep -q "^# AllowUsers" "${SSHD_CUSTOM}"; then
sed -i "s/^# AllowUsers.*/AllowUsers ${ALLOW_USERS}/" "${SSHD_CUSTOM}"
else
echo "AllowUsers ${ALLOW_USERS}" >> "${SSHD_CUSTOM}"
fi
# --- 4. Configure fail2ban for SSH ---
echo "[+] Configuring fail2ban for SSH..."
cat > /etc/fail2ban/jail.d/sshd.conf << 'EOF'
[sshd]
enabled = true
port = ssh
filter = sshd
backend = systemd
maxretry = 3
findtime = 600
bantime = 3600
banaction = nftables[type=allports]
EOF
systemctl enable fail2ban
systemctl restart fail2ban
# --- 5. Generate new host keys (replace defaults) ---
echo "[+] Regenerating SSH host keys..."
rm -f /etc/ssh/ssh_host_*
ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""
ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ""
# Remove small DH moduli
echo "[+] Hardening DH moduli..."
awk '$5 >= 3071' /etc/ssh/moduli > /etc/ssh/moduli.safe
mv /etc/ssh/moduli.safe /etc/ssh/moduli
# --- 6. Validate and restart ---
echo "[+] Validating SSH configuration..."
if sshd -t; then
systemctl restart ssh 2>/dev/null || systemctl restart sshd
echo "[+] SSH hardening complete."
else
echo "[!] SSH config validation failed. Restoring backup..."
cp "${SSHD_BACKUP}" "${SSHD_CONFIG}"
rm -f "${SSHD_CUSTOM}"
exit 1
fi
echo ""
echo "[!] IMPORTANT: Before closing this session:"
echo " 1. Add your SSH public key to /home/${ADMIN_USER}/.ssh/authorized_keys"
echo " 2. Test login with: ssh ${ADMIN_USER}@<this-vm-ip>"
echo " 3. Verify you can sudo: sudo whoami"
echo ""
echo "[!] WARNING: Password login is DISABLED. Ensure key-based access works first!"

476
vm/04-install-tools.sh Normal file
View File

@@ -0,0 +1,476 @@
#!/bin/bash
# =============================================================================
# Security Scanning Tools Installation
# Run this inside the VM as root
# =============================================================================
set -euo pipefail
echo "============================================"
echo " Security Tools - Installation"
echo "============================================"
# --- 1. Base dependencies ---
echo "[+] Installing base dependencies..."
apt install -y \
curl wget git \
net-tools iproute2 \
dnsutils whois \
python3 python3-pip python3-venv \
jq tmux htop \
ca-certificates gnupg
# --- 2. Docker (for Greenbone OpenVAS) ---
if ! command -v docker &>/dev/null; then
echo "[+] Installing Docker..."
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod 644 /etc/apt/keyrings/docker.asc
# Detect distro (debian or ubuntu)
. /etc/os-release
DOCKER_DISTRO="${ID}" # "debian" or "ubuntu"
DOCKER_CODENAME="${VERSION_CODENAME}"
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
https://download.docker.com/linux/${DOCKER_DISTRO} \
${DOCKER_CODENAME} stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
systemctl enable docker
fi
# --- 3. Nmap (network scanner) ---
echo "[+] Installing Nmap..."
apt install -y nmap
# --- Helper: download and verify binary from GitHub ---
install_github_binary() {
local REPO="$1" # e.g., projectdiscovery/nuclei
local NAME="$2" # e.g., nuclei
local ARCH="linux_amd64"
echo "[+] Installing ${NAME}..."
local VERSION
VERSION=$(curl -s "https://api.github.com/repos/${REPO}/releases/latest" | jq -r '.tag_name' | sed 's/^v//')
local ZIP_URL="https://github.com/${REPO}/releases/download/v${VERSION}/${NAME}_${VERSION}_${ARCH}.zip"
local CHECKSUM_URL="https://github.com/${REPO}/releases/download/v${VERSION}/${NAME}_${VERSION}_checksums.txt"
wget -q "${ZIP_URL}" -O "/tmp/${NAME}.zip"
wget -q "${CHECKSUM_URL}" -O "/tmp/${NAME}_checksums.txt"
# Verify checksum
local EXPECTED_SHA
EXPECTED_SHA=$(grep "${ARCH}.zip" "/tmp/${NAME}_checksums.txt" | awk '{print $1}')
local ACTUAL_SHA
ACTUAL_SHA=$(sha256sum "/tmp/${NAME}.zip" | awk '{print $1}')
if [[ "${EXPECTED_SHA}" != "${ACTUAL_SHA}" ]]; then
echo "[ERROR] Checksum verification failed for ${NAME}!" >&2
echo " Expected: ${EXPECTED_SHA}" >&2
echo " Actual: ${ACTUAL_SHA}" >&2
rm -f "/tmp/${NAME}.zip" "/tmp/${NAME}_checksums.txt"
return 1
fi
echo " Checksum verified for ${NAME} v${VERSION}"
unzip -o "/tmp/${NAME}.zip" -d "/tmp/${NAME}-bin"
mv "/tmp/${NAME}-bin/${NAME}" "/usr/local/bin/${NAME}"
chmod +x "/usr/local/bin/${NAME}"
rm -rf "/tmp/${NAME}.zip" "/tmp/${NAME}-bin" "/tmp/${NAME}_checksums.txt"
}
# --- 4. Nuclei (fast vulnerability scanner) ---
install_github_binary "projectdiscovery/nuclei" "nuclei"
# Update Nuclei templates
nuclei -update-templates 2>&1 | logger -t nuclei-setup || true
# --- 5. httpx (HTTP toolkit) ---
install_github_binary "projectdiscovery/httpx" "httpx"
# --- 6. Nikto (web server scanner) ---
echo "[+] Installing Nikto..."
apt install -y nikto
# --- 7. testssl.sh (TLS/SSL testing) ---
echo "[+] Installing testssl.sh..."
git clone --depth 1 https://github.com/drwetter/testssl.sh.git /opt/testssl
ln -sf /opt/testssl/testssl.sh /usr/local/bin/testssl
# --- 8. CrackMapExec / NetExec (network assessment) ---
echo "[+] Installing NetExec..."
python3 -m pip install --break-system-packages netexec 2>/dev/null || \
pipx install netexec 2>/dev/null || \
echo "[!] NetExec install failed - install manually if needed"
# --- 9. Greenbone OpenVAS (Docker Compose) ---
echo "[+] Setting up Greenbone OpenVAS..."
mkdir -p /opt/greenbone
cat > /opt/greenbone/docker-compose.yml << 'COMPEOF'
name: greenbone-community-edition
services:
vulnerability-tests:
image: registry.community.greenbone.net/community/vulnerability-tests
environment:
FEED_RELEASE: "24.10"
KEEP_ALIVE: 1
volumes:
- vt_data_vol:/mnt
notus-data:
image: registry.community.greenbone.net/community/notus-data
environment:
KEEP_ALIVE: 1
volumes:
- notus_data_vol:/mnt
scap-data:
image: registry.community.greenbone.net/community/scap-data
environment:
KEEP_ALIVE: 1
volumes:
- scap_data_vol:/mnt
cert-bund-data:
image: registry.community.greenbone.net/community/cert-bund-data
environment:
KEEP_ALIVE: 1
volumes:
- cert_data_vol:/mnt
dfn-cert-data:
image: registry.community.greenbone.net/community/dfn-cert-data
environment:
KEEP_ALIVE: 1
volumes:
- cert_data_vol:/mnt
depends_on:
cert-bund-data:
condition: service_healthy
data-objects:
image: registry.community.greenbone.net/community/data-objects
environment:
FEED_RELEASE: "24.10"
KEEP_ALIVE: 1
volumes:
- data_objects_vol:/mnt
report-formats:
image: registry.community.greenbone.net/community/report-formats
environment:
FEED_RELEASE: "24.10"
KEEP_ALIVE: 1
volumes:
- data_objects_vol:/mnt
depends_on:
data-objects:
condition: service_healthy
gpg-data:
image: registry.community.greenbone.net/community/gpg-data
volumes:
- gpg_data_vol:/mnt
redis-server:
image: registry.community.greenbone.net/community/redis-server
restart: on-failure
volumes:
- redis_socket_vol:/run/redis/
pg-gvm-migrator:
image: registry.community.greenbone.net/community/pg-gvm-migrator:stable
restart: "no"
volumes:
- psql_data_vol:/var/lib/postgresql
- psql_socket_vol:/var/run/postgresql
pg-gvm:
image: registry.community.greenbone.net/community/pg-gvm:stable
restart: on-failure:10
volumes:
- psql_data_vol:/var/lib/postgresql
- psql_socket_vol:/var/run/postgresql
depends_on:
pg-gvm-migrator:
condition: service_completed_successfully
gvmd:
image: registry.community.greenbone.net/community/gvmd:stable
restart: on-failure
volumes:
- gvmd_data_vol:/var/lib/gvm
- scap_data_vol:/var/lib/gvm/scap-data/
- cert_data_vol:/var/lib/gvm/cert-data
- data_objects_vol:/var/lib/gvm/data-objects/gvmd
- vt_data_vol:/var/lib/openvas/plugins
- psql_data_vol:/var/lib/postgresql
- gvmd_socket_vol:/run/gvmd
- ospd_openvas_socket_vol:/run/ospd
- psql_socket_vol:/var/run/postgresql
depends_on:
pg-gvm:
condition: service_started
scap-data:
condition: service_healthy
cert-bund-data:
condition: service_healthy
dfn-cert-data:
condition: service_healthy
data-objects:
condition: service_healthy
report-formats:
condition: service_healthy
gsa:
image: registry.community.greenbone.net/community/gsa:stable-slim
environment:
MOUNT_PATH: "/mnt/web"
KEEP_ALIVE: 1
healthcheck:
test: ["CMD-SHELL", "test -e /run/gsa/copying.done"]
start_period: 5s
volumes:
- gsa_data_vol:/mnt/web
gsad:
image: registry.community.greenbone.net/community/gsad:stable
restart: on-failure
environment:
GSAD_ARGS: "--listen=0.0.0.0 --http-only --api-only -f"
volumes:
- gvmd_socket_vol:/run/gvmd
depends_on:
gvmd:
condition: service_started
gvm-config:
image: registry.community.greenbone.net/community/gvm-config:latest
environment:
ENABLE_NGINX_CONFIG: "true"
ENABLE_TLS_GENERATION: "true"
volumes:
- nginx_config_vol:/mnt/nginx/configs
- nginx_certificates_vol:/mnt/nginx/certs
nginx:
image: registry.community.greenbone.net/community/nginx:latest
ports:
- "0.0.0.0:9392:9392"
volumes:
- nginx_config_vol:/etc/nginx/conf.d:ro
- nginx_certificates_vol:/etc/nginx/certs:ro
- gsa_data_vol:/usr/share/nginx/html:ro
depends_on:
gvm-config:
condition: service_completed_successfully
gsa:
condition: service_healthy
gsad:
condition: service_started
configure-openvas:
image: registry.community.greenbone.net/community/openvas-scanner:stable
volumes:
- openvas_data_vol:/mnt
- openvas_log_data_vol:/var/log/openvas
command:
- /bin/sh
- -c
- |
printf "table_driven_lsc = yes\nopenvasd_server = http://openvasd:80\n" > /mnt/openvas.conf
sed "s/127/128/" /etc/openvas/openvas_log.conf | sed 's/gvm/openvas/' > /mnt/openvas_log.conf
chmod 644 /mnt/openvas.conf
chmod 644 /mnt/openvas_log.conf
touch /var/log/openvas/openvas.log
chmod 666 /var/log/openvas/openvas.log
openvas:
image: registry.community.greenbone.net/community/openvas-scanner:stable
restart: on-failure
volumes:
- openvas_data_vol:/etc/openvas
- openvas_log_data_vol:/var/log/openvas
command:
- /bin/sh
- -c
- |
cat /etc/openvas/openvas.conf
tail -f /var/log/openvas/openvas.log
depends_on:
configure-openvas:
condition: service_completed_successfully
openvasd:
image: registry.community.greenbone.net/community/openvas-scanner:stable
restart: on-failure
environment:
OPENVASD_MODE: service_notus
GNUPGHOME: /etc/openvas/gnupg
LISTENING: 0.0.0.0:80
volumes:
- openvas_data_vol:/etc/openvas
- openvas_log_data_vol:/var/log/openvas
- gpg_data_vol:/etc/openvas/gnupg
- notus_data_vol:/var/lib/notus
depends_on:
vulnerability-tests:
condition: service_healthy
notus-data:
condition: service_healthy
configure-openvas:
condition: service_completed_successfully
gpg-data:
condition: service_completed_successfully
networks:
default:
aliases:
- openvasd
ospd-openvas:
image: registry.community.greenbone.net/community/ospd-openvas:stable
restart: on-failure
hostname: ospd-openvas.local
# SECURITY NOTE: NET_ADMIN, NET_RAW, and seccomp=unconfined are required
# for raw packet scanning (SYN scans, OS detection). Risk is mitigated by
# host-level nftables, PVE firewall, and VM-level isolation.
cap_add:
- NET_ADMIN
- NET_RAW
security_opt:
- seccomp=unconfined
- apparmor=unconfined
command:
[
"ospd-openvas",
"-f",
"--config",
"/etc/gvm/ospd-openvas.conf",
"--notus-feed-dir",
"/var/lib/notus/advisories",
"-m",
"666",
]
volumes:
- gpg_data_vol:/etc/openvas/gnupg
- vt_data_vol:/var/lib/openvas/plugins
- notus_data_vol:/var/lib/notus
- ospd_openvas_socket_vol:/run/ospd
- redis_socket_vol:/run/redis/
- openvas_data_vol:/etc/openvas/
- openvas_log_data_vol:/var/log/openvas
depends_on:
redis-server:
condition: service_started
gpg-data:
condition: service_completed_successfully
configure-openvas:
condition: service_completed_successfully
vulnerability-tests:
condition: service_healthy
notus-data:
condition: service_healthy
gvm-tools:
image: registry.community.greenbone.net/community/gvm-tools
volumes:
- gvmd_socket_vol:/run/gvmd
- ospd_openvas_socket_vol:/run/ospd
depends_on:
- gvmd
- ospd-openvas
volumes:
gpg_data_vol:
scap_data_vol:
cert_data_vol:
data_objects_vol:
gvmd_data_vol:
psql_data_vol:
vt_data_vol:
notus_data_vol:
psql_socket_vol:
gvmd_socket_vol:
ospd_openvas_socket_vol:
redis_socket_vol:
openvas_data_vol:
openvas_log_data_vol:
gsa_data_vol:
nginx_config_vol:
nginx_certificates_vol:
COMPEOF
# --- 10. Create scan scripts directory ---
mkdir -p /opt/scans/{results,scripts}
# Quick scan helper script
cat > /opt/scans/scripts/quick-scan.sh << 'SCANEOF'
#!/bin/bash
# Quick internal network scan
# Usage: ./quick-scan.sh 192.168.1.0/24
set -euo pipefail
TARGET="${1:?Usage: $0 <target-subnet>}"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
RESULTS_DIR="/opt/scans/results/${TIMESTAMP}"
mkdir -p "${RESULTS_DIR}"
echo "[+] Starting quick scan of ${TARGET}"
echo "[+] Results will be saved to ${RESULTS_DIR}"
# Step 1: Host discovery
echo "[+] Step 1/4: Host discovery..."
nmap -sn "${TARGET}" -oG "${RESULTS_DIR}/hosts-alive.gnmap"
HOSTS=$(grep "Up" "${RESULTS_DIR}/hosts-alive.gnmap" | awk '{print $2}')
echo "${HOSTS}" > "${RESULTS_DIR}/hosts-alive.txt"
HOST_COUNT=$(wc -l < "${RESULTS_DIR}/hosts-alive.txt")
echo " Found ${HOST_COUNT} alive hosts"
# Step 2: Port scan top 1000
echo "[+] Step 2/4: Port scanning (top 1000 ports)..."
nmap -sV --script=safe -T4 --top-ports 1000 -iL "${RESULTS_DIR}/hosts-alive.txt" \
-oA "${RESULTS_DIR}/port-scan"
# Step 3: HTTP service detection
echo "[+] Step 3/4: HTTP service detection..."
grep -oP '\d+\.\d+\.\d+\.\d+' "${RESULTS_DIR}/hosts-alive.txt" | \
httpx -silent -ports 80,443,8080,8443,9090 -o "${RESULTS_DIR}/http-services.txt" 2>/dev/null || true
# Step 4: Nuclei scan on HTTP services
if [ -s "${RESULTS_DIR}/http-services.txt" ]; then
echo "[+] Step 4/4: Vulnerability scanning with Nuclei..."
nuclei -l "${RESULTS_DIR}/http-services.txt" \
-severity medium,high,critical \
-o "${RESULTS_DIR}/nuclei-findings.txt" 2>/dev/null || true
else
echo "[+] Step 4/4: No HTTP services found, skipping Nuclei."
fi
echo ""
echo "[+] Scan complete. Results in: ${RESULTS_DIR}"
echo " - hosts-alive.txt: Live hosts"
echo " - port-scan.*: Port scan results"
echo " - http-services.txt: HTTP endpoints"
echo " - nuclei-findings.txt: Vulnerabilities found"
SCANEOF
chmod +x /opt/scans/scripts/quick-scan.sh
echo ""
echo "[+] All security tools installed."
echo ""
echo "Tool summary:"
echo " nmap - Network discovery and port scanning"
echo " nuclei - Fast vulnerability scanner"
echo " httpx - HTTP probing"
echo " nikto - Web server scanner"
echo " testssl - TLS/SSL testing"
echo " netexec - Network assessment (SMB, RDP, etc.)"
echo " OpenVAS (Docker) - Full vulnerability management"
echo ""
echo "Quick start:"
echo " cd /opt/greenbone && docker compose up -d # Start OpenVAS"
echo " /opt/scans/scripts/quick-scan.sh 192.168.1.0/24 # Quick scan"

166
vm/05-monitoring.sh Normal file
View File

@@ -0,0 +1,166 @@
#!/bin/bash
# =============================================================================
# Monitoring and Logging for Security Scanner VM
# Run this inside the VM as root
# =============================================================================
set -euo pipefail
echo "============================================"
echo " Monitoring & Logging - Security Scanner VM"
echo "============================================"
# --- 1. Configure rsyslog for centralized logging ---
echo "[+] Configuring rsyslog..."
cat > /etc/rsyslog.d/99-scanner.conf << 'EOF'
# Log all scanner-related activity to dedicated file
:programname, startswith, "nmap" /var/log/scanner/nmap.log
:programname, startswith, "nuclei" /var/log/scanner/nuclei.log
:programname, startswith, "nft" /var/log/scanner/firewall.log
# Log auth separately with more detail
auth,authpriv.* /var/log/scanner/auth.log
# Uncomment to forward to remote syslog server
# *.* @@syslog.internal.lan:514
EOF
mkdir -p /var/log/scanner
systemctl restart rsyslog
# --- 2. Log rotation ---
echo "[+] Configuring log rotation..."
cat > /etc/logrotate.d/scanner << 'EOF'
/var/log/scanner/*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
create 0640 root adm
sharedscripts
postrotate
systemctl reload rsyslog > /dev/null 2>&1 || true
endscript
}
/opt/scans/results/*/*.txt {
weekly
missingok
rotate 12
compress
notifempty
}
EOF
# --- 3. Logwatch (daily summary reports) ---
echo "[+] Configuring Logwatch..."
cat > /etc/logwatch/conf/logwatch.conf << 'EOF'
LogDir = /var/log
MailTo = root
MailFrom = scanner@localhost
Range = yesterday
Detail = Med
Service = All
Format = text
EOF
# --- 4. Disk usage monitoring ---
echo "[+] Setting up disk usage monitoring..."
cat > /opt/scans/scripts/check-disk.sh << 'DISKEOF'
#!/bin/bash
set -euo pipefail
# Alert if disk usage exceeds threshold
THRESHOLD=85
USAGE=$(df / | tail -1 | awk '{print $5}' | sed 's/%//')
if [ "${USAGE}" -ge "${THRESHOLD}" ]; then
echo "[ALERT] Disk usage at ${USAGE}% on security scanner" | \
logger -t disk-monitor -p user.warning
echo "[ALERT] Disk usage at ${USAGE}% - consider cleaning /opt/scans/results/"
fi
DISKEOF
chmod +x /opt/scans/scripts/check-disk.sh
# --- 5. Docker health check ---
cat > /opt/scans/scripts/check-openvas.sh << 'HEALTHEOF'
#!/bin/bash
set -euo pipefail
# Check if OpenVAS containers are healthy
if [[ ! -d /opt/greenbone ]]; then
echo "[!] /opt/greenbone not found - skipping health check"
exit 0
fi
UNHEALTHY=$(docker compose -f /opt/greenbone/docker-compose.yml ps --format json 2>/dev/null | jq -r 'select(.State != "running") | .Name' 2>/dev/null || true)
if [ -n "${UNHEALTHY}" ]; then
echo "[ALERT] Unhealthy OpenVAS containers: ${UNHEALTHY}" | \
logger -t openvas-health -p user.warning
echo "[ALERT] Restarting unhealthy containers..."
docker compose -f /opt/greenbone/docker-compose.yml up -d
fi
HEALTHEOF
chmod +x /opt/scans/scripts/check-openvas.sh
# --- 6. Cron jobs ---
echo "[+] Setting up monitoring cron jobs..."
cat > /etc/cron.d/scanner-monitoring << 'EOF'
# Disk check every 6 hours
0 */6 * * * root /opt/scans/scripts/check-disk.sh
# OpenVAS health check every 30 minutes
*/30 * * * * root /opt/scans/scripts/check-openvas.sh
# AIDE integrity check daily at 3am
0 3 * * * root /usr/bin/aide --check 2>&1 | logger -t aide-check -p user.info
# Lynis security audit weekly (Sunday 2am)
0 2 * * 0 root /usr/sbin/lynis audit system --quick --no-colors 2>&1 | logger -t lynis-audit -p user.info
# Clean scan results older than 90 days (maxdepth 1 for safety, log to syslog)
0 4 * * 0 root find /opt/scans/results -maxdepth 1 -type d -mtime +90 -print -exec rm -rf {} + 2>&1 | logger -t scan-cleanup
# Update Nuclei templates weekly
0 5 * * 1 root /usr/local/bin/nuclei -update-templates 2>&1 | logger -t nuclei-update -p user.info
EOF
# --- 7. Login banner ---
echo "[+] Setting login banner..."
cat > /etc/motd << 'EOF'
+=====================================================+
| SECURITY SCANNER - AUTHORIZED ACCESS ONLY |
| |
| All activity on this system is logged and audited. |
| Unauthorized access is prohibited. |
+=====================================================+
Tools: nmap | nuclei | httpx | nikto | testssl | OpenVAS
Scans: /opt/scans/scripts/quick-scan.sh <target>
Logs: /var/log/scanner/
OpenVAS: http://localhost:9392
EOF
cat > /etc/issue.net << 'EOF'
*************************************************************
* WARNING: This is a restricted system. *
* All connections are monitored and recorded. *
* Disconnect IMMEDIATELY if you are not authorized. *
*************************************************************
EOF
# Banner is configured in /etc/ssh/sshd_config.d/99-scanner-hardening.conf by 03-ssh-harden.sh
systemctl reload ssh 2>/dev/null || systemctl reload sshd 2>/dev/null || true
echo ""
echo "[+] Monitoring and logging configured."
echo ""
echo "Summary:"
echo " Logs: /var/log/scanner/"
echo " Scan results: /opt/scans/results/"
echo " Cron jobs: /etc/cron.d/scanner-monitoring"
echo " Logwatch: Daily email summary to root"
echo " AIDE: File integrity check daily at 3am"
echo " Lynis: Security audit weekly (Sunday 2am)"

35
vm/06-docker-autostart.sh Normal file
View File

@@ -0,0 +1,35 @@
#!/bin/bash
# =============================================================================
# Docker Compose Autostart for Greenbone OpenVAS
# Creates a systemd service so containers start on boot
# =============================================================================
set -euo pipefail
echo "[+] Creating systemd service for Greenbone OpenVAS..."
cat > /etc/systemd/system/greenbone-openvas.service << 'EOF'
[Unit]
Description=Greenbone OpenVAS Scanner
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/greenbone
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=300
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable greenbone-openvas.service
echo "[+] Greenbone OpenVAS will start automatically on boot."
echo " Manual control:"
echo " systemctl start greenbone-openvas"
echo " systemctl stop greenbone-openvas"
echo " systemctl status greenbone-openvas"

61
vm/setup.sh Normal file
View File

@@ -0,0 +1,61 @@
#!/bin/bash
# =============================================================================
# Security Scanner VM - Full Setup
# Run this inside the VM as root to execute all scripts in order
# =============================================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# --- Pre-flight checks ---
if [[ "$(id -u)" -ne 0 ]]; then
echo "[ERROR] This script must be run as root." >&2
exit 1
fi
if [[ -z "${SCANNER_ADMIN_IPS:-}" ]]; then
echo "[ERROR] Set SCANNER_ADMIN_IPS before running." >&2
echo " Example: export SCANNER_ADMIN_IPS='192.168.68.100, 192.168.68.101'" >&2
exit 1
fi
echo "============================================"
echo " Security Scanner VM - Full Setup"
echo "============================================"
echo ""
echo " ADMIN_IPS: ${SCANNER_ADMIN_IPS}"
echo " INTERNAL_NETS: ${SCANNER_INTERNAL_NETS:-10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16}"
echo " DNS_SERVERS: ${SCANNER_DNS_SERVERS:-192.168.68.1}"
echo ""
SCRIPTS=(
"01-system-harden.sh"
"02-firewall.sh"
"03-ssh-harden.sh"
"04-install-tools.sh"
"05-monitoring.sh"
)
for script in "${SCRIPTS[@]}"; do
SCRIPT_PATH="${SCRIPT_DIR}/${script}"
if [[ ! -f "${SCRIPT_PATH}" ]]; then
echo "[ERROR] Script not found: ${SCRIPT_PATH}" >&2
exit 1
fi
echo ""
echo ">>> Running ${script}..."
bash "${SCRIPT_PATH}"
echo ">>> ${script} completed."
done
echo ""
echo "============================================"
echo " Setup complete!"
echo "============================================"
echo ""
echo " Next steps:"
echo " 1. Add SSH key: /home/scanner-admin/.ssh/authorized_keys"
echo " 2. Start OpenVAS: cd /opt/greenbone && docker compose up -d"
echo " 3. Set OpenVAS password:"
echo " docker compose exec -u gvmd gvmd gvmd --user=admin --new-password=<PASSWORD>"
echo " 4. Reboot: shutdown -r now"