Пример #1
0
 def test_encrypt_decrypt_aes256(self):
     if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
         raise SkipTest
     v = VaultLib('ansible')
     v.cipher_name = 'AES256'
     enc_data = v.encrypt("foobar")
     dec_data = v.decrypt(enc_data)
     assert enc_data != "foobar", "encryption failed"
     assert dec_data == "foobar", "decryption failed"
Пример #2
0
 def test_encrypt_encrypted(self):
     if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
         raise SkipTest
     v = VaultLib('ansible')
     v.cipher_name = 'AES'
     data = "$ANSIBLE_VAULT;9.9;TEST\n%s" % hexlify(six.b("ansible"))
     error_hit = False
     try:
         enc_data = v.encrypt(data)
     except errors.AnsibleError as e:
         error_hit = True
     assert error_hit, "No error was thrown when trying to encrypt data with a header"
Пример #3
0
 def test_cipher_not_set(self):
     # not setting the cipher should default to AES256
     if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
         raise SkipTest
     v = VaultLib('ansible')
     data = "ansible"
     error_hit = False
     try:
         enc_data = v.encrypt(data)
     except errors.AnsibleError as e:
         error_hit = True
     assert not error_hit, "An error was thrown when trying to encrypt data without the cipher set"
     assert v.cipher_name == "AES256", "cipher name is not set to AES256: %s" % v.cipher_name
Пример #4
0
def vault_encrypt(plaintext, secret):
    '''
    Vault encrypt a piece of data.
    '''
    try:
        vault = VaultLib()
        secret_file = get_file_vault_secret(filename=secret,
                                            loader=DataLoader())
        secret_file.load()
        vault.secrets = [('default', secret_file)]

        return vault.encrypt(plaintext)
    except AnsibleError as e:
        logger.critical(f"Cannot encrypt string: {e}")
        sys.exit(1)
Пример #5
0
def vault_encrypt(plaintext, secret):
    """
    Vault encrypt a piece of data.
    """
    try:
        vault = VaultLib()
        secret_file = get_file_vault_secret(filename=secret,
                                            loader=DataLoader())
        secret_file.load()
        vault.secrets = [("default", secret_file)]

        return vault.encrypt(plaintext)
    except AnsibleError as e:
        logger.critical("Cannot encrypt string: {}".format(e))
        sys.exit(1)
Пример #6
0
def vault_encrypt(plaintext, secret):
    """
    Vault encrypt a piece of data.
    """
    try:
        vault = VaultLib()
        secret_file = get_file_vault_secret(filename=secret,
                                            loader=DataLoader())
        secret_file.load()
        vault.secrets = [('default', secret_file)]

        return vault.encrypt(plaintext)
    except AnsibleError as exc:
        LOGGER.critical('Cannot encrypt string: %s', exc)
        sys.exit(1)
Пример #7
0
class Vault(object):
    '''R/W an ansible-vault yaml file'''

    def __init__(self, password):
        self._ansible_ver = _ansible_ver

        self.secret = password.encode('utf-8')
        self.vault = VaultLib(self._make_secrets(self.secret))

    def _make_secrets(self, secret):
        if self._ansible_ver < 2.4:
            return secret

        from ansible.constants import DEFAULT_VAULT_ID_MATCH
        from ansible.parsing.vault import VaultSecret
        return [(DEFAULT_VAULT_ID_MATCH, VaultSecret(secret))]

    def load_raw(self, stream):
        """Read vault stream and return raw data."""
        return self.vault.decrypt(stream)

    def dump_raw(self, text, stream=None):
        """Encrypt raw data and write to stream."""
        encrypted = self.vault.encrypt(text)
        if stream:
            stream.write(encrypted)
        else:
            return encrypted

    def load(self, stream):
        """Read vault steam and return python object."""
        return yaml.safe_load(self.load_raw(stream))

    def dump(self, data, stream=None):
        """Encrypt data and print stdout or write to stream."""
        yaml_text = yaml.dump(
            data,
            default_flow_style=False,
            allow_unicode=True)
        return self.dump_raw(yaml_text, stream=stream)
Пример #8
0
def encrypt_string(passwordfile, var_name, plain_text):
    loader = DataLoader()
    secret = FileVaultSecret(filename=passwordfile,
                             encoding='utf8',
                             loader=loader)
    secret.load()

    encrypt_vault_id = 'default'
    encrypt_secret = secret

    vault_secrets = [(encrypt_vault_id, encrypt_secret)]

    vault = VaultLib(vault_secrets)
    b_vaulttext = vault.encrypt(plain_text,
                                secret=secret,
                                vault_id=encrypt_vault_id)
    code = b_vaulttext.decode()

    code = code.rstrip()
    code = code.replace("\n", "\n      ")

    return f"{var_name}: !vault |\n      {code}"
Пример #9
0
def encrypt_file(path, password_file, newpath=None, secrets=None):
    """Encrypts an Ansible Vault file. Returns encrypted data. Set newpath to
        write the result somewhere. Set secrets to specify inline secret addresses."""
    # log.debug('Reading decrypted data from {}...'.format(path))
    with open(path, 'rb') as f:
        data = f.read()

    if not data:
        raise ValueError('Unable to parse/read file {}'.format(path))
    else:
        log.debug('Got vars/file: {}'.format(data))

    with open(password_file) as f:
        p = f.read().strip()
        log.debug('Read pass from {}: {}'.format(password_file, p))

    vault = VaultLib([(DEFAULT_VAULT_ID_MATCH, VaultSecret(p.encode('utf-8')))
                      ])
    encrypted = vault.encrypt(data)
    with open(newpath, 'wb') as f:
        f.write(encrypted)
    return encrypted
