def _encrypt_block_device(args, client, config): """Encrypt and open a block device Stores the dm-crypt key direct in vault :param: args: argparser generated cli arguments :param: client: hvac.Client for Vault access :param: config: configparser object of vaultlocker config """ block_device = args.block_device[0] key = dmcrypt.generate_key() block_uuid = str(uuid.uuid4()) if not args.uuid else args.uuid vault_path = _get_vault_path(block_uuid, config) # NOTE: store and validate key before trying to encrypt disk try: client.write(vault_path, dmcrypt_key=key) except hvac.exceptions.VaultError as write_error: logger.error( 'Vault write to path {}. Failed with error: {}'.format( vault_path, write_error)) raise exceptions.VaultWriteError(vault_path, write_error) try: stored_data = client.read(vault_path) except hvac.exceptions.VaultError as read_error: logger.error('Vault access to path {}' 'failed with error: {}'.format(vault_path, read_error)) raise exceptions.VaultReadError(vault_path, read_error) if not key == stored_data['data']['dmcrypt_key']: raise exceptions.VaultKeyMismatch(vault_path) # All function calls within try/catch raise a CalledProcessError # if return code is non-zero # This way if any of the calls fail, the key can be removed from vault try: dmcrypt.luks_format(key, block_device, block_uuid) # Ensure sym link for new encrypted device is created # LP Bug #1780332 dmcrypt.udevadm_rescan(block_device) dmcrypt.udevadm_settle(block_uuid) dmcrypt.luks_open(key, block_uuid) except subprocess.CalledProcessError as luks_error: logger.error( 'LUKS formatting {} failed with error code: {}\n' 'LUKS output: {}'.format( block_device, luks_error.returncode, luks_error.output)) try: client.delete(vault_path) except hvac.exceptions.VaultError as del_error: raise exceptions.VaultDeleteError(vault_path, del_error) raise exceptions.LUKSFailure(block_device, luks_error.output) systemd.enable('vaultlocker-decrypt@{}.service'.format(block_uuid))
def test_vault_write_operation(self, _get_vault_path, _dmcrypt, _systemd): _get_vault_path.return_value = 'backend/host/uuid' _dmcrypt.generate_key.return_value = 'testkey' args = mock.MagicMock() args.uuid = 'passed-UUID' args.block_device = ['/dev/sdb'] client = mock.MagicMock() client.read.return_value = {'data': {'dmcrypt_key': 'testkey'}} self.assertIsNot( exceptions.VaultWriteError, shell._encrypt_block_device(args, client, self.config)) client.write.side_effect = exceptions.VaultWriteError( 'backend/host/uuid', 'Write Failed') self.assertRaises(exceptions.VaultWriteError, shell._encrypt_block_device, args, client, self.config)
def _vault_kv_write(client, config, vault_path, data): try: if config.get('vault', 'kv_version') == 'v2': client.secrets.kv.v2.create_or_update_secret( path=vault_path, secret=data, cas=None, mount_point=config.get('vault', 'kv_mount'), ) else: client.secrets.kv.v1.create_or_update_secret( path=vault_path, secret=data, method=None, mount_point=config.get('vault', 'kv_mount'), ) except hvac.exceptions.VaultError as write_error: logger.error('Vault write to path {}' 'failed with error: {}'.format(vault_path, write_error)) raise exceptions.VaultWriteError(vault_path, write_error)
def _rotate_keys(args, client, config): block_device, block_uuid = _resolve_device(args.device[0]) vault_path = _get_vault_path(block_uuid, config) stored_data = client.read(vault_path) if stored_data is None: raise ValueError('Unable to locate key for {}'.format(block_uuid)) old_key = stored_data['data']['dmcrypt_key'] old_slot = int(stored_data['data'].get('slot', 0)) # be sure values are consistent dmcrypt.luks_try_open(old_key, block_uuid, old_slot) new_slot = 1 if old_slot == 0 else 0 new_key = dmcrypt.generate_key() # slot can be empty at present try: dmcrypt.luks_kill_slot(old_key, block_uuid, slot=new_slot) except subprocess.CalledProcessError: pass try: dmcrypt.luks_add_key(old_key, block_uuid, new_key, slot=new_slot) except subprocess.CalledProcessError as luks_error: logger.error( 'LUKS adding {}@{} slot {} failed with error code: {}\n' 'LUKS output: {}'.format( block_uuid, block_device, new_slot, luks_error.returncode, luks_error.output)) raise exceptions.LUKSFailure(block_device, luks_error.output) # validating both keys still are valid try: dmcrypt.luks_try_open(old_key, block_uuid, old_slot) dmcrypt.luks_try_open(new_key, block_uuid, new_slot) except subprocess.CalledProcessError as luks_error: logger.error( 'LUKS check {}@{} failed with error code: {}\n' 'LUKS output: {}'.format( block_device, block_uuid, luks_error.returncode, luks_error.output)) raise exceptions.LUKSFailure(block_device, luks_error.output) logger.info("writing Vault") try: client.write(vault_path, dmcrypt_key=new_key, slot=new_slot, prev_key=old_key, prev_slot=old_slot) except hvac.exceptions.VaultError as write_error: logger.error( 'Vault write to path {}. Failed with error: {}'.format( vault_path, write_error)) raise exceptions.VaultWriteError(vault_path, write_error) try: stored_data = client.read(vault_path) except hvac.exceptions.VaultError as read_error: logger.error('Vault access to path {}' 'failed with error: {}'.format(vault_path, read_error)) raise exceptions.VaultReadError(vault_path, read_error) if not new_key == stored_data['data']['dmcrypt_key'] or not old_key == stored_data['data']['prev_key']: raise exceptions.VaultKeyMismatch(vault_path)