Add bridge provisioning for networks (root-only)

Commit 3/6 of the network isolation feature (docs/networking-design.md).
network_bridge.{h,cpp}: ensure_network_provisioned() stands up a
network's real bridge -- idempotent (checks `ip link show` first), so
this doubles as the reboot-reconciliation mechanism, no separate code
path. extern's bridge lives in the host's own root namespace with
net.ipv4.ip_forward + an iptables MASQUERADE rule for the subnet (+
IPv6 equivalents if enabled); intern's bridge lives inside its own
dedicated persistent namespace (persistent_netns.h) with no forwarding
or NAT at all -- a structural isolation boundary, not just a missing
rule. Bridge names are a deterministic FNV-1a hash of the network name
(not std::hash, whose value isn't guaranteed stable across a rebuild),
kept under Linux's 15-char interface name limit.

network_subnet.{h,cpp} gains ipv4_gateway_address()/
ipv6_gateway_address() (mask a CIDR to its network address, +1 for the
bridge's own ".1"). create_network_command() now calls
ensure_network_provisioned() before persisting the config entry -- a
network that fails to provision isn't saved.

Verified end-to-end as root (via a scoped doas rule): a real extern
network's bridge/gateway IPs/forwarding/NAT rule, and a real intern
network's isolated bridge with neither, both came up correctly; test
networks removed via --delete-network afterward.
This commit is contained in:
2026-08-30 12:50:55 +00:00
parent db3a9d82c7
commit 24b8ddcce7
8 changed files with 380 additions and 26 deletions
+7 -7
View File
@@ -92,7 +92,7 @@ slocker-lite -V|--version
| `--list-volumes` | List all named volumes (see `-v/--volume`) with their host directory. |
| `--delete-volume <name>` | Remove a named volume from the config. The host directory is left untouched. |
| `--delete-volume-full <name>` | Like `--delete-volume`, but also recursively deletes the volume's host directory. |
| `-n, --network <name>` | Create/manage a persistent named network: requires exactly one of `--extern` (has a path to the host's real network) or `--intern` (only reachable by other containers on the same network). `--subnet <cidr>` overrides the auto-allocated IPv4 range (`10.168.0.0/24`, incrementing per network); `--no-ipv6` disables (and `--subnet6 <cidr>` overrides) the auto-allocated IPv6 range, on by default. With `--run`, instead joins `<name>` to the container; repeatable, no membership limit. See [`docs/networking-design.md`](docs/networking-design.md) — as of this, network creation is config-only: no bridge/namespace/iptables state exists yet. |
| `-n, --network <name>` | Create/manage a persistent named network: requires exactly one of `--extern` (a real Linux bridge in the host's own namespace, with NAT/forwarding set up so containers on it reach the host's real network) or `--intern` (a bridge inside its own dedicated, routeless namespace, only reachable by other containers on the same network). `--subnet <cidr>` overrides the auto-allocated IPv4 range (`10.168.0.0/24`, incrementing per network); `--no-ipv6` disables (and `--subnet6 <cidr>` overrides) the auto-allocated IPv6 range, on by default. With `--run`, instead joins `<name>` to the container; repeatable, no membership limit (not wired up yet, see below). Root-only for now. See [`docs/networking-design.md`](docs/networking-design.md). |
| `--list-networks` | List all named networks (see `-n/--network`) with their kind, IPv4 subnet, and IPv6 subnet (or `(no ipv6)`). |
| `--delete-network <name>` | Remove a named network from the config. |
| `--list-processes` | List running `--run` sessions found by their pid files under `$XDG_STATE_HOME/slocker-lite/run/`, with their pid, container name, and status (`running` or `exited`). |
@@ -203,12 +203,12 @@ managed by `-v/--volume` (see above) rather than hand-edited — it's what
`-r/--run`'s own `-v` usage looks named volumes up in. The `networks` section
is likewise managed by `-n/--network` rather than hand-edited — see
[`docs/networking-design.md`](docs/networking-design.md) for the full
persistent-network feature design (as of this, network creation is
config-only: no bridge/namespace/iptables state exists yet, so
`global.unshare-net` above is still the only thing actually affecting a
sandboxed container's network access). A missing config file is fine either
way (nothing is overridden, and one gets created the first time
`-v/--volume`/`-n/--network` is used).
persistent-network feature design. `-n/--network` creating a network does
stand up its real bridge/iptables state (root-only) as of this, but joining
one from `-r/--run` isn't wired up yet, so `global.unshare-net` above is
still the only thing actually affecting a sandboxed container's own network
access. A missing config file is fine either way (nothing is overridden, and
one gets created the first time `-v/--volume`/`-n/--network` is used).
Run `-w/--write-config` to bootstrap a config file: it writes out every
supported option explicitly (filling in the current or default value for