Пример #10
0
class Vault(object):
    '''R/W an ansible-vault yaml file'''
    def __init__(self, password):
        self._ansible_ver = _ansible_ver

        self.secret = password.encode('utf-8')
        self.vault = VaultLib(self._make_secrets(self.secret))

    def _make_secrets(self, secret):
        if self._ansible_ver < 2.4:
            return secret

        from ansible.constants import DEFAULT_VAULT_ID_MATCH
        from ansible.parsing.vault import VaultSecret
        return [(DEFAULT_VAULT_ID_MATCH, VaultSecret(secret))]

    def load_raw(self, stream):
        """Read vault stream and return raw data."""
        return self.vault.decrypt(stream)

    def dump_raw(self, text, stream=None):
        """Encrypt raw data and write to stream."""
        encrypted = self.vault.encrypt(text)
        if stream:
            stream.write(encrypted)
        else:
            return encrypted

    def load(self, stream):
        """Read vault steam and return python object."""
        return yaml.safe_load(self.load_raw(stream))

    def dump(self, data, stream=None):
        """Encrypt data and print stdout or write to stream."""
        yaml_text = yaml.dump(data,
                              default_flow_style=False,
                              allow_unicode=True)
        return self.dump_raw(yaml_text, stream=stream)
Пример #11
0
class Convert:
    def __init__(self, password):
        self.text = b''
        self.err = ''
        self.password = password
        if password:
            self.vault = VaultLib([(DEFAULT_VAULT_ID_MATCH,
                                    VaultSecret(password.encode("utf-8")))])

    def convert(self, source):
        try:
            if self.vault and source:
                self.decrypt(source) if source.startswith(
                    "$ANSIBLE_VAULT") else self.encrypt(source)
        finally:
            return {
                'password': self.password,
                'source': source,
                'result': self.text.decode('utf-8'),
                'error': self.err
            }

    def encrypt(self, source):
        logging.debug("Request encrypt")
        try:
            self.text = self.vault.encrypt(source)
        except:
            self.err = "Impossible de chiffrer ces informations"
            logging.error("Problem: %s", sys.exc_info()[0])

    def decrypt(self, source):
        logging.debug("Request decrypt")
        try:
            self.text = self.vault.decrypt(source)
        except:
            self.err = "La clé ne correspond pas"
            logging.error("Problem: %s", sys.exc_info()[0])
Пример #12
0
class Vault(object):
    """ R/W an ansible-vault file """
    def __init__(self, password):
        self.password = password

        try:
            from ansible.parsing.vault import VaultSecret
            from ansible.module_utils._text import to_bytes
            pass_bytes = to_bytes(password, encoding='utf-8', errors='strict')
            secrets = [('password', VaultSecret(_bytes=pass_bytes))]
            self.vault = VaultLib(secrets=secrets)
        except ImportError:
            self.vault = VaultLib(password)

    def load(self, stream):
        """ Read vault steam and return python object

        :param stream: The stream to read data from
        :returns: The decrypted data
        """
        try:
            return self.vault.decrypt(stream)
        except AnsibleError:
            print(t("Unable to decrypt using given password"))
            sys.exit(1)

    def load_secure_file(self, secure_file):
        """ Read vault secured file and return python object

        :param secure_file: The file to read data from
        :returns: The decrpted data
        """
        return self.load(open(secure_file).read())

    def load_as_json(self, secure_file):
        """ Read vault secured file and return json decoded object

        :param secure_file: The file to read data from as json
        :returns: The JSON data
        """
        return json.loads(self.load_secure_file(secure_file).decode('UTF-8'))

    def dump(self, data, stream=None):
        """ Encrypt data and print stdout or write to stream

        :param data: The information to be encrypted
        :param stream: If not None the location to write the encrypted data to.
        :returns: If stream is None then the encrypted bytes otherwise None.
        """
        encrypted = self.vault.encrypt(data)
        if stream:
            stream.write(encrypted)
        else:
            return encrypted

    def dump_as_json(self, obj, stream=None):
        """ Convert object to json and encrypt the data.

        :param obj: Python object to convert to json
        :param stream: If not None the location to write the encrypted data to.
            If this is a file in Python 3, it must be open in binary mode.
        :returns: If stream is None then the encrypted bytes otherwise None.
        """
        data = json.dumps(obj, separators=(',', ': '))
        return self.dump(data, stream)

    def dump_as_json_to_file(self, obj, file_path):
        """ Convert object to json and encrypt the data.

        :param obj: Python object to convert to json
        :param file_path: The file to write data to via temp file
        """
        with tempfile.NamedTemporaryFile(delete=False) as data_temp:
            self.dump_as_json(obj, data_temp)
        data_temp.close()
        move(data_temp.name, os.path.abspath(file_path))

    def dump_as_yaml(self, obj, stream=None):
        """ Convert object to yaml and encrypt the data.

        :param obj: Python object to convert to yaml
        :param stream: If not None the location to write the encrypted data to.
        :returns: If stream is None then the encrypted bytes otherwise None.
        """
        data = yaml.dump(obj, default_flow_style=False)
        return self.dump(data, stream)

    def dump_as_yaml_to_file(self, obj, file_path):
        """ Convert object to yaml and encrypt the data.

        :param obj: Python object to convert to yaml
        :param file_path: The file to write data to via temp file
        """
        with tempfile.NamedTemporaryFile(delete=False) as data_temp:
            self.dump_as_yaml(obj, data_temp)
        data_temp.close()
        move(data_temp.name, os.path.abspath(file_path))
