Splits the repo into Stage 1 (base host config/hardening, applies to any node) and Stage 2 (future HA/Ceph cluster, deferred - pve1's mini-PC hardware can't support the assumed split-disk/multi-NIC layout). Adds the Stage 1 toolset: firewall deploy, named admin user creation, unattended security upgrades, subscription-nag removal (with an apt hook so the patch survives package updates), and a read-only audit script. Fixes switch-to-no-subscription-repo.sh, which only handled the legacy .list format and silently no-op'd against PVE 9's deb822 .sources files; it now removes enterprise sources outright rather than commenting them out. Shared logic (root check, idempotent file writes, backups) factored into scripts/lib/common.sh. Ran the full sequence against pve1 via scripts/bootstrap.sh + create-admin-user.sh; scripts/audit.sh confirms all checks pass.
4.3 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 | SSH: key-only root login + fail2ban | harden-ssh.sh |
no (requires an authorized_keys already in place — script warns if missing) |
| 3 | Unattended security upgrades, no auto-reboot | setup-unattended-upgrades.sh |
no |
| 4 | PVE firewall, default-deny, mgmt-only SSH/8006 | deploy-firewall.sh |
needs MGMT_CIDR set |
| 5 | Disable subscription nag (cosmetic) | disable-subscription-nag.sh |
no |
| 6 | Named PVE admin user, Administrator role | create-admin-user.sh <username> |
yes — pick the username, change the generated password on first login |
| 7 | 2FA/TOTP on that user and root@pam |
— | yes — web UI only: Datacenter → Permissions → Two Factor, or user menu → TFA |
| 8 | Verify everything above | audit.sh |
no |
Linux/SSH layer
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. - A separate Linux sudo user isn't strictly required for day-to-day PVE admin (the PVE permission system below governs that), but worth adding if multiple people SSH into the box directly, for accountability.
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.
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)