日志里出现明文密码,是Ansible落地时比较常见的告警。我拿到这类问题,一般不会立刻给所有任务加no_log,而是先在本地复现,把泄露点定位清楚。
先确认现象和泄露范围
Ansible默认会把命令行参数、模块参数以及部分变量值写入日志,尤其是使用-vvv调试模式时,敏感信息可能直接暴露。常见的风险点在于copy、template、uri等模块的src、dest、content、body字段,以及自定义变量中携带的密码、Token。判断是否泄露的方法很简单:在本地环境配置一个相同版本的Ansible,执行一次含敏感字段的任务,分别以-v、-vv、-vvv级别运行,然后grep日志中的敏感字符串。若能在输出中看到,说明存在泄露风险,需要做限制处理。
复现时要注意,如果ansible.cfg没有配置log_path,默认日志只输出到标准输出或syslog,未必有文件可grep。建议先设置一个临时日志文件,再用不同级别跑一遍。还有一点:如果本地环境可以grep到,但生产环境用了AWX或Tower,那么日志位置和格式不同,需要找对应作业的日志入口,不能只看ansible.log。先确认现象是为了不把误报当真实泄露。有些密码是任务内加密后打印的,不是明文,需要结合上下文看。
容易误判的地方
第一个容易误判的是,以为只要把log_path权限设为0600,敏感信息就安全了。实际上任务的标准输出和回调插件,比如mail或junit,同样可能把变量打印到其他位置。第二个误判是,认为no_log加在一个任务上就万事大吉;如果后面的任务用set_fact重新读取同一个变量,再通过debug输出,日志里依然会出现。第三个误判是,用了ansible-vault加密变量文件,就把vault密码写在脚本里,结果脚本本身被日志记录。
还有一个容易踩的坑:no_log: true会屏蔽任务名和整个输出,如果任务失败,看到的报错也被吞掉。所以只在确实包含敏感字段的任务上开启,不要全playbook一把梭。
建议的处理顺序
限制日志敏感信息的第一道防线是设置ansible.cfg中的log_path,并确保日志文件权限为0600,同时使用systemd的journald时设置PrivateTmp。但更根本的做法是避免在playbook中直接写明文密码,而是使用ansible-vault加密文件,然后在运行时通过--vault-id提供密码。对于必须传入模块的参数,可以在任务执行前用set_fact将真实值赋给临时变量,并在执行后立即用set_fact清空,或者使用no_log: true对该任务关闭日志输出。注意no_log只对当前任务生效,如果后续任务有同样参数,需要逐个添加。
我建议的处理顺序是:先把vault用起来,再收紧log_path权限,最后逐任务加no_log。因为no_log会隐藏报错,越晚加越容易排查。如果环境里已经有明文密码写在playbook,先改为vault加密,再考虑其他限制。配置log_path时,目录要存在且可写,否则ansible会退回到syslog。以下是基础配置:
[defaults]
log_path = /var/log/ansible.log
对于确实需要传入模块的敏感参数,no_log的写法如下:
- name: 写API凭据
uri:
url: https://example.com/api
body: '{{ credential }}'
method: POST
no_log: true
这里要说明,no_log只屏蔽当前任务的输出,如果后续任务引用变量并输出,还需要在后续任务上也加。所以更好的方式是用vault加密变量,而不是依赖no_log。还需要注意,set_fact清空变量并不能完全避免泄露,因为Ansible在运行时会缓存已注册的变量,后续任务仍然可能拿到旧值。所以临时方案只能配合no_log使用,不能作为唯一屏障。
验证是否仍然泄露
检查是否产生敏感日志,不一定要靠肉眼查看。可以在playbook运行后,通过shell管道过滤日志文件,例如grep -E '(password|token|secret|api_key)' /var/log/ansible.log。更可靠的方法是在ansible.cfg中开启callback_whitelist = junit,然后解析生成的JUnit XML文件,查找system-out节点中的输出。如果使用AWX或Tower,则需要在作业设置中启用'启用事实缓存',并在凭据中引用外部密钥,这样任务日志中就不会出现明文。若有历史日志文件,使用strings命令提取其中的可疑字符串,也能快速定位泄露点。
验证时不要只看默认级别,把-vvv和JUnit XML都跑一遍。重点检查system-out以及log_path文件,而且要在任务失败场景下也做一次,因为很多泄露往往在失败时出现。上面的grep命令只覆盖了password、token等常见词,自定义变量名如果叫passwd或api_key_2,需要单独加进去。可以把这些词写进一个检查脚本,在每次批量运行后自动执行。
回滚与后续维护
改动之后,如果发现调试困难或任务异常,回滚最快的方式是恢复ansible.cfg和playbook的备份。因为no_log是逐任务加的,建议在改动前把当前版本的playbook用git或svn做好标记,并搜索所有no_log出现的行,列一个清单。
后续维护上,可以把敏感字段统一到一个变量文件,比如group_vars/all.yml,用ansible-vault加密。CI流程里可以加一道检查,对每次运行后的日志做敏感词匹配。注意日志保留周期,定期清理历史日志。如果团队对no_log行为还不熟悉,保守做法是把敏感任务单独抽成一个role,并把role的日志级别设为0。这样能减少泄露面,但调试确实不方便,需要平衡。还需要注意Ansible版本升级,不同版本对no_log和vault的处理可能有细微差异,升级后要重新跑一遍日志泄露检查,别默认旧行为会一直保留。