sudo: no tty present and no askpass — 30-Second Diagnosis and Recovery (cron, CI, Ansible)
It's 3 a.m. Instead of a deploy notification, a job-failure alert goes off. You open the cron log and find a single line.
sudo: no tty present and no askpass program specifiedHere's the 30-second summary. In a non-interactive environment with no terminal (tty), sudo tried to prompt for a password, had nowhere to take input, and failed. On an SSH shell with a human attached, sudo can display a prompt and accept a password. cron, systemd, CI, and Ansible have no tty to receive that prompt. So sudo dies immediately with "I need to ask for a password, but there's no tty to ask on, and no askpass program was specified either."
Once you know the principle, recovery is one of three options: ① Don't ask for a password at all (NOPASSWD), ② Turn off the setting that requires a tty (requiretty), ③ Specify an askpass helper that supplies the password. Assuming you're on-call, we'll go in order: diagnosis table, copy-paste recovery, then how to avoid the security traps.
30-second diagnosis table: Where did it blow up?
Once you know which environment it ran in, the first-line fix is obvious.
| Execution environment | tty present? | Typical symptoms / log location | First-line fix |
|---|---|---|---|
| cron job | none | /var/mail/$USER or journalctl -u cron, grep CRON /var/log/syslog | sudoers NOPASSWD |
| systemd unit | none | journalctl -u myapp.service -e | NOPASSWD, or run the unit as root |
| GitHub Actions / GitLab CI | none | Failed step in the CI job log | Hosted runners are usually passwordless; self-hosted runners need NOPASSWD |
Ansible become: true | none by default | MODULE FAILURE / sudo: a password is required | become_password, or remove requiretty |
Wrapped in su -c "sudo ..." | none | Script stderr | Drop the wrap + NOPASSWD |
The key point is that every non-interactive environment has no tty. cron and systemd spawn child processes without a tty, and CI runners are headless. GitHub Actions' default runners already grant passwordless sudo to the runner user, so you rarely hit this error there—but on a self-hosted runner you have to configure it yourself.
Three copy-paste recovery options
(A) Add NOPASSWD to sudoers correctly — the safest, canonical fix
Why it works: If sudo never asks for a password, it has no reason to look for a tty.
Don't edit /etc/sudoers directly; always use visudo to create a drop-in file. A syntax error can lock sudo itself and cut off server access.
sudo visudo -f /etc/sudoers.d/deployKeep the file contents scoped to a specific user and command, as tightly as possible:
# /etc/sudoers.d/deploy
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart myappPermission and syntax checks are mandatory:
sudo chmod 0440 /etc/sudoers.d/deploy
sudo visudo -c # syntax OK 확인
sudo -l -U deploy # deploy에게 실제 부여된 권한 검증If the mode isn't 0440, sudo will ignore the file or emit a warning.
(B) Remove Defaults requiretty and how to verify
Why it works: requiretty is a hard rule: "sudo may only run from a tty." Non-interactive environments have no tty, so if this rule is on, you're blocked every time.
It's sometimes enabled by default on older CentOS/RHEL. Check and comment it out:
# /etc/sudoers (visudo로 편집)
-Defaults requiretty
+# Defaults requirettysudo grep -R requiretty /etc/sudoers /etc/sudoers.dNote that in recent sudo and OpenSSH, requiretty is off by default, so on a modern distro (A) alone usually solves it.
(C) SUDO_ASKPASS + sudo -A — supply the password via a helper
Why it works: Instead of a tty, you tell sudo about a helper program that prints the password to stdout.
cat > /usr/local/bin/askpass.sh <<'EOF'
#!/bin/sh
echo "$MY_SECRET"
EOF
chmod 700 /usr/local/bin/askpass.sh
export SUDO_ASKPASS=/usr/local/bin/askpass.sh
sudo -A systemctl restart myapp⚠️ Warning: This approach can leak the password into environment variables, the process list, and the script file. Avoid storing it in plaintext; inject it from Vault or a CI secret if you can. Prefer (A) NOPASSWD, and treat (C) as a last resort.
Handling this in Ansible
become: true internally calls sudo and hits the same error. Two tips:
# ansible.cfg
[ssh_connection]
pipelining = True # sudo에 requiretty가 남아있으면 이게 오히려 충돌할 수 있으니
# requiretty 제거 후 사용 권장# playbook — 대상 노드에 미리 NOPASSWD를 깔아두는 게 정석
- hosts: web
become: true
tasks:
- name: restart app
ansible.builtin.systemd:
name: myapp
state: restartedIf you really need a password, inject ansible_become_password safely via --ask-become-pass or Vault.
Security trap: Bind NOPASSWD to least privilege
If you panic and drop this in, the incident starts:
deploy ALL=(ALL) NOPASSWD:ALL # ❌ 절대 금지If the deploy account is compromised, that's an instant full root takeover. Three rules to keep the principle of least privilege (PoLP):
- Whitelist commands with absolute paths: specify the full path like
/usr/bin/systemctl. Justsystemctlcan be bypassed via PATH manipulation. - Pin the arguments too: lock it down to the target service, e.g.
NOPASSWD: /usr/bin/systemctl restart myapp. - Split by service in
sudoers.d: don't dump everything in one file; split by role (deploy,backup) for management and audit.
Always verify the actual grants with sudo -l -U deploy.
One war story: Years ago on-call, I slapped in NOPASSWD:ALL thinking "we'll tighten it later." That temporary setting got flagged in a security audit six months later. Scoping the command from the start is actually the fastest path. These days the standard is to version-control sudoers with Ansible/Terraform as IaC, and turn on audit logging (/var/log/sudo.log) at the same time.
Conclusion: 5-line recovery checklist
- Confirm where it blew up using the diagnosis table (cron/systemd/CI/Ansible)
- Add command-scoped NOPASSWD with
sudo visudo -f /etc/sudoers.d/<role> - Verify permissions and syntax with
chmod 0440+visudo -c - Confirm actual grants with
sudo -l -U <user> grepfor leftover requiretty; comment it out if present
Prevent recurrence: Version-control sudoers as IaC, and add a visudo -c syntax-check gate to the CI pipeline. That stops a single bad sudoers line from locking the whole server.
FAQ
Q. Why doesn't this error show up on GitHub Actions?
A. GitHub-hosted runners already grant passwordless sudo to the runner user. On a self-hosted runner, you have to set NOPASSWD in /etc/sudoers.d/ yourself to avoid this error.
Q. Doesn't removing requiretty weaken security? A. requiretty only forces a tty; it does not strengthen authentication. Its main effect is blocking automation. Get your security from a command whitelist, least privilege, and audit logging—not from requiretty.
Q. Should I use NOPASSWD or SUDO_ASKPASS? A. Prefer command-scoped NOPASSWD. The password is never stored anywhere, so it's safer. SUDO_ASKPASS can leak the password into processes and environment variables; treat it as a last resort.
Nodelog는 모든 콘텐츠의 내용과 출처를 공개 전에 검토합니다. 환경(OS·버전)에 따라 결과가 달라질 수 있는 기술 정보는 공식 문서와 함께 확인하며, 검토 기준과 정정 원칙은 편집 정책에서 안내합니다. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.
Comments
Be the first to comment.