#!/usr/bin/python # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import (absolute_import, division, print_function) __metaclass__ = type import os import traceback from ansible.module_utils.basic import AnsibleModule, missing_required_lib IMPORT_ERR = None try: # import _argon2_xffi_bindings import pykeepass as keepass except ImportError: IMPORT_ERR = traceback.format_exc() DOCUMENTATION = r''' --- module: ansible-keepassxc short_description: Module to read credentials from KeePassXC version_added: "0.0.1" description: Module to read credentials from KeePassXC options: database: description: Path to database file required: true type: str password: description: Database Password required: true type: str keyfile: description: Path to key file required: false type: str entry: description: Entry name for the attribute to fetch required: true type: str group: decription: Group name that the Entry belongs to required: false type: str author: - Jeremy Lumley (@jlumley) ''' EXAMPLES = r''' # Fetch the credentials for the server_1 entry in any group - name: Fetch server_1 credentials jlumley.jlumley.ansible-keepassxc: database: "/secrets/db.kdbx" password: "s3cure_p4550rd" entry: "server_1" # Fetch the reddit entry in the social group - name: Fetching reddit credentials jlumley.jlumley.ansible-keepassxc: database: "/secrets/db.kdbx" password: "sup3r_s3cure_p4550rd" entry: "reddit" group: "social" # Fetch a custom strig attribute from the github entry - name: Fetch Github API Token jlumley.jlumley.ansible-keepassxc: database: "/secrets/db.kdbx" password: "d0pe_s3cure_p4550rd" keyfile: "/secrets/top_secret_key" entry: "github" group: "development" ''' RETURN = r''' # Return values username: description: Username of entry if present type: str returned: always sample: 's3cr3t_us3r' password: description: Password of entry if present type: str returned: always sample: 's3cr3t_p455word' url: description: Url of entry if present type: str returned: always sample: 'http://reddit.com' custom_fields: description: dictionary containing all custom fields type: dict returned: always sample: False no_log: description: suppress logging of password type: bool returned: never sample: False ''' def run_module(): # define available arguments/parameters a user can pass to the module module_args = dict( database = dict(type='str', required=True), password = dict(type='str', required=False, default=os.environ.get('ANSIBLE_KEEPASSXC_PASSWORD')), keyfile = dict(type='str', required=False, default=None), entry = dict(type='str', required=True), group = dict(type='str', required=False), no_log = dict(type='bool', required=False, default=False), ) # seed the result dict in the object result = dict( changed=False, username='', password='', url='', custom_fields={} ) # Currently no support for a check_mode this maybe added later if # functionality to modify the database is added later module = AnsibleModule( argument_spec=module_args, supports_check_mode=False, ) if IMPORT_ERR: module.fail_json( msg=missing_required_lib("pykeepass"), exception=IMPORT_ERR ) # unlock local keepass database try: kp = keepass.PyKeePass( module.params['database'], password=module.params['password'], keyfile=module.params['keyfile']) except keepass.exceptions.CredentialsError: module.fail_json(msg='Invalid Credentials') # find entry entry = kp.find_entries( title=module.params['entry'], group=module.params['group'] ) # fail is entry is not present if not entry: module.fail_json(msg=f"Unable to find entry: {module.params['entry']}") else: entry = entry[0] custom_field_keys = entry._get_string_field_keys(exclude_reserved=True) custom_fields = dict() for key in custom_field_keys: custom_fields[key] = entry.get_custom_property(key) result = dict ( changed=False, username=entry.username, password=entry.password, url=entry.url, custom_fields=custom_fields ) # in the event of a successful module execution, you will want to # simple AnsibleModule.exit_json(), passing the key/value results module.exit_json(**result) def main(): run_module() if __name__ == '__main__': main()