From 0b141cecb1889749b2a40e2e513845fdc6544119 Mon Sep 17 00:00:00 2001 From: John Baublitz Date: Tue, 17 Sep 2024 15:32:38 -0400 Subject: [PATCH] Add online reencrypt ability for Stratis pools --- src/dbus_api/pool/mod.rs | 1 + src/dbus_api/pool/pool_3_8/api.rs | 12 +- src/dbus_api/pool/pool_3_8/methods.rs | 33 +++ src/dbus_api/pool/pool_3_8/mod.rs | 4 +- src/engine/engine.rs | 11 +- src/engine/sim_engine/pool.rs | 8 +- .../strat_engine/backstore/backstore/v1.rs | 20 ++ .../strat_engine/backstore/backstore/v2.rs | 14 ++ .../strat_engine/backstore/blockdev/v1.rs | 10 + src/engine/strat_engine/crypt/handle/v1.rs | 11 +- src/engine/strat_engine/crypt/handle/v2.rs | 60 ++---- src/engine/strat_engine/crypt/shared.rs | 198 +++++++++++++++++- src/engine/strat_engine/pool/dispatch.rs | 11 +- src/engine/strat_engine/pool/v1.rs | 9 +- src/engine/strat_engine/pool/v2.rs | 14 +- src/engine/types/actions.rs | 9 + src/engine/types/mod.rs | 6 +- 17 files changed, 359 insertions(+), 72 deletions(-) diff --git a/src/dbus_api/pool/mod.rs b/src/dbus_api/pool/mod.rs index 339869e984..ac7d47227b 100644 --- a/src/dbus_api/pool/mod.rs +++ b/src/dbus_api/pool/mod.rs @@ -295,6 +295,7 @@ pub fn create_dbus_pool<'a>( .add_m(pool_3_7::get_metadata_method(&f)) .add_m(pool_3_7::get_fs_metadata_method(&f)) .add_m(pool_3_8::encrypt_pool_method(&f)) + .add_m(pool_3_8::reencrypt_pool_method(&f)) .add_p(pool_3_0::name_property(&f)) .add_p(pool_3_0::uuid_property(&f)) .add_p(pool_3_0::encrypted_property(&f)) diff --git a/src/dbus_api/pool/pool_3_8/api.rs b/src/dbus_api/pool/pool_3_8/api.rs index ec4db87dd8..b3789a3617 100644 --- a/src/dbus_api/pool/pool_3_8/api.rs +++ b/src/dbus_api/pool/pool_3_8/api.rs @@ -10,7 +10,7 @@ use crate::dbus_api::{ pool_3_8::{ methods::{ bind_clevis, bind_keyring, encrypt_pool, rebind_clevis, rebind_keyring, - unbind_clevis, unbind_keyring, + reencrypt_pool, unbind_clevis, unbind_keyring, }, props::{get_pool_clevis_infos, get_pool_key_descs}, }, @@ -128,3 +128,13 @@ pub fn encrypt_pool_method(f: &Factory, TData>) -> Method, TData>) -> Method, TData> { + f.method("ReencryptPool", (), reencrypt_pool) + // b: true if successful + // + // Rust representation: bool + .out_arg(("results", "b")) + .out_arg(("return_code", "q")) + .out_arg(("return_string", "s")) +} diff --git a/src/dbus_api/pool/pool_3_8/methods.rs b/src/dbus_api/pool/pool_3_8/methods.rs index 7f3519d5b6..898512c893 100644 --- a/src/dbus_api/pool/pool_3_8/methods.rs +++ b/src/dbus_api/pool/pool_3_8/methods.rs @@ -513,3 +513,36 @@ pub fn encrypt_pool(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { }; Ok(vec![msg]) } + +pub fn reencrypt_pool(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { + let message: &Message = m.msg; + + let dbus_context = m.tree.get_data(); + let object_path = m.path.get_name(); + let return_message = message.method_return(); + let default_return = false; + + let pool_path = m + .tree + .get(object_path) + .expect("implicit argument must be in tree"); + let pool_uuid = typed_uuid!( + get_data!(pool_path; default_return; return_message).uuid; + Pool; + default_return; + return_message + ); + + let mut guard = get_mut_pool!(dbus_context.engine; pool_uuid; default_return; return_message); + let (_, _, pool) = guard.as_mut_tuple(); + + let result = handle_action!(pool.reencrypt_pool(), dbus_context, pool_path.get_name()); + let msg = match result { + Ok(_) => return_message.append3(true, DbusErrorEnum::OK as u16, OK_STRING.to_string()), + Err(err) => { + let (rc, rs) = engine_to_dbus_err_tuple(&err); + return_message.append3(default_return, rc, rs) + } + }; + Ok(vec![msg]) +} diff --git a/src/dbus_api/pool/pool_3_8/mod.rs b/src/dbus_api/pool/pool_3_8/mod.rs index 28f1a2588b..7515193f9d 100644 --- a/src/dbus_api/pool/pool_3_8/mod.rs +++ b/src/dbus_api/pool/pool_3_8/mod.rs @@ -8,6 +8,6 @@ mod props; pub use api::{ bind_clevis_method, bind_keyring_method, clevis_infos_property, encrypt_pool_method, - key_descs_property, rebind_clevis_method, rebind_keyring_method, unbind_clevis_method, - unbind_keyring_method, + key_descs_property, rebind_clevis_method, rebind_keyring_method, reencrypt_pool_method, + unbind_clevis_method, unbind_keyring_method, }; diff --git a/src/engine/engine.rs b/src/engine/engine.rs index 216d452eb3..d9a85743f2 100644 --- a/src/engine/engine.rs +++ b/src/engine/engine.rs @@ -25,10 +25,10 @@ use crate::{ EncryptedDevice, EncryptionInfo, FilesystemUuid, GrowAction, InputEncryptionInfo, IntegritySpec, Key, KeyDescription, LockedPoolsInfo, MappingCreateAction, MappingDeleteAction, Name, OptionalTokenSlotInput, PoolDiff, PoolEncryptionInfo, - PoolIdentifier, PoolUuid, PropChangeAction, RegenAction, RenameAction, ReportType, - SetCreateAction, SetDeleteAction, SetUnlockAction, StartAction, StopAction, - StoppedPoolsInfo, StratBlockDevDiff, StratFilesystemDiff, StratSigblockVersion, - TokenUnlockMethod, UdevEngineEvent, UnlockMethod, + PoolIdentifier, PoolUuid, PropChangeAction, ReencryptedDevice, RegenAction, + RenameAction, ReportType, SetCreateAction, SetDeleteAction, SetUnlockAction, + StartAction, StopAction, StoppedPoolsInfo, StratBlockDevDiff, StratFilesystemDiff, + StratSigblockVersion, TokenUnlockMethod, UdevEngineEvent, UnlockMethod, }, }, stratis::StratisResult, @@ -396,6 +396,9 @@ pub trait Pool: Debug + Send + Sync { encryption_info: &InputEncryptionInfo, ) -> StratisResult>; + /// Reencrypt an encrypted pool. + fn reencrypt_pool(&mut self) -> StratisResult; + /// Return the metadata that would be written if metadata were written. fn current_metadata(&self, pool_name: &Name) -> StratisResult; diff --git a/src/engine/sim_engine/pool.rs b/src/engine/sim_engine/pool.rs index 4c858bbce4..263ad6de99 100644 --- a/src/engine/sim_engine/pool.rs +++ b/src/engine/sim_engine/pool.rs @@ -29,8 +29,8 @@ use crate::{ ActionAvailability, BlockDevTier, Clevis, CreateAction, DeleteAction, DevUuid, EncryptedDevice, EncryptionInfo, FilesystemUuid, GrowAction, InputEncryptionInfo, Key, KeyDescription, Name, OptionalTokenSlotInput, PoolDiff, PoolEncryptionInfo, PoolUuid, - PropChangeAction, RegenAction, RenameAction, SetCreateAction, SetDeleteAction, - StratSigblockVersion, UnlockMechanism, ValidatedIntegritySpec, + PropChangeAction, ReencryptedDevice, RegenAction, RenameAction, SetCreateAction, + SetDeleteAction, StratSigblockVersion, UnlockMechanism, ValidatedIntegritySpec, }, }, stratis::{StratisError, StratisResult}, @@ -881,6 +881,10 @@ impl Pool for SimPool { Ok(CreateAction::Created(EncryptedDevice)) } + fn reencrypt_pool(&mut self) -> StratisResult { + Ok(ReencryptedDevice) + } + fn current_metadata(&self, pool_name: &Name) -> StratisResult { serde_json::to_string(&self.record(pool_name)).map_err(|e| e.into()) } diff --git a/src/engine/strat_engine/backstore/backstore/v1.rs b/src/engine/strat_engine/backstore/backstore/v1.rs index d61c5ae45a..4e3299d09a 100644 --- a/src/engine/strat_engine/backstore/backstore/v1.rs +++ b/src/engine/strat_engine/backstore/backstore/v1.rs @@ -862,6 +862,26 @@ impl Backstore { } } + /// Reencrypt all encrypted devices in the pool. + /// + /// Returns: + /// * Ok(()) if successful + /// * Err(_) if an operation fails while reencrypting the devices. + pub fn reencrypt(&mut self) -> StratisResult<()> { + if self.encryption_info().is_none() { + return Err(StratisError::Msg( + "Requested pool does not appear to be encrypted".to_string(), + )); + }; + + // Keys are not the same but key description is present + operation_loop( + self.blockdevs_mut().into_iter().map(|(_, _, bd)| bd), + |blockdev| blockdev.reencrypt(), + )?; + Ok(()) + } + /// Regenerate the Clevis bindings with the block devices in this pool using /// the same configuration. /// diff --git a/src/engine/strat_engine/backstore/backstore/v2.rs b/src/engine/strat_engine/backstore/backstore/v2.rs index fcbdd3b6e1..3c1a5b2b88 100644 --- a/src/engine/strat_engine/backstore/backstore/v2.rs +++ b/src/engine/strat_engine/backstore/backstore/v2.rs @@ -1258,6 +1258,20 @@ impl Backstore { Ok(()) } + pub fn reencrypt(&self) -> StratisResult<()> { + match self.enc { + Some(Either::Left(_)) => { + Err(StratisError::Msg("Encrypted pool where the encrypted device has not yet been created cannot be reencrypted".to_string())) + } + Some(Either::Right(ref handle)) => { + handle.reencrypt() + } + None => { + Err(StratisError::Msg("Unencrypted device cannot be reencrypted".to_string())) + } + } + } + /// A summary of block sizes pub fn block_size_summary(&self, tier: BlockDevTier) -> Option { match tier { diff --git a/src/engine/strat_engine/backstore/blockdev/v1.rs b/src/engine/strat_engine/backstore/blockdev/v1.rs index c521497030..90538731d8 100644 --- a/src/engine/strat_engine/backstore/blockdev/v1.rs +++ b/src/engine/strat_engine/backstore/blockdev/v1.rs @@ -416,6 +416,16 @@ impl StratBlockDev { } } } + + /// Reencrypt an individual block device in a pool. + pub fn reencrypt(&self) -> StratisResult<()> { + let crypt_handle = self + .underlying_device + .crypt_handle() + .expect("Checked that pool is encrypted"); + crypt_handle.reencrypt() + } + #[cfg(test)] pub fn invariant(&self) { assert!(self.total_size() == self.used.size()); diff --git a/src/engine/strat_engine/crypt/handle/v1.rs b/src/engine/strat_engine/crypt/handle/v1.rs index 95ed2e2235..29a095aba5 100644 --- a/src/engine/strat_engine/crypt/handle/v1.rs +++ b/src/engine/strat_engine/crypt/handle/v1.rs @@ -45,7 +45,7 @@ use crate::{ check_luks2_token, clevis_decrypt, device_from_physical_path, encryption_info_from_metadata, ensure_inactive, ensure_wiped, get_keyslot_number, interpret_clevis_config, luks2_token_type_is_valid, - read_key, wipe_fallback, + read_key, reencrypt_shared, wipe_fallback, }, }, dm::DEVICEMAPPER_PATH, @@ -989,6 +989,15 @@ impl CryptHandle { Ok(()) } + /// Encrypt an unencrypted pool. + pub fn reencrypt(&self) -> StratisResult<()> { + reencrypt_shared( + &format_crypt_name(&self.metadata.identifiers.device_uuid).to_string(), + self.luks2_device_path(), + self.encryption_info(), + ) + } + /// Rename the pool in the LUKS2 token. pub fn rename_pool_in_metadata(&mut self, pool_name: Name) -> StratisResult<()> { let mut device = self.acquire_crypt_device()?; diff --git a/src/engine/strat_engine/crypt/handle/v2.rs b/src/engine/strat_engine/crypt/handle/v2.rs index d0e7aad90a..721d4d91d5 100644 --- a/src/engine/strat_engine/crypt/handle/v2.rs +++ b/src/engine/strat_engine/crypt/handle/v2.rs @@ -43,9 +43,10 @@ use crate::{ }, shared::{ acquire_crypt_device, activate, activate_by_token, add_keyring_keyslot, - clevis_decrypt, clevis_info_from_json, device_from_physical_path, + clevis_info_from_json, device_from_physical_path, encryption_info_from_metadata, ensure_wiped, get_keyslot_number, - interpret_clevis_config, read_key, wipe_fallback, + get_passphrase, interpret_clevis_config, read_key, reencrypt_shared, + wipe_fallback, }, }, device::blkdev_size, @@ -61,8 +62,6 @@ use crate::{ stratis::{StratisError, StratisResult}, }; -type PassphraseInfo = Either<(u32, u32, SizedKeyMemory), (u32, SizedKeyMemory)>; - /// Load crypt device metadata. pub fn load_crypt_metadata( device: &mut CryptDevice, @@ -184,50 +183,6 @@ fn setup_crypt_handle( ))) } -/// Get one of the passphrases for the encrypted device. -fn get_passphrase( - device: &mut CryptDevice, - encryption_info: &EncryptionInfo, -) -> StratisResult { - for (ts, mech) in encryption_info.all_infos() { - let keyslot = match get_keyslot_number(device, *ts) { - Ok(Some(ks)) => ks, - Ok(None) => { - warn!("Unable to find associated keyslot for token slot"); - continue; - } - Err(e) => { - warn!("Error while querying associated keyslot for token slot: {e}"); - continue; - } - }; - match mech { - UnlockMechanism::KeyDesc(kd) => match read_key(kd) { - Ok(Some(key)) => { - return Ok(Either::Left((keyslot, *ts, key))); - } - Ok(None) => { - info!("Key description was not in keyring; trying next unlock mechanism") - } - Err(e) => info!("Error searching keyring: {e}"), - }, - UnlockMechanism::ClevisInfo(_) => match clevis_decrypt(device, *ts) { - Ok(Some(pass)) => { - return Ok(Either::Right((keyslot, pass))); - } - Ok(None) => { - info!("Failed to find the given token; trying next unlock method"); - } - Err(e) => info!("Error searching keyring: {e}"), - }, - } - } - - Err(StratisError::Msg( - "Unable to get passphrase for any available token slots".to_string(), - )) -} - /// Handle for performing all operations on an encrypted device. /// /// `Clone` is derived for this data structure because `CryptHandle` acquires @@ -878,6 +833,15 @@ impl CryptHandle { .map(|h| h.expect("should have crypt device after online encrypt")) } + /// Encrypt an unencrypted pool. + pub fn reencrypt(&self) -> StratisResult<()> { + reencrypt_shared( + &format_crypt_backstore_name(&self.metadata.pool_uuid).to_string(), + self.luks2_device_path(), + self.encryption_info(), + ) + } + /// Deactivate the device referenced by the current device handle. #[cfg(test)] pub fn deactivate(&self) -> StratisResult<()> { diff --git a/src/engine/strat_engine/crypt/shared.rs b/src/engine/strat_engine/crypt/shared.rs index eb5315b5f4..304613ae06 100644 --- a/src/engine/strat_engine/crypt/shared.rs +++ b/src/engine/strat_engine/crypt/shared.rs @@ -21,12 +21,14 @@ use devicemapper::{Bytes, DevId, DmName, DmOptions}; use libcryptsetup_rs::{ c_uint, consts::{ - flags::{CryptActivate, CryptVolumeKey, CryptWipe}, + flags::{CryptActivate, CryptReencrypt, CryptVolumeKey, CryptWipe}, vals::{ - CryptDebugLevel, CryptLogLevel, CryptStatusInfo, CryptWipePattern, EncryptionFormat, + CryptDebugLevel, CryptLogLevel, CryptReencryptDirectionInfo, CryptReencryptModeInfo, + CryptStatusInfo, CryptWipePattern, EncryptionFormat, }, }, - register, set_debug_level, set_log_callback, CryptDevice, CryptInit, + get_sector_size, register, set_debug_level, set_log_callback, CryptDevice, CryptInit, + CryptParamsLuks2, CryptParamsReencrypt, SafeMemHandle, TokenInput, }; use crate::{ @@ -36,7 +38,7 @@ use crate::{ crypt::consts::{ CLEVIS_RECURSION_LIMIT, CLEVIS_TANG_TRUST_URL, CLEVIS_TOKEN_NAME, CLEVIS_TOKEN_TYPE, DEFAULT_CRYPT_KEYSLOTS_SIZE, LUKS2_SECTOR_SIZE, LUKS2_TOKEN_ID, - LUKS2_TOKEN_TYPE, TOKEN_KEYSLOTS_KEY, TOKEN_TYPE_KEY, + LUKS2_TOKEN_TYPE, STRATIS_MEK_SIZE, TOKEN_KEYSLOTS_KEY, TOKEN_TYPE_KEY, }, dm::get_dm, keys, @@ -48,6 +50,8 @@ use crate::{ static CLEVIS_ERROR: OnceLock>> = OnceLock::new(); +type PassphraseInfo = Either<(u32, u32, SizedKeyMemory), (u32, SizedKeyMemory)>; + /// Set up crypt logging to log cryptsetup debug information at the trace level. pub fn set_up_crypt_logging() { fn logging_callback(level: CryptLogLevel, msg: &str, _: Option<&mut ()>) { @@ -972,6 +976,192 @@ pub fn clevis_decrypt( cmd::clevis_decrypt(&jwe).map(Some) } +/// Get one of the passphrases for the encrypted device. +pub fn get_passphrase( + device: &mut CryptDevice, + encryption_info: &EncryptionInfo, +) -> StratisResult { + for (ts, mech) in encryption_info.all_infos() { + let keyslot = match get_keyslot_number(device, *ts) { + Ok(Some(ks)) => ks, + Ok(None) => { + warn!("Unable to find associated keyslot for token slot"); + continue; + } + Err(e) => { + warn!("Error while querying associated keyslot for token slot: {e}"); + continue; + } + }; + match mech { + UnlockMechanism::KeyDesc(kd) => match read_key(kd) { + Ok(Some(key)) => { + return Ok(Either::Left((keyslot, *ts, key))); + } + Ok(None) => { + info!("Key description was not in keyring; trying next unlock mechanism") + } + Err(e) => info!("Error searching keyring: {e}"), + }, + UnlockMechanism::ClevisInfo(_) => match clevis_decrypt(device, *ts) { + Ok(Some(pass)) => { + return Ok(Either::Right((keyslot, pass))); + } + Ok(None) => { + info!("Failed to find the given token; trying next unlock method"); + } + Err(e) => info!("Error searching keyring: {e}"), + }, + } + } + + Err(StratisError::Msg( + "Unable to get passphrase for any available token slots".to_string(), + )) +} + +/// Get all of the passphrases for the encrypted device for online reencryption. +pub fn get_all_passphrases( + device: &mut CryptDevice, + encryption_info: &EncryptionInfo, +) -> StratisResult> { + let mut passphrases = Vec::new(); + for (ts, mech) in encryption_info.all_infos() { + match mech { + UnlockMechanism::KeyDesc(kd) => match read_key(kd) { + Ok(Some(pass)) => { + passphrases.push((*ts, pass)); + } + Ok(None) => { + return Err(StratisError::Msg(format!( + "Key description {} was not in keyring", + kd.as_application_str(), + ))) + } + Err(e) => { + return Err(StratisError::Chained( + "Error searching keyring".to_string(), + Box::new(e), + )) + } + }, + UnlockMechanism::ClevisInfo(_) => match clevis_decrypt(device, *ts) { + Ok(Some(pass)) => { + passphrases.push((*ts, pass)); + } + Ok(None) => { + return Err(StratisError::Msg( + "Error getting Clevis passphrase".to_string(), + )) + } + Err(e) => { + return Err(StratisError::Chained( + "Error getting Clevis passphrase".to_string(), + Box::new(e), + )) + } + }, + } + } + + Ok(passphrases) +} + +pub fn reencrypt_shared( + device_name: &str, + luks2_path: &Path, + encryption_info: &EncryptionInfo, +) -> StratisResult<()> { + let mut device = acquire_crypt_device(luks2_path)?; + + let mut keys = get_all_passphrases(&mut device, encryption_info)?; + let (single_ts, single_key) = keys.pop().expect("Must have at least one unlock mechanism"); + let mut single_token_contents = device.token_handle().json_get(single_ts)?; + let single_keyslot = get_keyslot_number(&mut device, single_ts)?.ok_or_else(|| { + StratisError::Msg(format!( + "Could not find keyslot associated with token slot {single_ts}" + )) + })?; + + let single_new_keyslot = device.keyslot_handle().add_by_key( + None, + Some(Either::Right(STRATIS_MEK_SIZE)), + single_key.as_ref(), + CryptVolumeKey::NO_SEGMENT, + )?; + if let Some(obj) = single_token_contents.as_object_mut() { + obj.remove(TOKEN_KEYSLOTS_KEY); + obj.insert( + TOKEN_KEYSLOTS_KEY.to_string(), + Value::Array(vec![Value::String(single_new_keyslot.to_string())]), + ); + } + device + .token_handle() + .json_set(TokenInput::ReplaceToken(single_ts, &single_token_contents))?; + + let mut new_vk = SafeMemHandle::alloc(STRATIS_MEK_SIZE)?; + device + .volume_key_handle() + .get(single_new_keyslot, new_vk.as_mut(), single_key.as_ref())?; + + for (ts, key) in keys { + let mut token_contents = device.token_handle().json_get(ts)?; + + let new_keyslot = device.keyslot_handle().add_by_key( + None, + Some(Either::Left(new_vk.as_ref())), + key.as_ref(), + CryptVolumeKey::NO_SEGMENT | CryptVolumeKey::DIGEST_REUSE, + )?; + if let Some(obj) = token_contents.as_object_mut() { + obj.remove(TOKEN_KEYSLOTS_KEY); + obj.insert( + TOKEN_KEYSLOTS_KEY.to_string(), + Value::Array(vec![Value::String(new_keyslot.to_string())]), + ); + } + device + .token_handle() + .json_set(TokenInput::ReplaceToken(ts, &token_contents))?; + } + + let cipher = device.status_handle().get_cipher()?; + let cipher_mode = device.status_handle().get_cipher_mode()?; + let sector_size = convert_int!(get_sector_size(Some(&mut device)), i32, u32)?; + device.reencrypt_handle().reencrypt_init_by_passphrase( + Some(device_name), + single_key.as_ref(), + Some(single_keyslot), + single_new_keyslot, + Some((&cipher, &cipher_mode)), + CryptParamsReencrypt { + mode: CryptReencryptModeInfo::Reencrypt, + direction: CryptReencryptDirectionInfo::Forward, + resilience: "checksum".to_string(), + hash: "sha256".to_string(), + data_shift: 0, + max_hotzone_size: 0, + device_size: 0, + luks2: CryptParamsLuks2 { + data_alignment: 0, + data_device: None, + integrity: None, + integrity_params: None, + pbkdf: None, + label: None, + sector_size, + subsystem: None, + }, + flags: CryptReencrypt::empty(), + }, + )?; + + device.reencrypt_handle().reencrypt2::<()>(None, None)?; + + Ok(()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/engine/strat_engine/pool/dispatch.rs b/src/engine/strat_engine/pool/dispatch.rs index 244a328606..32035ddd90 100644 --- a/src/engine/strat_engine/pool/dispatch.rs +++ b/src/engine/strat_engine/pool/dispatch.rs @@ -17,8 +17,8 @@ use crate::{ ActionAvailability, BlockDevTier, Clevis, CreateAction, DeleteAction, DevUuid, EncryptedDevice, EncryptionInfo, FilesystemUuid, GrowAction, InputEncryptionInfo, Key, KeyDescription, Name, OptionalTokenSlotInput, PoolDiff, PoolEncryptionInfo, PoolUuid, - PropChangeAction, RegenAction, RenameAction, SetCreateAction, SetDeleteAction, - StratSigblockVersion, + PropChangeAction, ReencryptedDevice, RegenAction, RenameAction, SetCreateAction, + SetDeleteAction, StratSigblockVersion, }, }, stratis::StratisResult, @@ -353,6 +353,13 @@ impl Pool for AnyPool { } } + fn reencrypt_pool(&mut self) -> StratisResult { + match self { + AnyPool::V1(p) => p.reencrypt_pool(), + AnyPool::V2(p) => p.reencrypt_pool(), + } + } + fn current_metadata(&self, pool_name: &Name) -> StratisResult { match self { AnyPool::V1(p) => p.current_metadata(pool_name), diff --git a/src/engine/strat_engine/pool/v1.rs b/src/engine/strat_engine/pool/v1.rs index 92c23a0114..435dc4bd54 100644 --- a/src/engine/strat_engine/pool/v1.rs +++ b/src/engine/strat_engine/pool/v1.rs @@ -47,8 +47,8 @@ use crate::{ ActionAvailability, BlockDevTier, Clevis, Compare, CreateAction, DeleteAction, DevUuid, Diff, EncryptedDevice, EncryptionInfo, FilesystemUuid, GrowAction, InputEncryptionInfo, Key, KeyDescription, Name, OffsetDirection, OptionalTokenSlotInput, PoolDiff, - PoolEncryptionInfo, PoolUuid, PropChangeAction, RegenAction, RenameAction, - SetCreateAction, SetDeleteAction, StratFilesystemDiff, StratPoolDiff, + PoolEncryptionInfo, PoolUuid, PropChangeAction, ReencryptedDevice, RegenAction, + RenameAction, SetCreateAction, SetDeleteAction, StratFilesystemDiff, StratPoolDiff, StratSigblockVersion, }, }, @@ -1306,6 +1306,11 @@ impl Pool for StratPool { )) } + fn reencrypt_pool(&mut self) -> StratisResult { + self.backstore.reencrypt()?; + Ok(ReencryptedDevice) + } + fn current_metadata(&self, pool_name: &Name) -> StratisResult { serde_json::to_string(&self.record(pool_name)).map_err(|e| e.into()) } diff --git a/src/engine/strat_engine/pool/v2.rs b/src/engine/strat_engine/pool/v2.rs index 29e8ac65ad..c797f0604f 100644 --- a/src/engine/strat_engine/pool/v2.rs +++ b/src/engine/strat_engine/pool/v2.rs @@ -40,9 +40,9 @@ use crate::{ ActionAvailability, BlockDevTier, Clevis, Compare, CreateAction, DeleteAction, DevUuid, Diff, EncryptedDevice, EncryptionInfo, FilesystemUuid, GrowAction, InputEncryptionInfo, Key, KeyDescription, Name, OffsetDirection, OptionalTokenSlotInput, PoolDiff, - PoolEncryptionInfo, PoolUuid, PropChangeAction, RegenAction, RenameAction, - SetCreateAction, SetDeleteAction, SizedKeyMemory, StratFilesystemDiff, StratPoolDiff, - StratSigblockVersion, TokenUnlockMethod, ValidatedIntegritySpec, + PoolEncryptionInfo, PoolUuid, PropChangeAction, ReencryptedDevice, RegenAction, + RenameAction, SetCreateAction, SetDeleteAction, SizedKeyMemory, StratFilesystemDiff, + StratPoolDiff, StratSigblockVersion, TokenUnlockMethod, ValidatedIntegritySpec, }, DEFAULT_CRYPT_METADATA_SIZE_V2, }, @@ -1200,6 +1200,14 @@ impl Pool for StratPool { } } + #[pool_mutating_action("NoRequests")] + fn reencrypt_pool(&mut self) -> StratisResult { + self.thin_pool.suspend()?; + let encrypt_res = self.backstore.reencrypt(); + self.thin_pool.resume()?; + encrypt_res.map(|_| ReencryptedDevice) + } + fn current_metadata(&self, pool_name: &Name) -> StratisResult { serde_json::to_string(&self.record(pool_name)).map_err(|e| e.into()) } diff --git a/src/engine/types/actions.rs b/src/engine/types/actions.rs index a4ea0ff733..accd479fac 100644 --- a/src/engine/types/actions.rs +++ b/src/engine/types/actions.rs @@ -851,3 +851,12 @@ impl EngineAction for PropChangeAction { } } } + +/// Return value indicating a successful reencrypt operation on the pool +pub struct ReencryptedDevice; + +impl Display for ReencryptedDevice { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Reencryption operation was completed successfully") + } +} diff --git a/src/engine/types/mod.rs b/src/engine/types/mod.rs index 18a7f2d55a..988a3b225e 100644 --- a/src/engine/types/mod.rs +++ b/src/engine/types/mod.rs @@ -28,9 +28,9 @@ pub use crate::{ types::{ actions::{ Clevis, CreateAction, DeleteAction, EncryptedDevice, EngineAction, GrowAction, Key, - MappingCreateAction, MappingDeleteAction, PropChangeAction, RegenAction, - RenameAction, SetCreateAction, SetDeleteAction, SetUnlockAction, StartAction, - StopAction, ToDisplay, + MappingCreateAction, MappingDeleteAction, PropChangeAction, ReencryptedDevice, + RegenAction, RenameAction, SetCreateAction, SetDeleteAction, SetUnlockAction, + StartAction, StopAction, ToDisplay, }, diff::{ Compare, Diff, PoolDiff, StratBlockDevDiff, StratFilesystemDiff, StratPoolDiff,