Initial documentation of the hubris Proxmox homelab as a cross-linked markdown wiki. Per-node pages, cross-cutting infrastructure pages, an investigation log, and an operations cheatsheet. Each node and topic ends with a Changelog section so changes can be tracked in-place going forward. Refreshed against live state on 2026-04-28 — 14 active LXCs (109 syncthing currently stopped) + 1 VM (108 haos). Reflects post-A/B-test state of the 2026-04-21 hubris crash-loop investigation.
85 lines
4.5 KiB
Markdown
85 lines
4.5 KiB
Markdown
# VPS hardening — `82.165.190.79` / `100.122.165.149`
|
|
|
|
IONOS VPS that runs the Netbird control plane and the [public ingress traefik](ingress.md). Hardened 2026-04-23 from its stock-Plesk state.
|
|
|
|
## At a glance
|
|
- **Hostname:** `inspiring-ramanujan.82-165-190-79.plesk.page`
|
|
- **OS:** Debian 13
|
|
- **Mesh:** netbird `100.122.165.149` (peer of the lab mesh; routes `192.168.8.0/24` via [hubris](../hosts/hubris.md)).
|
|
- **Public:** `82.165.190.79` (`ens6`).
|
|
- **Public DNS:** IONOS wildcard `*.hubris.network → 82.165.190.79`.
|
|
|
|
## SSH
|
|
|
|
- Key-only (`PasswordAuthentication no`, `PermitRootLogin prohibit-password`) via drop-in at `/etc/ssh/sshd_config.d/10-hubris-hardening.conf`. Original config backed up at `/etc/ssh/sshd_config.bak.<ts>`.
|
|
- **Mesh-only**: public `:22` is dropped by the nftables firewall. SSH reaches the VPS only over `wt0`. `ListenAddress` itself is still `0.0.0.0` — gating is firewall-layer.
|
|
- Authorized root keys: PVE (`root@hubris`), Mac Mini (`d.toro.v@pm.me`). Add a new device with `ssh-copy-id root@100.122.165.149` from a mesh peer **before** disabling its access paths.
|
|
|
|
## Firewall — nftables (`inet hubris-fw`)
|
|
|
|
Config at `/etc/nftables.conf`, service enabled.
|
|
|
|
- Public iface `ens6`. Wireguard iface `wt0`.
|
|
- **INPUT on `ens6`** allow-list: DHCP (67→68), rate-limited ICMP/ICMPv6. Everything else drops.
|
|
- `wt0` fully accepted in INPUT. `lo` accepted.
|
|
- **FORWARD chain at priority `filter-10`** (runs before Docker's FORWARD) hosts the fail2ban ban enforcement — see below.
|
|
- Set `banned4` (typed `ipv4_addr`, flag `timeout`) holds fail2ban's drops.
|
|
- Coexists with Docker's `ip nat` / `ip filter` tables (iptables-nft compat). **Do NOT `flush ruleset`** in this config — it'll wipe Docker's state too.
|
|
|
|
## fail2ban
|
|
|
|
- **Jail `traefik-4xx`** tails `/var/log/traefik/access.log` (bind-mounted from container). Filter at `/etc/fail2ban/filter.d/traefik-4xx.conf` matches 401/403/404/429 from `blog-public@file` or `artifacto-public@file` routers only — netbird-grpc traffic isn't considered.
|
|
- Tunables: `findtime=600, maxretry=30, bantime=3600`.
|
|
- **Action** at `/etc/fail2ban/action.d/nft-hubris.conf` adds/removes elements from `inet hubris-fw banned4` with per-element timeout.
|
|
|
|
### CRITICAL invariant — wireguard / fail2ban
|
|
|
|
**Bans must never affect `wt0` or wireguard UDP.** The FORWARD chain explicitly `accept`s the following *before* the ban check:
|
|
- `udp 51820` (wireguard)
|
|
- `udp 3478` (STUN)
|
|
- `ct state established,related`
|
|
|
|
The INPUT ban rule is scoped to `iifname "ens6"`.
|
|
|
|
Violating this takes the mesh down for every home device (they share one public IP) and the only recovery is IONOS console → `nft flush set inet hubris-fw banned4`.
|
|
|
|
## Traefik access log
|
|
|
|
- Written to `/var/log/traefik/access.log` on the host via a bind mount added to `/opt/docker-compose.yml` (traefik volumes include `/var/log/traefik:/logs`) plus `--accesslog.filepath=/logs/access.log`.
|
|
- CLF format. Real client IP arrives correctly because docker userland-proxy is off — see [public ingress](ingress.md).
|
|
|
|
## Plesk / mail / FTP / Dr.Web
|
|
|
|
Stopped and disabled (not uninstalled). All of:
|
|
`dovecot`, `dovecot.socket`, `postfix`, `postfix@-`, `pc-remote`, `xinetd`, `plesk-task-manager`, `plesk-web-socket`, `sw-cp-server`, `sw-engine`, `plesk-repaird`, `plesk-repaird.socket`, `drwebd`.
|
|
|
|
`psa.service` is masked (was a one-shot boot bootstrap). `/etc/cron.d/plesk-backup-manager-task` renamed to `.disabled`.
|
|
|
|
Reverse: `systemctl unmask psa; systemctl enable --now <svc>`.
|
|
|
|
## Auto-patching
|
|
|
|
- `unattended-upgrades` enabled (stock).
|
|
- Drop-in at `/etc/apt/apt.conf.d/52hubris-reboot.conf` sets auto-reboot at **04:00 UTC** when `/var/run/reboot-required` is set.
|
|
- Runs inside the stock `apt-daily-upgrade.timer`.
|
|
|
|
## Recovery paths
|
|
|
|
Ordered by preference:
|
|
1. **SSH via mesh** — primary. Any mesh peer with an authorized key.
|
|
2. **IONOS web console** (my.ionos.com → VPS → Console) — uses the system password, not SSH keys. Bypasses any firewall misconfig.
|
|
3. **IONOS rescue mode** — boot rescue, mount rootfs, edit `/etc/nftables.conf` or `/etc/ssh/sshd_config.d/10-hubris-hardening.conf` to a known-good state, reboot.
|
|
|
|
## Related
|
|
- [Public ingress (VPS traefik)](ingress.md)
|
|
- [Mesh migration](mesh.md) — VPS as a mesh peer
|
|
- [SSH access](ssh-access.md)
|
|
|
|
## Changelog
|
|
|
|
### 2026-04-28 — wiki entry created
|
|
Initial documentation.
|
|
|
|
### 2026-04-23 — hardened
|
|
nftables firewall, mesh-only SSH, fail2ban traefik jail, Plesk disabled, auto-reboot 04:00 UTC, wireguard/fail2ban invariant established.
|