test: add an end-to-end drill against a real Incus #18
No reviewers
Labels
No labels
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-triage
ready
release
scope:cli
scope:drill
scope:host
scope:installer
scope:templates
scope:tiers
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/box#18
Loading…
Reference in a new issue
No description provided.
Delete branch "drill/rehearsal"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The repo has no tests and no CI. Everything in #9, #10 and #13 was verified against an
incusstub — the assumptions those PRs rest on had never been checked against the real thing. And #15 needs a live audit of the isolation boundary before #16/#17 can move.This PR is both, as one run-and-forget script on a throwaway host:
~20 minutes, most of it the cold box. Exit 0 = all green. The run ends with an audit-answers block to paste into #15.
What it checks
A. Incus semantics — the assumptions the CLI is built on, probed directly: the
user.claudeboxtag read (on the path of every command since #13), the list filter selecting ours and excluding an untagged instance, CSV field shapes, theRUNNINGstate string, rename refusing a running instance, snapshot-list labels — plus two #15 mechanism checks: an unset config key reads empty with exit 0 (B4, the shape #17's user lookup depends on) andincus copypreservesuser.*keys (B2, the whole metadata design).B. The surface — mint, list, info, snapshot, clone-from-a-snapshot-of-a-renamed-box, rename preconditions, the escape hatch and its isolation warning, the
rmguard, the CLI contract. The boundary gets its own test: an instance claudebox did not mint must survivedown,rm --force, and the escape hatch untouched.C. Isolation baseline (#15 section A) — from inside a real box: public egress passes; box → host gateway listener blocked; RFC1918 dropped; box → sibling box blocked — the drill clones a peer and runs a listener on it, so refused (packet arrived) cannot masquerade as dropped; DNS enumeration of the sibling recorded (#12 predicts it leaks — audit data either way); IPv6 off; host → box inbound dropped.
D. Hardening rehearsal (#15 section B) — the host is disposable, so the drill applies exactly what #16 proposes and watches what breaks:
dns.mode=none(must kill sibling resolution, must not kill egress),security.mac_filtering+security.ipv4_filtering(box networking and in-box Docker must survive), and@internalas a bridge-ACL drop destination (accepted + egress survives ⇒ #16's sibling drop is renumber-proof by construction; otherwise #16 derives the subnet). A FAIL here is a design veto for #16, caught before its code is written.It already found one thing — now fixed here
setup-host.shdrivesnft, but a stock Debian 13 cloud image ships no nftables and no UFW — host setup died on its firstnftcall on exactly the kind of fresh cloud host this tool targets. This PR fixessetup-host.shto install nftables itself when neither exists; the drill keeps a tripwire NOTE should that regress.Honest limits
/dev/kvm→ no verdict on the VM boundary. The drill says so loudly rather than passing quietly on container mode.claude /loginisn't drilled — interactive by design; the drill confirms Claude Code is installed and runnable.🤖 Generated with Claude Code
Reviewed against
main@0982a2dand against #15 (the audit this is now the vehicle for). Verdict: right instrument, was incomplete as an audit — fixed in the two commits above. What the review found:peerfrom the snapshot and probes it with a listener running, so the three outcomes are distinguishable: connected (isolation broken), refused (packet arrived — the egress drop isn't covering siblings), timeout (dropped, as designed).incus copypreservinguser.*), B3 + B5 + B1 as a new phase D that applies #16's exact proposed changes to the throwaway host and re-probes. A phase-D FAIL vetoes a piece of #16's design before it's written — cheaper than finding out in review.claude-hdb/claudebox@refactor/command-table— a fork branch that stopped moving when #13 merged. Nowheavy-duty/claudebox@main.0.3.0— would fail the drill on every release. Now compares against the installedVERSIONfile.sleep 10afterstart— a VM agent can take longer; a false "box cannot reach the internet" would poison the audit. Now an agent poll (~2 min budget).setup-host.shinstalls nftables when neithernftnor UFW exists (a stock Debian 13 cloud image ships neither — host setup died on its firstnftcall). The drill's manual workaround became a regression tripwire.Also verified every grep target in the drill against the merged CLI (
no such box,refusing,renamed {} to,isolation stack,did you mean,no boxes yet— all present), and both scripts are shellcheck-clean.Run plan: provision the throwaway server →
git clone … && bash drill/drill.sh --yes→ paste the closing audit-answers block into #15. Green run + answers on #15 unblocks #16's design points; #17 queues behind it. Known open risk, stated in the body: the drill itself has never executed against a live Incus — first-run friction is expected and is the point.