#!/usr/bin/env python # (c) 2019 Cr@ns # Authors : Alexandre IOOSS # Based on cranspasswords by : Daniel Stan # Vincent Le Gallic # # This file is part of Cr@ns ansible deploiement """ Ansible Vault CransPassword script. ======================================== Returns Ansible vault from CransPassword. Configuration is read from `vault_cranspassword.ini`. """ import json import os import subprocess import sys from ansible.module_utils.six.moves import configparser from ansible.plugins.vars import BaseVarsPlugin class VarsModule(BaseVarsPlugin): @staticmethod def gpg_decrypt(crypt_text): full_command = ['gpg', '-d'] proc = subprocess.Popen(full_command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=sys.stderr, close_fds=True) proc.stdin.write(crypt_text.encode()) proc.stdin.close() clear_text = proc.stdout.read().decode() return clear_text def getfile_command(self, filename): """Exécute la commande distante, et retourne la sortie de cette commande""" # Get full command from settings file command = self.config.get('cranspassword', 'server_cmd').split(" ") command.append("getfiles") proc = subprocess.Popen( command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=sys.stderr, close_fds=True ) proc.stdin.write(json.dumps([filename]).encode()) proc.stdin.flush() raw_out, raw_err = proc.communicate() ret = proc.returncode if ret != 0: print("Mauvais code retour côté serveur", file=sys.stderr) sys.exit(ret) try: answer = json.loads(raw_out.strip()) except ValueError: print("Impossible de parser le résultat", file=sys.stderr) sys.exit(42) return answer[0] def get_encrypted(self, filename): """ Get encrypted content of a cranspassword file """ gotit, value = self.getfile_command(filename) if not gotit: print(value, file=sys.stderr) # value contient le message d'erreur else: crypt_text = value['contents'] return crypt_text def __init__(self): super().__init__() # Load config self.config = configparser.ConfigParser() self.config.read(os.path.dirname(os.path.realpath(__file__)) + '/vault_cranspassword.ini') def get_vars(self, loader, path, entities, cache=True): """ Get all vars for entities, called by Ansible """ super().get_vars(loader, path, entities) # We do not want to request N time the same file from cranspassword # But VarsModule object get instanced each time # So the hack is to use loader._FILE_CACHE that *should* be private # Sorry for this, don't judge me on this please <3 if 'cranspassword' not in loader._FILE_CACHE: # Get text then decrypt and return crypt_text = self.get_encrypted('ansible_vault') clear_text = self.gpg_decrypt(crypt_text) data = loader.load(clear_text) loader._FILE_CACHE['cranspassword'] = data else: data = loader._FILE_CACHE['cranspassword'] return data