How can I reduce the verbosity of certain Ansible tasks to not leak passwords into syslog?

Sometimes I would like to use Ansible's lineinfile or blockinfile modules to write a password into some configuration file. If I do so, the whole line or block, password included, ends up in my syslog.

As I don't consider syslog to be a secure place to have my passwords stored in, how can I tell Ansible not to leak my password into syslog? I hope there is a way to do this, otherwise I would consider this to be a big security problem in Ansible.

You can reproduce it for example with this ad-hoc command:

ansible localhost -m blockinfile -a 'dest=/tmp/ansible_password_leak create=yes block="Password = {{password}}"' -e 'password=secret'

Here is what ends up in syslog:

ansible-blockinfile: Invoked with directory_mode=None force=None remote_src=None insertafter=None owner=None follow=False marker=# {mark} ANSIBLE MANAGED BLOCK group=None insertbefore=None create=True setype=None content=None serole=None state=present dest=/tmp/ansible_password_leak selevel=None regexp=None validate=None src=None seuser=None delimiter=None mode=None backup=False block=Password = secret

For the example I used Ansible 2.0.0.2 from the official Ansible Ubuntu PPA on a Debian "Jessie" 8 system.


Solution 1:

The no_log attribute hides data in syslog. It can be applied to a single task

- name: secret task
  shell: /usr/bin/do_something --value={{ secret_value }}
  no_log: True

or the playbook:

- hosts: all
  no_log: True

Debugging is not really possible when activated so it is recommended to use it only for single tasks. This feature is available since version 1.5 of Ansible. As stated in the release announcement for the 1.5 release:

Tasks also can now take a "no_log=True" option to prevent sensitive tasks from hitting syslog. (Parameters that looked like passwords were already filtered)

passwords should be filtered in most cases.

Solution 2:

I developped a callback plugin to hide passwords for default outputs, it parse ouput dictionary for key that contains password, for each of them, it replace value by ********.

Create a file named protect_data.py in folder ./plugins/callback add add this code :

from ansible.plugins.callback.default import CallbackModule as CallbackModule_default
import os, collections

class CallbackModule(CallbackModule_default):
    CALLBACK_VERSION = 2.0
    CALLBACK_TYPE = 'stdout'
    CALLBACK_NAME = 'protect_data'

    def __init__(self, display=None):
        super(CallbackModule, self).__init__(display)

    def hide_password(self, result):
        ret = {}
        for key, value in result.iteritems():
            if isinstance(value, collections.Mapping):
                ret[key] = self.hide_password(value)
            else:
                if "password" in key:
                    ret[key] = "********"
                else:
                    ret[key] = value
        return ret

    def _dump_results(self, result, indent=None, sort_keys=True, keep_invocation=False):
        return super(CallbackModule, self)._dump_results(self.hide_password(result), indent, sort_keys, keep_invocation)

In file ansible.cfg:

  • uncomment line with stdout_callback and set this plugin name a value (stdout_callback=protect_data)
  • uncomment line with callback_plugins and set value ./plugins/callback

Output is only modified for this plugin, if you use another plugin to display output (logentries, ...), you have to do the same with it