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 _create_last_resort(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)) key = stored_data['data']['dmcrypt_key'] slot = int(stored_data['data'].get('slot', 0)) last_slot = 16 last_key = dmcrypt.generate_key() # will raise if any problems with recipients list encrypted_last_key = encryption.encrypt( last_key, args.recipients.split(',')).decode() # be sure values are consistent dmcrypt.luks_try_open(key, block_uuid, slot) # slot can be empty try: dmcrypt.luks_kill_slot(key, block_uuid, slot=last_slot) except subprocess.CalledProcessError: pass try: dmcrypt.luks_add_key(key, block_uuid, last_key, slot=last_slot) dmcrypt.luks_try_open(last_key, block_uuid, last_slot) except subprocess.CalledProcessError as luks_error: logger.error( 'LUKS updating {} failed with error code: {}\n' 'LUKS output: {}'.format( block_device, luks_error.returncode, luks_error.output)) raise exceptions.LUKSFailure(block_device, luks_error.output) if args.print: print(encrypted_last_key) client.write(vault_path + '-last-resort', key=encrypted_last_key, recipients=args.recipients)
def _validate_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 keys for {}'.format(block_uuid)) if args.mode == 'prev': key = stored_data['data'].get('prev_key') slot = stored_data['data'].get('prev_slot') elif args.mode == 'current': key = stored_data['data'].get('dmcrypt_key') slot = stored_data['data'].get('slot') else: raise ValueError("Unrecognized mode '{}'".format(args.mode)) if key == None or slot == None: raise ValueError('Unable to locate {} key for {} ({})'.format( args.mode, block_device, block_uuid)) try: dmcrypt.luks_try_open(key, block_uuid, slot) except subprocess.CalledProcessError as luks_error: logger.error( 'LUKS {} key on {} ({}) slot {} failed with error code: {}\n' 'LUKS output: {}'.format( args.mode, block_device, block_uuid, slot, luks_error.returncode, luks_error.output)) raise exceptions.LUKSFailure(block_device, luks_error.output) else: print("{} key in vault is valid for {} ({}) slot {}".format( args.mode, block_device, block_uuid, slot))
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)