From 716b0effd17af193c30e42c3a0ec9d49c37f2000 Mon Sep 17 00:00:00 2001 From: Takwa Date: Fri, 30 Oct 2026 15:09:50 +0100 Subject: [PATCH] Put the CVE-2026-98432 filter on sf and sf2 too sf and sf2 (ipni/infra's indexers) run Debian 12 kernels without the fix (6.1.180 and 6.1.135; Debian fixed it in 6.1.190-1), and nothing filtered them. They join the inventory in a group of their own, infra_boxes, which only cve.yml targets: it runs the hardening role's CVE tasks and nothing else. They stay out of ipfs_nodes, where hardening would replace their SSH keys and turn on ufw. They take the shared `user` account on port 5222, through their gateway, and sudo there wants a password, so cve.yml is an operator's run with -K and never CI's. Their mode starts at `count`, set apart from the ipfs_nodes boxes' so they go to enforce on their own schedule. The ruleset path moves from a var on the import to the role's defaults, so tasks_from picks it up; site.yml renders the same paths as before. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 2 +- AGENTS.md | 14 +++++++++++++- README.md | 13 +++++++++++++ cve.yml | 22 ++++++++++++++++++++++ group_vars/infra_boxes/main.yml | 14 ++++++++++++++ inventory/hosts.yml | 12 ++++++++++++ roles/hardening/defaults/main.yml | 5 +++++ roles/hardening/tasks/main.yml | 3 +-- roles/monitoring/defaults/main.yml | 6 +++--- 9 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 cve.yml create mode 100644 group_vars/infra_boxes/main.yml create mode 100644 roles/hardening/defaults/main.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8275142..dbdbae4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,7 +75,7 @@ jobs: # others and nothing else would say so. - name: Syntax-check every playbook run: | - for playbook in site.yml routing.yml bootstrap.yml content.yml k3s-upgrade.yml platform.yml; do + for playbook in site.yml routing.yml bootstrap.yml content.yml k3s-upgrade.yml platform.yml cve.yml; do echo "== $playbook" ansible-playbook --syntax-check "$playbook" done diff --git a/AGENTS.md b/AGENTS.md index 0138f00..510b7f6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,6 +10,18 @@ three at once is a full outage of that service. | lith-1 | lithuania | 46.166.169.131 | | chic-1 | chicago | 84.32.48.211 | +The inventory also has **sf** (103.232.206.165) and **sf2** +(103.232.206.162), ipni/infra's indexers, in their own group `infra_boxes`. +They are here for `cve.yml` and nothing else. Never add them to `ipfs_nodes` +or run another playbook on them: `site.yml`'s hardening would swap their SSH +keys and turn on ufw. They take the shared `user` account on port 5222, and +sudo needs its password, so runs are an operator's with `-K`: + +```bash +ansible-playbook cve.yml -K --check --diff -l sf # dry run first +ansible infra_boxes -m ping # no sudo needed +``` + ## Getting in SSH is key-only as the hardened admin user `ipni`; root login is disabled and @@ -198,7 +210,7 @@ deliberately disables `line-length` and `braces` (see `.ansible-lint`). A pass looks like `Passed: 0 failure(s), 0 warning(s) ... profile ... 'production'`. ```bash -for pb in site.yml routing.yml bootstrap.yml content.yml k3s-upgrade.yml platform.yml; do +for pb in site.yml routing.yml bootstrap.yml content.yml k3s-upgrade.yml platform.yml cve.yml; do ansible-playbook --syntax-check "$pb" done ANSIBLE_STDOUT_CALLBACK=default ansible-playbook scripts/render-manifests.yml -e dest=rendered diff --git a/README.md b/README.md index a72bfcf..f3d8de4 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,18 @@ To watch for attempts on a box: Undo instantly with `sudo systemctl stop cve-2026-98432-filter`, which deletes the table; set `off` to remove it for good once the fixed kernel is in. +**sf and sf2** (ipni/infra's indexers) get the same filter from `cve.yml`, +which runs only these tasks, on the `infra_boxes` group. Those boxes are not +ours to harden: they stay out of `ipfs_nodes`, and nothing else here targets +them. Everyone signs in as their shared `user` account on port 5222, and sudo +there wants its password, so it is an operator's run with `-K`, never CI's: +`ansible-playbook cve.yml -K --check --diff -l sf`, then without `--check`. +Their mode is `cve_2026_98432_mode` in `group_vars/infra_boxes/main.yml`, +rolled out the same way: `count`, then `enforce` one box at a time. To test +from outside, use 5222. Both run Debian 12 with nft 1.0.6, and both have +loaded the enforce ruleset in a throwaway namespace. The real fix is Debian's +`6.1.190-1` kernel and a reboot. + The provider's unused **`ubuntu` account (uid 1000, in `sudo`) is removed**. Several container images run as uid 1000, including k3s's own metrics-server, and a container escape as that uid should not land in a @@ -1780,6 +1792,7 @@ dns.txt Cloudflare-importable bootstrap DNS records (generated dns-libp2p.txt bootstrap.libp2p.io records for the legacy identities (generated) site.yml base preparation playbook, run from CI k3s-upgrade.yml k3s upgrade, one minor version at a time, backup per step +cve.yml the CVE-2026-98432 filter alone, on sf and sf2 (infra_boxes), with -K platform.yml Argo CD (first install) and the monitoring stack's prerequisites, run from CI scripts/ci-playbook.sh CI's wrapper for site.yml and platform.yml: secrets to variables, SSH key, pinned host keys inventory/known_hosts the boxes' SSH host keys, pinned for CI diff --git a/cve.yml b/cve.yml new file mode 100644 index 0000000..1e6ff45 --- /dev/null +++ b/cve.yml @@ -0,0 +1,22 @@ +--- +# The CVE-2026-98432 filter on sf and sf2 (ipni/infra's indexers), and nothing +# else: the tasks the hardening role runs on the ipfs_nodes boxes +# (roles/hardening/tasks/cve_2026_98432.yml), without the rest of that role. +# The mode is cve_2026_98432_mode in group_vars/infra_boxes. +# +# ansible-playbook cve.yml -K --check --diff -l sf # dry run first +# ansible-playbook cve.yml -K -l sf # one box +# +# -K because sudo on these boxes wants the `user` account's password: an +# operator's run, never CI's. Loading the table restarts nothing, but these +# are the live indexers, so one box at a time, stopping at the first failure. +- name: Mitigate CVE-2026-98432 on the ipni/infra boxes + hosts: infra_boxes + become: true + gather_facts: false + serial: 1 + tasks: + - name: Run the hardening role's CVE-2026-98432 tasks + ansible.builtin.include_role: + name: hardening + tasks_from: cve_2026_98432 diff --git a/group_vars/infra_boxes/main.yml b/group_vars/infra_boxes/main.yml new file mode 100644 index 0000000..c6ca393 --- /dev/null +++ b/group_vars/infra_boxes/main.yml @@ -0,0 +1,14 @@ +--- +# sf and sf2: ipni/infra's boxes, here only for cve.yml (see inventory/hosts.yml). +# +# Their gateway (a MikroTik) lets SSH through on 5222 only. Everyone signs in +# as the shared `user` account, whose sudo wants a password, so every run is an +# operator's with -K (--ask-become-pass); CI has neither the key nor the +# password, and must not get them. +ansible_user: user +ansible_port: 5222 + +# The same switch as group_vars/ipfs_nodes (see its comment), set separately so +# these boxes go through count before enforce on their own schedule. Override +# per box in host_vars to enforce one before the other. +cve_2026_98432_mode: count diff --git a/inventory/hosts.yml b/inventory/hosts.yml index 2d41e56..22252cb 100644 --- a/inventory/hosts.yml +++ b/inventory/hosts.yml @@ -20,3 +20,15 @@ all: chic-1: ansible_host: 84.32.48.211 site: chicago + + # sf and sf2 (the indexers) are ipni/infra's boxes, not ours: Debian, k3s + # 1.28, a password for sudo, nothing built by site.yml. They are here for + # cve.yml alone, which is the only playbook that targets this group. Never + # move them into ipfs_nodes: hardening would replace their SSH keys and + # turn on ufw, which would cut them off. + infra_boxes: + hosts: + sf: + ansible_host: 103.232.206.165 + sf2: + ansible_host: 103.232.206.162 diff --git a/roles/hardening/defaults/main.yml b/roles/hardening/defaults/main.yml new file mode 100644 index 0000000..5d48070 --- /dev/null +++ b/roles/hardening/defaults/main.yml @@ -0,0 +1,5 @@ +--- +# Where the CVE-2026-98432 ruleset is written and loaded from +# (tasks/cve_2026_98432.yml). A default rather than a var on the import, so +# cve.yml can run those tasks on their own with tasks_from. +cve_2026_98432_ruleset: /etc/nftables.d/cve-2026-98432.nft diff --git a/roles/hardening/tasks/main.yml b/roles/hardening/tasks/main.yml index 69a594e..d95fdc5 100644 --- a/roles/hardening/tasks/main.yml +++ b/roles/hardening/tasks/main.yml @@ -223,10 +223,9 @@ # Its own table and unit beside ufw, never through ufw or nftables.conf. # Tagged so it can go out on its own: site.yml --tags cve_2026_98432 -l . +# cve.yml runs the same file, alone, on sf and sf2. - name: Mitigate CVE-2026-98432 at the host firewall ansible.builtin.import_tasks: cve_2026_98432.yml - vars: - cve_2026_98432_ruleset: /etc/nftables.d/cve-2026-98432.nft tags: [cve_2026_98432] # --------------------------------------------------------------------------- diff --git a/roles/monitoring/defaults/main.yml b/roles/monitoring/defaults/main.yml index 4f1f59d..7a27fb2 100644 --- a/roles/monitoring/defaults/main.yml +++ b/roles/monitoring/defaults/main.yml @@ -1,8 +1,8 @@ --- # Addresses outside the inventory that push metrics and logs to the hub too. -# The boxes in the inventory are always allowed. sf and sf2 are not in it: -# ipni/infra manages them, and their Alloy pushes to the hub like the other -# boxes' (ipni/infra#166, #188). +# The ipfs_nodes boxes are always allowed. sf and sf2 are not among them +# (they are in infra_boxes, for cve.yml only): ipni/infra manages them, and +# their Alloy pushes to the hub like the other boxes' (ipni/infra#166, #188). monitoring_ingest_extra_sources: - 103.232.206.165 # sf - 103.232.206.162 # sf2