commit 5e49b977ab0da3b584739e1642e9c6d1bc846126 Author: Yaojia Wang Date: Sun Mar 8 20:21:29 2026 +0100 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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..a8f6c8e --- /dev/null +++ b/README.md @@ -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 diff --git a/pve/create-vm.sh b/pve/create-vm.sh new file mode 100644 index 0000000..c00fa68 --- /dev/null +++ b/pve/create-vm.sh @@ -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= 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}" diff --git a/pve/firewall.sh b/pve/firewall.sh new file mode 100644 index 0000000..eb38bfa --- /dev/null +++ b/pve/firewall.sh @@ -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." diff --git a/vm/01-system-harden.sh b/vm/01-system-harden.sh new file mode 100644 index 0000000..7a6293e --- /dev/null +++ b/vm/01-system-harden.sh @@ -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" diff --git a/vm/02-firewall.sh b/vm/02-firewall.sh new file mode 100644 index 0000000..3fb0ae1 --- /dev/null +++ b/vm/02-firewall.sh @@ -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" diff --git a/vm/03-ssh-harden.sh b/vm/03-ssh-harden.sh new file mode 100644 index 0000000..455ce5f --- /dev/null +++ b/vm/03-ssh-harden.sh @@ -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}@" +echo " 3. Verify you can sudo: sudo whoami" +echo "" +echo "[!] WARNING: Password login is DISABLED. Ensure key-based access works first!" diff --git a/vm/04-install-tools.sh b/vm/04-install-tools.sh new file mode 100644 index 0000000..97835c8 --- /dev/null +++ b/vm/04-install-tools.sh @@ -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 }" +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" diff --git a/vm/05-monitoring.sh b/vm/05-monitoring.sh new file mode 100644 index 0000000..529c171 --- /dev/null +++ b/vm/05-monitoring.sh @@ -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 + 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)" diff --git a/vm/06-docker-autostart.sh b/vm/06-docker-autostart.sh new file mode 100644 index 0000000..06f70df --- /dev/null +++ b/vm/06-docker-autostart.sh @@ -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" diff --git a/vm/setup.sh b/vm/setup.sh new file mode 100644 index 0000000..5c330bf --- /dev/null +++ b/vm/setup.sh @@ -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=" +echo " 4. Reboot: shutdown -r now"