Skip to content

Commit e1273b6

Browse files
bcocamattclay
authored andcommitted
fix vault temp file handling (#68433)
* fix vault tmpe file handling * use local temp dir instead of system temp * ensure each worker clears dataloader temp files * added test for dangling temp files * added notes to data loader CVE-2020-10685 (cherry picked from commit 6452a82)
1 parent 6c74a29 commit e1273b6

File tree

8 files changed

+73
-2
lines changed

8 files changed

+73
-2
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bugfixes:
2+
- Ensure DataLoader temp files are removed at appropriate times and that we observe the LOCAL_TMP setting.

lib/ansible/executor/process/worker.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ def __init__(self, final_q, task_vars, host, task, play_context, loader, variabl
6767
self._variable_manager = variable_manager
6868
self._shared_loader_obj = shared_loader_obj
6969

70+
# NOTE: this works due to fork, if switching to threads this should change to per thread storage of temp files
71+
# clear var to ensure we only delete files for this child
72+
self._loader._tempfiles = set()
73+
7074
def _save_stdin(self):
7175
self._new_stdin = os.devnull
7276
try:
@@ -200,6 +204,8 @@ def _run(self):
200204
except Exception:
201205
display.debug(u"WORKER EXCEPTION: %s" % to_text(e))
202206
display.debug(u"WORKER TRACEBACK: %s" % to_text(traceback.format_exc()))
207+
finally:
208+
self._clean_up()
203209

204210
display.debug("WORKER PROCESS EXITING")
205211

@@ -210,3 +216,8 @@ def _run(self):
210216
# ps.print_stats()
211217
# with open('worker_%06d.stats' % os.getpid(), 'w') as f:
212218
# f.write(s.getvalue())
219+
220+
def _clean_up(self):
221+
# NOTE: see note in init about forks
222+
# ensure we cleanup all temp files for this worker
223+
self._loader.cleanup_all_tmp_files()

lib/ansible/parsing/dataloader.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,16 @@ class DataLoader:
5151
'''
5252

5353
def __init__(self):
54+
5455
self._basedir = '.'
56+
57+
# NOTE: not effective with forks as the main copy does not get updated.
58+
# avoids rereading files
5559
self._FILE_CACHE = dict()
60+
61+
# NOTE: not thread safe, also issues with forks not returning data to main proc
62+
# so they need to be cleaned independantly. See WorkerProcess for example.
63+
# used to keep track of temp files for cleaning
5664
self._tempfiles = set()
5765

5866
# initialize the vault stuff with an empty password
@@ -322,7 +330,7 @@ def path_dwim_relative_stack(self, paths, dirname, source, is_role=False):
322330

323331
def _create_content_tempfile(self, content):
324332
''' Create a tempfile containing defined content '''
325-
fd, content_tempfile = tempfile.mkstemp()
333+
fd, content_tempfile = tempfile.mkstemp(dir=C.DEFAULT_LOCAL_TMP)
326334
f = os.fdopen(fd, 'wb')
327335
content = to_bytes(content)
328336
try:
@@ -385,6 +393,10 @@ def cleanup_tmp_file(self, file_path):
385393
self._tempfiles.remove(file_path)
386394

387395
def cleanup_all_tmp_files(self):
396+
"""
397+
Removes all temporary files that DataLoader has created
398+
NOTE: not thread safe, forks also need special handling see __init__ for details.
399+
"""
388400
for f in self._tempfiles:
389401
try:
390402
self.cleanup_tmp_file(f)

lib/ansible/parsing/vault/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -848,7 +848,7 @@ def _edit_file_helper(self, filename, secret,
848848

849849
# Create a tempfile
850850
root, ext = os.path.splitext(os.path.realpath(filename))
851-
fd, tmp_path = tempfile.mkstemp(suffix=ext)
851+
fd, tmp_path = tempfile.mkstemp(suffix=ext, dir=C.DEFAULT_LOCAL_TMP)
852852
os.close(fd)
853853

854854
cmd = self._editor_shell_command(tmp_path)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
THIS IS OK
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
$ANSIBLE_VAULT;1.1;AES256
2+
37626439373465656332623633333336353334326531333666363766303339336134313136616165
3+
6561333963343739386334653636393363396366396338660a663537666561643862343233393265
4+
33336436633864323935356337623861663631316530336532633932623635346364363338363437
5+
3365313831366365350a613934313862313538626130653539303834656634353132343065633162
6+
34316135313837623735653932663139353164643834303534346238386435373832366564646236
7+
3461333465343434666639373432366139363566303564643066

test/integration/targets/vault/runme.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,3 +507,7 @@ ansible-playbook "$@" -i invalid_format/inventory --vault-id invalid_format/vaul
507507

508508
# Run playbook with vault file with unicode in filename (https://github.com/ansible/ansible/issues/50316)
509509
ansible-playbook -i ../../inventory -v "$@" --vault-password-file vault-password test_utf8_value_in_filename.yml
510+
511+
# Ensure we don't leave unencrypted temp files dangling
512+
ansible-playbook -v "$@" --vault-password-file vault-password test_dangling_temp.yml
513+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
- hosts: localhost
2+
gather_facts: False
3+
vars:
4+
od: "{{output_dir|default('/tmp')}}/test_vault_assemble"
5+
tasks:
6+
- name: create target directory
7+
file:
8+
path: "{{od}}"
9+
state: directory
10+
11+
- name: assemble_file file with secret
12+
assemble:
13+
src: files/test_assemble
14+
dest: "{{od}}/dest_file"
15+
remote_src: no
16+
mode: 0600
17+
18+
- name: remove assembled file with secret (so nothing should have unencrypted secret)
19+
file: path="{{od}}/dest_file" state=absent
20+
21+
- name: find temp files with secrets
22+
find:
23+
paths: '{{temp_paths}}'
24+
contains: 'VAULT TEST IN WHICH BAD THING HAPPENED'
25+
recurse: yes
26+
register: badthings
27+
vars:
28+
temp_paths: "{{[lookup('env', 'TMP'), lookup('env', 'TEMP'), hardcoded]|flatten(1)|unique|list}}"
29+
hardcoded: ['/tmp', '/var/tmp']
30+
31+
- name: ensure we failed to find any
32+
assert:
33+
that:
34+
- badthings['matched'] == 0

0 commit comments

Comments
 (0)