require network isolation, deleting the residual non-isolated branch

Per mara on #3725: the on/off toggle is removed, and required env vars
unset lead to a crash. HIVE_NETWORK_ISOLATION is gone from
hive-network.nix -- it was the toggle.

Validation happens once at daemon startup rather than per container.
The variables are process-global, so a bad value breaks every container
rather than one: failing at boot gives a single diagnostic naming the
bad value, and cannot reach a state where some containers were
configured before it was noticed.

Option<NetworkIsolation> collapses to NetworkIsolation through the wire
type, client and helper, which deletes the branch instead of leaving it
unreachable. serde(default) is dropped on that field deliberately: a
request omitting isolation is now rejected rather than defaulting to a
container sharing the host's network namespace.

What this replaces was a silent security downgrade. Of the four ways
into the old fallback, two logged nothing at all -- a container came up
without isolation and the journal agreed it was fine.

Doc comments that still described the removed branch are updated
(argus's note on #3723 scoped that to this issue). The hive-priv one is
a minimal edit inside the block #3723 rewrites; de-splicing is that
PR's job.
This commit is contained in:
atlas 2026-08-29 12:34:05 +02:00 committed by mara
commit 83c0e4b4bf
8 changed files with 166 additions and 83 deletions

View file

@ -291,9 +291,10 @@ pub struct CredentialMount {
pub host_path: String,
}
/// Network isolation parameters for `WriteNspawnFlags`. When `Some`,
/// hive-priv writes `PRIVATE_NETWORK=1` + veth bridge wiring instead
/// of the default `PRIVATE_NETWORK=0`. Containers receive their IP
/// Network isolation parameters for `WriteNspawnFlags`. hive-priv writes
/// `PRIVATE_NETWORK=1` + veth bridge wiring from these; every container
/// is isolated, so they are required rather than a mode selector.
/// Containers receive their IP
/// dynamically via DHCP from the bridge dnsmasq pool (`networking.useDHCP`
/// in `harness-base.nix`); no static address is pre-assigned here.
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -379,15 +380,19 @@ pub enum PrivRequest {
/// Update `/etc/nixos-containers/<container>.conf`: strip old network-isolation
/// vars, write `PRIVATE_NETWORK` + bridge settings, and set `EXTRA_NSPAWN_FLAGS`
/// from the provided bind-mount list. Written by `lifecycle::set_nspawn_flags`.
/// When `isolation` is `Some`, writes `PRIVATE_NETWORK=1` + veth wiring;
/// when `None`, writes `PRIVATE_NETWORK=0`.
/// Always writes `PRIVATE_NETWORK=1` + veth wiring: isolation is the only
/// mode, so there is no request shape that yields a container sharing the
/// host's network namespace.
WriteNspawnFlags {
container: String,
binds: Vec<BindMount>,
/// `None` = host netns (`PRIVATE_NETWORK=0`). `Some` = private netns with
/// veth on the specified bridge (`PRIVATE_NETWORK=1`).
#[serde(default)]
isolation: Option<NetworkIsolation>,
/// Private netns with a veth on the given bridge
/// (`PRIVATE_NETWORK=1`). Required: isolation is the only
/// supported mode, so there is no value meaning "host netns".
/// Deliberately **not** `#[serde(default)]` — a request that
/// omits it is rejected rather than quietly configuring a
/// container that shares the host's network namespace.
isolation: NetworkIsolation,
/// Host secrets forwarded into the container's credential store via
/// nspawn `--load-credential=<name>:<host_path>`. Empty for agents
/// with no credentials configured (the common case). `#[serde(default)]`