Granting Sudo or Write Access to Non-Interactive AI Agents on Linux

When you let an AI coding agent (Codex, Claude Code, or any CLI agent) operate on a remote Linux server over SSH, you eventually hit a wall the moment it tries to run sudo:

sudo: a password is required

The agent is not “broken.” It is simply running in a non-interactive shell, with no TTY attached, so sudo has nowhere to read the password from. Even if you know the password, you cannot type it into a remote process the agent spawned.

This post documents two practical ways to fix this. They solve the same symptom from very different angles.


The Root Cause

Non-interactive shell + sudo asking for password = deadlock
agent ──ssh──> remote bash (no tty) ──> sudo ──> "Password:"
nobody can answer

There are three theoretical escape routes:

  1. Make the operation not need sudo (file-level permissions / ACL)
  2. Make sudo stop asking for a password (NOPASSWD in sudoers)
  3. Force a pseudo-TTY and pipe the password (works locally, fragile remotely)

Approach 3 is rarely worth the trouble on a remote machine. Below are the two that actually scale.


Approach A — NOPASSWD in sudoers

Tell sudo to skip the password prompt for a specific user. The agent still calls sudo, but it goes through without authentication friction.

When to use

  • The agent legitimately needs root-level operations across the system
  • The target paths are spread across multiple root-owned locations
  • You are comfortable granting (a scoped or full) passwordless root

Setup

Always edit sudoers through visudo — it validates syntax before saving. Direct vim /etc/sudoers* is dangerous: one typo can lock the entire sudo subsystem.

# Use visudo with vim as the editor
sudo EDITOR=vim visudo -f /etc/sudoers.d/your-agent

Full passwordless root (simplest, but broadest):

your-user ALL=(ALL) NOPASSWD: ALL

Scoped to specific commands (safer):

Cmnd_Alias TARGET_OPS = \
/bin/cp * /opt/your-app/target-dir/*, \
/bin/mv * /opt/your-app/target-dir/*, \
/bin/mkdir /opt/your-app/target-dir/*, \
/bin/rm /opt/your-app/target-dir/*
your-user ALL=(ALL) NOPASSWD: TARGET_OPS

Fix permissions (visudo usually does this automatically):

sudo chmod 440 /etc/sudoers.d/your-agent
sudo chown root:root /etc/sudoers.d/your-agent

Verification

sudo su your-user
sudo -n cp /tmp/src /tmp/dst
OutputMeaning
Silent successNOPASSWD is active
sudo: a password is requiredConfig not picked up — check filename, syntax, permissions
cp: cannot stat '...'sudo already passed; this is a cp error, not a permission issue

Filename rules for /etc/sudoers.d/

RuleDetail
Allowed charactersletters, digits, -, _
Forbidden. and ~ — sudo silently ignores files containing these
File mode0440, owner root:root
Load orderalphabetical; later files override earlier ones

Trade-offs

  • ✅ Simple, well-understood, standard for CI / agents
  • ✅ Works for any command, including ones that need real root (package installs, service restarts)
  • ⚠️ NOPASSWD: ALL effectively gives that account unrestricted root
  • ⚠️ Hard to express “any operation on this directory” — sudoers matches on commands, not on paths as a primary concept
  • ⚠️ Shell redirection (>) and pipes are handled by the shell, not sudo, so sudo echo x > file doesn’t work the way you’d expect

Approach B — POSIX ACL (setfacl)

Step back and ask the more useful question: does the agent actually need sudo, or does it just need write access to one directory?

If the answer is the latter, the cleanest solution doesn’t touch sudo at all. You give the user filesystem-level permission to the directory and the agent runs every command without sudo.

When to use

  • The agent only needs to write to a specific directory tree
  • You want to avoid granting any form of sudo
  • You want shell redirection, sed -i, git, rsync, tar, etc. to all “just work”

Setup

TARGET=/opt/your-app/target-dir
# Grant rwx on existing files and directories
sudo setfacl -R -m u:your-user:rwx "$TARGET"
# Default ACL so newly created files inherit the permission
sudo setfacl -R -d -m u:your-user:rwx "$TARGET"

The two commands are both required. Without the default ACL, files created inside $TARGET later will revert to the original ownership and the agent will lose write access to them.

Verification

getfacl /opt/your-app/target-dir

You should see entries like:

user::rwx
user:your-user:rwx
default:user:your-user:rwx

Now the agent can do whatever it wants inside that tree, with no sudo prefix:

cp src.md /opt/your-app/target-dir/
sed -i 's/old/new/' /opt/your-app/target-dir/*.md
echo "data" > /opt/your-app/target-dir/output.log
rm /opt/your-app/target-dir/stale.tmp
mkdir /opt/your-app/target-dir/subdir

Trade-offs

  • ✅ Native semantics — “user X owns write rights to directory Y” is exactly what ACL expresses
  • ✅ No sudoers changes, no security audit on /etc/sudoers.d/
  • ✅ Directory ownership stays root, respecting FHS conventions for /opt/
  • ✅ Redirection, pipes, and arbitrary tools all work
  • ⚠️ A package upgrade that replaces files inside the target may strip ACLs from the new files
  • ⚠️ Doesn’t help if the agent legitimately needs root for non-file operations (systemd, networking, etc.)

Side-by-Side

DimensionNOPASSWD sudoersPOSIX ACL
Conceptual axisAuthorizes commandsAuthorizes files/directories
Agent needs sudo prefixYesNo
Scope of trustBroad (or carefully enumerated)Tight (one directory tree)
Supports shell redirectionNo (shell handles it before sudo)Yes
Risk if account compromisedPotentially full rootWrite access to one directory
Best forSystem-wide tasks, package management, service controlWriting into a specific app/data directory
Setup complexityLow–mediumLow
MaintenanceGrows as commands growOne-time

A Quick Decision Tree

Does the agent need root for anything beyond writing one directory?
├─ Yes → NOPASSWD in /etc/sudoers.d/
│ (prefer scoped command list over `ALL` if you can)
└─ No → POSIX ACL with setfacl
(no sudo involved, no sudoers risk)

A Note on Local vs Remote Agents

You may have noticed that the same agent running locally can happily prompt you for a sudo password mid-task, while the same agent over SSH cannot. That is not a bug, it is a property of the channel:

  • Locally, the agent shares stdin with your terminal. When sudo writes Password:, the agent passes it to your screen, you type, the keystrokes go back into the child process. The TTY chain is intact.
  • Remotely, the agent’s SSH invocation typically runs in capture-stdout mode without an interactive TTY. The password prompt appears on the remote side, but there is no live keyboard wired to it.

That is the whole reason both approaches above are framed around “make the password go away” rather than “answer the password prompt.”


Conclusion

Two clean options, with different philosophies:

  • NOPASSWD is the right answer when the agent genuinely needs root and you want the standard SRE-style solution.
  • ACL is the right answer when the agent only needs to write somewhere, and you want the smallest, most surgical permission change.

For most “let my AI agent write into this one project directory” cases, ACL is the better starting point. Reach for NOPASSWD when the work genuinely crosses the root boundary.

留下评论

通过 WordPress.com 设计一个这样的站点
从这里开始