Skip to content

Commit

Permalink
Add online reencrypt ability for Stratis pools
Browse files Browse the repository at this point in the history
  • Loading branch information
jbaublitz committed Jan 9, 2025
1 parent 1826cb0 commit 686d5d6
Show file tree
Hide file tree
Showing 17 changed files with 255 additions and 73 deletions.
1 change: 1 addition & 0 deletions src/dbus_api/pool/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
12 changes: 11 additions & 1 deletion src/dbus_api/pool/pool_3_8/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
},
Expand Down Expand Up @@ -128,3 +128,13 @@ pub fn encrypt_pool_method(f: &Factory<MTSync<TData>, TData>) -> Method<MTSync<T
.out_arg(("return_code", "q"))
.out_arg(("return_string", "s"))
}

pub fn reencrypt_pool_method(f: &Factory<MTSync<TData>, TData>) -> Method<MTSync<TData>, 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"))
}
33 changes: 33 additions & 0 deletions src/dbus_api/pool/pool_3_8/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -513,3 +513,36 @@ pub fn encrypt_pool(m: &MethodInfo<'_, MTSync<TData>, TData>) -> MethodResult {
};
Ok(vec![msg])
}

pub fn reencrypt_pool(m: &MethodInfo<'_, MTSync<TData>, 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])
}
4 changes: 2 additions & 2 deletions src/dbus_api/pool/pool_3_8/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
7 changes: 5 additions & 2 deletions src/engine/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ 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,
PoolIdentifier, PoolUuid, PropChangeAction, Reencrypt, RegenAction, RenameAction,
ReportType, SetCreateAction, SetDeleteAction, SetUnlockAction, StartAction, StopAction,
StoppedPoolsInfo, StratBlockDevDiff, StratFilesystemDiff, StratSigblockVersion,
TokenUnlockMethod, UdevEngineEvent, UnlockMethod,
},
Expand Down Expand Up @@ -396,6 +396,9 @@ pub trait Pool: Debug + Send + Sync {
encryption_info: &InputEncryptionInfo,
) -> StratisResult<CreateAction<EncryptedDevice>>;

/// Reencrypt an encrypted pool.
fn reencrypt_pool(&mut self) -> StratisResult<Reencrypt>;

/// Return the metadata that would be written if metadata were written.
fn current_metadata(&self, pool_name: &Name) -> StratisResult<String>;

Expand Down
8 changes: 6 additions & 2 deletions src/engine/sim_engine/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,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, Reencrypt, RegenAction, RenameAction, SetCreateAction,
SetDeleteAction, StratSigblockVersion, UnlockMechanism, ValidatedIntegritySpec,
},
},
stratis::{StratisError, StratisResult},
Expand Down Expand Up @@ -854,6 +854,10 @@ impl Pool for SimPool {
Ok(CreateAction::Created(EncryptedDevice))
}

fn reencrypt_pool(&mut self) -> StratisResult<Reencrypt> {
Ok(Reencrypt)
}

fn current_metadata(&self, pool_name: &Name) -> StratisResult<String> {
serde_json::to_string(&self.record(pool_name)).map_err(|e| e.into())
}
Expand Down
20 changes: 20 additions & 0 deletions src/engine/strat_engine/backstore/backstore/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down
14 changes: 14 additions & 0 deletions src/engine/strat_engine/backstore/backstore/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<BlockSizeSummary> {
match tier {
Expand Down
10 changes: 10 additions & 0 deletions src/engine/strat_engine/backstore/blockdev/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
7 changes: 6 additions & 1 deletion src/engine/strat_engine/crypt/handle/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -981,6 +981,11 @@ impl CryptHandle {
Ok(())
}

/// Encrypt an unencrypted pool.
pub fn reencrypt(&self) -> StratisResult<()> {
reencrypt_shared(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()?;
Expand Down
65 changes: 8 additions & 57 deletions src/engine/strat_engine/crypt/handle/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -182,61 +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<(c_uint, SizedKeyMemory)> {
for (ts, mech) in encryption_info.all_infos() {
match mech {
UnlockMechanism::KeyDesc(kd) => match read_key(kd) {
Ok(Some(pass)) => {
let keyslot = match get_keyslot_number(device, *ts) {
Ok(Some(ks)) => ks,
Ok(None) => {
warn!("Failed to find keyslot associated with the given token");
continue;
}
Err(e) => {
warn!("Failure while finding keyslot associated with the given token: {e}");
continue;
}
};
return Ok((keyslot, pass));
}
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)) => {
let keyslot = match get_keyslot_number(device, *ts) {
Ok(Some(ks)) => ks,
Ok(None) => {
warn!("Failed to find keyslot associated with the given token");
continue;
}
Err(e) => {
warn!("Failure while finding keyslot associated with the given token: {e}");
continue;
}
};
return Ok((keyslot, pass));
}
Ok(None) => {
info!("Key description was not in keyring; trying next unlock mechanism")
}
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
Expand Down Expand Up @@ -856,6 +802,11 @@ impl CryptHandle {
.map(|h| h.expect("should have crypt device after online encrypt"))
}

/// Encrypt an unencrypted pool.
pub fn reencrypt(&self) -> StratisResult<()> {
reencrypt_shared(self.luks2_device_path(), self.encryption_info())
}

/// Deactivate the device referenced by the current device handle.
#[cfg(test)]
pub fn deactivate(&self) -> StratisResult<()> {
Expand Down
Loading

0 comments on commit 686d5d6

Please sign in to comment.