Skip to content

hisn 0.4.1

Choose a tag to compare

@github-actions github-actions released this 08 Nov 19:13
· 43 commits to main since this release
9b44908

hisn v0.4.1

0.4.1 makes run --detach return, completes the ls --json client view, removes the fixed 100 ms waits on session start and stop, and hardens the 0.4.0 seat model.

Highlights

  • hisn run --detach forks the supervisor, waits for readiness, prints <id> <name|-> ready and exits 0.
  • A failed start exits 125, with or without --detach.
  • Session readiness and hisn stop are event-driven. No fixed 100 ms poll remains on either path.
  • hisn ls --json now reports live clients and execs (hisn-ls/v2, additive).
  • hisn ls --prune gains --name-prefix and --dry-run.
  • Observer queries (ls, inspect, sessioninfo) no longer count as clients, so they cannot keep an idle session alive.

Added

  • hisn run --detach returns. The child calls setsid and writes stdio to <session-dir>/supervisor.log (mode 0600). The parent prints RISK lines before forking, because the child's stderr is not the caller's.
  • hisn ls --prune --name-prefix <prefix> prunes only stopped or crashed sessions whose name starts with <prefix>. An unnamed session never matches. Plain --prune is unchanged.
  • hisn ls --prune --dry-run lists the sessions that would be pruned and removes nothing.
  • hisn ls --json additions (hisn-ls/v2, same schema name):
    • clients: live control connections (uid, pid, label, label_source, connected_since).
    • execs: a live attach appears with attach: true.
    • services, rootfs, members, lease, and dir fields.
    • For a stopped or crashed session, clients is [] and CLIENTS is 0.
  • A sessioninfo control op. The supervisor uses it to supply the live client and exec view, which session.json on disk cannot.
  • hisn exec --client LABEL, the same flag as attach.
  • hisn run --env-file PATH. It reads KEY=VALUE lines (blank lines and # comments ignored; validated like --env). Use it for secret-shaped values so they stay out of /proc/<pid>/cmdline.
  • Leak tests L73 (ls --prune --name-prefix), L74 (ls --json clients) and L75 (run --detach returns).
  • Tests tests/latency.rs, tests/detach_pipes.rs and tests/observers.rs.

Changed

  • Readiness polls the control socket with a nonblocking connect and exponential backoff, 1 ms up to 20 ms. A stalled listener cannot block the waiter.
  • hisn stop waits on the supervisor's pidfd. Only the no-pidfd fallback polls, with bounded exponential backoff.
  • A stopped session keeps its name. The name is reusable once hisn ls --prune removes the directory and repairs the index.
  • Name reservation checks the index, so concurrent runs are race-free. release drops a name only if it still maps to the releasing session's id.
  • hisn stop on a stopped session prints already stopped. On a crashed one it prints crashed, no supervisor. Both exit 0.
  • hisn inspect redacts [env.set] values whose names match looks_secret.
  • touch_lease writes session.json only when the session has a lease.

Fixed

  • A failed run --detach start returned 1 instead of 125 (refused profile, missing risk_ack, home.persist refusal, service readiness timeout). It now exits with the named constant EXIT_START_FAILED (125) in every case. A running session's command status is unchanged.
  • A detached supervisor inherited the parent's stdout, stderr and ready-pipe write ends. A caller that held those pipes never saw EOF. Go's exec reported WaitDelay expired before I/O complete and treated a live session as dead. The child now closes every inherited fd except the ready pipe.
  • RISK lines never reached the caller under --detach. The parent now prints them before the fork.
  • Client records were keyed by file descriptor number. The kernel reuses a closed fd at once, so a later connection could replace a live record and a close could remove the wrong one. clients stayed at 1 after the connection closed and idle never fired. Connections now have a process-unique id, and the count and records live in one registry.
  • Observers and bare probes could register as clients and bump last_activity. Registration now happens only after a request is admitted. A refused request never registers. A disconnect bumps activity only for a counted connection.

Hardening

  • redact_env_set redacts [env.set] values by parsing the policy TOML.
  • The host environment is read with vars_os(). A non-UTF-8 variable no longer panics the supervisor.
  • Client connect and exec registration take lease_lock, the same lock the idle check holds. An exec admitted during the idle check is not stopped under it.
  • is_loopback_ns also rejects 0.0.0.0, ::, ::ffff:127.0.0.0/8 and ::ffff:0.0.0.0.
  • build_resolv_conf refuses a [net] nameservers override that is empty or loopback-only. Before, only hisn profile validate did.
  • Detached-child fd hygiene: all inherited fds of 3 and above are closed, and 0 to 2 are reopened from /dev/null until stdio moves to supervisor.log.
  • RISK lines are printed before the fork under --detach.

Compatibility

  • The ls --json schema name stays hisn-ls/v2. The changes are additive, and 0.4.0 fields are kept.
  • hisn ls --prune without the new flags behaves as in 0.4.0. run without --detach is unchanged.
  • If the sessioninfo request fails, ls shows empty clients, execs and services for that session. It does not fail.
  • 0.4.1-only: run --detach returning, --env-file, exec --client, --name-prefix, --dry-run, the sessioninfo op, and the live clients, execs, services and rootfs data in ls --json.
  • Not tested in the tree: a 0.4.1 client against a running 0.4.0 supervisor.

Known issue carried forward: ttyname() inside an attach still fails. The fix needs a protocol change and a host to verify it. See CHANGELOG and DESIGN section 20.1.

Verified

  • Unit tests and tests/leak/test_l8.py were run in the build sandbox, per the CHANGELOG.
  • The build sandbox blocks user namespaces. The leak suite and the --detach and attach host steps could not run there.
  • Latency is asserted in tests/latency.rs: a socket that appears after 8 ms must be observed ready in under 60 ms, an already-ready socket in under 30 ms, and a stop in under 80 ms. A fallback test asserts no fixed 100 ms sleep. The tree holds no trace output. The test prints the measured times, but no trace output is committed, so this note quotes no measured figure. The earlier CHANGELOG figures in microseconds were removed: they cannot describe the 8 ms case, which takes at least 8 ms by construction.
  • No leak suite result for 0.4.1 is committed. The last recorded one is 308 passed, 0 failed, 6 skipped, 1 weak, for 0.4.0. This note gives no 0.4.1 count. The same applies to two-host seat and audit archival runs: nothing in the tree records them for 0.4.1.
  • Host steps to run on rubin: cargo test --release, HISN_TEST_ROOTFS="$(hisn rootfs path wakil-dev)" make leaktest, hisn run --name t --detach, hisn ls --json, hisn ls --prune --name-prefix wakil-.