Add persistent network namespace primitives
Commit 2/6 of the network isolation feature (docs/networking-design.md).
persistent_netns.{h,cpp}: create/verify/remove a network namespace kept
alive with no process in it, the way `ip netns add` does (fork a child,
unshare(CLONE_NEWNET), bind-mount its /proc/self/ns/net onto a
persistent path, exit -- the bind mount keeps it alive). Root-only
(CAP_SYS_ADMIN for the bind mount), best-effort like this project's
other host-state primitives. Not wired into -n/--network yet.
xdg_state_dir() (pid_file.cpp) moved out of its anonymous namespace so
this file can reuse the same $XDG_STATE_HOME resolution rather than a
second, drifting copy.
-t/--test now exercises the create/verify/remove cycle (skipped with a
message, not a failure, when not root) -- confirmed working via doas.
This commit is contained in:
@@ -196,10 +196,17 @@ Source layout (all under `src/`):
|
||||
- `self_test.{h,cpp}` — `run_self_tests()` implements `-t/--test`, this
|
||||
project's own built-in self-test mode (distinct from the Meson-driven
|
||||
fixture smoke test under `tests/`, described in "Build & test commands"
|
||||
below). Currently an empty placeholder (previously reported
|
||||
`detect_bwrap_unshare_args()`'s output — `bwrap.{h,cpp}` — unplugged since
|
||||
that's kernel-capability diagnostics, not a test); deliberately its own
|
||||
small file since real tests are expected here soon.
|
||||
below; previously reported `detect_bwrap_unshare_args()`'s output —
|
||||
`bwrap.{h,cpp}` — unplugged since that's kernel-capability diagnostics, not
|
||||
a test). Currently exercises `persistent_netns.{h,cpp}`'s (see below)
|
||||
create/verify/remove cycle: skipped with a message (not a failure) when not
|
||||
root, since `create_persistent_netns()` requires it for the bind mount.
|
||||
Confirms the namespace is missing before creation, exists right after
|
||||
(checked from this process, *after* the forked child that actually did the
|
||||
`unshare()`/bind-mount has already exited — the actual claim being tested:
|
||||
the namespace outlives its creating process), then gone again after
|
||||
removal. Deliberately its own small file since more real tests are expected
|
||||
here as more of the networking feature lands.
|
||||
- `env_spec.{h,cpp}` — `resolve_env_specs()` turns an ordered list of
|
||||
`EnvSpec {is_file, value}` (see `cli_args.{h,cpp}` above) into a flat, ordered list of
|
||||
`(key, value)` pairs. A literal (`--env`) is split at its *first* `=` (the
|
||||
@@ -406,7 +413,12 @@ Source layout (all under `src/`):
|
||||
back to `"container"` if that leaves nothing) is exported here (not just
|
||||
`.cpp`-local) specifically so `session_cgroup.{h,cpp}` (see below) can reuse
|
||||
the exact same `<name>-<pid>` naming rule for its own per-session cgroup
|
||||
directory without drifting from this file's own. `session_pid_file_path()` resolves
|
||||
directory without drifting from this file's own. `xdg_state_dir()`
|
||||
(`$XDG_STATE_HOME/slocker-lite`, or the `$HOME/.local/state/...` fallback)
|
||||
is likewise exported (moved out of this file's own anonymous namespace) so
|
||||
`persistent_netns.{h,cpp}` (see below) can resolve its own subdirectory
|
||||
under the same state root without a second, drifting copy of this
|
||||
resolution logic. `session_pid_file_path()` resolves
|
||||
`$XDG_STATE_HOME/slocker-lite/run/<container_name>-<pid>` (falling back to
|
||||
`$HOME/.local/state/...` when `XDG_STATE_HOME` is unset/empty — same
|
||||
resolution pattern as `config_file_path()` below, for state instead of
|
||||
@@ -450,6 +462,29 @@ Source layout (all under `src/`):
|
||||
a check and a later removal. Only files it actually removes are reported back
|
||||
(as `SessionInfo`s with `running=false`); still-locked (running) files are
|
||||
left untouched and not reported.
|
||||
- `persistent_netns.{h,cpp}` — generic, narrow infrastructure for keeping a
|
||||
network namespace alive with no process in it, the way `ip netns add` does;
|
||||
no `intern`/`extern` policy or bridge logic here (that's a later commit,
|
||||
`network_bridge.{h,cpp}`, per `docs/networking-design.md`'s commit
|
||||
sequence), and not yet wired into `-n/--network` at all. `persistent_netns_path()`
|
||||
resolves `xdg_state_dir() / "netns" / sanitize_for_filename(name)`
|
||||
(`pid_file.h`, see above). `persistent_netns_exists()` checks whether that
|
||||
path is actually a live bind-mounted namespace, not just a stale/never-
|
||||
mounted file: `stat()`s the path and its parent directory and compares
|
||||
`st_dev` — a genuine bind mount always has a different device number than
|
||||
its parent, the same "is this a mountpoint" technique used elsewhere. Never
|
||||
needs root itself (just `stat()`). `create_persistent_netns()` forks a
|
||||
child (never touches the caller's own network namespace — `unshare(2)`
|
||||
affects only the calling process) that `unshare(CLONE_NEWNET)`s its own
|
||||
fresh namespace, bind-mounts its `/proc/self/ns/net` onto the target path,
|
||||
then exits immediately — the bind mount itself is what keeps the namespace
|
||||
alive from then on, independent of the now-exited child, exactly `ip netns
|
||||
add`'s own technique. Requires `CAP_SYS_ADMIN` (root) for the bind mount,
|
||||
matching this feature's current root-only scope (see
|
||||
`docs/networking-design.md`) — best-effort like this project's other
|
||||
host-state primitives (session locks, cgroups): logs and returns `false` on
|
||||
any failure (already exists, fork/unshare/mount failure) rather than
|
||||
throwing. `remove_persistent_netns()` unmounts then removes the file.
|
||||
- `session_cgroup.{h,cpp}` — gives `--kill` (`kill_session.{h,cpp}`, see
|
||||
below) a reliable way to find every process a session ever started, however
|
||||
deeply forked/daemonized/reparented, by putting it in a dedicated cgroup v2
|
||||
|
||||
Reference in New Issue
Block a user