feat(privops): authorize-before-dispatch for per-user-ownable verbs#208
Merged
Conversation
The privops libnv transport carries a real peer uid (getpeereid) but
ran verbs purely on request arguments — the uid only fed the audit
tail. A hostile operator in the privops group could name another
operator's ZFS dataset or RCTL umbrella (see docs/trust-model.md).
Adds lib/privops_authz_pure.{h,cpp}: a pure authorize-before-dispatch
decision for the verbs that carry a robust, request-borne ownership
signal, checked against the caller's PerUserEnvPure::composeForUid env:
- attach_zfs / detach_zfs : dataset must lie within the caller's
per-user ZFS prefix <master>/<uid>;
- set_loginclass_rctl / clear_loginclass_rctl : loginclass must be
the caller's crate-<uid> umbrella.
A foreign target is denied 403 before the handler runs (fail closed).
Wired into dispatchPrivOpFromMap (libnv only); the HTTP/admin path
(uid==0) is unchanged and stays host-wide by design. Host-global verbs
(iface/pf/ipfw/nat/epair) cannot be pool-scoped and pass through;
jid-scoped verbs (set_rctl, signal_jail, create_jail, ...) carry no
request-borne owner and are deferred pending a jid->owner registry —
documented in trust-model.{md,uk.md} as the remaining gap.
Per-user config registered at startup via setPerUserAuthzConfig
(mirrors the setUmbrellaConfig pattern). New unit test
privops_authz_pure_test (12 cases) covers prefix ownership boundaries,
loginclass match, fail-closed empties, and the ungated verb classes.
https://claude.ai/code/session_01X6t6tzVypHye5bDGLxzmZK
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.
Follow-up to #207 (trust-model docs, already merged). Closes the cleanly-ownable part of the privops isolation gap that doc identified.
What
The privops libnv transport carries a real peer uid (
getpeereid) but ran verbs purely on request arguments — the uid only fed the audit tail. A hostile operator in the privops group could name another operator's ZFS dataset or RCTL umbrella.New pure module
lib/privops_authz_pure.{h,cpp}: an authorize-before-dispatch decision, checked against the caller'sPerUserEnvPure::composeForUidenv, for the verbs that carry a robust, request-borne ownership signal: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>.A foreign target is denied
403before the handler runs (fail closed). Wired intodispatchPrivOpFromMaponly; the HTTP/admin path (uid==0) is unchanged and host-wide by design. Per-user config registered at startup viasetPerUserAuthzConfig(mirrors the existingsetUmbrellaConfigpattern). The trust-model docs are updated to mark this part of the gap closed.Deliberately deferred
set_rctl,signal_jail,create_jail,set_jail_cpuset, devfs,query_jail_rctl) — no request-borne owner; need a jid→owner registry (record operator uid atcreate_jail, check on each jid-keyed verb). Natural follow-up PR if wanted.Verification
make test-unit(kyua + libatf) locally on Linux: 1329/1329 passed, incl. 12 newprivops_authz_pure_testcases (prefix-ownership boundaries incl. slash-anchoring, loginclass match, fail-closed empties, ungated verb classes).privops_handlers.cpp,main.cpp) compiles on FreeBSD only (libnv); uses portable facilities + the existing startup-global pattern. Please confirm FreeBSD CI.Heads-up for reviewers
admin(it does here), or eventually carry pool/uid too?https://claude.ai/code/session_01X6t6tzVypHye5bDGLxzmZK
Generated by Claude Code