From 19f5ce2c9c86d665466f4f82668de8e769d63aa1 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Tue, 3 Jun 2014 09:34:42 -0500 Subject: [PATCH] Allow --vault-password-file to work with a script as well as a flat file --- bin/ansible | 13 ++----------- bin/ansible-playbook | 14 +++----------- docsite/rst/playbooks_vault.rst | 6 +++++- lib/ansible/constants.py | 1 + lib/ansible/utils/__init__.py | 31 +++++++++++++++++++++++++++++-- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/bin/ansible b/bin/ansible index 1e2540fafb7..0544049a58e 100755 --- a/bin/ansible +++ b/bin/ansible @@ -124,17 +124,8 @@ class Cli(object): (sshpass, sudopass, su_pass, vault_pass) = utils.ask_passwords(ask_pass=options.ask_pass, ask_sudo_pass=options.ask_sudo_pass, ask_su_pass=options.ask_su_pass, ask_vault_pass=options.ask_vault_pass) # read vault_pass from a file - if options.vault_password_file: - this_path = os.path.expanduser(options.vault_password_file) - try: - f = open(this_path, "rb") - tmp_vault_pass=f.read().strip() - f.close() - except (OSError, IOError), e: - raise errors.AnsibleError("Could not read %s: %s" % (this_path, e)) - - if not options.ask_vault_pass: - vault_pass = tmp_vault_pass + if not options.ask_vault_pass and options.vault_password_file: + vault_pass = utils.read_vault_file(options.vault_password_file) inventory_manager = inventory.Inventory(options.inventory) if options.subset: diff --git a/bin/ansible-playbook b/bin/ansible-playbook index d7c9182e2f6..00a3b3d63cc 100755 --- a/bin/ansible-playbook +++ b/bin/ansible-playbook @@ -125,17 +125,9 @@ def main(args): options.sudo_user = options.sudo_user or C.DEFAULT_SUDO_USER options.su_user = options.su_user or C.DEFAULT_SU_USER - if options.vault_password_file: - this_path = os.path.expanduser(options.vault_password_file) - try: - f = open(this_path, "rb") - tmp_vault_pass=f.read().strip() - f.close() - except (OSError, IOError), e: - raise errors.AnsibleError("Could not read %s: %s" % (this_path, e)) - - if not options.ask_vault_pass: - vault_pass = tmp_vault_pass + # read vault_pass from a file + if not options.ask_vault_pass and options.vault_password_file: + vault_pass = utils.read_vault_file(options.vault_password_file) extra_vars = {} for extra_vars_opt in options.extra_vars: diff --git a/docsite/rst/playbooks_vault.rst b/docsite/rst/playbooks_vault.rst index 991c58f16ce..934ca150820 100644 --- a/docsite/rst/playbooks_vault.rst +++ b/docsite/rst/playbooks_vault.rst @@ -83,12 +83,16 @@ To run a playbook that contains vault-encrypted data files, you must pass one of This prompt will then be used to decrypt (in memory only) any vault encrypted files that are accessed. Currently this requires that all passwords be encrypted with the same password. -Alternatively, passwords can be specified with a file. If this is done, be careful to ensure permissions on the file are such that no one else can access your key, and do not add your key to source control:: +Alternatively, passwords can be specified with a file or a script. If this is done, be careful to ensure permissions on the file are such that no one else can access your key, and do not add your key to source control:: ansible-playbook site.yml --vault-password-file ~/.vault_pass.txt + ansible-playbook site.yml --vault-password-file ~/.vault_pass.py + The password should be a string stored as a single line in the file. +If you are using a script instead of a flat file, ensure that it is marked as executable, and that the password is printed to STDOUT. If your script needs to prompt for data, prompts can be sent to STDERR. + This is likely something you may wish to do if using Ansible from a continuous integration system like Jenkins. (The `--vault-password-file` option can also be used with the :ref:`ansible-pull` command if you wish, though this would require distributing the keys to your nodes, so understand the implications -- vault is more intended for push mode). diff --git a/lib/ansible/constants.py b/lib/ansible/constants.py index e47d9043d6e..c50bee0765d 100644 --- a/lib/ansible/constants.py +++ b/lib/ansible/constants.py @@ -118,6 +118,7 @@ DEFAULT_SUDO_USER = get_config(p, DEFAULTS, 'sudo_user', 'ANSIBLE DEFAULT_ASK_SUDO_PASS = get_config(p, DEFAULTS, 'ask_sudo_pass', 'ANSIBLE_ASK_SUDO_PASS', False, boolean=True) DEFAULT_REMOTE_PORT = get_config(p, DEFAULTS, 'remote_port', 'ANSIBLE_REMOTE_PORT', None, integer=True) DEFAULT_ASK_VAULT_PASS = get_config(p, DEFAULTS, 'ask_vault_pass', 'ANSIBLE_ASK_VAULT_PASS', False, boolean=True) +DEFAULT_VAULT_PASSWORD_FILE = shell_expand_path(get_config(p, DEFAULTS, 'vault_password_file', 'ANSIBLE_VAULT_PASSWORD_FILE', None)) DEFAULT_TRANSPORT = get_config(p, DEFAULTS, 'transport', 'ANSIBLE_TRANSPORT', 'smart') DEFAULT_SCP_IF_SSH = get_config(p, 'ssh_connection', 'scp_if_ssh', 'ANSIBLE_SCP_IF_SSH', False, boolean=True) DEFAULT_MANAGED_STR = get_config(p, DEFAULTS, 'ansible_managed', None, 'Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}') diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py index e3ad20ad897..339bb64f533 100644 --- a/lib/ansible/utils/__init__.py +++ b/lib/ansible/utils/__init__.py @@ -44,6 +44,7 @@ import traceback import getpass import sys import json +import subprocess #import vault from vault import VaultLib @@ -148,6 +149,32 @@ def decrypt(key, msg): # UTILITY FUNCTIONS FOR COMMAND LINE TOOLS ############################################################### +def read_vault_file(vault_password_file): + """Read a vault password from a file or if executable, execute the script and + retrieve password from STDOUT + """ + if vault_password_file: + this_path = os.path.realpath(os.path.expanduser(vault_password_file)) + if is_executable(this_path): + try: + # STDERR not captured to make it easier for users to prompt for input in their scripts + p = subprocess.Popen(this_path, stdout=subprocess.PIPE) + except OSError, e: + raise errors.AnsibleError("problem running %s (%s)" % (' '.join(this_path), e)) + stdout, stderr = p.communicate() + vault_pass = stdout.strip('\r\n') + else: + try: + f = open(this_path, "rb") + vault_pass=f.read().strip() + f.close() + except (OSError, IOError), e: + raise errors.AnsibleError("Could not read %s: %s" % (this_path, e)) + + return vault_pass + else: + return None + def err(msg): ''' print an error message to stderr ''' @@ -757,8 +784,8 @@ def base_parser(constants=C, usage="", output_opts=False, runas_opts=False, help='ask for su password') parser.add_option('--ask-vault-pass', default=False, dest='ask_vault_pass', action='store_true', help='ask for vault password') - parser.add_option('--vault-password-file', default=None, dest='vault_password_file', - help="vault password file") + parser.add_option('--vault-password-file', default=constants.DEFAULT_VAULT_PASSWORD_FILE, + dest='vault_password_file', help="vault password file") parser.add_option('--list-hosts', dest='listhosts', action='store_true', help='outputs a list of matching hosts; does not execute anything else') parser.add_option('-M', '--module-path', dest='module_path',