Claude Code Sudo Lease & Read-Only Allowlist
Why: Urgent debugging sessions with Claude Code kept stalling at sudo — the assistant would propose a fix, I would approve it, and then have to copy-paste a multi-line echo … | sudo tee command back into a terminal myself because Claude can't type a password. We wanted sudo commands to flow through Claude's normal approval prompt with as little friction as a file read, while keeping me in the loop for every individual mutating action.
The setup has two halves: a permanent read-only NOPASSWD allowlist for diagnostic commands that can't mutate state, and a session-scoped credential lease driven by sudo -v for mutating commands. Together they remove the copy-paste friction without giving Claude blanket passwordless root.
1. Read-only allowlist: /etc/sudoers.d/claude-readonly
This file grants plex NOPASSWD access to a narrow set of read-only diagnostic binaries. The worst-case outcome of any of these commands is "Claude sees something it shouldn't"; none of them can change system state.
journalctl— all log reading (covers ~90% of debugging cases on modern systemd).systemctl status / is-active / is-enabled / list-units / list-unit-files / show— service introspection only; neverstart/stop/restart.ufw status(and variants) — firewall introspection.ss,lsof— sockets and open files.tail,less,grep,findagainst/var/log/*(andfindagainst/etc) — log and config inspection.
Installed via:
sudo visudo -cf /tmp/claude-readonly && sudo install -o root -g root -m 0440 /tmp/claude-readonly /etc/sudoers.d/claude-readonly
The visudo -c parse-check is non-negotiable — a syntactically broken sudoers file can lock you out of sudo entirely.
2. Session lease via timestamp_type=global
The existing /etc/sudoers.d/claude-audit-tty file (despite the now-misleading name — it was created during the 2026-05 audit) already sets Defaults:plex timestamp_type=global. This makes sudo's credential cache shared across every process owned by the plex user instead of per-tty. Practical consequence: I can type sudo -v in any terminal tab, enter my password once, and for the next ~15 min Claude's bash subshell inherits the warm credential and can run any approved sudo command without a password prompt — including from a Claude session that was already open before I typed sudo -v.
The file is now considered permanent, not "temporary audit residue" as the cheat sheet previously implied. It is the load-bearing piece that makes the whole lease pattern work.
3. Belt-and-suspenders deny list in Claude settings
Added a permissions.deny block to ~/.claude/settings.json blocking sudo invocations that bypass per-command visibility: sudo bash, sudo sh, sudo -s, sudo -i, sudo su, sudo visudo, sudo tee /etc/sudoers…, and sudo rm /etc/sudoers…. Even with a warm credential lease, Claude can't accidentally spawn a root shell or rewrite sudoers itself — those require me to run them manually.
4. Verification
After install, the smoke test was:
sudo -k # clear any cached creds
sudo journalctl -n 5 # should run with no prompt
sudo systemctl restart something # should still prompt for password
Both behaved as expected: the read-only command passed silently, the mutating command demanded a password until sudo -v warmed the lease.
5. What this is and isn't
This is not passwordless root for Claude. It is two narrowly-scoped capabilities:
- Read-only: permanent, narrow, and audited by the contents of
claude-readonly. - Mutating: opt-in per session (I have to type
sudo -v), bounded by sudo's 15-min timeout, and each command still goes through Claude Code's normal approval prompt — so I see and approve everysystemctl restart nginxbefore it runs.
If I close my laptop or wait 15 minutes, the lease evaporates on its own. There is no file to forget to remove.
6. Replicating this on another machine
To get the same setup on the dev machine (or any other host), drop the following prompt into a Claude Code session running on that machine. The prompt is self-contained — it does not assume the assistant has read this journal entry.
Please replicate the "Claude Code sudo lease" setup from skyhouse on this machine. The goal is to make sudo commands flow through your normal approval prompt without me having to copy-paste them, while keeping me in the loop for every individual mutating action.
The setup has three pieces:
1. Create /etc/sudoers.d/claude-readonly granting NOPASSWD to the current user for read-only diagnostic commands only:
- /usr/bin/journalctl
- /usr/bin/systemctl status *, is-active *, is-enabled *, list-units, list-unit-files, show *
- /usr/sbin/ufw status (and variants)
- /usr/bin/ss, /usr/bin/lsof
- /usr/bin/tail /var/log/*, /usr/bin/less /var/log/*, /usr/bin/grep * /var/log/*
- /usr/bin/find /var/log -type f *, /usr/bin/find /etc -type f *
Before writing, run `which` on each binary to confirm absolute paths on this distro — they may differ. Stage the file in /tmp first and install with `sudo visudo -cf /tmp/claude-readonly && sudo install -o root -g root -m 0440 /tmp/claude-readonly /etc/sudoers.d/claude-readonly`. The visudo parse-check is mandatory.
2. Create /etc/sudoers.d/claude-session containing exactly:
Defaults:$USER timestamp_type=global
(Replace $USER with my actual username.) Install with the same visudo + install pattern. This makes sudo's credential cache shared across all my processes so `sudo -v` in any terminal warms the lease for your bash subshell.
3. Add a permissions.deny block to ~/.claude/settings.json blocking shell-spawning sudo forms:
sudo bash*, sudo sh*, sudo -s*, sudo -i*, sudo su*, sudo visudo*, sudo tee /etc/sudoers*, sudo rm /etc/sudoers*
Merge this into any existing settings.json; do not overwrite the file.
4. Write a feedback memory so future Claude sessions on this machine know the lease exists. The memory directory is `~/.claude/projects//memory/` where `` matches the current working directory (run `pwd` to see it; the slug replaces `/` with `-` and drops the leading slash, e.g. `/home/me` → `-home-me`). Create `feedback_sudo_lease.md` in that directory with frontmatter `type: feedback` and body explaining: (a) read-only diagnostics run NOPASSWD via /etc/sudoers.d/claude-readonly, (b) mutating sudo needs a warm lease — ask the user to run `sudo -v` rather than hand-running commands yourself, (c) shell-spawning sudo forms are denied in settings.json. Then add a one-line pointer to that file in `MEMORY.md` in the same directory (create MEMORY.md if it does not exist). This is what makes the setup reproducibly *useful* — without it, future sessions will still try to hand-run sudo commands instead of asking for a lease.
Then verify with: `sudo -k && sudo journalctl -n 5` (should pass silently) and `sudo systemctl is-system-running` (should also pass silently since that's read-only). Try `sudo touch /etc/foo` — it should prompt for password. Then `sudo -v`, enter password, retry — should now pass without prompting.
Walk me through each step before executing it, and pause for confirmation before installing either sudoers file. Do not skip the visudo -c parse-check — a broken sudoers file can lock me out of sudo.
Note for the dev machine: the file is named claude-session on the dev machine (clearer) rather than claude-audit-tty (legacy name on skyhouse from the 2026-05 audit).
Net effect: urgent debugging with Claude no longer stalls on sudo copy-paste friction, while every mutating action still passes through me — either via password entry or via the per-command Claude approval prompt during a warm lease.
← Back to Admin Hub