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:
parent
16ac84ca63
commit
83c0e4b4bf
8 changed files with 166 additions and 83 deletions
|
|
@ -65,6 +65,65 @@ fn bridge_gateway_ip_rejects_bad_input() {
|
|||
assert!(bridge_gateway_ip("10.42.0/24").is_none()); // 3 octets
|
||||
}
|
||||
|
||||
/// The presence control for the two rejection tests below: with both
|
||||
/// variables set and well-formed, the settings are built. Without this,
|
||||
/// a `network_isolation_from_vars` that rejected *everything* would pass
|
||||
/// every absence assertion and look like a working guard.
|
||||
#[test]
|
||||
fn network_isolation_accepts_a_well_formed_pair() {
|
||||
let iso = network_isolation_from_vars(Some("hive0"), Some("10.42.0.1/24"))
|
||||
.expect("well-formed bridge + subnet must be accepted");
|
||||
assert_eq!(iso.bridge, "hive0");
|
||||
// The gateway is the verbatim bridge address, prefix stripped.
|
||||
assert_eq!(iso.gateway_ip, "10.42.0.1");
|
||||
}
|
||||
|
||||
/// A missing or empty variable is fatal, not a fallback to the host
|
||||
/// netns. Empty is tested alongside unset because `std::env::var` on a
|
||||
/// variable set to `""` returns `Ok("")`, so treating only `None` as
|
||||
/// missing would let an empty value through.
|
||||
#[test]
|
||||
fn network_isolation_rejects_missing_or_empty_vars() {
|
||||
assert!(network_isolation_from_vars(None, Some("10.42.0.1/24")).is_err());
|
||||
assert!(network_isolation_from_vars(Some("hive0"), None).is_err());
|
||||
assert!(network_isolation_from_vars(None, None).is_err());
|
||||
assert!(network_isolation_from_vars(Some(""), Some("10.42.0.1/24")).is_err());
|
||||
assert!(network_isolation_from_vars(Some("hive0"), Some("")).is_err());
|
||||
}
|
||||
|
||||
/// A malformed subnet is fatal too. Previously this logged a warning and
|
||||
/// silently produced a container on the host netns — a dropped security
|
||||
/// boundary with nothing in the journal saying so.
|
||||
#[test]
|
||||
fn network_isolation_rejects_a_malformed_subnet() {
|
||||
assert!(network_isolation_from_vars(Some("hive0"), Some("notanip/24")).is_err());
|
||||
assert!(network_isolation_from_vars(Some("hive0"), Some("10.42.0.1")).is_err());
|
||||
assert!(network_isolation_from_vars(Some("hive0"), Some("10.42.0.1/33")).is_err());
|
||||
assert!(network_isolation_from_vars(Some("hive0"), Some("10.42.0.999/24")).is_err());
|
||||
}
|
||||
|
||||
/// The error has to name the variable an operator must fix — these are
|
||||
/// read at startup, so the message is the whole diagnostic.
|
||||
#[test]
|
||||
fn network_isolation_errors_name_the_offending_variable() {
|
||||
let e = network_isolation_from_vars(None, Some("10.42.0.1/24")).unwrap_err();
|
||||
assert!(
|
||||
format!("{e:#}").contains("HIVE_NETWORK_BRIDGE"),
|
||||
"bridge error must name the variable, got: {e:#}"
|
||||
);
|
||||
let e = network_isolation_from_vars(Some("hive0"), None).unwrap_err();
|
||||
assert!(
|
||||
format!("{e:#}").contains("HIVE_NETWORK_SUBNET"),
|
||||
"subnet error must name the variable, got: {e:#}"
|
||||
);
|
||||
let e = network_isolation_from_vars(Some("hive0"), Some("nope/24")).unwrap_err();
|
||||
let msg = format!("{e:#}");
|
||||
assert!(
|
||||
msg.contains("HIVE_NETWORK_SUBNET") && msg.contains("nope/24"),
|
||||
"malformed-subnet error must name the variable and the bad value, got: {msg}"
|
||||
);
|
||||
}
|
||||
|
||||
/// `setup_proposed` is idempotent: calling it on an existing repo is a
|
||||
/// no-op (the fresh guard skips all writes).
|
||||
#[tokio::test]
|
||||
|
|
|
|||
Loading…
Reference in a new issue