Ubuntu24.04下Ansible嵌套sudo执行失败的原因及解决方案咨询
Ansible跨版本sudo嵌套调用失败问题排查与解决
问题复现
涉及文件
child.py(要求不能用sudo直接执行):
# child.py # This program must NOT been executed with sudo import subprocess subprocess.run(['sudo', 'apt-get', 'update'], check=True)
- Ansible角色任务文件
playbooks/roles/debug_role/tasks/main.yml:
- name: Debug sudo in child process command: >- sudo -E -H -u {{ ansible_user }} \ /bin/bash -lc "python3 child.py" become: yes
- Ansible剧本
playbooks/playbook.yml:
- hosts: all roles: [debug_role]
执行差异
- Ubuntu 20.04 + Ansible core 2.12.10:执行以下命令后任务成功,
apt-get update正常在目标机器运行:
ansible-playbook -i inventory.yml -u ubuntu --ask-become-pass playbooks/playbook.yml
- Ubuntu 24.04 + Ansible core 2.17.8:执行同一命令,输入正确become密码后仍报错:
stderr: 'sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper' stderr_lines: <omitted> stdout: '' stdout_lines: <omitted>
原因分析
问题核心是Ansible版本对sudo执行环境的处理变化:
- 旧版Ansible(2.12)在
become: yes的root环境中,切换回普通用户时,默认保留了sudo的免密上下文,或者对非交互式环境的sudo嵌套调用限制较松; - 新版Ansible(2.17)严格遵循sudo的安全规范:Ansible任务默认运行在非交互式终端环境,当普通用户在这个环境下再次调用sudo时,没有TTY终端用于输入密码,直接触发报错。
Ubuntu 24.04默认的sudo配置更严格,进一步凸显了这个差异,但本质是Ansible新版本对执行环境的管控更规范。
解决办法(满足child.py不被sudo直接执行的前提)
方案1:给子进程sudo传递密码
通过环境变量把Ansible的become密码传递给child.py,让sudo从标准输入读取密码:
- 修改Ansible任务,传递密码并开启日志隐藏:
- name: Debug sudo in child process command: >- sudo -E -H -u {{ ansible_user }} \ /bin/bash -lc "export SUDO_PASS='{{ ansible_become_pass }}'; python3 child.py" become: yes no_log: true # 必须添加,防止密码泄露到日志
- 修改
child.py,使用sudo -S选项读取密码:
# child.py # This program must NOT been executed with sudo import subprocess import os sudo_pass = os.environ.get('SUDO_PASS', '').encode('utf-8') # 用-S让sudo从标准输入读密码,同时重定向输出避免干扰 subprocess.run( ['sudo', '-S', 'apt-get', 'update'], input=sudo_pass, check=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE )
方案2:配置目标用户sudo免密(针对特定命令)
在目标机器上配置sudoers,允许普通用户无需密码执行apt-get update:
添加Ansible任务配置sudo规则(需谨慎操作,避免破坏系统安全):
- name: Allow ansible user to run apt-get update without password lineinfile: path: /etc/sudoers.d/ansible_apt_update line: "{{ ansible_user }} ALL=(ALL) NOPASSWD: /usr/bin/apt-get update" state: present mode: 0440 validate: '/usr/sbin/visudo -cf %s' # 验证sudoers配置正确性 become: yes
配置完成后,原child.py和Ansible任务无需修改即可正常执行。
方案3:用runuser替代sudo切换用户
既然Ansible已经处于root环境,使用runuser切换到普通用户,它比sudo -u更适合root环境下的用户切换,且能保留足够上下文让sudo免密执行:
修改Ansible任务为:
- name: Debug sudo in child process command: >- runuser -l {{ ansible_user }} -c "python3 child.py" become: yes
注意:此方案依赖目标用户的sudo配置允许免密执行对应命令,或Ansible的become上下文传递了相关权限。
内容的提问来源于stack exchange,提问作者furushchev
相关产品推荐
相关产品推荐

