fix(policy): survive atomic replacement of resolv.conf; report stale … - #1448
Merged
lukehinds merged 5 commits intoJul 24, 2026
Merged
Conversation
…file grants in nono why Landlock rules bind to the inode open at ruleset build time, not to the path. The system_read_linux_core grant for /etc/resolv.conf canonicalizes to /run/systemd/resolve/stub-resolv.conf, which systemd-resolved rewrites via write-temp-then-rename whenever resolver config changes — leaving the rule pointing at the old inode and every subsequent read failing EACCES for the rest of the session. Grant the /run/systemd/resolve directory as well: directory rules cover children created after ruleset application, so they survive the replacement. The failure was also undiagnosable: nono why reasons from the path spec and reported ALLOWED while the kernel denied. Record each file grant's (dev, ino) in the capability state file at sandbox start, and have path queries re-stat the granted path: a stale file grant now reports a stale_file_grant denial naming the inode change (or, when a directory grant still covers the path, stays ALLOWED with a warning about the stale grant). Co-Authored-By: Fable 5 (nono) <noreply@anthropic.com> Signed-off-by: Felix Konstantin Maurer <felix@wave.com>
… unreadable The stale-file-grant detection made every `nono why` query load NONO_CAP_FILE through the strict loader, which exits on any failure. Inside a sandbox the state file is frequently not covered by a read grant, so plain `nono why --path ...` — which never touched the state before — started failing with "Error reading capability state file" instead of answering. Load the state leniently for non-self queries: any failure degrades to no staleness hints, restoring the previous behavior. `--self` queries, whose whole answer comes from the state file, keep the strict fail-loud loader. Co-Authored-By: Fable 5 (nono) <noreply@anthropic.com> Signed-off-by: Felix Konstantin Maurer <felix@wave.com>
…agnostic Co-Authored-By: Fable 5 (nono) <noreply@anthropic.com> Signed-off-by: Felix Konstantin Maurer <felix@wave.com>
A device number without an inode (or vice versa) identifies nothing, so the previous (Option<u64>, Option<u64>) return implied states that don't exist. Keep the split into two scalar keys at the serialization boundary only. Co-Authored-By: Fable 5 (nono) <noreply@anthropic.com> Signed-off-by: Felix Konstantin Maurer <felix@wave.com>
Contributor
PR Review SummarySize
Affected crates
Blast radius — ModerateThis PR touches: source code,configuration / policy files Updated automatically on each push to this PR. |
lukehinds
self-requested a review
July 24, 2026 13:06
Contributor
|
Thanks @maufl , I am good with the /run/systemd/resolve grant, there is nothing private there , the alternative (broken DNS on every resolver-config change) is much worse. approved |
lukehinds
approved these changes
Jul 24, 2026
1 task
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.
Linked Issue
References #986 — a partial fix by design (diagnostics plus one built-in policy instance, per the disclosure comment on the issue), so intentionally not
Closes.Summary
Landlock rules bind to the inode open at ruleset build, so a file-level grant goes stale when the file is atomically replaced (write-temp +
rename(2)). This PR:data/policy.json):system_read_linux_coreadditionally grants the directory/run/systemd/resolve(read). Its/etc/resolv.conffile grant canonicalizes tostub-resolv.conf, which systemd-resolved replaces on any resolver-config change, breaking DNS for the rest of the session. Directory rules cover children created later, so they survive; non-systemd systems skip the missing path.(dev, ino)per file grant at sandbox start (Linux only; optional fields, compatible both ways).nono whyre-stats the granted path and reportsDENIED / stale_file_grantwith grant-time vs. current inode and a restart hint — or ALLOWED with a warning when a directory grant still covers the path. Non---selfqueries load the state leniently (unreadable state ⇒ no hints, never an error);--selfstays strict.Deliberately not done: Landlock has no path-based rules, so the only way a file grant can survive replacement is granting its parent directory. We did not apply that automatically to all file grants — it silently widens each to its whole directory, a posture change maintainers should make explicitly, per grant (as done here). The issue's repro still reproduces for user/profile file grants, now diagnosable. Other rename-prone builtin file grants (
/etc/ld.so.cache,/etc/hosts, …) remain — their parent/etcis too broad./run/systemd/resolveis systemd-specific in-tree policy; per NOGENT.md a nono package may be the longer-term home — guidance welcome.Agent Disclosure
AGENTS.md,NOGENT.md,sandbox/linux.rs(PathFd inode binding),capability.rs(grant-time canonicalization),policy.rs,sandbox_state.rs,why_runtime.rs,query_ext.rs, the profile authoring guide, and this template. All code newly written; nothing reused or adapted.Test Plan
whyverdicts, pass-through, inode recording, legacy-state compatibility, lenient-loader failure modes.make check,make test,cargo audit,make lint-aliases,make lint-docspass locally (remaining failures are pre-existing PTY/AF_UNIX tests needing permissions the sandboxed dev env lacks, all in untouched files).--selfwhystill answers with an unreadable state file.Checklist
CHANGELOG.mdif needed (generated via git-cliff from conventional commits)Agent Compliance Check (Required for AI/Automated PRs)