copy - redact 'content' from invocation in check mode (#71033)
* sanitize copy module invocation secrets in check mode
This commit is contained in:
parent
9850915bd6
commit
991714b9d1
4 changed files with 98 additions and 2 deletions
|
@ -0,0 +1,7 @@
|
||||||
|
security_fixes:
|
||||||
|
- >
|
||||||
|
**security issue** - copy - Redact the value of the no_log 'content'
|
||||||
|
parameter in the result's invocation.module_args in check mode.
|
||||||
|
Previously when used with check mode and with '-vvv', the module
|
||||||
|
would not censor the content if a change would be made to the
|
||||||
|
destination path. (CVE-2020-14332)
|
|
@ -209,6 +209,8 @@ class ActionModule(ActionBase):
|
||||||
# NOTE: adding invocation arguments here needs to be kept in sync with
|
# NOTE: adding invocation arguments here needs to be kept in sync with
|
||||||
# any no_log specified in the argument_spec in the module.
|
# any no_log specified in the argument_spec in the module.
|
||||||
# This is not automatic.
|
# This is not automatic.
|
||||||
|
# NOTE: do not add to this. This should be made a generic function for action plugins.
|
||||||
|
# This should also use the same argspec as the module instead of keeping it in sync.
|
||||||
if 'invocation' not in result:
|
if 'invocation' not in result:
|
||||||
if self._play_context.no_log:
|
if self._play_context.no_log:
|
||||||
result['invocation'] = "CENSORED: no_log is set"
|
result['invocation'] = "CENSORED: no_log is set"
|
||||||
|
@ -218,8 +220,11 @@ class ActionModule(ActionBase):
|
||||||
result['invocation'] = self._task.args.copy()
|
result['invocation'] = self._task.args.copy()
|
||||||
result['invocation']['module_args'] = self._task.args.copy()
|
result['invocation']['module_args'] = self._task.args.copy()
|
||||||
|
|
||||||
if isinstance(result['invocation'], dict) and 'content' in result['invocation']:
|
if isinstance(result['invocation'], dict):
|
||||||
|
if 'content' in result['invocation']:
|
||||||
result['invocation']['content'] = 'CENSORED: content is a no_log parameter'
|
result['invocation']['content'] = 'CENSORED: content is a no_log parameter'
|
||||||
|
if result['invocation'].get('module_args', {}).get('content') is not None:
|
||||||
|
result['invocation']['module_args']['content'] = 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER'
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,8 @@
|
||||||
- import_tasks: selinux.yml
|
- import_tasks: selinux.yml
|
||||||
when: ansible_os_family == 'RedHat' and ansible_selinux.get('mode') == 'enforcing'
|
when: ansible_os_family == 'RedHat' and ansible_selinux.get('mode') == 'enforcing'
|
||||||
|
|
||||||
|
- import_tasks: no_log.yml
|
||||||
|
|
||||||
- import_tasks: check_mode.yml
|
- import_tasks: check_mode.yml
|
||||||
|
|
||||||
# https://github.com/ansible/ansible/issues/57618
|
# https://github.com/ansible/ansible/issues/57618
|
||||||
|
|
82
test/integration/targets/copy/tasks/no_log.yml
Normal file
82
test/integration/targets/copy/tasks/no_log.yml
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
- block:
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
dest: "{{ local_temp_dir }}/test_no_log"
|
||||||
|
|
||||||
|
- name: ensure playbook and dest files don't exist yet
|
||||||
|
file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: absent
|
||||||
|
loop:
|
||||||
|
- "{{ local_temp_dir }}/test_no_log.yml"
|
||||||
|
- "{{ dest }}"
|
||||||
|
|
||||||
|
- name: create a playbook to run with command
|
||||||
|
copy:
|
||||||
|
dest: "{{local_temp_dir}}/test_no_log.yml"
|
||||||
|
content: !unsafe |
|
||||||
|
---
|
||||||
|
- hosts: localhost
|
||||||
|
gather_facts: no
|
||||||
|
tasks:
|
||||||
|
- copy:
|
||||||
|
dest: "{{ dest }}"
|
||||||
|
content: "{{ secret }}"
|
||||||
|
|
||||||
|
- name: copy the secret while using -vvv and check mode
|
||||||
|
command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=SECRET -e dest={{dest}} --check"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- "'SECRET' not in result.stdout"
|
||||||
|
|
||||||
|
- name: copy the secret while using -vvv
|
||||||
|
command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=SECRET -e dest={{dest}}"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- "'SECRET' not in result.stdout"
|
||||||
|
|
||||||
|
- name: copy the secret while using -vvv and check mode again
|
||||||
|
command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=SECRET -e dest={{dest}} --check"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- "'SECRET' not in result.stdout"
|
||||||
|
|
||||||
|
- name: copy the secret while using -vvv again
|
||||||
|
command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=SECRET -e dest={{dest}}"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- "'SECRET' not in result.stdout"
|
||||||
|
|
||||||
|
- name: copy a new secret while using -vvv and check mode
|
||||||
|
command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=NEWSECRET -e dest={{dest}} --check"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- "'NEWSECRET' not in result.stdout"
|
||||||
|
|
||||||
|
- name: copy a new secret while using -vvv
|
||||||
|
command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=NEWSECRET -e dest={{dest}}"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- "'NEWSECRET' not in result.stdout"
|
||||||
|
|
||||||
|
always:
|
||||||
|
|
||||||
|
- name: remove temp test files
|
||||||
|
file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: absent
|
||||||
|
loop:
|
||||||
|
- "{{ local_temp_dir }}/test_no_log.yml"
|
||||||
|
- "{{ dest }}"
|
Loading…
Reference in a new issue