Trust model docs + privops authorize-before-dispatch (1.1.11)#207
Merged
Conversation
The per-name list missed test binaries whose sources live on other branches, leaving stale ELF artifacts untracked. A tests/unit/*_test glob covers all current and future test binaries; *_test.cpp sources don't match and stay tracked. https://claude.ai/code/session_01X6t6tzVypHye5bDGLxzmZK
Adds docs/trust-model.{md,uk.md} stating where cross-tenant isolation
is enforced and where it is not, accurate to 1.1.11 (rootless model):
- The privileged execution plane is no longer setuid crate(1) (removed
in 1.0.0); it relocated into crated's privops surface (HTTP admin-only
+ libnv group-gated socket). That plane performs root ops with no
per-resource pool/ownership check before dispatch, so it is a single
trust domain — equivalent to the old setuid crate(1).
- Per-tenant isolation is enforced only on the pooled control plane:
dedicated control sockets (getpeereid gid + pool ACL), bearer tokens
(scope + pool ACL), and ws-console. Local Unix access to the main API
remains a single trust domain.
- Notes that per-user namespacing (uid-derived paths/datasets/CIDRs)
separates honest operators but is not an adversarial boundary on the
privops path, since handlers take request args at face value.
- Restates the authorize-before-dispatch guardrail as the open gap for
making privops safe for mutually-distrusting operators, plus
deployment guidance.
Cross-links security-command-paths.{md,uk.md} to the trust model and
clarifies that command-path hardening now protects crated (the root
process that execs host tools).
https://claude.ai/code/session_01X6t6tzVypHye5bDGLxzmZK
09b3b1c to
ccf8ed0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two related changes on top of
main(1.1.11):1. Trust-model docs (
docs/trust-model.{md,uk.md})Document where cross-tenant isolation is enforced and where it is not, accurate to the rootless model: the privileged plane is no longer setuid
crate(1)(removed in 1.0.0) — it relocated intocrated's privops surface (HTTP admin-only + libnv group-gated socket). Per-tenant isolation is enforced on the pooled control plane (control sockets / bearer tokens / ws-console); local Unix access to the main API stays a single trust domain. Per-user namespacing separates honest operators but isn't an adversarial boundary on the privops path. Cross-linkssecurity-command-paths.{md,uk.md}.2. privops authorize-before-dispatch (
feat)Closes the cleanly-ownable part of the gap the doc identified. New pure module
lib/privops_authz_pure.{h,cpp}+privops_authz_pure_test(12 cases). On the libnv transport (real peer uid viagetpeereid), when rootless per-user is on, a verb is denied403before dispatch if it names another operator's resource:attach_zfs/detach_zfs—datasetmust lie within the caller's per-user ZFS prefix<master>/<uid>;set_loginclass_rctl/clear_loginclass_rctl—loginclassmust be the caller'scrate-<uid>.Wired into
dispatchPrivOpFromMaponly; the HTTP/admin path (uid==0) is unchanged and host-wide by design. Per-user config registered at startup viasetPerUserAuthzConfig(mirrorssetUmbrellaConfig).Deliberately deferred (documented in the doc as the remaining gap):
set_rctl,signal_jail,create_jail,set_jail_cpuset, devfs, …) — no request-borne owner; need a jid→owner registry (record operator uid atcreate_jail, check on each jid-keyed verb). Not in this PR.Verification
make test-unit(kyua + libatf) locally on Linux: 1329/1329 passed, incl. the 12 newprivops_authz_pure_testcases.privops_handlers.cpp,main.cpp) compiles on FreeBSD only (libnv); it uses portable facilities and the existingsetUmbrellaConfigpattern. Please confirm FreeBSD CI is green.Heads-up for reviewers
admin(it does here), or should it eventually carry pool/uid too?Test plan
https://claude.ai/code/session_01X6t6tzVypHye5bDGLxzmZK