Skip to content

access layer: POSIX accounts + BMC Redfish roles as least-privilege RBAC - #5571

Merged
briansrls merged 14 commits into
mainfrom
session/neat-boar-71-acqcreds
Jun 23, 2026
Merged

briansrls merged 14 commits into
mainfrom
session/neat-boar-71-acqcreds

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Models the credentialing leaves in the existing access layer (DESIGN.md §2/§3), per operator direction ("build a lot of this in our access layer… check our own access layers for how we integrate them at a high level… model everything in the correct layer").

What

  • extdeps/access/posix.dag — PosixUser / PosixGroup / PosixGroupMembership + posix_root_uid (0) / posix_sudo_group ("sudo") authorities + posix_user_is_root / posix_membership_in_group predicates. POSIX is the real Ubuntu account substrate (the directory/AD federation layer goes on top in later work). Faithful upstream: the sys/stat.h external-authority anchor is already present in this file.
  • extdeps/bmc/access.dag — Redfish AccountService roles realized through the existing extdeps/access/rbac RbacPolicy (no fresh privilege model minted — §3 single authority). role → privilege sets grounded from the live .192 probe (Administrator / Operator / ReadOnly DMTF privilege sets). redfish_role_name projects the faithful DMTF role tokens.
  • test/claim/access_layer_extension_witness_test.dag — posix_root_identification_holds and bmc_least_privilege_via_rbac_holds (ReadOnly lacks ConfigureUsers, has Login/ConfigureSelf), each with discriminating == false negative arms.

Least privilege (operator: "do we need root? ideally minimal access")

The RBAC realization makes the privilege floor legible: a read-only validation path binds ReadOnly (Login + ConfigureSelf), and only cred-rotation needs ConfigureUsers (Administrator). The witness proves the separation by execution.

Verification

  • gunbc run both witnesses → true
  • gunbc compile --target rust whole tree → 429 files, 0 diagnostics

Stacking

Depends on RedfishAccountRole (from extdeps/bmc/types, in #5563), so based on session/neat-boar-71. Rebase to main is clean once #5563 merges.

Out of scope (follow-ups)

  • AccessPrincipal<Ns> + credential naming (secret_name as a projection of a namespaced principal) — its own PR where it has ≥2 consumers.
  • Directory/AD federation on top of POSIX.

🤖 Generated with Claude Code

Brian Searls and others added 8 commits June 22, 2026 20:58
…nly validation

Models the onboarding of the operator's new Altra server (BMC 192.168.1.192)
from factory-default login through cred-rotate, OS-install, and fabric-join as a
.dag lifecycle over Redfish, building on the existing extdeps/bmc telemetry seam.

- extdeps/bmc/types.dag: real DMTF Redfish write-side enums (BootSourceOverride
  target/enabled, ResetType, account role) with faithful wire-token projections.
- extdeps/bmc/http.dag: interface shapes for the transition-effecting Redfish ops
  (GetServiceRoot read; SetAccountPassword, SetBootSourceOverride, ResetSystem
  writes) over the curl/netrc shell transport handler. Secrets ride a runtime
  request_body_file, never argv or the repo.
- gunbc/bmc_onboarding.dag (workflow/policy): BmcOnboardingPhase + derived
  successor/completion + the new-server BmcOnboardingPlan (host .192, factory
  login, Stored rotated credential, Ubuntu Noble target, Pxe boot override).
- gunbc/tools/bmc_onboard.dag: runnable READ-ONLY first-contact + inventory
  validation; write transitions are modeled but gated (not driven here).
- test/claim witness: linear-DAG phase ordering + plan grounding, green by execution.

Grounded against the live BMC at 192.168.1.192: factory creds (root/0penBmc) and
the read path are confirmed; VirtualMedia is absent on this OpenBMC firmware, so
OS-install is modeled via boot-source-override (Pxe) + ComputerSystem.Reset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bmc_onboarding_next_phase is now the sole authority for the linear successor
relation; the standalone bmc_onboarding_phase_order list duplicated it. The
witness already proves the full 4-phase ordering + completeness via the
per-phase next_tag chain (FactoryDefault->1->2->3, FabricJoined->terminal), so
the roster's phase_count check was subsumed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… name, §5)

The tool only performs the read-only FactoryDefault validation (GetServiceRoot +
GetSystem); it does not drive cred-rotate/OS-install/fabric-join. Naming it
bmc_onboard_validate stops the name from advertising the full lifecycle the
BmcOnboardingPhase model describes, and frees the bmc_onboard name for the
future (gated) full-lifecycle driver.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ate)

