From 88f84cefee6b3595987f859e906dfc344719ac1a Mon Sep 17 00:00:00 2001 From: Rafael Guterres Jeffman Date: Thu, 17 Sep 2020 18:08:14 -0300 Subject: [PATCH] Bypass Ansible filtering on data returned by the module. Due to Ansible filtering out values in the output that might be match values in sensible attributes that have `no_log` set, if a module need to return data to the controller, it cannot rely on `ansible_module.exit_json` if there is a chance that a partial match may occur. See: https://github.com/ansible/ansible/issues/71789 The change provided here uses the same implementation that is used on Ansible's `AnsibleModule.exit_json`, without the data filtering layer, so every attribute with be printed and, therefore, logged by Ansible. This is needed for the Vault module, as we need to return values that are explicit requested by the user and that might, at least partially, match the values in attributes with `no_log` set. Tests that reproduced the issue, and show it was fixed were provided for all Vault types. --- .../module_utils/ansible_freeipa_module.py | 22 +++++++++++++++++++ plugins/modules/ipavault.py | 7 ++++-- tests/vault/test_vault_asymmetric.yml | 17 ++++++++++++++ tests/vault/test_vault_standard.yml | 16 ++++++++++++++ tests/vault/test_vault_symmetric.yml | 18 +++++++++++++++ 5 files changed, 78 insertions(+), 2 deletions(-) diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py index 30302b4f..43f96eb6 100644 --- a/plugins/module_utils/ansible_freeipa_module.py +++ b/plugins/module_utils/ansible_freeipa_module.py @@ -22,6 +22,7 @@ # along with this program. If not, see . +import sys import os import uuid import tempfile @@ -44,6 +45,7 @@ from ipaplatform.paths import paths from ipalib.krb_utils import get_credentials_if_valid from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_text +from ansible.module_utils.common.text.converters import jsonify try: from ipalib.x509 import Encoding @@ -388,6 +390,26 @@ def is_ipv6_addr(ipaddr): return True +def exit_raw_json(module, **kwargs): + """ + Print the raw parameters in JSON format, without masking. + + Due to Ansible filtering out values in the output that match values + in variables which has `no_log` set, if a module need to return user + defined dato to the controller, it cannot rely on + AnsibleModule.exit_json, as there is a chance that a partial match may + occur, masking the data returned. + + This method is a replacement for AnsibleModule.exit_json. It has + nearly the same implementation as exit_json, but does not filter + data. Beware that this data will be logged by Ansible, and if it + contains sensible data, it will be appear in the logs. + """ + module.do_cleanup_files() + print(jsonify(kwargs)) + sys.exit(0) + + class AnsibleFreeIPAParams(Mapping): def __init__(self, ansible_module): self.mapping = ansible_module.params diff --git a/plugins/modules/ipavault.py b/plugins/modules/ipavault.py index c0717979..f1d68256 100644 --- a/plugins/modules/ipavault.py +++ b/plugins/modules/ipavault.py @@ -319,7 +319,7 @@ from base64 import b64decode from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ temp_kdestroy, valid_creds, api_connect, api_command, \ - gen_add_del_lists, compare_args_ipa, module_params_get + gen_add_del_lists, compare_args_ipa, module_params_get, exit_raw_json from ipalib.errors import EmptyModlist @@ -964,7 +964,10 @@ def main(): temp_kdestroy(ccache_dir, ccache_name) # Done - ansible_module.exit_json(changed=changed, **exit_args) + + # exit_raw_json is a replacement for ansible_module.exit_json that + # does not mask the output. + exit_raw_json(ansible_module, changed=changed, **exit_args) if __name__ == "__main__": diff --git a/tests/vault/test_vault_asymmetric.yml b/tests/vault/test_vault_asymmetric.yml index 1e675a04..f229b6bb 100644 --- a/tests/vault/test_vault_asymmetric.yml +++ b/tests/vault/test_vault_asymmetric.yml @@ -27,6 +27,23 @@ register: result failed_when: result.changed + - name: Archive data to asymmetric vault, matching `no_log` field. + ipavault: + ipaadmin_password: SomeADMINpassword + name: asymvault + vault_data: SomeADMINpassword + register: result + failed_when: not result.changed + + - name: Retrieve data from asymmetric vault. + ipavault: + ipaadmin_password: SomeADMINpassword + name: asymvault + private_key: "{{ lookup('file', 'private.pem') | b64encode }}" + state: retrieved + register: result + failed_when: result.vault.data != 'SomeADMINpassword' or result.changed + - name: Archive data to asymmetric vault ipavault: ipaadmin_password: SomeADMINpassword diff --git a/tests/vault/test_vault_standard.yml b/tests/vault/test_vault_standard.yml index 4a9d9904..aa41eb1c 100644 --- a/tests/vault/test_vault_standard.yml +++ b/tests/vault/test_vault_standard.yml @@ -25,6 +25,22 @@ register: result failed_when: result.changed + - name: Archive data to standard vault, matching `no_log` field. + ipavault: + ipaadmin_password: SomeADMINpassword + name: stdvault + vault_data: SomeADMINpassword + register: result + failed_when: not result.changed + + - name: Retrieve data from standard vault. + ipavault: + ipaadmin_password: SomeADMINpassword + name: stdvault + state: retrieved + register: result + failed_when: result.vault.data != 'SomeADMINpassword' or result.changed + - name: Archive data to standard vault ipavault: ipaadmin_password: SomeADMINpassword diff --git a/tests/vault/test_vault_symmetric.yml b/tests/vault/test_vault_symmetric.yml index 31b6d7dd..a07afec4 100644 --- a/tests/vault/test_vault_symmetric.yml +++ b/tests/vault/test_vault_symmetric.yml @@ -27,6 +27,24 @@ register: result failed_when: result.changed + - name: Archive data to symmetric vault, matching `no_log` field. + ipavault: + ipaadmin_password: SomeADMINpassword + name: symvault + vault_data: SomeADMINpassword + password: SomeVAULTpassword + register: result + failed_when: not result.changed + + - name: Retrieve data from symmetric vault. + ipavault: + ipaadmin_password: SomeADMINpassword + name: symvault + password: SomeVAULTpassword + state: retrieved + register: result + failed_when: result.vault.data != 'SomeADMINpassword' or result.changed + - name: Archive data to symmetric vault ipavault: ipaadmin_password: SomeADMINpassword