vault secrets file, keep context when symlink (#78734)

* vault secrets file, keep context when symlink

	fixes #18319

Co-authored-by: Sloane Hertel <19572925+s-hertel@users.noreply.github.com>
pull/78167/head
Brian Coca 2 years ago committed by GitHub
parent e5e87a3927
commit b1ff0f4ebc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,2 @@
bugfixes:
- vault secrets file now executes in the correct context when it is a symlink (not resolved to canonical file).

@ -87,16 +87,16 @@ def ensure_value(namespace, name, value):
#
# Callbacks to validate and normalize Options
#
def unfrack_path(pathsep=False):
def unfrack_path(pathsep=False, follow=True):
"""Turn an Option's data into a single path in Ansible locations"""
def inner(value):
if pathsep:
return [unfrackpath(x) for x in value.split(os.pathsep) if x]
return [unfrackpath(x, follow=follow) for x in value.split(os.pathsep) if x]
if value == '-':
return value
return unfrackpath(value)
return unfrackpath(value, follow=follow)
return inner
@ -388,4 +388,4 @@ def add_vault_options(parser):
base_group.add_argument('--ask-vault-password', '--ask-vault-pass', default=C.DEFAULT_ASK_VAULT_PASS, dest='ask_vault_pass', action='store_true',
help='ask for vault password')
base_group.add_argument('--vault-password-file', '--vault-pass-file', default=[], dest='vault_password_files',
help="vault password file", type=unfrack_path(), action='append')
help="vault password file", type=unfrack_path(follow=False), action='append')

@ -408,8 +408,7 @@ class VaultCLI(CLI):
# (the text itself, which input it came from, its name)
b_plaintext, src, name = b_plaintext_info
b_ciphertext = self.editor.encrypt_bytes(b_plaintext, self.encrypt_secret,
vault_id=vault_id)
b_ciphertext = self.editor.encrypt_bytes(b_plaintext, self.encrypt_secret, vault_id=vault_id)
# block formatting
yaml_text = self.format_ciphertext_yaml(b_ciphertext, name=name)

@ -57,7 +57,7 @@ from ansible import constants as C
from ansible.module_utils.six import binary_type
from ansible.module_utils._text import to_bytes, to_text, to_native
from ansible.utils.display import Display
from ansible.utils.path import makedirs_safe
from ansible.utils.path import makedirs_safe, unfrackpath
display = Display()
@ -349,17 +349,25 @@ def script_is_client(filename):
def get_file_vault_secret(filename=None, vault_id=None, encoding=None, loader=None):
this_path = os.path.realpath(os.path.expanduser(filename))
''' Get secret from file content or execute file and get secret from stdout '''
# we unfrack but not follow the full path/context to possible vault script
# so when the script uses 'adjacent' file for configuration or similar
# it still works (as inventory scripts often also do).
# while files from --vault-password-file are already unfracked, other sources are not
this_path = unfrackpath(filename, follow=False)
if not os.path.exists(this_path):
raise AnsibleError("The vault password file %s was not found" % this_path)
# it is a script?
if loader.is_executable(this_path):
if script_is_client(filename):
display.vvvv(u'The vault password file %s is a client script.' % to_text(filename))
# this is special script type that handles vault ids
display.vvvv(u'The vault password file %s is a client script.' % to_text(this_path))
# TODO: pass vault_id_name to script via cli
return ClientScriptVaultSecret(filename=this_path, vault_id=vault_id,
encoding=encoding, loader=loader)
return ClientScriptVaultSecret(filename=this_path, vault_id=vault_id, encoding=encoding, loader=loader)
# just a plain vault password script. No args, returns a byte array
return ScriptVaultSecret(filename=this_path, encoding=encoding, loader=loader)
@ -432,8 +440,7 @@ class ScriptVaultSecret(FileVaultSecret):
vault_pass = stdout.strip(b'\r\n')
empty_password_msg = 'Invalid vault password was provided from script (%s)' % filename
verify_secret_is_not_empty(vault_pass,
msg=empty_password_msg)
verify_secret_is_not_empty(vault_pass, msg=empty_password_msg)
return vault_pass
@ -659,8 +666,7 @@ class VaultLib:
msg += "%s is not a vault encrypted file" % to_native(filename)
raise AnsibleError(msg)
b_vaulttext, dummy, cipher_name, vault_id = parse_vaulttext_envelope(b_vaulttext,
filename=filename)
b_vaulttext, dummy, cipher_name, vault_id = parse_vaulttext_envelope(b_vaulttext, filename=filename)
# create the cipher object, note that the cipher used for decrypt can
# be different than the cipher used for encrypt

@ -0,0 +1,10 @@
- hosts: localhost
gather_facts: false
vars_files:
- vaulted.yml
tasks:
- name: see if we can decrypt
assert:
that:
- control is defined
- realpath == 'this is a secret'

@ -549,3 +549,28 @@ grep out.txt -e "[WARNING]: Error in vault password file loading (id2)"
grep out.txt -e "ERROR! Did not find a match for --encrypt-vault-id=id2 in the known vault-ids ['id3']"
set -e
unset ANSIBLE_VAULT_IDENTITY_LIST
# 'real script'
ansible-playbook realpath.yml "$@" --vault-password-file script/vault-secret.sh
# using symlink
ansible-playbook symlink.yml "$@" --vault-password-file symlink/get-password-symlink
### NEGATIVE TESTS
ER='Attempting to decrypt'
#### no secrets
# 'real script'
ansible-playbook realpath.yml "$@" 2>&1 |grep "${ER}"
# using symlink
ansible-playbook symlink.yml "$@" 2>&1 |grep "${ER}"
ER='Decryption failed'
### wrong secrets
# 'real script'
ansible-playbook realpath.yml "$@" --vault-password-file symlink/get-password-symlink 2>&1 |grep "${ER}"
# using symlink
ansible-playbook symlink.yml "$@" --vault-password-file script/vault-secret.sh 2>&1 |grep "${ER}"

@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -eu
# shellcheck disable=SC2086
basename="$(basename $0)"
# shellcheck disable=SC2046
# shellcheck disable=SC2086
dirname="$(basename $(dirname $0))"
basename_prefix="get-password"
default_password="foo-bar"
case "${basename}" in
"${basename_prefix}"-*)
password="${default_password}-${basename#${basename_prefix}-}"
;;
*)
password="${default_password}"
;;
esac
# the password is different depending on the path used (direct or symlink)
# it would be the same if symlink is 'resolved'.
echo "${password}_${dirname}"

@ -0,0 +1,10 @@
- hosts: localhost
gather_facts: false
vars_files:
- vaulted.yml
tasks:
- name: see if we can decrypt
assert:
that:
- control is defined
- symlink == 'this is a test'

@ -0,0 +1,15 @@
control: 1
realpath: !vault |
$ANSIBLE_VAULT;1.1;AES256
64343436666664636436363065356463363630653766323230333931366661656262343030386366
6536616433353864616132303033623835316430623762360a646234383932656637623439353333
36336362616564333663353739313766363333376461353962643531366338633336613565636636
3663663664653538620a646132623835666336393333623439363361313934666530646334333765
39386364646262396234616666666438313233626336376330366539663765373566
symlink: !vault |
$ANSIBLE_VAULT;1.1;AES256
61656138353366306464386332353938623338336333303831353164633834353437643635343635
3461646235303261613766383437623664323032623137350a663934653735316334363832383534
33623733346164376430643535616433383331663238383363316634353339326235663461353166
3064663735353766660a653963373432383432373365633239313033646466653664346236363635
6637
Loading…
Cancel
Save