mirror of https://github.com/ansible/ansible.git
[stable-2.18] Preserve `_ansible_no_log` from action result; fix `include_vars` to set properly (#84143) (#84179)
* fixes for CVE-2024-8775
* propagate truthy `_ansible_no_log` in action result (previously superseded by task-calculated value)
* always mask entire `include_vars` action result if any file loaded had a false `show_content` flag (previously used only the flag value from the last file loaded)
* update no_log tests for CVE-2024-8775
* include validation of _ansible_no_log preservation when set by actions
* replace static values with dynamic for increased robustness to logging/display/callback changes (but still using grep counts :( )
* changelog
* use ternary, coerce to bool explicitly
(cherry picked from commit c9ac477e53
)
pull/84193/head
parent
3b6de811ab
commit
23f8639a4b
@ -0,0 +1,5 @@
|
|||||||
|
security_fixes:
|
||||||
|
- task result processing - Ensure that action-sourced result masking (``_ansible_no_log=True``)
|
||||||
|
is preserved. (CVE-2024-8775)
|
||||||
|
- include_vars action - Ensure that result masking is correctly requested when vault-encrypted
|
||||||
|
files are read. (CVE-2024-8775)
|
@ -0,0 +1,6 @@
|
|||||||
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
|
31613539636636336264396235633933633839646337323533316638633336653461393036336664
|
||||||
|
3939386435313638366366626566346135623932653238360a366261303663343034633865626132
|
||||||
|
31646231623630333636383636383833656331643164656366623332396439306132663264663131
|
||||||
|
6439633766376261320a616265306430366530363866356433366430633265353739373732646536
|
||||||
|
37623661333064306162373463616231636365373231313939373230643936313362
|
@ -1,6 +1,22 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
set -eux
|
set -eux -o pipefail
|
||||||
|
|
||||||
ansible testhost -i ../../inventory -m include_vars -a 'dir/inc.yml' "$@"
|
echo "single file include"
|
||||||
ansible testhost -i ../../inventory -m include_vars -a 'dir=dir' "$@"
|
ansible testhost -i ../../inventory -m include_vars -a 'dir/inc.yml' -vvv 2>&1 | grep -q 'porter.*cable'
|
||||||
|
|
||||||
|
echo "single file encrypted include"
|
||||||
|
ansible testhost -i ../../inventory -m include_vars -a 'dir/encrypted.yml' -vvv --vault-password-file vaultpass > output.txt 2>&1
|
||||||
|
|
||||||
|
echo "directory include with encrypted"
|
||||||
|
ansible testhost -i ../../inventory -m include_vars -a 'dir=dir' -vvv --vault-password-file vaultpass >> output.txt 2>&1
|
||||||
|
|
||||||
|
grep -q 'output has been hidden' output.txt
|
||||||
|
|
||||||
|
# all content should be masked if any file is encrypted
|
||||||
|
if grep -e 'i am a secret' -e 'porter.*cable' output.txt; then
|
||||||
|
echo "FAIL: vault masking failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo PASS
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo supersecurepassword
|
@ -0,0 +1,8 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from ansible.plugins.action import ActionBase
|
||||||
|
|
||||||
|
|
||||||
|
class ActionModule(ActionBase):
|
||||||
|
def run(self, tmp=None, task_vars=None):
|
||||||
|
return dict(changed=False, failed=False, msg="action result should be masked", _ansible_no_log="yeppers") # ensure that a truthy non-bool works here
|
@ -0,0 +1,13 @@
|
|||||||
|
- hosts: localhost
|
||||||
|
gather_facts: no
|
||||||
|
tasks:
|
||||||
|
- action_sets_no_log:
|
||||||
|
register: res_action
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- res_action.msg == "action result should be masked"
|
||||||
|
|
||||||
|
- action_sets_no_log:
|
||||||
|
loop: [1, 2, 3]
|
||||||
|
register: res_action
|
@ -1,27 +1,42 @@
|
|||||||
- name: test dynamic no log
|
- name: test dynamic no log
|
||||||
hosts: testhost
|
hosts: testhost
|
||||||
gather_facts: no
|
gather_facts: no
|
||||||
ignore_errors: yes
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: no loop, task fails, dynamic no_log
|
- name: no loop, task fails, dynamic no_log
|
||||||
debug:
|
raw: echo {{ var_does_not_exist }}
|
||||||
msg: "SHOW {{ var_does_not_exist }}"
|
|
||||||
no_log: "{{ not (unsafe_show_logs|bool) }}"
|
no_log: "{{ not (unsafe_show_logs|bool) }}"
|
||||||
|
ignore_errors: yes
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- result is failed
|
||||||
|
- result.results is not defined
|
||||||
|
|
||||||
- name: loop, task succeeds, dynamic does no_log
|
- name: loop, task succeeds, dynamic does no_log
|
||||||
debug:
|
raw: echo {{ item }}
|
||||||
msg: "SHOW {{ item }}"
|
|
||||||
loop:
|
loop:
|
||||||
- a
|
- a
|
||||||
- b
|
- b
|
||||||
- c
|
- c
|
||||||
no_log: "{{ not (unsafe_show_logs|bool) }}"
|
no_log: "{{ not (unsafe_show_logs|bool) }}"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- result.results | length == 3
|
||||||
|
|
||||||
- name: loop, task fails, dynamic no_log
|
- name: loop, task fails, dynamic no_log
|
||||||
debug:
|
raw: echo {{ var_does_not_exist }}
|
||||||
msg: "SHOW {{ var_does_not_exist }}"
|
|
||||||
loop:
|
loop:
|
||||||
- a
|
- a
|
||||||
- b
|
- b
|
||||||
- c
|
- c
|
||||||
no_log: "{{ not (unsafe_show_logs|bool) }}"
|
no_log: "{{ not (unsafe_show_logs|bool) }}"
|
||||||
|
ignore_errors: yes
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- result is failed
|
||||||
|
- result.results is not defined # DT needs result.results | length == 3
|
||||||
|
@ -1,26 +1,32 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
set -eux
|
set -eux -o pipefail
|
||||||
|
|
||||||
|
# ensure _ansible_no_log returned by actions is actually respected
|
||||||
|
ansible-playbook ansible_no_log_in_result.yml -vvvvv > "${OUTPUT_DIR}/output.log" 2> /dev/null
|
||||||
|
|
||||||
|
[ "$(grep -c "action result should be masked" "${OUTPUT_DIR}/output.log")" = "0" ]
|
||||||
|
[ "$(grep -c "the output has been hidden" "${OUTPUT_DIR}/output.log")" = "4" ]
|
||||||
|
|
||||||
# This test expects 7 loggable vars and 0 non-loggable ones.
|
# This test expects 7 loggable vars and 0 non-loggable ones.
|
||||||
# If either mismatches it fails, run the ansible-playbook command to debug.
|
# If either mismatches it fails, run the ansible-playbook command to debug.
|
||||||
[ "$(ansible-playbook no_log_local.yml -i ../../inventory -vvvvv "$@" | awk \
|
[ "$(ansible-playbook no_log_local.yml -i ../../inventory -vvvvv "$@" | awk \
|
||||||
'BEGIN { logme = 0; nolog = 0; } /LOG_ME/ { logme += 1;} /DO_NOT_LOG/ { nolog += 1;} END { printf "%d/%d", logme, nolog; }')" = "27/0" ]
|
'BEGIN { logme = 0; nolog = 0; } /LOG_ME/ { logme += 1;} /DO_NOT_LOG/ { nolog += 1;} END { printf "%d/%d", logme, nolog; }')" = "26/0" ]
|
||||||
|
|
||||||
# deal with corner cases with no log and loops
|
# deal with corner cases with no log and loops
|
||||||
# no log enabled, should produce 6 censored messages
|
# no log enabled, should produce 6 censored messages
|
||||||
[ "$(ansible-playbook dynamic.yml -i ../../inventory -vvvvv "$@" -e unsafe_show_logs=no|grep -c 'output has been hidden')" = "6" ]
|
[ "$(ansible-playbook dynamic.yml -i ../../inventory -vvvvv "$@" -e unsafe_show_logs=no|grep -c 'output has been hidden')" = "6" ] # DT needs 7
|
||||||
|
|
||||||
# no log disabled, should produce 0 censored
|
# no log disabled, should produce 0 censored
|
||||||
[ "$(ansible-playbook dynamic.yml -i ../../inventory -vvvvv "$@" -e unsafe_show_logs=yes|grep -c 'output has been hidden')" = "0" ]
|
[ "$(ansible-playbook dynamic.yml -i ../../inventory -vvvvv "$@" -e unsafe_show_logs=yes|grep -c 'output has been hidden')" = "0" ]
|
||||||
|
|
||||||
# test no log for sub options
|
# test no log for sub options
|
||||||
[ "$(ansible-playbook no_log_suboptions.yml -i ../../inventory -vvvvv "$@" | grep -Ec '(MANPOWER|UNTAPPED|CONCERNED|MARLIN|FLICK)')" = "0" ]
|
[ "$(ansible-playbook no_log_suboptions.yml -i ../../inventory -vvvvv "$@" | grep -Ec 'SECRET')" = "0" ]
|
||||||
|
|
||||||
# test invalid data passed to a suboption
|
# test invalid data passed to a suboption
|
||||||
[ "$(ansible-playbook no_log_suboptions_invalid.yml -i ../../inventory -vvvvv "$@" | grep -Ec '(SUPREME|IDIOM|MOCKUP|EDUCATED|FOOTREST|CRAFTY|FELINE|CRYSTAL|EXPECTANT|AGROUND|GOLIATH|FREEFALL)')" = "0" ]
|
[ "$(ansible-playbook no_log_suboptions_invalid.yml -i ../../inventory -vvvvv "$@" | grep -Ec 'SECRET')" = "0" ]
|
||||||
|
|
||||||
# test variations on ANSIBLE_NO_LOG
|
# test variations on ANSIBLE_NO_LOG
|
||||||
[ "$(ansible-playbook no_log_config.yml -i ../../inventory -vvvvv "$@" | grep -Ec 'the output has been hidden')" = "1" ]
|
[ "$(ansible-playbook no_log_config.yml -i ../../inventory -vvvvv "$@" | grep -Ec 'the output has been hidden')" = "1" ]
|
||||||
[ "$(ANSIBLE_NO_LOG=0 ansible-playbook no_log_config.yml -i ../../inventory -vvvvv "$@" | grep -Ec 'the output has been hidden')" = "1" ]
|
[ "$(ANSIBLE_NO_LOG=0 ansible-playbook no_log_config.yml -i ../../inventory -vvvvv "$@" | grep -Ec 'the output has been hidden')" = "1" ]
|
||||||
[ "$(ANSIBLE_NO_LOG=1 ansible-playbook no_log_config.yml -i ../../inventory -vvvvv "$@" | grep -Ec 'the output has been hidden')" = "6" ]
|
[ "$(ANSIBLE_NO_LOG=1 ansible-playbook no_log_config.yml -i ../../inventory -vvvvv "$@" | grep -Ec 'the output has been hidden')" = "6" ] # DT needs 5
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
# These values are in a separate vars file and referenced dynamically to avoid spurious counts from contextual error messages
|
||||||
|
# that show the playbook contents inline (since unencrypted playbook contents are not considered secret).
|
||||||
|
log_me_prefix: LOG_ME_
|
||||||
|
|
||||||
|
# Unique values are used for each secret below to ensure that one secret "learned" does not cause another non-secret
|
||||||
|
# value to be considered secret simply because they share the same value. A common substring is, however, present in
|
||||||
|
# each one to simplify searching for secret values in test output. Having a unique value for each also helps in
|
||||||
|
# debugging when unexpected output is encountered.
|
||||||
|
|
||||||
|
# secrets for no_log_suboptions.yml
|
||||||
|
s101: SECRET101
|
||||||
|
s102: SECRET102
|
||||||
|
s103: SECRET103
|
||||||
|
s104: SECRET104
|
||||||
|
s105: SECRET105
|
||||||
|
s106: SECRET106
|
||||||
|
s107: SECRET107
|
||||||
|
|
||||||
|
# secrets for no_log_suboptions_invalid.yml
|
||||||
|
s201: SECRET201
|
||||||
|
s202: SECRET202
|
||||||
|
s203: SECRET203
|
||||||
|
s204: SECRET204
|
||||||
|
s205: SECRET205
|
||||||
|
s206: SECRET206
|
||||||
|
s207: SECRET207
|
||||||
|
s208: SECRET208
|
||||||
|
s209: SECRET209
|
||||||
|
s210: SECRET210
|
||||||
|
s211: SECRET211
|
||||||
|
s212: SECRET212
|
||||||
|
s213: SECRET213
|
Loading…
Reference in New Issue