sudo: no tty present and no askpass 에러 30초 진단·복구 (cron·CI·Ansible)
새벽 3시, 배포 알림 대신 잡 실패 알림이 울립니다. cron 로그를 열어보니 딱 한 줄.
sudo: no tty present and no askpass program specified30초 요약부터 드리겠습니다. 터미널(tty)이 없는 비대화형 환경에서 sudo가 비밀번호를 물어보려다 입력받을 곳이 없어 실패한 겁니다. 사람이 붙어 있는 SSH 셸에서는 sudo가 프롬프트를 띄우고 비밀번호를 받지만, cron·systemd·CI·Ansible에는 그 프롬프트를 받아줄 tty가 없습니다. 그래서 sudo는 "비밀번호를 물어봐야 하는데 물어볼 창구(tty)도 없고, 대신 물어봐줄 askpass 프로그램도 지정 안 됐다"며 즉시 죽습니다.
원리를 알면 복구는 셋 중 하나입니다. ① 애초에 비밀번호를 안 묻게 한다(NOPASSWD), ② tty를 요구하는 설정(requiretty)을 끈다, ③ 비밀번호를 대신 넘겨줄 askpass를 지정한다. 온콜 상황을 가정해 진단표부터 복붙 복구, 그리고 보안 함정 회피까지 순서대로 갑니다.
30초 진단표: 어디서 터졌나
어느 환경에서 실행됐는지만 파악하면 1차 복구안이 바로 나옵니다.
| 실행 환경 | tty 유무 | 대표 증상·로그 위치 | 1차 복구안 |
|---|---|---|---|
| cron 잡 | 없음 | /var/mail/$USER 또는 journalctl -u cron, grep CRON /var/log/syslog | sudoers NOPASSWD |
| systemd unit | 없음 | journalctl -u myapp.service -e | NOPASSWD 또는 unit을 root로 실행 |
| GitHub Actions / GitLab CI | 없음 | CI job log의 실패 스텝 | 러너는 보통 passwordless, 자체 러너면 NOPASSWD |
Ansible become: true | 기본 없음 | MODULE FAILURE / sudo: a password is required | become_password 또는 requiretty 제거 |
su -c "sudo ..." 래핑 | 없음 | 스크립트 stderr | 래핑 제거 + NOPASSWD |
핵심은 모든 비대화형 환경에 tty가 없다는 점입니다. cron·systemd는 데몬이 자식 프로세스를 tty 없이 띄우고, CI 러너 역시 헤드리스입니다. GitHub Actions의 기본 러너는 runner 사용자에게 passwordless sudo를 이미 부여하기 때문에 이 에러를 잘 안 만나지만, 자체 호스팅 러너(self-hosted runner) 에서는 직접 설정해야 합니다.
복붙 복구 3안
(A) sudoers에 NOPASSWD 정확히 추가 — 가장 안전한 정석
왜 되나: 비밀번호 자체를 요구하지 않으니 sudo가 tty를 찾을 이유가 없어집니다.
/etc/sudoers를 직접 건드리지 말고 반드시 visudo로 드롭인 파일을 만드세요. 문법 오류가 나면 sudo 자체가 잠겨 서버 접근이 막힐 수 있습니다.
sudo visudo -f /etc/sudoers.d/deploy파일 내용은 사용자·명령을 한정해서 최소한으로:
# /etc/sudoers.d/deploy
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp권한과 문법 검사는 필수입니다:
sudo chmod 0440 /etc/sudoers.d/deploy
sudo visudo -c # syntax OK 확인
sudo -l -U deploy # deploy에게 실제 부여된 권한 검증0440이 아니면 sudo가 파일을 무시하거나 경고를 냅니다.
(B) Defaults requiretty 제거 및 확인법
왜 되나: requiretty는 "sudo는 반드시 tty에서만 실행하라"는 강제 규칙입니다. 비대화형 환경엔 tty가 없으니 이 규칙이 켜져 있으면 무조건 막힙니다.
CentOS/RHEL 계열 구버전에서 기본 활성인 경우가 있습니다. 확인 후 주석 처리:
# /etc/sudoers (visudo로 편집)
-Defaults requiretty
+# Defaults requirettysudo grep -R requiretty /etc/sudoers /etc/sudoers.d참고로 sudo·OpenSSH 최신 버전 흐름에서는 requiretty가 기본 비활성이라, 최근 배포판을 쓴다면 (A)만으로 해결되는 경우가 대부분입니다.
(C) SUDO_ASKPASS + sudo -A — 비밀번호를 대신 넘기기
왜 되나: tty 대신 "비밀번호를 표준출력으로 뱉는 헬퍼 프로그램"을 sudo에게 알려주는 방식입니다.
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⚠️ 경고: 이 방식은 비밀번호가 환경변수·프로세스 목록·스크립트 파일에 노출될 위험이 큽니다. 평문 저장은 피하고, 가능하면 Vault·CI Secret에서 주입하세요. 가능하면 (A) NOPASSWD를 쓰고, (C)는 최후의 수단으로만 씁니다.
Ansible에서의 처리
become: true가 내부적으로 sudo를 호출하다 같은 에러를 냅니다. 두 가지 팁:
# 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: restarted비밀번호가 꼭 필요하면 --ask-become-pass나 Vault로 ansible_become_password를 안전하게 주입합니다.
보안 함정: NOPASSWD를 최소권한으로 묶기
급하다고 이렇게 넣는 순간 사고가 시작됩니다.
deploy ALL=(ALL) NOPASSWD:ALL # ❌ 절대 금지이건 deploy 계정이 탈취되면 곧바로 전체 루트 권한 탈취로 이어집니다. 최소권한 원칙(PoLP) 을 지키는 세 가지 규칙:
- 절대경로로 명령 화이트리스트:
/usr/bin/systemctl처럼 전체 경로 지정.systemctl만 쓰면 PATH 조작으로 우회될 수 있습니다. - 인자까지 고정:
NOPASSWD: /usr/bin/systemctl restart myapp처럼 대상 서비스까지 못박기. sudoers.d로 서비스별 분리: 파일 하나에 몰지 말고deploy,backup처럼 역할별로 나눠 관리·감사.
부여 결과는 항상 sudo -l -U deploy로 실제 확인하세요.
실무 경험담 한 줄: 예전에 온콜에서 급한 마음에 NOPASSWD:ALL을 넣고 "나중에 조이자" 했다가, 그 임시 설정이 6개월 뒤 보안 감사에서 지적된 적이 있습니다. 처음부터 명령 한정으로 넣는 게 결국 제일 빠릅니다. 요즘은 sudoers도 Ansible·Terraform으로 IaC 버전관리하고, 감사 로깅(/var/log/sudo.log)을 함께 켜두는 게 표준이 되고 있습니다.
결론: 복구 체크리스트 5줄
- 어느 환경에서 터졌는지 진단표로 확인 (cron/systemd/CI/Ansible)
sudo visudo -f /etc/sudoers.d/<role>로 명령 한정 NOPASSWD 추가chmod 0440+visudo -c로 권한·문법 검증sudo -l -U <user>로 실제 부여 권한 확인- requiretty 잔존 여부
grep, 있으면 주석 처리
재발 방지: sudoers 설정을 IaC로 버전관리하고, CI 파이프라인에 visudo -c 문법 검사 게이트를 넣으세요. 잘못된 sudoers 한 줄이 서버 전체를 잠그는 사고를 미리 막아줍니다.
자주 묻는 질문 (FAQ)
Q. GitHub Actions에서는 왜 이 에러가 안 뜨나요?
A. GitHub 호스팅 러너는 runner 사용자에게 이미 passwordless sudo를 부여합니다. 다만 self-hosted 러너에서는 직접 /etc/sudoers.d/에 NOPASSWD를 설정해야 이 에러를 피할 수 있습니다.
Q. requiretty를 지우면 보안이 약해지지 않나요? A. requiretty는 tty 강제일 뿐 인증 강도를 높이는 옵션이 아닙니다. 오히려 자동화 환경을 막는 부작용이 큽니다. 보안은 requiretty가 아니라 명령 화이트리스트·최소권한·감사 로깅으로 확보하세요.
Q. NOPASSWD와 SUDO_ASKPASS 중 뭘 써야 하나요? A. 가능하면 NOPASSWD를 명령 한정으로 쓰세요. 비밀번호가 어디에도 저장되지 않아 더 안전합니다. SUDO_ASKPASS는 비밀번호가 프로세스·환경변수에 노출될 위험이 있어 최후의 수단으로만 권장합니다.
이 글은 AI 에이전트가 자료 조사와 1차 초안 작성을 담당하고, 사람 편집자가 사실관계·출처·톤과 맥락을 검토한 뒤 발행했습니다. 환경(OS·버전)에 따라 결과가 다를 수 있으니 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.
댓글
첫 번째 댓글을 남겨보세요.