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
|
||||
|
||||
set -eux
|
||||
set -eux -o pipefail
|
||||
|
||||
ansible testhost -i ../../inventory -m include_vars -a 'dir/inc.yml' "$@"
|
||||
ansible testhost -i ../../inventory -m include_vars -a 'dir=dir' "$@"
|
||||
echo "single file include"
|
||||
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
|
||||
hosts: testhost
|
||||
gather_facts: no
|
||||
ignore_errors: yes
|
||||
tasks:
|
||||
- name: no loop, task fails, dynamic no_log
|
||||
debug:
|
||||
msg: "SHOW {{ var_does_not_exist }}"
|
||||
raw: echo {{ var_does_not_exist }}
|
||||
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
|
||||
debug:
|
||||
msg: "SHOW {{ item }}"
|
||||
raw: echo {{ item }}
|
||||
loop:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
no_log: "{{ not (unsafe_show_logs|bool) }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result.results | length == 3
|
||||
|
||||
- name: loop, task fails, dynamic no_log
|
||||
debug:
|
||||
msg: "SHOW {{ var_does_not_exist }}"
|
||||
raw: echo {{ var_does_not_exist }}
|
||||
loop:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
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
|
||||
|
||||
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.
|
||||
# If either mismatches it fails, run the ansible-playbook command to debug.
|
||||
[ "$(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
|
||||
# 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
|
||||
[ "$(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
|
||||
[ "$(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
|
||||
[ "$(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
|
||||
[ "$(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