Privacy archetype: Tor-only egress, fail-closed firewall, nothing persists #143

Open
opened 2026-10-12 00:29:27 +01:00 by vnprc-agent · 0 comments

Give the privacy archetype the hardening every privacy VM repeats by hand today: Tor-only egress, a fail-closed firewall, nothing persisted off the data disk, a locked kernel; so a fork gets a privacy machine by declaring one, the way it gets a service machine.

Primary goals:

  • Tor-only egress in the archetype — a system Tor as the one account allowed off the box, and an nftables ruleset with policy drop on input, forward and output, loopback and established traffic accepted, the Tor uid accepted, and the DHCP exchange as the only root exception; NetworkManager ordered after nftables so no interface comes up without a table.
  • Nothing persists off the data disk — volatile journald, tmpfs /tmp cleaned on boot, core dumps disabled, dmesg restricted, no swap.
  • No side channels — services.resolved and services.timesyncd off; security.lockKernelModules after boot.
  • A seam for a deliberate exception — one option a profile uses to add rules to a dedicated chain the base ruleset jumps to, so a profile never replaces the ruleset to open one hole.
  • The builder stays a function of its inputs — the same shape as nix/builders/service.nix: a privacyModules list composed before definition.nixosModules, with assertions that fail the build when a profile widens the posture by accident.

Current state and specifics

nix/builders/privacy.nix composes the shared guest base and the profile's modules and nothing else, while nix/builders/service.nix carries the service posture (root-only login, the data-disk filesystem, the firewall, service-egress.nix with its assertions). Three private privacy profiles in one deployment, a bitcoin node, a wallet machine and a nostr signer, each carry the same hardening block by hand, drift included: the block in the node profile proposed for the template in allod/profiles#14 is the reference copy. Each profile also adds what is its own: the node's one-way clearnet exception for the first sync, a chain the base ruleset jumps to that its switch empties once the sync finishes; the wallet's display and clipboard, which need uinput loaded before the kernel locks and spice allowed through; the signer's loopback relay.

Those additions are the seam test. The clearnet exception must stay expressible as "add this rule to the exception chain until the marker exists" without the profile owning the ruleset; the display exception must stay a profile addition that the archetype's assertions do not refuse. security.lockKernelModules with a module the profile needs loaded is the known trap: the profile has to list it in boot.kernelModules so it loads before the lock.

The witness is derivation equality on the existing privacy machine: composed through the new archetype with its profile reduced to the service-specific remainder, config.system.build.toplevel.drvPath equals today's, or the diff names only the lines that moved. The fork-safety fixture gains a privacy machine so a fork's privacy profile is checked against the posture too.

Scope

In: nix/builders/privacy.nix and a privacy-egress.nix beside service-egress.nix, the exception-chain option, the assertions, the fork fixture, the README's Builders section. Out: the profiles themselves, which shrink in their own repos once this lands; the bitcoin node profile's move to the template, allod/profiles#14, which lands first so the archetype has a public consumer to witness against.

Give the privacy archetype the hardening every privacy VM repeats by hand today: Tor-only egress, a fail-closed firewall, nothing persisted off the data disk, a locked kernel; so a fork gets a privacy machine by declaring one, the way it gets a service machine. Primary goals: - **Tor-only egress in the archetype** — a system Tor as the one account allowed off the box, and an nftables ruleset with `policy drop` on input, forward and output, loopback and established traffic accepted, the Tor uid accepted, and the DHCP exchange as the only root exception; NetworkManager ordered after nftables so no interface comes up without a table. - **Nothing persists off the data disk** — volatile journald, tmpfs `/tmp` cleaned on boot, core dumps disabled, `dmesg` restricted, no swap. - **No side channels** — `services.resolved` and `services.timesyncd` off; `security.lockKernelModules` after boot. - **A seam for a deliberate exception** — one option a profile uses to add rules to a dedicated chain the base ruleset jumps to, so a profile never replaces the ruleset to open one hole. - **The builder stays a function of its inputs** — the same shape as `nix/builders/service.nix`: a `privacyModules` list composed before `definition.nixosModules`, with assertions that fail the build when a profile widens the posture by accident. ### Current state and specifics `nix/builders/privacy.nix` composes the shared guest base and the profile's modules and nothing else, while `nix/builders/service.nix` carries the service posture (root-only login, the data-disk filesystem, the firewall, `service-egress.nix` with its assertions). Three private privacy profiles in one deployment, a bitcoin node, a wallet machine and a nostr signer, each carry the same hardening block by hand, drift included: the block in the node profile proposed for the template in allod/profiles#14 is the reference copy. Each profile also adds what is its own: the node's one-way clearnet exception for the first sync, a chain the base ruleset jumps to that its switch empties once the sync finishes; the wallet's display and clipboard, which need `uinput` loaded before the kernel locks and spice allowed through; the signer's loopback relay. Those additions are the seam test. The clearnet exception must stay expressible as "add this rule to the exception chain until the marker exists" without the profile owning the ruleset; the display exception must stay a profile addition that the archetype's assertions do not refuse. `security.lockKernelModules` with a module the profile needs loaded is the known trap: the profile has to list it in `boot.kernelModules` so it loads before the lock. The witness is derivation equality on the existing privacy machine: composed through the new archetype with its profile reduced to the service-specific remainder, `config.system.build.toplevel.drvPath` equals today's, or the diff names only the lines that moved. The fork-safety fixture gains a privacy machine so a fork's privacy profile is checked against the posture too. ### Scope In: `nix/builders/privacy.nix` and a `privacy-egress.nix` beside `service-egress.nix`, the exception-chain option, the assertions, the fork fixture, the README's Builders section. Out: the profiles themselves, which shrink in their own repos once this lands; the bitcoin node profile's move to the template, allod/profiles#14, which lands first so the archetype has a public consumer to witness against.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
allod/archetypes#143
No description provided.