#!/usr/bin/env python # (c) 2014, Matt Martz # (c) 2016, Justin Mayer # # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # # ============================================================================= # # This script is to be used with ansible-vault's --vault-id arg # to retrieve the vault password via your OS's native keyring application. # # This file *MUST* be saved with executable permissions. Otherwise, Ansible # will try to parse as a password file and display: "ERROR! Decryption failed" # # The `keyring` Python module is required: https://pypi.org/project/keyring/ # # By default, this script will store the specified password in the keyring of # the user that invokes the script. To specify a user keyring, add a [vault] # section to your ansible.cfg file with a 'username' option. Example: # # [vault] # username = 'ansible-vault' # # In useage like: # # ansible-vault --vault-id keyring_id@contrib/vault/vault-keyring-client.py view some_encrypted_file # # --vault-id will call this script like: # # contrib/vault/vault-keyring-client.py --vault-id keyring_id # # That will retrieve the password from users keyring for the # keyring service 'keyring_id'. The equivalent of: # # keyring get keyring_id $USER # # If no vault-id name is specified to ansible command line, the vault-keyring-client.py # script will be called without a '--vault-id' and will default to the keyring service 'ansible' # This is equivalent to: # # keyring get ansible $USER # # You can configure the `vault_password_file` option in ansible.cfg: # # [defaults] # ... # vault_password_file = /path/to/vault-keyring-client.py # ... # # To set your password, `cd` to your project directory and run: # # # will use default keyring service / vault-id of 'ansible' # /path/to/vault-keyring-client.py --set # # or to specify the keyring service / vault-id of 'my_ansible_secret': # # /path/to/vault-keyring-client.py --vault-id my_ansible_secret --set # # If you choose not to configure the path to `vault_password_file` in # ansible.cfg, your `ansible-playbook` command might look like: # # ansible-playbook --vault-id=keyring_id@/path/to/vault-keyring-client.py site.yml from __future__ import absolute_import, division, print_function __metaclass__ = type import argparse import getpass import sys import keyring from ansible.config.manager import ConfigManager, get_ini_config_value KEYNAME_UNKNOWN_RC = 2 def build_arg_parser(): parser = argparse.ArgumentParser( description="Get a vault password from user keyring" ) parser.add_argument( "--vault-id", action="store", default=None, dest="vault_id", help="name of the vault secret to get from keyring", ) parser.add_argument( "--username", action="store", default=None, help="the username whose keyring is queried", ) parser.add_argument( "--set", action="store_true", default=False, dest="set_password", help="set the password instead of getting it", ) return parser def main(): # Set default values username = getpass.getuser() keyname = "ansible" # Try to load values from config if one exists config = ConfigManager() if config._config_file: username = ( get_ini_config_value( config._parsers[config._config_file], dict(section="vault", key="username"), ) or username ) keyname = ( get_ini_config_value( config._parsers[config._config_file], dict(section="vault", key="keyname"), ) or keyname ) # Read values from command line (which override the previous if given) arg_parser = build_arg_parser() args = arg_parser.parse_args() username = args.username or username keyname = args.vault_id or keyname if args.set_password: intro = 'Storing password in "{}" user keyring using key name: {}\n' sys.stdout.write(intro.format(username, keyname)) password = getpass.getpass() confirm = getpass.getpass("Confirm password: ") if password == confirm: keyring.set_password(keyname, username, password) else: sys.stderr.write("Passwords do not match\n") sys.exit(1) else: secret = keyring.get_password(keyname, username) if secret is None: sys.stderr.write( 'vault-keyring-client could not find key="%s" for user="%s" via backend="%s"\n' % (keyname, username, keyring.get_keyring().name) ) sys.exit(KEYNAME_UNKNOWN_RC) # print('secret: %s' % secret) sys.stdout.write("%s\n" % secret) sys.exit(0) if __name__ == "__main__": main()