All existing content moved from repo root into proxmox/ to make room for other Debian machine configs. Adds pihole/ with: - config/pihole.toml — snapshot of current Pi-hole v6 config - config/dnsmasq.d/99-ipxe-chainload.conf — custom PXE DHCP rules (EFI/BIOS iPXE chainload, fixed tag-specificity bug for UEFI boot) - pull-config.sh <source-host> <dest-dir> — pull live config to disk - apply-config.sh <source-dir> <dest-host> — push config to a Pi-hole Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XRzqNDrbnYR22ZgZj1Bg3s
6.0 KiB
Security Hardening
Stage 1 (see 00-overview.md) — applies to any Proxmox host, independent
of cluster plans. Proxmox has no sudo out of the box — everything
defaults to root. That's the install default, not the recommended end
state. Two layers to harden separately.
Checklist / script mapping
Run scripts/bootstrap.sh for everything except the admin user (needs a
username decision) and 2FA enrollment (must be done interactively via the
web UI — there's no safe way to script TOTP secret generation over SSH).
Then run scripts/audit.sh to verify. Order matters (matches
bootstrap.sh):
| # | Item | Script | Manual step required? |
|---|---|---|---|
| 1 | Remove enterprise repos, switch to no-subscription | switch-to-no-subscription-repo.sh |
no |
| 2 | Linux admin user with SSH key + sudo access | setup-linux-admin-user.sh <user> <pubkey> |
yes — choose a username and provide your SSH public key. Run this before SSH hardening or pass ADMIN_USER/ADMIN_SSH_KEY to bootstrap.sh so it runs automatically at the right point |
| 3 | Passwordless sudo for pvesh/qm/pct | setup-admin-sudo.sh <username> |
yes — same username as step 2 |
| 4 | SSH: key-only root login + fail2ban | harden-ssh.sh |
no — step 2 ensures authorized_keys are in place first |
| 5 | Unattended security upgrades, no auto-reboot | setup-unattended-upgrades.sh |
no |
| 6 | PVE firewall, default-deny, mgmt-only SSH/8006 | deploy-firewall.sh |
needs MGMT_CIDR set |
| 7 | Disable subscription nag (cosmetic) | disable-subscription-nag.sh |
no |
| 8 | Named PVE admin user, Administrator role | create-admin-user.sh <username> |
yes — pick the username, change the generated password on first login |
| 9 | 2FA/TOTP on that user and root@pam |
— | yes — web UI only: Datacenter → Permissions → Two Factor, or user menu → TFA |
| 10 | Verify everything above | audit.sh |
no |
Linux/SSH layer
- A named Linux system user (created by
setup-linux-admin-user.sh) with an SSH authorized key andsudogroup membership is the primary SSH login account. Root SSH is locked to key-only afterharden-ssh.shruns; the Linux admin user is how you get shell access day-to-day without using root. setup-admin-sudo.shadds a narrower, password-free sudoers rule for the Proxmox management tools (pvesh,qm,pct) specifically — needed for non-interactive automation scripts that SSH in and run these tools without a TTY.PermitRootLogin prohibit-passwordinsshd_config— root can only log in via SSH key, never password. Kills most brute-force attempts.- fail2ban jail for SSH on top of that.
- Restrict SSH to the management VLAN/trusted IPs via the Proxmox
firewall (see
03-networking.md) rather than exposing broadly.
PVE/web layer (the one that actually matters day-to-day)
- Keep
root@pamfor emergencies only. - Create a named user (e.g.
wayne@pve) with the Administrator role for routine cluster management —create-admin-user.shdoes this, or Datacenter → Permissions → Users manually. - Enable 2FA (TOTP or hardware key) on both that account and
root@pam: Datacenter → Permissions → Realms/Users. - For API integrations (monitoring, automation, Terraform, etc.), issue
scoped API tokens with least-privilege roles (e.g.
PVEAuditoror a custom role) — never hand out root credentials.
Firewall
Default-deny at datacenter/node level, whitelist only what's needed (see
03-networking.md for the specifics). Template in
config/pve-firewall/cluster.fw.example, applied by
scripts/deploy-firewall.sh.
ICMP echo (ping) is explicitly allowed from the management network,
alongside SSH/8006 — not required for anything to function, but without
it policy_in: DROP silently eats ping while SSH/web UI keep working.
That split (ping dead, everything else fine) reads exactly like a real
outage mid-troubleshooting; see 06-pve-test-wifi-network.md for a case
this caused genuine confusion after a network change. If you ever debug
"can't ping but can SSH", check this firewall before assuming the
network itself is broken.
This file is cluster-wide, not per-node: cluster.fw lives in
/etc/pve/firewall/ — shared via pmxcfs across every node in a cluster.
A node that joins a cluster inherits whatever's already there, and (per
docs/05-node-roles.md) keeps its own local copy after leaving. Don't
assume a node's firewall state matches what deploy-firewall.sh was
last run with directly against it — check pve-firewall status /
/etc/pve/firewall/cluster.fw on the actual node.
Repos and updates
Fresh installs point at the enterprise repo, which fails on apt update
without a subscription. scripts/switch-to-no-subscription-repo.sh
removes the enterprise sources entirely (renamed .disabled, not just
commented out) and switches to the no-subscription repo — handles both
the legacy .list format and the deb822 .sources format current
installers write. Keep the host patched — hypervisor CVEs are high-value
targets; scripts/setup-unattended-upgrades.sh automates security
patches (deliberately no auto-reboot on a hypervisor — check
/var/run/reboot-required and reboot during a planned window).
The web UI's "No valid subscription" popup and dashboard indicator are
cosmetic upsell, not a security control, but with no subscription they'll
nag on every login — scripts/disable-subscription-nag.sh patches
proxmox-widget-toolkit's JS to suppress them, and installs an apt
Post-Invoke hook that reapplies the patch automatically after every
apt/dpkg run, since a proxmox-widget-toolkit package upgrade
overwrites the patched file.
Misc
- Management interface on a network you trust, not the same broadcast domain as guest VM traffic.
- If the web UI is ever needed outside the LAN, put it behind a VPN — don't port-forward 8006 directly.
Further reading / not yet automated here
- CIS Benchmark for Proxmox VE
- Community PVE hardening guides (kernel parameters, audit logging, storage encryption)