From a58fd7c303c0db53bba5df293e1cdfe86006ff08 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 14 Nov 2026 00:34:33 +0000 Subject: [PATCH 1/3] docs(oracle): record its key-only SSH baseline (#1500) The 2026-11-13 audit found oracle's sshd offering password logins on the host that holds every off-host copy. A drop-in applied by hand on 2026-11-14 turns them off. This records the step in the wiki stack's README so a rebuild repeats it, notes it on oracle's network.md row, and logs it in the changelog. Co-Authored-By: Claude Opus 5.5 --- docs/changelog.md | 13 +++++++++++++ docs/network.md | 6 +++++- stacks/wiki/README.md | 22 ++++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 2f2fc079..1fbf25f9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -17,6 +17,19 @@ roadmap as it read that day, and the *Done* entries keep the shape they had there. `check_docs.py` does not check this file, for the reason its module docstring gives: it is a record, not a claim about now. +## 2026-11-14 + +- **`oracle`'s sshd now takes keys only.** The audit of 2026-11-13 found it + offering `publickey,password` on the host that holds every off-host copy + ([#1500](https://github.com/Gerrrt/HomeLab/issues/1500)). The last + password login was 2026-09-02, and every login since had used a key. At + 00:28 UTC a drop-in, `/etc/ssh/sshd_config.d/10-oracle-baseline.conf`, + turned off password and keyboard-interactive logins, root login and X11 + forwarding. `sshd -t` passed. After the reload the server refuses + everything but `publickey`, on `localhost` and on `10.0.99.30`. The step + is now in `stacks/wiki/README.md`. Still open: the five `authorized_keys` + entries are not limited by `from=` or `command=`. + ## 2026-11-13 - **An audit of `golem`, read-only, found its last verify failed and a diff --git a/docs/network.md b/docs/network.md index 17989c9d..d74bff88 100644 --- a/docs/network.md +++ b/docs/network.md @@ -284,7 +284,11 @@ listed under [Hicks](#hicks--vlan-50--trusted), and nothing else. jobs: [ADR-0015](adr/0015-give-oracle-the-off-host-jobs.md). Its NIC supports 10/100 only, so that link runs at 100 Mb/s — measured 2026-09-03 — and no cable will lift it. `prometheus` links at a gigabit through the same - switch. + switch. Its sshd takes keys only, since 2026-11-14 + ([#1500](https://github.com/Gerrrt/HomeLab/issues/1500)). Nothing deploys + that, so a rebuilt `oracle` needs the step in + [`stacks/wiki/README.md`](../stacks/wiki/README.md#the-hosts-ssh-once) + again. [^UPS]: [APC Smart-UPS](https://www.apc.com/us/en/product-range/61913-smart-ups/) [^MacBookPro]: [Apple MacBook Pro (2012)](https://support.apple.com/en-us/111958) diff --git a/stacks/wiki/README.md b/stacks/wiki/README.md index a6c96d1d..90e24805 100644 --- a/stacks/wiki/README.md +++ b/stacks/wiki/README.md @@ -119,6 +119,28 @@ the first `up` from the checkout adopts the running containers and volumes rather than creating a second set. The old `/opt/wiki/compose.yaml` is then unused; remove it so nothing runs from it by habit. +### The host's SSH, once + +`oracle` holds every off-host copy, and nothing in this repository manages +its sshd. Ubuntu's install leaves password logins on. Turn them off, along +with root login and X11 forwarding. The file sorts before +`50-cloud-init.conf`, and sshd keeps the first value it reads. First check +that your key works, or this locks you out of SSH. The laptop's own console +still works if it does: + +```bash +printf '%s\n' 'PasswordAuthentication no' 'KbdInteractiveAuthentication no' 'PermitRootLogin no' 'X11Forwarding no' | sudo tee /etc/ssh/sshd_config.d/10-oracle-baseline.conf >/dev/null +``` + +```bash +sudo sshd -t && sudo systemctl reload ssh && sudo sshd -T | grep -E '^(passwordauthentication|kbdinteractiveauthentication|permitrootlogin|x11forwarding) ' +``` + +All four lines should end in `no`. From another host, +`ssh -o PreferredAuthentications=none atropos@10.0.99.30` should be refused +with `Permission denied (publickey)` alone. The backup pushes from +`prometheus` and `trinity` log in by key and are not affected. + ## TLS Caddy serves the wiki on 443 with a leaf from the estate's CA, and answers From 41639e706ccc0c3dc07328244e9ec6dcbea9195e Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 14 Nov 2026 06:42:45 +0000 Subject: [PATCH 2/3] docs(oracle): enrol backup keys without a password (#1500) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit restore-the-firewall.md §0 used ssh-copy-id with oracle's password, which the key-only baseline now refuses. Add the key from oracle's console or an existing key session, then check BatchMode login. Co-Authored-By: Claude Opus 5.5 --- docs/runbooks/restore-the-firewall.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/runbooks/restore-the-firewall.md b/docs/runbooks/restore-the-firewall.md index d3f1f600..aae451d4 100644 --- a/docs/runbooks/restore-the-firewall.md +++ b/docs/runbooks/restore-the-firewall.md @@ -94,11 +94,20 @@ The copy needs two things once, both done on `prometheus` as `robo`. Accept ssh atropos@10.0.99.30 true ``` -Then authorise this host's key there — it prompts for `oracle`'s password one -time: +Then authorise this host's key there. `oracle` takes keys only +([#1500](https://github.com/Gerrrt/HomeLab/issues/1500)), so `ssh-copy-id` +with a password is refused. Print the public key here: ```bash -ssh-copy-id atropos@10.0.99.30 +cat ~/.ssh/id_ed25519.pub +``` + +On `oracle` as `atropos`, at its console or over a session that already logs +in by key, add that line to `~/.ssh/authorized_keys`. Then check from here +that the key alone gets in: + +```bash +ssh -o BatchMode=yes atropos@10.0.99.30 true ``` The next `make backup-firewall` seeds `oracle` with every retained export From 1b5fced767ef3e0f0b36527dfd65a84acca8e314 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 14 Nov 2026 06:48:14 +0000 Subject: [PATCH 3/3] docs(oracle): pin its authorized keys to their sources (#1500) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each key on oracle now carries a from= pin to the host or subnet it has logged in from, with forwarding and ~/.ssh/rc turned off. Record the options in the wiki README, point restore-the-firewall §0 at them, and log it in the changelog. Co-Authored-By: Claude Opus 5.5 --- docs/changelog.md | 12 ++++++++++-- docs/runbooks/restore-the-firewall.md | 6 ++++-- stacks/wiki/README.md | 15 +++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 1fbf25f9..8c025b66 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -27,8 +27,16 @@ docstring gives: it is a record, not a claim about now. turned off password and keyboard-interactive logins, root login and X11 forwarding. `sshd -t` passed. After the reload the server refuses everything but `publickey`, on `localhost` and on `10.0.99.30`. The step - is now in `stacks/wiki/README.md`. Still open: the five `authorized_keys` - entries are not limited by `from=` or `command=`. + is now in `stacks/wiki/README.md`. +- **`oracle`'s five authorized keys are now limited to where they log in + from.** In 120 days of the journal, each key had logged in from a single + host or from Hicks. At 06:44 UTC each line got a `from=` pin: `robo`'s to + `10.0.99.20`, `trinity`'s backup key to `10.0.99.40` with no pty, and the + three operator keys to `10.0.50.0/24`. Port, agent and X11 forwarding and + `~/.ssh/rc` were turned off as well. One operator key had not logged in + within those 120 days. The 2026-11-15 runs from `prometheus` and `trinity` + are the first real logins under the pins. `command=` is not set, because + the pull runs `docker exec` and the copies run shell commands. ## 2026-11-13 diff --git a/docs/runbooks/restore-the-firewall.md b/docs/runbooks/restore-the-firewall.md index aae451d4..d89fe9a4 100644 --- a/docs/runbooks/restore-the-firewall.md +++ b/docs/runbooks/restore-the-firewall.md @@ -103,8 +103,10 @@ cat ~/.ssh/id_ed25519.pub ``` On `oracle` as `atropos`, at its console or over a session that already logs -in by key, add that line to `~/.ssh/authorized_keys`. Then check from here -that the key alone gets in: +in by key, add that line to `~/.ssh/authorized_keys`. Put the `from=` options +for `robo` from +[`stacks/wiki/README.md`](../../stacks/wiki/README.md#the-hosts-ssh-once) +in front of it. Then check from here that the key alone gets in: ```bash ssh -o BatchMode=yes atropos@10.0.99.30 true diff --git a/stacks/wiki/README.md b/stacks/wiki/README.md index 90e24805..4153b6b8 100644 --- a/stacks/wiki/README.md +++ b/stacks/wiki/README.md @@ -141,6 +141,21 @@ All four lines should end in `no`. From another host, with `Permission denied (publickey)` alone. The backup pushes from `prometheus` and `trinity` log in by key and are not affected. +Then limit each key in `~atropos/.ssh/authorized_keys` to the hosts it comes +from. Put the options in front of the key's line: + +| Whose key | Options in front of it | +| --- | --- | +| `robo` on `prometheus` (the volume, firewall, NAS and library copies, and the wiki's nightly `pg_dump` pull) | `from="10.0.99.20",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-user-rc` | +| `trinity`'s backup key (the sensitive tier's copies) | `from="10.0.99.40",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-user-rc,no-pty` | +| An operator key on Hicks | `from="10.0.50.0/24",no-agent-forwarding,no-X11-forwarding,no-user-rc` | + +A key that logs in from anywhere else is now refused, and sshd's journal +says why. The pull from `prometheus` runs `docker exec`, and the copies run +shell commands, so `command=` cannot pin these keys yet. `from=` is the limit +for now. The remote path's tunnel reaches the lab, not VLAN 99, so the Hicks +pin locks no one out. + ## TLS Caddy serves the wiki on 443 with a leaf from the estate's CA, and answers