Пример #13
0
class Vault(object):
    """ R/W an ansible-vault file """
    def __init__(self, password):
        self.password = password
        self.vault = VaultLib(password)

    def load(self, stream):
        """ Read vault steam and return python object
        :param stream: The stream to read data from
        :returns: The decrypted data
        """
        return self.vault.decrypt(stream)

    def load_secure_file(self, secure_file):
        """ Read vault secured file and return python object
        :param secure_file: The file to read data from
        :returns: The decrpted data
        """
        return self.load(open(secure_file).read())

    def load_as_json(self, secure_file):
        """ Read vault secured file and return json decoded object
        :param secure_file: The file to read data from as json
        :returns: The JSON data
        """
        return json.loads(self.load_secure_file(secure_file).decode('UTF-8'))

    def dump(self, data, stream=None):
        """ Encrypt data and print stdout or write to stream
        :param data: The information to be encrypted
        :param stream: If not None the location to write the encrypted data to.
        :returns: If stream is None then the encrypted bytes otherwise None.
        """
        encrypted = self.vault.encrypt(data)
        if stream:
            stream.write(encrypted)
        else:
            return encrypted

    def dump_as_json(self, obj, stream=None):
        """ Convert object to json and encrypt the data.
        :param obj: Python object to convert to json
        :param stream: If not None the location to write the encrypted data to.
        :returns: If stream is None then the encrypted bytes otherwise None.
        """
        data = json.dumps(obj, separators=(',', ': '))
        return self.dump(data, stream)

    def dump_as_json_to_file(self, obj, file_path):
        """ Convert object to json and encrypt the data.
        :param obj: Python object to convert to json
        :param file_path: The file to write data to via temp file
        """
        tempdir = gettempdir()
        tempfilename = 'tmp_' + str(uuid.uuid4())
        temppath = os.path.join(tempdir, tempfilename)
        with open(temppath, 'wb') as data_temp:
            self.dump_as_json(obj, data_temp)
        data_temp.close()
        move(temppath, os.path.abspath(file_path))

    def dump_as_yaml(self, obj, stream=None):
        """ Convert object to yaml and encrypt the data.
        :param obj: Python object to convert to yaml
        :param stream: If not None the location to write the encrypted data to.
        :returns: If stream is None then the encrypted bytes otherwise None.
        """
        data = yaml.dump(obj, default_flow_style=False)
        return self.dump(data, stream)

    def dump_as_yaml_to_file(self, obj, file_path):
        """ Convert object to yaml and encrypt the data.
        :param obj: Python object to convert to yaml
        :param file_path: The file to write data to via temp file
        """
        tempdir = gettempdir()
        tempfilename = 'tmp_' + str(uuid.uuid4())
        temppath = os.path.join(tempdir, tempfilename)
        with open(temppath, 'wb') as data_temp:
            self.dump_as_yaml(obj, data_temp)
        data_temp.close()
        move(temppath, os.path.abspath(file_path))
