Skip to content

fix(core): resolve the account home through the host resolver - #95

Merged
FeathBow merged 2 commits into
mainfrom
fix/account-lookup-nss
Sep 6, 2026
Merged

FeathBow merged 2 commits into
mainfrom
fix/account-lookup-nss

Conversation

@FeathBow

@FeathBow FeathBow commented Sep 6, 2026

Copy link
Copy Markdown
Owner

The defect

Every mutating command derives its activation anchor from the account home. A static musl build cannot see an account held in LDAP, SSSD, or winbind, because the name service switch reaches those backends by loading plugins and a static image has no way to load them. degu's stated deliverable is no-root HPC login nodes, and HPC sites resolve accounts exactly that way, so init, clean, undo, and trash purge did not work there at all. Read-only scan and quota were unaffected; they never touch authority.

Observed on a login node with an SSSD account and on a workstation with a winbind account. Same source, same machine, same account — only the libc differs:

build degu doctor
static musl uncertain, every diagnostic field null, remediation "retry"
glibc missing, both anchor paths shown, points at degu init --initial

Two things compound there. The remediation could never succeed, because retrying a lookup that cannot see the account is not a route to seeing it. And musl reports the miss with a success status and a null result rather than an errno, so "no such user" and "cannot see this user" are indistinguishable at the libc call — the caller cannot even tell which one it hit.

The fix

A miss now puts the same question to the host's own resolver, in a separate process.

This is not a weaker source and not a guess. getent consults exactly the name services the host is configured to use, which is what a dynamically linked build would have consulted in-process, so the answer carries the same authority and can name no path the account database does not already name. Ambient HOME, XDG, cwd, configuration, and CLI input still cannot redirect either side of the protocol — the property provision/account.rs documents is preserved rather than traded away, and the public wording in docs/safety.md and docs/installation.md remains accurate as written.

Absence of the resolver is itself an answer: a host with no getent has no name service switch either, so there the local file's lookup was already complete and a miss really is a missing account. The ladder cannot lie.

The invocation runs from an absolute path with a fixed argument list, an emptied environment, a neutral working directory and a closed stdin, is bounded in both time and output, and accepts a record only when its UID column is the UID that was asked for. Nothing resolves through PATH and no shell is involved. Everything downstream is unchanged: trusted ancestry, no-follow preflight, ownership, exact modes, ACL absence, backend certification, strong identity, and binding re-validation all still run on held descriptors after the string arrives.

On a host where the local file does answer — every glibc build, and any host with local accounts — nothing changes and no process is spawned.

Diagnosis

The three account outcomes stop reading alike in doctor:

  • an account no source can name is settled rather than uncertain, so it reports as unsupported and says what to check;
  • a resolver that exists but cannot be consulted stays uncertain and retryable, with its own reason;
  • only a real errno keeps the previous generic lookup reading.

This uses the existing unsupported status, so the JSON schema is unchanged.

Placement

The exec bounds live in a shared module rather than beside their caller. The Lustre quota probe already runs the same shape against lfs and is the intended next caller, so this adds one bounded-invocation implementation rather than a third ad-hoc one.

Verification

Built for x86_64-unknown-linux-musl and run on a host whose account is absent from /etc/passwd and served by winbind, so the delegate is the only path that can resolve it. doctor moves from uncertain with every field null to missing with both anchor paths and a usable next step. The artifact remains static-pie linked, so the release contract's static assertion still holds.

cargo clippy --workspace --all-targets --locked -- -D warnings is clean on Linux and macOS. 25 tests were added: record parsing including a non-UTF-8 home, a UID that does not match the request, an ambiguous multi-record answer and a wrong column count; delegate behaviour against a stand-in resolver, covering a normal record, the arguments the resolver receives, a not-found exit, a missing first path, no resolver at all, and a flood; and the invocation bounds themselves, covering environment isolation, output overflow, timeout kill, and a flood past the pipe buffer.

@coderabbitai

coderabbitai Bot commented Sep 6, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: 6736b595-c746-4113-8c73-884a2321c685


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@FeathBow
FeathBow merged commit e92c504 into main Sep 6, 2026
11 checks passed
@FeathBow
FeathBow deleted the fix/account-lookup-nss branch September 6, 2026 00:36
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