Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 13 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions cve.yml
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions group_vars/infra_boxes/main.yml
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions inventory/hosts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 5 additions & 0 deletions roles/hardening/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -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
3 changes: 1 addition & 2 deletions roles/hardening/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 <box>.
# 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]

# ---------------------------------------------------------------------------
Expand Down
6 changes: 3 additions & 3 deletions roles/monitoring/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -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
Loading