install.sh: persist only what is ours — a firewall unit instead of iptables-persistent, scoped sysctl instead of --system#7
Merged
Conversation
…tables-persistent, scoped sysctl instead of --system The end-to-end install test (issues #4 and #5) caught the two steps that reached beyond what is Dormice's to change on an operator's host. The cloud-metadata step persisted its two DROP rules by installing iptables-persistent — and apt resolves that package's conflict with ufw by removing ufw, silently discarding the operator's firewall. The netfilter-persistent snapshot was also always broader than our claim: it saves the whole ruleset, operator rules and Docker's ephemeral NAT entries included, when exactly two rules are ours to persist. Both problems share one fix: a oneshot unit (dormice-metadata-firewall .service), ordered after docker.service and before dormice.service, re-adds the rules idempotently at boot — identical on every host, no package dependency, no hand in anyone's firewall. It is started at install time too, because a unit that has never run is an unproven promise. Hosts persisted the old way keep their rules.v4 untouched; doctor accepts either form and its fix line now points at install.sh. The swappiness step applied its one-line file with `sysctl --system`, which replays every sysctl source on the host mid-install. On the test host that re-applied a dormant net.ipv4.ip_forward=0 from /etc/sysctl.conf and cut Docker networking, surfacing two steps later as an apt failure inside the base-image build with no visible connection to sysctl. The step (now "kernel parameters") writes both vm.swappiness=100 and net.ipv4.ip_forward=1 to 99-dormice.conf, applies it scoped with sysctl -p, and proves boot-order effectiveness explicitly instead: systemd-sysctl --cat-config yields the real application order, and the last setter of each key must be our file — which also catches the /etc/sysctl.conf-via-99-sysctl.conf symlink that always outsorts 99-dormice.conf, and names the offending file and value when it dies. ip_forward gains persistence because dockerd only masks the problem: it flips the flag on at every daemon start, so a boot config saying 0 stays invisible until the next config replay cuts every sandbox's networking. That is also why the new doctor check is the one allowed to read boot config — the effective value can never expose this hazard. Effective 0 fails (sandboxes are cut right now); effective 1 with a boot winner of 0 warns and names the file. Closes #4. Closes #5.
sweetcornna
force-pushed
the
install-scoped-persistence
branch
from
July 17, 2026 15:59
1841301 to
64a2a8d
Compare
Collaborator
Author
|
Real-machine acceptance on the Debian 12 test host (the same machine both issues were found on) — all green, at 64a2a8d:
|
This was referenced Jul 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Fixes the two remaining defects from the end-to-end install test — both cases of a step reaching beyond what is Dormice's to change on an operator's host. Directions were agreed on the issues before this PR.
#4 — installing iptables-persistent silently apt-removes ufw. The metadata-firewall step now persists its two DROP rules with its own oneshot unit,
dormice-metadata-firewall.service(After=/Requires=docker.service,Before=dormice.service, idempotentiptables -C || iptables -I), and stops installingiptables-persistententirely. Beyond the ufw conflict,netfilter-persistent savealways snapshotted the whole ruleset — operator rules and Docker's ephemeral NAT entries included — when exactly two rules are ours to persist. The unit is started at install time too: a unit that has never run is an unproven promise. Hosts persisted the old way keep theirrules.v4untouched; doctor'smetadata-persistedcheck accepts either form.#5 —
sysctl --systemreplays operator config mid-install. The sysctl step (now "kernel parameters") applies scoped withsysctl -p /etc/sysctl.d/99-dormice.confand proves boot-order effectiveness explicitly instead:systemd-sysctl --cat-configgives the real application order, and the winning value of each key must be the one we need — dying loudly with the offending file and value otherwise. Value, not file: a host may legitimately setip_forward=1in its own later-sorting config (the real test host does, via its VPN relay configs), and that is agreement, not a conflict. The scan also catches the/etc/sysctl.conf-via-99-sysctl.confsymlink that always outsorts99-dormice.conf, which the old filename-sorting assumption missed. The file gainsnet.ipv4.ip_forward=1: dockerd only masks a boot config that says 0, so the hazard stays invisible until the next config replay cuts every sandbox's networking — exactly how the test install failed. A newip-forwarddoctor check fails on effective 0 (sandboxes are cut right now) and warns, naming the file, when the boot winner is 0 behind an effective 1; it is the one check allowed to read boot config, because the effective value can never expose this hazard.Closes #4. Closes #5.
Checklist
pnpm build && pnpm typecheck && pnpm lint && pnpm testpasses locally (build first — the e2e suite runs the built daemon)@dormice/shared/sdk/clihave a changeset (pnpm changeset)docs/doctoron the website is updated)Real-machine acceptance on the test host (from-scratch reinstall + loud-die negative test + reboot) in progress — results will be posted below before merge.