How to view/decrypt Ansible vault credentials files from within a Python script?

落爺英雄遲暮 提交于 2020-08-21 11:36:07

问题


I'm trying to figure out how to provide the following facilities to a Python script so that it can:

  1. Import Ansible Python modules
  2. Open up my defined ansible.cfg and read vault_password_file variable
  3. Read vault_password_file and temporarily store in a Python variable
  4. Decrypt a referenced Ansible vaulted file

I found this code via google but it did not appear to work when I tried it:

import ansible.utils

bar = dict()

bar = ansible.utils._load_vars_from_path("secrets.yml", results=bar, vault_password="password")

print bar

Throws this error:

$ python ansible-vault-ex.py
Traceback (most recent call last):
  File "ansible-vault-ex.py", line 5, in <module>
    bar = ansible.utils._load_vars_from_path("credentials.vault", results=bar, vault_password="password")
AttributeError: 'module' object has no attribute '_load_vars_from_path'

When I investigated this I saw no indications of this function in any Ansible related files, leading me to believe that this method no longer worked with some newer version(s) of Ansible.

Bottom line is that I'd like some method for importing Ansible libraries/modules from a Python script, so that I can interact with ansible-vault managed files programmatically from Python.


回答1:


Extending Kuba's answer, ansible-vault is a wrapper around VaultLib. It nicely handles the pre Ansible 2.4 version of Vaultlib along with the post 2.4 version.

The ansible-vault load() method not only decrypts the file, but it also parses it and returns the contents as a dict. If you want the contents without parsing, it is probably easiest to just extend ansible-vault with something like:

from ansible_vault import Vault

class MyVault(Vault):
    def load_raw(self, stream):
        return self.vault.decrypt(stream)

    def dump_raw(self, text, stream=None):
        encrypted = self.vault.encrypt(text)
        if stream:
            stream.write(encrypted)
        else:
            return encrypted



回答2:


Consider using the the ansible-vault package

Install it by:

$ pip install ansible-vault

and then it is as simple as:

from ansible_vault import Vault

vault = Vault('password')
print vault.load(open('/path/to/your/vault.yml').read())

To use the ansible code directly look at the source of that package. The simplest would be:

Ansible <= 2.3

from ansible.parsing.vault import VaultLib

vault = VaultLib('password')
print vault.decrypt(open('/path/to/vault.yml').read())

Ansible >= 2.4

from ansible.constants import DEFAULT_VAULT_ID_MATCH
from ansible.parsing.vault import VaultLib
from ansible.parsing.vault import VaultSecret

vault = VaultLib([(DEFAULT_VAULT_ID_MATCH, VaultSecret('password'))])
print vault.decrypt(open('/path/to/vault.yml').read())

The amount of source code is equal but the package provides automatic yaml parsing + handling of both Ansible versions.




回答3:


If you have configured a vault_password_file in ansible.cfg, you can pass the password to VaultLib as followed

Import :

from ansible import constants as C
from ansible.parsing.vault import VaultLib
from ansible.cli import CLI
from ansible.parsing.dataloader import DataLoader

And then, you can call :

loader = DataLoader()
vault_secret = CLI.setup_vault_secrets(
    loader=loader,
    vault_ids=C.DEFAULT_VAULT_IDENTITY_LIST
)
vault = VaultLib(vault_secret)
vault.decrypt(open('/path/to/vault.yml').read())



回答4:


You want to read and decrypt an encrypted file by Ansible Python API, right?

In Ansible 2.0 and above:

def execute_ansible_command(play_source, stdout_callback):
    from ansible.executor.task_queue_manager import TaskQueueManager
    from ansible.inventory import Inventory
    from ansible.parsing.dataloader import DataLoader
    from ansible.playbook import Play
    from ansible.vars import VariableManager

    Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'remote_user',
                                     'private_key_file', 'ssh_common_args', 'ssh_extra_args', 'sftp_extra_args',
                                     'scp_extra_args', 'become', 'become_method', 'become_user', 'verbosity',
                                     'check'])
    variable_manager = VariableManager()
    loader = DataLoader()
    loader.set_vault_password(ANSIBLE_VAULT_PASS)
    options = Options(connection='smart', module_path=None, forks=100,
                      remote_user=None, private_key_file=None, ssh_common_args="-o StrictHostKeyChecking=no",
                      ssh_extra_args=None, sftp_extra_args=None, scp_extra_args=None, become=None,
                      become_method=None, become_user=None, verbosity=None, check=False)
    passwords = dict()
    inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list=ANSIBLE_INVENTORY)
    variable_manager.set_inventory(inventory)

    play = Play().load(play_source, variable_manager=variable_manager, loader=loader)

    tqm = None
    try:
        tqm = TaskQueueManager(
            inventory=inventory,
            variable_manager=variable_manager,
            loader=loader,
            options=options,
            passwords=passwords,
            stdout_callback=stdout_callback,
        )
        tqm.run(play)
    finally:
        if tqm is not None:
            tqm.cleanup()

The DataLoader class is used to load and parse YAML or JSON content, and it has a set_vault_password function, You can send a vault password to decrypt a vault-encrypted file




回答5:


broferek's answer works if your entire yaml file is encrypted. if your yaml file is unencrypted but contains encrypted variables, it'll complain. This should work either way:

import:

from ansible import constants as C
from ansible.cli import CLI
from ansible.parsing.vault import VaultLib
from ansible.parsing.dataloader import DataLoader

then use the DataLoader class to read the file in to a dict

cfgfile = "/path/to/yaml/file.yml"
loader = DataLoader()
vault_secrets = CLI.setup_vault_secrets(loader=loader,
            vault_ids=C.DEFAULT_VAULT_IDENTITY_LIST)
loader.set_vault_secrets(vault_secrets)
data = loader.load_from_file(cfgfile)
pprint.pprint(data)



回答6:


I couldn't get the decryption working with the above answers but I do have a function here that uses subprocess and works well:

def vault_decrypt(text, vault_password_file):
    """
    Calls ansible vault and pass the payload via stdin
    :param text: str, text to decode
    :param vault_password_file: str, vault password
    :return: str, decoded text
    """
    cmd = ['ansible-vault', 'decrypt', '--vault-password-file={}'.format(vault_password_file)]
    p = Popen(cmd,
              stdout=PIPE, stdin=PIPE, stderr=PIPE)
    output = p.communicate(input=str.encode(text))
    return output[0].decode("utf-8")

If I do update it to use the ansible python module directly I'll update. Cheers.




回答7:


This isn't exactly what I want but did work around my above issue by running the ansible view <vaultfile> command via subprocess.

import subprocess
import yaml

def getCreds():
    cmd = "ansible-vault view credentials.vault"
    process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    (result, error) = process.communicate()

    resultYaml = yaml.load(result)

    accesskey = resultYaml['accesskey']
    secretkey = resultYaml['secretkey']

    return(accesskey, secretkey)

Directly calling Ansible methods directly in Python would still be best.



来源:https://stackoverflow.com/questions/44142208/how-to-view-decrypt-ansible-vault-credentials-files-from-within-a-python-script

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!