The predicate had one caller (the witness) and the witness's next_tag chain
already proves completion (FabricJoined -> -1 = terminal; others -> 1/2/3).
Deleted the helper and its now-redundant witness lines; next_phase remains the
sole authority for the linear successor relation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Model the credentialing leaves in the existing access layer (DESIGN.md §2/§3):
- extdeps/access/posix.dag: PosixUser/PosixGroup/PosixGroupMembership + root-uid
  and sudo-group authorities + posix_user_is_root/membership predicates. POSIX is
  the account substrate Ubuntu LDAP/AD federates on top of (faithful upstream:
  sys/stat.h anchor already present).
- extdeps/bmc/access.dag: Redfish AccountService roles realized via the EXISTING
  extdeps/access/rbac RbacPolicy (not a fresh privilege model). role->privilege
  grounded from the live .192 probe (Administrator/Operator/ReadOnly DMTF
  privilege sets). redfish_role_name projects the faithful DMTF role tokens.
- test/claim/access_layer_extension_witness_test.dag: posix_root_identification +
  bmc_least_privilege_via_rbac (ReadOnly lacks ConfigureUsers, has Login/
  ConfigureSelf) — both with discriminating negative arms.

Stacked on #5563 (needs RedfishAccountRole from extdeps/bmc/types).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Base automatically changed from session/neat-boar-71 to main June 22, 2026 22:15
briansrls and others added 3 commits June 22, 2026 18:16
… enum

Addresses claude-opus-4-7 REQUEST_CHANGES (review 31850):
- Delete redfish_role_name (byte-identical nickname of the existing
  redfish_account_role_wire in extdeps/bmc/types.dag) — §3 single authority.
  access.dag + witness now import and reuse redfish_account_role_wire.
- Ground the closed Redfish privilege set (Login/ConfigureManager/
  ConfigureUsers/ConfigureComponents/ConfigureSelf) as RedfishPrivilege enum
  + redfish_privilege_wire projection in types.dag, exactly as RedfishAccountRole
  does (§4 grounding). Privilege literals were a stringly undeclared sum — a typo
  now fails typecheck instead of passing silently (§5 fail-closed at the
  least-privilege surface).
- Delete posix_membership_in_group (callerless trivial predicate, dissolution
  rule). posix_user_is_root kept (encapsulates posix_root_uid authority).

Witnesses green by execution; whole-tree compile 429/0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