Пример #14
0
class VaultpwCLI(CLI):
    '''
    '''

    VALID_ACTIONS = frozenset(("show", "store"))

    def __init__(self, args):
        super().__init__(args)

        self.encrypt_secret = None
        self.encrypt_vault_id = None

    def set_action(self):
        super().set_action()

        if self.action == "show":
            self.parser.set_usage(
                "usage: %prog show /path/to/example_password.yml")
        elif self.action == "store":
            self.parser.set_usage(
                "usage: %prog store /path/to/example_password.yml [-c command]"
            )
            self.parser.add_option('-c',
                                   '--command',
                                   dest='password_command',
                                   action='store',
                                   type='string',
                                   help="command to run to obtain a password")
            self.parser.add_option(
                '--encrypt-vault-id',
                default=[],
                dest='encrypt_vault_id',
                action='store',
                type='string',
                help=
                'the vault id used to encrypt (required if more than one vault-id is provided)'
            )

    def init_parser(self):
        super().init_parser(
            usage="usage: %%prog [%s] [options] /path/to/example_password.yml"
            % "|".join(sorted(self.VALID_ACTIONS)),
            desc=
            "utility to store or fetch vault-encrypted passwords in YAML inventory files",
            epilog=
            "\nSee '%s <command> --help' for more information on a specific command.\n\n"
            % os.path.basename(sys.argv[0]))
        opt_help.add_vault_options(self.parser)

        self.set_action()

    def post_process_args(self, options, args):
        options, args = super().post_process_args(options, args)
        self.validate_conflicts(options,
                                vault_opts=True,
                                vault_rekey_opts=False)

        display.verbosity = options.verbosity

        if options.vault_ids:
            for vault_id in options.vault_ids:
                if u';' in vault_id:
                    raise AnsibleOptionsError(
                        "Invalid character ';' found in vault id: %s" %
                        vault_id)

        return options, args

    def run(self):
        super().run()
        self.loader = DataLoader()

        vault_ids = C.DEFAULT_VAULT_IDENTITY_LIST + list(
            context.CLIARGS['vault_ids'])
        vault_secrets = self.setup_vault_secrets(
            self.loader,
            vault_ids=vault_ids,
            vault_password_files=list(context.CLIARGS['vault_password_files']),
            ask_vault_pass=context.CLIARGS['ask_vault_pass'])

        if not vault_secrets:
            raise AnsibleOptionsError(
                "A vault password is required to use ansible-vault")

        encrypt_vault_id = context.CLIARGS.get(
            'encrypt_vault_id') or C.DEFAULT_VAULT_ENCRYPT_IDENTITY
        if len(vault_secrets) > 1 and not encrypt_vault_id:
            raise AnsibleOptionsError(
                "Use '--encrypt-vault-id id' to choose one of the following vault ids to use for encryption: %s"
                % ','.join([x[0] for x in vault_secrets]))

        encrypt_secret = match_encrypt_secret(
            vault_secrets, encrypt_vault_id=encrypt_vault_id)

        self.encrypt_vault_id = encrypt_secret[0]
        self.encrypt_secret = encrypt_secret[1]

        self.loader.set_vault_secrets(vault_secrets)

        self.vault = VaultLib(vault_secrets)

        if len(context.CLIARGS['args']) != 1:
            raise AnsibleOptionsError(
                "Exactly one inventory file must be specified")

        self.file = os.path.expanduser(context.CLIARGS['args'][0])

        old_umask = os.umask(0o077)

        self.execute()

        os.umask(old_umask)

    def execute_store(self):
        '''
        Takes the path to an inventory file such as
        inventory/group_vars/tag_Cluster_xxx/secrets/example_password.yml and
        overwrites the file with an assignment of "example_password: password"
        in vault-encrypted YAML format. The password is obtained by prompting
        the user or, if a command is specified, by running the command and
        reading stdout.
        '''

        b_plaintext = b''

        command = context.CLIARGS['password_command']
        if command:
            try:
                pw = subprocess.run(command, capture_output=True)
                if pw.returncode != 0:
                    raise Exception('non-zero exit code: %s' % pw.returncode)
                b_plaintext = pw.stdout.strip()
            except Exception as e:
                print("ERROR: password command failed: %s" % str(e),
                      file=sys.stderr)
                sys.exit(-1)
        else:
            b_plaintext = to_bytes(display.prompt("Password: "******"ERROR: cannot encrypt password: %s" % str(e),
                  file=sys.stderr)
            sys.exit(-1)

        name = os.path.basename(self.file).replace('.yml', '')

        lines = []
        lines.append("%s: !vault |\n" % name)
        for l in to_text(b_ciphertext).splitlines():
            lines.append("    %s\n" % l)

        try:
            fh = open(self.file, 'wb')
            fh.write(to_bytes(''.join(lines)))
            fh.close()
        except Exception as e:
            print("ERROR: cannot write output to %s: %s" % (self.file, str(e)),
                  file=sys.stderr)
            sys.exit(-1)

    def execute_show(self):
        '''
        Takes the path to an inventory file such as
        inventory/group_vars/tag_Cluster_xxx/secrets/example_password.yml and
        prints the password defined therein. The file must contain a variable
        assignment of the form "example_password: password"; either the whole
        file is vault-encrypted, or only the password is.
        '''

        if not os.path.exists(self.file):
            print("ERROR: inventory file does not exist: %s" % self.file,
                  file=sys.stderr)
            sys.exit(-1)

        try:
            name = os.path.basename(self.file).replace('.yml', '')
            y = self.loader.load_from_file(self.file)
            print(y[name])
        except Exception as e:
            print("ERROR: cannot show password from %s: %s" %
                  (self.file, str(e)),
                  file=sys.stderr)
            sys.exit(-1)
Пример #15
0
    def test_encrypt(self):
        v = VaultLib(password='******')
        plaintext = u'Some text to encrypt.'
        ciphertext = v.encrypt(plaintext)

        self.assertIsInstance(ciphertext, (bytes, str))
    def create(self):
        try:
            print('')
            new_file = self.args.file
            if new_file is None:
                new_file = input('File to create: ')
            if os.path.exists(os.path.join(self.args.vault_path, new_file)):
                eprint('This file already exists')
                sys.exit(2)

            plugin_name = self.args.plugin
            if plugin_name is None:
                plugin_name = input('Keyring plugin name to use [' +
                                    ', '.join(list_plugins()) + ']: ')
            plugin = self.get_plugin_instance(plugin_name)
            id = plugin.generate_id(self.args.plugin_vars)

            print('New ID to use: ' + id)
            print('')
            stdin_pass = self.args.stdin_pass
            if not stdin_pass and sys.stdin.isatty():
                password = getpass.getpass('New password: '******'Confirm password: '******'Passwords missmatch')
                    sys.exit(2)
            else:
                password = sys.stdin.read()

            password = password.strip()
            if password == '':
                print('Your password is empty !')
                sys.exit(2)
        except KeyboardInterrupt:
            print('')
            sys.exit(0)

        try:
            new_version = plugin.set_password(id, password)
            id = plugin.append_id_version(new_version)

            vault_metadata = get_metadata(self.args.vault_path)
            vault_metadata['vault_ids'].append({
                METADATA_ID_KEY: id,
                METADATA_PLUGIN_KEY: plugin_name,
                METADATA_VAULT_FILES: [new_file]
            })
            write_metadata(vault_metadata, self.args.vault_path)

            VaultLib = get_vault_lib()
            vault_api = VaultLib(_make_secrets(password))
            with open(os.path.join(self.args.vault_path, new_file),
                      'w') as stream:
                encrypted = str(vault_api.encrypt('---'), 'utf-8')
                stream.write(encrypted)
        except Exception as e:
            eprint(e)
            sys.exit(2)
            if (self.args.verbose):
                import traceback
                traceback.print_exc()
Пример #17
0
class TestVaultLib(unittest.TestCase):
    def setUp(self):
        self.v = VaultLib('test-vault-password')

    def test_encrypt(self):
        plaintext = u'Some text to encrypt in a café'
        b_vaulttext = self.v.encrypt(plaintext)

        self.assertIsInstance(b_vaulttext, six.binary_type)

        b_header = b'$ANSIBLE_VAULT;1.1;AES256\n'
        self.assertEqual(b_vaulttext[:len(b_header)], b_header)

    def test_encrypt_bytes(self):

        plaintext = to_bytes(u'Some text to encrypt in a café')
        b_vaulttext = self.v.encrypt(plaintext)

        self.assertIsInstance(b_vaulttext, six.binary_type)

        b_header = b'$ANSIBLE_VAULT;1.1;AES256\n'
        self.assertEqual(b_vaulttext[:len(b_header)], b_header)

    def test_is_encrypted(self):
        self.assertFalse(
            self.v.is_encrypted(b"foobar"),
            msg="encryption check on plaintext yielded false positive")
        b_data = b"$ANSIBLE_VAULT;9.9;TEST\n%s" % hexlify(b"ansible")
        self.assertTrue(self.v.is_encrypted(b_data),
                        msg="encryption check on headered text failed")

    def test_format_output(self):
        self.v.cipher_name = "TEST"
        b_ciphertext = b"ansible"
        b_vaulttext = self.v._format_output(b_ciphertext)
        b_lines = b_vaulttext.split(b'\n')
        self.assertGreater(len(b_lines),
                           1,
                           msg="failed to properly add header")

        b_header = b_lines[0]
        self.assertTrue(b_header.endswith(b';TEST'),
                        msg="header does not end with cipher name")

        b_header_parts = b_header.split(b';')
        self.assertEqual(len(b_header_parts),
                         3,
                         msg="header has the wrong number of parts")
        self.assertEqual(b_header_parts[0],
                         b'$ANSIBLE_VAULT',
                         msg="header does not start with $ANSIBLE_VAULT")
        self.assertEqual(b_header_parts[1],
                         self.v.b_version,
                         msg="header version is incorrect")
        self.assertEqual(b_header_parts[2],
                         b'TEST',
                         msg="header does not end with cipher name")

    def test_split_header(self):
        b_vaulttext = b"$ANSIBLE_VAULT;9.9;TEST\nansible"
        b_ciphertext = self.v._split_header(b_vaulttext)
        b_lines = b_ciphertext.split(b'\n')
        self.assertEqual(b_lines[0],
                         b"ansible",
                         msg="Payload was not properly split from the header")
        self.assertEqual(self.v.cipher_name,
                         u'TEST',
                         msg="cipher name was not properly set")
        self.assertEqual(self.v.b_version,
                         b"9.9",
                         msg="version was not properly set")

    def test_encrypt_decrypt_aes(self):
        if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
            raise SkipTest
        self.v.cipher_name = u'AES'
        self.v.b_password = b'ansible'
        # AES encryption code has been removed, so this is old output for
        # AES-encrypted 'foobar' with password 'ansible'.
        b_vaulttext = b'''$ANSIBLE_VAULT;1.1;AES
53616c7465645f5fc107ce1ef4d7b455e038a13b053225776458052f8f8f332d554809d3f150bfa3
fe3db930508b65e0ff5947e4386b79af8ab094017629590ef6ba486814cf70f8e4ab0ed0c7d2587e
786a5a15efeb787e1958cbdd480d076c
'''
        b_plaintext = self.v.decrypt(b_vaulttext)
        self.assertEqual(b_plaintext, b"foobar", msg="decryption failed")

    def test_encrypt_decrypt_aes256(self):
        if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
            raise SkipTest
        self.v.cipher_name = u'AES256'
        plaintext = u"foobar"
        b_vaulttext = self.v.encrypt(plaintext)
        b_plaintext = self.v.decrypt(b_vaulttext)
        self.assertNotEqual(b_vaulttext, b"foobar", msg="encryption failed")
        self.assertEqual(b_plaintext, b"foobar", msg="decryption failed")

    def test_encrypt_decrypt_aes256_existing_vault(self):
        if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
            raise SkipTest
        self.v.cipher_name = u'AES256'
        b_orig_plaintext = b"Setec Astronomy"
        vaulttext = u'''$ANSIBLE_VAULT;1.1;AES256
33363965326261303234626463623963633531343539616138316433353830356566396130353436
3562643163366231316662386565383735653432386435610a306664636137376132643732393835
63383038383730306639353234326630666539346233376330303938323639306661313032396437
6233623062366136310a633866373936313238333730653739323461656662303864663666653563
3138'''

        b_plaintext = self.v.decrypt(vaulttext)
        self.assertEqual(b_plaintext, b_plaintext, msg="decryption failed")

        b_vaulttext = to_bytes(vaulttext, encoding='ascii', errors='strict')
        b_plaintext = self.v.decrypt(b_vaulttext)
        self.assertEqual(b_plaintext,
                         b_orig_plaintext,
                         msg="decryption failed")

    def test_encrypt_decrypt_aes256_bad_hmac(self):
        # FIXME This test isn't working quite yet.
        raise SkipTest

        if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
            raise SkipTest
        self.v.cipher_name = 'AES256'
        # plaintext = "Setec Astronomy"
        enc_data = '''$ANSIBLE_VAULT;1.1;AES256
33363965326261303234626463623963633531343539616138316433353830356566396130353436
3562643163366231316662386565383735653432386435610a306664636137376132643732393835
63383038383730306639353234326630666539346233376330303938323639306661313032396437
6233623062366136310a633866373936313238333730653739323461656662303864663666653563
3138'''
        b_data = to_bytes(enc_data, errors='strict', encoding='utf-8')
        b_data = self.v._split_header(b_data)
        foo = binascii.unhexlify(b_data)
        lines = foo.splitlines()
        # line 0 is salt, line 1 is hmac, line 2+ is ciphertext
        b_salt = lines[0]
        b_hmac = lines[1]
        b_ciphertext_data = b'\n'.join(lines[2:])

        b_ciphertext = binascii.unhexlify(b_ciphertext_data)
        # b_orig_ciphertext = b_ciphertext[:]

        # now muck with the text
        # b_munged_ciphertext = b_ciphertext[:10] + b'\x00' + b_ciphertext[11:]
        # b_munged_ciphertext = b_ciphertext
        # assert b_orig_ciphertext != b_munged_ciphertext

        b_ciphertext_data = binascii.hexlify(b_ciphertext)
        b_payload = b'\n'.join([b_salt, b_hmac, b_ciphertext_data])
        # reformat
        b_invalid_ciphertext = self.v._format_output(b_payload)

        # assert we throw an error
        self.v.decrypt(b_invalid_ciphertext)

    def test_encrypt_encrypted(self):
        if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
            raise SkipTest
        self.v.cipher_name = u'AES'
        b_vaulttext = b"$ANSIBLE_VAULT;9.9;TEST\n%s" % hexlify(b"ansible")
        vaulttext = to_text(b_vaulttext, errors='strict')
        self.assertRaises(errors.AnsibleError, self.v.encrypt, b_vaulttext)
        self.assertRaises(errors.AnsibleError, self.v.encrypt, vaulttext)

    def test_decrypt_decrypted(self):
        if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
            raise SkipTest
        plaintext = u"ansible"
        self.assertRaises(errors.AnsibleError, self.v.decrypt, plaintext)

        b_plaintext = b"ansible"
        self.assertRaises(errors.AnsibleError, self.v.decrypt, b_plaintext)

    def test_cipher_not_set(self):
        # not setting the cipher should default to AES256
        if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
            raise SkipTest
        plaintext = u"ansible"
        self.v.encrypt(plaintext)
        self.assertEquals(self.v.cipher_name, "AES256")
 def encrypt_file(self, password):
     # TODO change to use vault property
     vault = VaultLib([(DEFAULT_VAULT_ID_MATCH, VaultSecret(to_bytes(password)))])
     buffer = self.vim.current.buffer
     buffer[:] = vault.encrypt("\n".join(buffer[:])).split()
Пример #19
0
class Vault():
    """Read and write data using the Ansible vault."""
    def __init__(self, password):
        """Create a vault."""
        self.password = password
        try:
            from ansible.parsing.vault import VaultSecret
            from ansible.module_utils._text import to_bytes
            pass_bytes = to_bytes(password, encoding='utf-8', errors='strict')
            secrets = [('password', VaultSecret(_bytes=pass_bytes))]
            # pylint: disable=unexpected-keyword-arg, no-value-for-parameter
            self.vault = VaultLib(secrets=secrets)
        except ImportError:
            self.vault = VaultLib(password)

    # pylint: disable=inconsistent-return-statements
    def dump(self, data, stream=None):
        """Encrypt data and print stdout or write to stream.

        :param data: The information to be encrypted
        :param stream: If not None the location to write the encrypted data to.
        :returns: If stream is None then the encrypted bytes otherwise None.
        """
        encrypted = self.vault.encrypt(data)
        if stream:
            stream.write(encrypted)
        else:
            return encrypted

    def load(self, stream):
        """Read vault steam and return python object.

        :param stream: The stream to read data from
        :returns: The decrypted data
        """
        return self.vault.decrypt(stream)

    def dump_as_yaml(self, obj, stream=None):
        """Convert object to yaml and encrypt the data.

        :param obj: Python object to convert to yaml
        :param stream: If not None the location to write the encrypted data to.
        :returns: If stream is None then the encrypted bytes otherwise None.
        """
        data = yaml.dump(obj,
                         allow_unicode=True,
                         default_flow_style=False,
                         Dumper=AnsibleDumper)
        return self.dump(data, stream)

    def dump_as_yaml_to_tempfile(self, obj):
        """Convert object to yaml and encrypt the data.

        :param obj: Python object to convert to yaml
        :returns: The filepath to write data
        """
        with tempfile.NamedTemporaryFile(delete=False,
                                         suffix='.yaml') as data_temp:
            self.dump_as_yaml(obj, data_temp)
        data_temp.close()
        return data_temp.name
            vault_pw = VaultSecret(pw)
            vault_pw.load()
    except FileNotFoundError:
        print("Password file not found")
        sys.exit(1)
else:
    vault_pw = PromptVaultSecret(prompt_formats=["password: "])
    vault_pw.load()

vl = VaultLib(secrets=[
    (None, vault_pw)
])

def to_yaml(representer, node):
    return representer.represent_scalar('!vault', node, style='|')

yaml = YAML()
yaml.indent(mapping=2, sequence=4, offset=2)
yaml.representer.add_representer(LiteralScalarString, to_yaml)

with open(sys.argv[1], 'r') as orig:
    y = yaml.load(orig)

for value in y:
    y[value] = vl.encrypt(y[value], vault_pw).decode('utf-8')

scalarstring.walk_tree(y)

with open(sys.argv[2], 'w') as dest:
    yaml.dump(y, dest)
Пример #21
0
def ansible_vault_encrypt(key, text):
    vault = VaultLib([(DEFAULT_VAULT_ID_MATCH,
                       VaultSecret(key.rstrip('\n').encode('utf-8')))])
    return vault.encrypt(text.rstrip('\n').encode('utf-8')).decode('utf-8')
Пример #22
0
    def run(self, tmp=None, task_vars=None):
        if task_vars is None:
            task_vars = dict()

        result = super(ActionModule, self).run(tmp, task_vars)
        del tmp  # tmp is deprecated

        # If user supplies vault-id and vault-pass, use them.  Otherwise use those that are automatically loaded with the playbook
        if 'vaultpass' in self._task.args:
            oVaultSecret = VaultSecret(
                self._task.args["vaultpass"].encode('utf-8'))
            if 'vaultid' in self._task.args:
                oVaultLib = VaultLib([(self._task.args["vaultid"],
                                       oVaultSecret)])
            else:
                display.v(u'No vault-id supplied, using default identity.')
                oVaultLib = VaultLib([(C.DEFAULT_VAULT_IDENTITY, oVaultSecret)
                                      ])
        else:
            display.v(
                u'No vault-id or vault-pass supplied, using playbook-sourced variables.'
            )
            oVaultLib = self._loader._vault
            if len(self._loader._vault.secrets) == 0:
                display.warning(
                    "No Vault secrets loaded by config and none supplied to plugin.  Vault operations are not possible."
                )

        if self._task.args["action"] == "encrypt":
            if "plaintext" not in self._task.args:
                return {
                    "failed": True,
                    "msg": "'plaintext' is required for encrypt."
                }

            b_vaulttext = oVaultLib.encrypt(self._task.args["plaintext"])
            b_ciphertext, b_version, cipher_name, vault_id = parse_vaulttext_envelope(
                b_vaulttext)

            vaulttext_header = b_vaulttext.decode('utf-8').split('\n', 1)[0]
            ciphertext = b_ciphertext.decode('utf-8')

            if 'multiline_out' in self._task.args and self._task.args[
                    "multiline_out"] == True:
                multiline_length = 80
                ciphertext = '\n'.join([
                    ciphertext[i:i + multiline_length]
                    for i in range(0, len(ciphertext), multiline_length)
                ])

            result['vaulttext'] = vaulttext_header + "\n" + ciphertext
            result['plaintext'] = self._task.args["plaintext"]

        else:
            if "vaulttext" not in self._task.args:
                return {
                    "failed": True,
                    "msg": "'vaulttext' is required for decrypt."
                }

            plaintext = oVaultLib.decrypt(self._task.args["vaulttext"])
            result['vaulttext'] = self._task.args["vaulttext"]
            result['plaintext'] = plaintext

        result['failed'] = False

        return result
Пример #23
0
class TestVaultLib(unittest.TestCase):
    def setUp(self):
        self.v = VaultLib('test-vault-password')

    def test_encrypt(self):
        plaintext = u'Some text to encrypt in a café'
        b_vaulttext = self.v.encrypt(plaintext)

        self.assertIsInstance(b_vaulttext, six.binary_type)

        b_header = b'$ANSIBLE_VAULT;1.1;AES256\n'
        self.assertEqual(b_vaulttext[:len(b_header)], b_header)

    def test_encrypt_bytes(self):

        plaintext = to_bytes(u'Some text to encrypt in a café')
        b_vaulttext = self.v.encrypt(plaintext)

        self.assertIsInstance(b_vaulttext, six.binary_type)

        b_header = b'$ANSIBLE_VAULT;1.1;AES256\n'
        self.assertEqual(b_vaulttext[:len(b_header)], b_header)

    def test_is_encrypted(self):
        self.assertFalse(self.v.is_encrypted(b"foobar"), msg="encryption check on plaintext yielded false positive")
        b_data = b"$ANSIBLE_VAULT;9.9;TEST\n%s" % hexlify(b"ansible")
        self.assertTrue(self.v.is_encrypted(b_data), msg="encryption check on headered text failed")

    def test_format_output(self):
        self.v.cipher_name = "TEST"
        b_ciphertext = b"ansible"
        b_vaulttext = self.v._format_output(b_ciphertext)
        b_lines = b_vaulttext.split(b'\n')
        self.assertGreater(len(b_lines), 1, msg="failed to properly add header")

        b_header = b_lines[0]
        self.assertTrue(b_header.endswith(b';TEST'), msg="header does not end with cipher name")

        b_header_parts = b_header.split(b';')
        self.assertEqual(len(b_header_parts), 3, msg="header has the wrong number of parts")
        self.assertEqual(b_header_parts[0], b'$ANSIBLE_VAULT', msg="header does not start with $ANSIBLE_VAULT")
        self.assertEqual(b_header_parts[1], self.v.b_version, msg="header version is incorrect")
        self.assertEqual(b_header_parts[2], b'TEST', msg="header does not end with cipher name")

    def test_split_header(self):
        b_vaulttext = b"$ANSIBLE_VAULT;9.9;TEST\nansible"
        b_ciphertext = self.v._split_header(b_vaulttext)
        b_lines = b_ciphertext.split(b'\n')
        self.assertEqual(b_lines[0], b"ansible", msg="Payload was not properly split from the header")
        self.assertEqual(self.v.cipher_name, u'TEST', msg="cipher name was not properly set")
        self.assertEqual(self.v.b_version, b"9.9", msg="version was not properly set")

    def test_encrypt_decrypt_aes(self):
        if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
            raise SkipTest
        self.v.cipher_name = u'AES'
        self.v.b_password = b'ansible'
        # AES encryption code has been removed, so this is old output for
        # AES-encrypted 'foobar' with password 'ansible'.
        b_vaulttext = b'''$ANSIBLE_VAULT;1.1;AES
53616c7465645f5fc107ce1ef4d7b455e038a13b053225776458052f8f8f332d554809d3f150bfa3
fe3db930508b65e0ff5947e4386b79af8ab094017629590ef6ba486814cf70f8e4ab0ed0c7d2587e
786a5a15efeb787e1958cbdd480d076c
'''
        b_plaintext = self.v.decrypt(b_vaulttext)
        self.assertEqual(b_plaintext, b"foobar", msg="decryption failed")

    def test_encrypt_decrypt_aes256(self):
        if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
            raise SkipTest
        self.v.cipher_name = u'AES256'
        plaintext = u"foobar"
        b_vaulttext = self.v.encrypt(plaintext)
        b_plaintext = self.v.decrypt(b_vaulttext)
        self.assertNotEqual(b_vaulttext, b"foobar", msg="encryption failed")
        self.assertEqual(b_plaintext, b"foobar", msg="decryption failed")

    def test_encrypt_decrypt_aes256_existing_vault(self):
        if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
            raise SkipTest
        self.v.cipher_name = u'AES256'
        b_orig_plaintext = b"Setec Astronomy"
        vaulttext = u'''$ANSIBLE_VAULT;1.1;AES256
33363965326261303234626463623963633531343539616138316433353830356566396130353436
3562643163366231316662386565383735653432386435610a306664636137376132643732393835
63383038383730306639353234326630666539346233376330303938323639306661313032396437
6233623062366136310a633866373936313238333730653739323461656662303864663666653563
3138'''

        b_plaintext = self.v.decrypt(vaulttext)
        self.assertEqual(b_plaintext, b_plaintext, msg="decryption failed")

        b_vaulttext = to_bytes(vaulttext, encoding='ascii', errors='strict')
        b_plaintext = self.v.decrypt(b_vaulttext)
        self.assertEqual(b_plaintext, b_orig_plaintext, msg="decryption failed")

    def test_encrypt_decrypt_aes256_bad_hmac(self):
        # FIXME This test isn't working quite yet.
        raise SkipTest

        if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
            raise SkipTest
        self.v.cipher_name = 'AES256'
        # plaintext = "Setec Astronomy"
        enc_data = '''$ANSIBLE_VAULT;1.1;AES256
33363965326261303234626463623963633531343539616138316433353830356566396130353436
3562643163366231316662386565383735653432386435610a306664636137376132643732393835
63383038383730306639353234326630666539346233376330303938323639306661313032396437
6233623062366136310a633866373936313238333730653739323461656662303864663666653563
3138'''
        b_data = to_bytes(enc_data, errors='strict', encoding='utf-8')
        b_data = self.v._split_header(b_data)
        foo = binascii.unhexlify(b_data)
        lines = foo.splitlines()
        # line 0 is salt, line 1 is hmac, line 2+ is ciphertext
        b_salt = lines[0]
        b_hmac = lines[1]
        b_ciphertext_data = b'\n'.join(lines[2:])

        b_ciphertext = binascii.unhexlify(b_ciphertext_data)
        # b_orig_ciphertext = b_ciphertext[:]

        # now muck with the text
        # b_munged_ciphertext = b_ciphertext[:10] + b'\x00' + b_ciphertext[11:]
        # b_munged_ciphertext = b_ciphertext
        # assert b_orig_ciphertext != b_munged_ciphertext

        b_ciphertext_data = binascii.hexlify(b_ciphertext)
        b_payload = b'\n'.join([b_salt, b_hmac, b_ciphertext_data])
        # reformat
        b_invalid_ciphertext = self.v._format_output(b_payload)

        # assert we throw an error
        self.v.decrypt(b_invalid_ciphertext)

    def test_encrypt_encrypted(self):
        if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
            raise SkipTest
        self.v.cipher_name = u'AES'
        b_vaulttext = b"$ANSIBLE_VAULT;9.9;TEST\n%s" % hexlify(b"ansible")
        vaulttext = to_text(b_vaulttext, errors='strict')
        self.assertRaises(errors.AnsibleError, self.v.encrypt, b_vaulttext)
        self.assertRaises(errors.AnsibleError, self.v.encrypt, vaulttext)

    def test_decrypt_decrypted(self):
        if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
            raise SkipTest
        plaintext = u"ansible"
        self.assertRaises(errors.AnsibleError, self.v.decrypt, plaintext)

        b_plaintext = b"ansible"
        self.assertRaises(errors.AnsibleError, self.v.decrypt, b_plaintext)

    def test_cipher_not_set(self):
        # not setting the cipher should default to AES256
        if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
            raise SkipTest
        plaintext = u"ansible"
        self.v.encrypt(plaintext)
        self.assertEquals(self.v.cipher_name, "AES256")