Archived
restructure: move proxmox/ into subfolder, add pihole/ section
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
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
# 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 and `sudo` group membership is the primary SSH login
|
||||
account. Root SSH is locked to key-only after `harden-ssh.sh` runs; the
|
||||
Linux admin user is how you get shell access day-to-day without using root.
|
||||
- `setup-admin-sudo.sh` adds 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-password` in `sshd_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@pam` for emergencies only.
|
||||
- Create a named user (e.g. `wayne@pve`) with the Administrator role for
|
||||
routine cluster management — `create-admin-user.sh` does 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. `PVEAuditor` or 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)
|
||||
Reference in New Issue
Block a user