docs: add P1-HARDENING prompt (DNS bake, firewall persist+RFC1918 DROP, provisioning fix)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1016,6 +1016,51 @@ Prefer `incus` over legacy lxc. Gather inventories with read-only commands (dpkg
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## P1-HARDENING — finish + secure the sandbox substrate (2026-07-09)
|
||||||
|
> **Why this exists:** the P0+GROUNDWORK agent built the P1 base (Incus 6.0.4, btrfs loop pool, `incusbr0` NAT, both mirrors launched + isolated) but left three gaps discovered during a live networking-fix detour (see memory `incus-on-docker-host-networking`): (a) the sandbox internet fixes were applied **live only, not baked into `build-sandbox.sh`** — so the next rebuild wipes them; (b) the host iptables fix that lets the incus bridge forward is **not reboot-persistent AND too broad** (allows sandbox→LAN egress, undercutting isolation); (c) in-container provisioning was **truncated by the sudo-bridge 60s exec cap** and has a **useradd/usermod ordering bug** (adds `administrator` to groups before creating the user). This agent closes all three so P2 can start on a trustworthy, self-rebuilding sandbox. **This is P1 hardening ONLY — do not touch P2/P3/P4/P5.**
|
||||||
|
|
||||||
|
**Blast radius: server-01 sandbox-side + server-01 host iptables/boot config** (no production containers, no primary, no live data). `--max-turns 30`.
|
||||||
|
|
||||||
|
```
|
||||||
|
You are an infrastructure-hardening agent finishing the Agent-Sudo sandbox substrate on server-01 (192.168.1.90). The base P1 build is already done; you are closing three known gaps and proving isolation. DO NOT redesign anything and DO NOT begin P2 (service). Design is LOCKED in agent_sudo_design_decisions.md (D1, D7).
|
||||||
|
|
||||||
|
## READ FIRST (do not re-derive — these are the exact, already-diagnosed fixes)
|
||||||
|
Read the memory file `/home/administrator/.claude/projects/-home-administrator-Desktop-claude/memory/reference_incus_on_docker_host_networking.md` — it contains BOTH networking fixes verbatim (Docker FORWARD DROP → DOCKER-USER accept; broken systemd-resolved → mask + static resolv.conf) and the incus-admin access note. Use those exact commands; do not rediscover them.
|
||||||
|
|
||||||
|
## Use the shared block (verified facts, sudo-bridge-server01 path, tier-2-broken guard, wrap-up, loop guard).
|
||||||
|
|
||||||
|
## EXECUTION PATH — two lanes, pick the cheaper one per command
|
||||||
|
- **Direct SSH as `administrator@192.168.1.90` (BatchMode, key-based):** `administrator` is now in the **`incus-admin`** group, so ALL `incus ...` commands and running `build-sandbox.sh` run over plain SSH with **NO 60s cap** (the cap is a sudo-bridge limitation — this is how you avoid the truncation that broke the groundwork run). Use this lane for every incus/container/provisioning/verification step.
|
||||||
|
- **sudo-bridge-server01 (`http://192.168.1.90:8082`, tier-1 only, per shared block):** use ONLY for root-owned writes `administrator` can't do — editing `build-sandbox.sh` (root:root) and host-level iptables/systemd. Obey the no-shell / single-argv / base64-write rules. If a host iptables/systemd step is NOT tier-1-eligible, DO NOT block on tier-2 (broken) — write the artifact and DEFER the privileged enable to the MANUAL-AUTH BATCH.
|
||||||
|
|
||||||
|
## Contract — what must be true when you finish (or be cleanly deferred with a manual-auth list)
|
||||||
|
1. **DNS fix baked into `build-sandbox.sh`.** Right AFTER the `waiting for network` loop, the script must, for the launched container: `systemctl mask systemd-resolved`, stop it, and write a static `/etc/resolv.conf` (`nameserver 1.1.1.1` + the incusbr0 gateway resolver — confirm the gateway via `incus network get incusbr0 ipv4.address`). Idempotent (safe on rebuild). Verify: after a rebuild, `incus exec <ct> -- getent hosts deb.debian.org` returns IPs, exit 0 — WITHOUT any manual post-fix.
|
||||||
|
2. **Provisioning bug fixed + provisioning completes.** In `build-sandbox.sh`, reorder so every user is **created before** any `usermod -aG`/group-membership step (the current script adds `administrator` to groups before `useradd`). Make the whole provisioning block idempotent (`getent passwd X || useradd ...`, `getent group Y || groupadd ...`). Because you run via direct SSH (no 60s cap), provisioning must now run to completion. Verify: after rebuild, the mirrored users/groups/path-skeleton from the inventory JSON are all present in the container with NO ordering error in the run output.
|
||||||
|
3. **Host iptables made persistent AND tightened (close the LAN-egress hole).** The live `iptables -I DOCKER-USER -i/-o incusbr0 -j ACCEPT` rules are (a) wiped by reboot / `systemctl restart docker`, and (b) too broad — they let a sandbox reach 192.168.1.x / other RFC1918. Produce a **single boot-persistent mechanism** (a small systemd unit, e.g. `incus-sandbox-firewall.service`, `After=docker.service`, that reapplies the rules — because Docker rebuilds its chains on restart) that installs, in DOCKER-USER, this ORDER:
|
||||||
|
- ACCEPT established/related for incusbr0,
|
||||||
|
- from incusbr0 **DROP** to `192.168.0.0/16`, `10.0.0.0/8`, `172.16.0.0/12` **EXCEPT** the incusbr0 subnet itself (get it from `incus network get incusbr0 ipv4.address`) and the incusbr0 gateway (so container→gateway DNS still works),
|
||||||
|
- ACCEPT incusbr0 in/out otherwise (internet egress).
|
||||||
|
Net effect: sandbox reaches the internet (apt/DNS) but CANNOT reach the LAN. If installing/enabling the systemd unit is not tier-1-eligible via sudo-bridge, WRITE the unit file + a `restore-rules.sh` to `/opt/appdata/docker/agent-sudo/firewall/` and DEFER `systemctl enable --now incus-sandbox-firewall.service` to the MANUAL-AUTH BATCH.
|
||||||
|
4. **Both mirrors rebuilt clean from the hardened script and RE-PROVEN isolated.** Run `build-sandbox.sh server-01` then `build-sandbox.sh primary` over direct SSH. For BOTH containers confirm: RUNNING on incusbr0 with NO forwarded LAN port; `getent hosts deb.debian.org` exit 0 (internet OK); and — once the firewall rules are active — a LAN reach test FAILS (e.g. `incus exec <ct> -- ping -c1 -W2 192.168.1.88` times out / is dropped). If the firewall unit was deferred to manual-auth, run the DROP rules live to prove the isolation test, note they still need the persistent enable, and record it.
|
||||||
|
|
||||||
|
## Pre-flight (idempotent)
|
||||||
|
- `ssh -o BatchMode=yes administrator@192.168.1.90 'incus list; incus network get incusbr0 ipv4.address'` — confirm both containers + the bridge subnet/gateway.
|
||||||
|
- Check whether the DOCKER-USER incusbr0 rules are currently present (`iptables -S DOCKER-USER` via sudo-bridge) — a docker restart since 2026-07-09 may have wiped them; if gone, that's expected, the persistent unit is the fix.
|
||||||
|
- Read the current `build-sandbox.sh` first (via SSH `cat` — it's world-readable) to locate the exact `waiting for network` loop and the provisioning block before editing.
|
||||||
|
|
||||||
|
## Abort / rollback
|
||||||
|
- Rollback = `incus delete --force sandbox-primary sandbox-server01` (rebuildable) and remove any firewall unit you installed (`systemctl disable --now incus-sandbox-firewall; rm` — restores the prior live-rule state). `build-sandbox.sh` edits are in a root file — keep a `.bak` copy before editing so the change is reversible. Nothing production is touched.
|
||||||
|
- Abort (record + go to wrap-up) if: both containers can't be rebuilt; or the root write to `build-sandbox.sh` needs tier-2 you can't get (defer via manual-auth batch, still deliver the firewall artifacts).
|
||||||
|
|
||||||
|
## Wrap-up notes MUST include:
|
||||||
|
- Confirmation of each of the 4 contract items (DNS baked + verified on rebuild; provisioning ordering fixed + completes; firewall persistent-mechanism written + whether ENABLED or deferred; both mirrors rebuilt + isolation RE-PROVEN incl. the LAN-DROP test result).
|
||||||
|
- The incusbr0 subnet/gateway you used.
|
||||||
|
- A single **MANUAL-AUTH BATCH** = verbatim any privileged command the user must run (e.g. `systemctl enable --now incus-sandbox-firewall.service`), or "none — substrate is fully hardened and self-rebuilding."
|
||||||
|
- One line: "P1 substrate hardened. Next phase: P2." (or which item blocked + what unblocks it).
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## P2 — Agent-Sudo service (drop-in sudo-bridge replacement)
|
## P2 — Agent-Sudo service (drop-in sudo-bridge replacement)
|
||||||
**Blast radius: NONE while built** (binds 8084; old bridge keeps 8082). `--max-turns 25`.
|
**Blast radius: NONE while built** (binds 8084; old bridge keeps 8082). `--max-turns 25`.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user