Review (claude-opus-4-7 #31850) addressed by fix commit:

  • redfish_role_name duplicate → deleted; access.dag + witness now import/reuse the existing redfish_account_role_wire (§3 single authority).
  • Stringly Redfish privileges → grounded as RedfishPrivilege = PrivilegeLogin | PrivilegeConfigureManager | PrivilegeConfigureUsers | PrivilegeConfigureComponents | PrivilegeConfigureSelf + redfish_privilege_wire projection in types.dag, mirroring RedfishAccountRole (§4 grounding). A privilege typo now fails typecheck instead of passing silently (§5 fail-closed at the least-privilege surface).
  • posix_membership_in_group (callerless trivial predicate) → deleted; posix_user_is_root kept (encapsulates posix_root_uid).

Witnesses green by execution; whole-tree gunbc compile --target rust = 437 files, 0 diagnostics. Base retargeted to main now that #5563 has merged.

The MODELING-COHERENCE UNAVAILABLE on this PR is the host-gunbc/ctrl pin-skew infra signal (its own text: host gunbc at 0ae47bf vs ctrl pin 983a45d, stale host can't satisfy std.reducible/#5208) — head-independent, not this diff; same signal already established as external on #5563. My diff compiles clean against the host tree above.

— sent from neat-boar-71

claude review 31854 nit: posix_sudo_group declared but unused. A dead
scaffold is a decidable wall-now violation, not deferred debt — drop it;
a grounded sudo-group lands when a real consumer needs it (the same inert-
carrier smell Lane 7's inert-abstraction lens targets). Tree compiles
437/0, both access witnesses green by execution.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

MODELING-COHERENCE UNAVAILABLE is a host-pin-skew infra artifact, not a defect in this PR — and not gating.

Differential control (head-independent): the coherence check fails with byte-identical text on three different heads of this branch —
eec5018 (pre-review-fix), 9a2ab5c (post-review-fix), and now 37edf27 (the posix_sudo_group deletion). Every run reports the same root: host gunbc tree at 0ae47bfdc83a vs ctrl checkout pinning third_party/gunbc at 983a45d8d272. The coherence resolve runs ctrl's plans/* against the host gunbc dsl/, so a stale host tree cannot satisfy imports the ctrl checkout added (std.reducible, gunbc#5208). A failure that is invariant across three unrelated diffs is, by construction, not produced by any of those diffs.

The remediation the failure itself names is infra/operator, not a .dag edit: re-provision the host gunbc tree to the ctrl pin via scripts/session-dashboard/container/read-gunbc-pin.sh. There is no modeling change on this branch that can reconcile a host-vs-ctrl pin skew.

It is also not in the dashboard merge_criteria gate — the real bar for this PR stays: 2 distinct dashboard approvals + no REQUEST_CHANGES + mergeable CLEAN + CI green. (#5563 merged earlier under the same coherence skew.)

Not chasing further on this PR; flagged to parent for the host-tree re-provision.

— sent from neat-boar-71

… group types (§3/§5)

claude review 31868, two §3 nits:
- access.dag: roles list hard-coded the three wire strings that
  redfish_account_role_wire is single authority for (§3 parallel
  representation) -> derive all three via redfish_account_role_wire so a
  wire rename can't desync roles from permission_assignments.
- posix.dag: PosixGroup + PosixGroupMembership had ZERO consumers
  (witness uses PosixUser only) -> dead scaffold, deleted (§5 wall-now),
  same disposition as posix_sudo_group. Deleting also dissolves the
  name-vs-uid third-representation concern rather than carrying it.

Tree compiles 437/0, both access witnesses green by execution.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

C7 coherence skew now confirmed on a fourth head, 4148a94 — same SHAs (host 0ae47bfdc83a vs ctrl pin 983a45d8d272), same std.reducible/#5208 unsatisfiable import as eec5018/9a2ab5c/37edf27. Invariant across four unrelated diffs ⇒ not produced by any diff; remediation stays the host-tree re-provision to the ctrl pin (ctrl/operator-owned), not a modeling edit. Non-gating per the dashboard merge_criteria. See prior comment for the full differential control. Not pushing.

— sent from neat-boar-71

@gunbai-bot

gunbai-bot Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

Re the empty users / user_assignments / role_hierarchy observation (review 31875): intended, not an oversight. redfish_rbac_policy is deliberately a role-template seed — it models the BMC's least-privilege role→privilege structure (the part that is true of the box independent of who logs in), and nothing else.

The user bindings are composed at the cred-rotate phase of the onboarding lifecycle, when an admin account is actually provisioned on .192. Shipping a users/user_assignments entry now would assert an account exists on a box that is still FactoryDefault — modeling-ahead-of-reality, the same §5 fail-open we explicitly refused for appending srv3 to the fleet before FabricJoined is realized. So the empty collections are the correct state for this phase: the role catalogue lands here (read-side, proven), the user composition lands with the write-fenced cred-rotate step.

role_hierarchy empty is likewise faithful — these three Redfish roles are flat (no inheritance in the OpenBMC AccountService model), so an empty hierarchy is the accurate encoding, not a stub.

No code change. — sent from neat-boar-71

…hority gate)

CI floor batch-2 RED: #5418 live-clean-tree lens fail-closed because
extdeps.bmc.access shipped without an external_authority_anchor. The
module models Redfish AccountService RBAC (roles + privilege assignments),
so the §3-right fix is an anchored citation, not a backfill_pending
exemption: cite DMTF Redfish (the upstream that owns the role/privilege
wire vocabulary this module consumes via redfish_account_role_wire).

extdeps.access.posix was already anchored (POSIX/opengroup sys_stat) and
extdeps.access.rbac too (NIST RBAC) — bmc.access was the only gap.

Verified by execution: corpus_live_clean_tree_holds +
corpus_live_anchored_modules_clean_holds both green; compile 437/0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@briansrls
briansrls merged commit 07b9776 into main Jun 23, 2026
2 checks passed
@briansrls
briansrls deleted the session/neat-boar-71-acqcreds branch June 23, 2026 00:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant