When managing sensitive data like passwords with Ansible's file modification modules, the default logging behavior can create security vulnerabilities. The issue manifests when using:
ansible localhost -m blockinfile -a 'dest=/tmp/config create=yes block="DB_PASSWORD = {{db_pass}}"' -e 'db_pass=S3cr3tP@ss'
This results in full credential exposure in syslog:
ansible-blockinfile: Invoked with [...] block=DB_PASSWORD = S3cr3tP@ss
1. Using no_log Parameter
The most effective solution is the no_log: true
parameter in playbooks:
- name: Secure password injection
blockinfile:
path: /etc/app.conf
block: |
[database]
password = {{ vaulted_db_password }}
marker: "# {mark} ANSIBLE MANAGED BLOCK"
no_log: true
2. Combining with Ansible Vault
For maximum security, combine with encrypted variables:
# In vars/main.yml
db_creds: !vault |
$ANSIBLE_VAULT;1.1;AES256
323864656538633165343939...
# In tasks/main.yml
- lineinfile:
path: /etc/secrets.conf
line: "API_KEY={{ db_creds.api_key }}"
no_log: true
Template Files with Restricted Permissions
Instead of inline modification:
- template:
src: secrets.conf.j2
dest: /etc/secrets.conf
mode: 0600
owner: root
group: root
no_log: true
Using the copy Module with Content Filtering
- name: Secure credential deployment
copy:
content: "{{ lookup('template', 'credentials.j2') }}"
dest: /secure/credentials.cfg
mode: 0400
no_log: true
When troubleshooting is needed:
- debug:
msg: "Configuration was updated"
when: config_change.changed
no_log: true
- Configure rsyslog to filter sensitive patterns
- Implement separate logging for Ansible operations
- Use syslog-ng's message content filtering
When working with sensitive data in Ansible (particularly credentials), the default logging behavior can inadvertently expose passwords in system logs. This occurs because Ansible's modules log their parameters by default, which becomes problematic when using modules like lineinfile
or blockinfile
to manage credential files.
Here's a typical scenario where credentials get logged:
ansible localhost -m blockinfile -a 'dest=/tmp/ansible_password_leak create=yes block="Password = {{password}}"' -e 'password=secret'
The syslog entry contains all module parameters, including our sensitive data:
ansible-blockinfile: Invoked with ... block=Password = secret ...
1. Using no_log Parameter
The most straightforward solution is using the no_log: true
parameter in your playbook:
- name: Secure credential file modification
blockinfile:
path: /etc/application/credentials.conf
block: |
username: appuser
password: {{ db_password }}
marker: "# {mark} ANSIBLE MANAGED BLOCK"
no_log: true
2. Environment Variable Control
For system-wide control, set these environment variables:
export ANSIBLE_NO_LOG=True
export ANSIBLE_LOG_PATH=/dev/null
3. Using Ansible Vault
For a more secure approach, encrypt sensitive data with Ansible Vault:
ansible-vault encrypt_string 'secretpassword' --name 'vaulted_password'
Then reference it in your playbook:
- name: Modify config with vaulted password
lineinfile:
path: /etc/config.cfg
line: "DB_PASSWORD={{ vaulted_password }}"
regexp: "^DB_PASSWORD="
no_log: true
- Always use
no_log
when handling sensitive data - Consider implementing a custom callback plugin to filter sensitive data
- For enterprise deployments, evaluate the
log_filter
plugin in ansible.cfg - Regularly audit your logs for accidental credential exposure
For fine-grained control, create a custom callback plugin (callback_plugins/sanitize_log.py
):
from ansible.plugins.callback.default import CallbackModule
class CallbackModule(CallbackModule):
def v2_runner_on_ok(self, result):
if 'no_log' in result._task_fields and result._task_fields['no_log']:
result._result = {'censored': 'the output has been hidden...'}
super(CallbackModule, self).v2_runner_on_ok(result)
Then configure ansible.cfg:
[defaults]
callback_whitelist = sanitize_log