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 Sep 17, 2024
1 parent 5e9aadb commit aba7ead
Show file tree
Hide file tree
Showing 17 changed files with 203 additions and 17 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_3::grow_physical_device_method(&f))
.add_m(pool_3_7::get_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
15 changes: 14 additions & 1 deletion src/dbus_api/pool/pool_3_8/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

use dbus_tree::{Factory, MTSync, Method};

use crate::dbus_api::{pool::pool_3_8::methods::encrypt_pool, types::TData};
use crate::dbus_api::{
pool::pool_3_8::methods::{encrypt_pool, reencrypt_pool},
types::TData,
};

pub fn encrypt_pool_method(f: &Factory<MTSync<TData>, TData>) -> Method<MTSync<TData>, TData> {
f.method("EncryptPool", (), encrypt_pool)
Expand All @@ -30,3 +33,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 @@ -97,3 +97,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])
}
2 changes: 1 addition & 1 deletion src/dbus_api/pool/pool_3_8/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
mod api;
mod methods;

pub use api::encrypt_pool_method;
pub use api::{encrypt_pool_method, reencrypt_pool_method};
7 changes: 5 additions & 2 deletions src/engine/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use crate::{
ActionAvailability, BlockDevTier, Clevis, CreateAction, DeleteAction, DevUuid,
EncryptedDevice, EncryptionInfo, FilesystemUuid, GrowAction, Key, KeyDescription,
LockedPoolsInfo, MappingCreateAction, MappingDeleteAction, Name, PoolDiff,
PoolEncryptionInfo, PoolIdentifier, PoolUuid, RegenAction, RenameAction, ReportType,
SetCreateAction, SetDeleteAction, SetUnlockAction, StartAction, StopAction,
PoolEncryptionInfo, PoolIdentifier, PoolUuid, Reencrypt, RegenAction, RenameAction,
ReportType, SetCreateAction, SetDeleteAction, SetUnlockAction, StartAction, StopAction,
StoppedPoolsInfo, StratFilesystemDiff, StratSigblockVersion, UdevEngineEvent,
UnlockMethod,
},
Expand Down Expand Up @@ -351,6 +351,9 @@ pub trait Pool: Debug + Send + Sync {
encryption_info: &EncryptionInfo,
) -> 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 @@ -25,8 +25,8 @@ use crate::{
types::{
ActionAvailability, BlockDevTier, Clevis, CreateAction, DeleteAction, DevUuid,
EncryptedDevice, EncryptionInfo, FilesystemUuid, GrowAction, Key, KeyDescription, Name,
PoolDiff, PoolEncryptionInfo, PoolUuid, RegenAction, RenameAction, SetCreateAction,
SetDeleteAction, StratSigblockVersion,
PoolDiff, PoolEncryptionInfo, PoolUuid, Reencrypt, RegenAction, RenameAction,
SetCreateAction, SetDeleteAction, StratSigblockVersion,
},
PropChangeAction,
},
Expand Down Expand Up @@ -756,6 +756,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 @@ -874,6 +874,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 @@ -1126,6 +1126,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
9 changes: 9 additions & 0 deletions src/engine/strat_engine/backstore/blockdev/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,15 @@ 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
8 changes: 7 additions & 1 deletion src/engine/strat_engine/crypt/handle/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ use crate::{
acquire_crypt_device, activate, add_keyring_keyslot, check_luks2_token,
clevis_decrypt, clevis_info_from_metadata, device_from_physical_path,
ensure_inactive, ensure_wiped, get_keyslot_number, interpret_clevis_config,
key_desc_from_metadata, luks2_token_type_is_valid, read_key, wipe_fallback,
key_desc_from_metadata, luks2_token_type_is_valid, read_key, reencrypt_shared,
wipe_fallback,
},
},
dm::DEVICEMAPPER_PATH,
Expand Down Expand Up @@ -1024,6 +1025,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
8 changes: 7 additions & 1 deletion src/engine/strat_engine/crypt/handle/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ use crate::{
acquire_crypt_device, activate, add_keyring_keyslot, clevis_decrypt,
clevis_info_from_metadata, device_from_physical_path, ensure_wiped,
get_keyslot_number, get_passphrase, interpret_clevis_config,
key_desc_from_metadata, luks2_token_type_is_valid, wipe_fallback,
key_desc_from_metadata, luks2_token_type_is_valid, reencrypt_shared,
wipe_fallback,
},
},
device::blkdev_size,
Expand Down Expand Up @@ -855,6 +856,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
53 changes: 50 additions & 3 deletions src/engine/strat_engine/crypt/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,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, LibcryptErr,
get_sector_size, register, set_debug_level, set_log_callback, CryptDevice, CryptInit,
CryptParamsLuks2, CryptParamsReencrypt, LibcryptErr,
};

use crate::{
Expand Down Expand Up @@ -929,3 +931,48 @@ pub fn get_passphrase(
},
}
}

pub fn reencrypt_shared(luks2_path: &Path, encryption_info: &EncryptionInfo) -> StratisResult<()> {
let mut device = CryptInit::init(luks2_path)?;

let (keyslot, key) = get_passphrase(&mut device, encryption_info)?;
let new_keyslot =
device
.keyslot_handle()
.add_by_key(None, None, key.as_ref(), CryptVolumeKey::NO_SEGMENT)?;

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(
None,
key.as_ref(),
Some(keyslot),
new_keyslot,
(&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::INITIALIZE_ONLY,
},
)?;

device.reencrypt_handle().reencrypt2::<()>(None, None)?;

Ok(())
}
11 changes: 9 additions & 2 deletions src/engine/strat_engine/pool/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use crate::{
types::{
ActionAvailability, BlockDevTier, Clevis, CreateAction, DeleteAction, DevUuid,
EncryptedDevice, EncryptionInfo, FilesystemUuid, GrowAction, Key, KeyDescription, Name,
PoolDiff, PoolEncryptionInfo, PoolUuid, PropChangeAction, RegenAction, RenameAction,
SetCreateAction, SetDeleteAction, StratSigblockVersion,
PoolDiff, PoolEncryptionInfo, PoolUuid, PropChangeAction, Reencrypt, RegenAction,
RenameAction, SetCreateAction, SetDeleteAction, StratSigblockVersion,
},
},
stratis::StratisResult,
Expand Down Expand Up @@ -340,6 +340,13 @@ impl Pool for AnyPool {
}
}

fn reencrypt_pool(&mut self) -> StratisResult<Reencrypt> {
match self {
AnyPool::V1(p) => p.reencrypt_pool(),
AnyPool::V2(p) => p.reencrypt_pool(),
}
}

fn current_metadata(&self, pool_name: &Name) -> StratisResult<String> {
match self {
AnyPool::V1(p) => p.current_metadata(pool_name),
Expand Down
7 changes: 6 additions & 1 deletion src/engine/strat_engine/pool/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use crate::{
types::{
ActionAvailability, BlockDevTier, Clevis, Compare, CreateAction, DeleteAction, DevUuid,
Diff, EncryptedDevice, EncryptionInfo, FilesystemUuid, GrowAction, Key, KeyDescription,
Name, OffsetDirection, PoolDiff, PoolEncryptionInfo, PoolUuid, RegenAction,
Name, OffsetDirection, PoolDiff, PoolEncryptionInfo, PoolUuid, Reencrypt, RegenAction,
RenameAction, SetCreateAction, SetDeleteAction, StratFilesystemDiff, StratPoolDiff,
StratSigblockVersion,
},
Expand Down Expand Up @@ -1297,6 +1297,11 @@ impl Pool for StratPool {
))
}

fn reencrypt_pool(&mut self) -> StratisResult<Reencrypt> {
self.backstore.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
10 changes: 9 additions & 1 deletion src/engine/strat_engine/pool/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ use crate::{
ActionAvailability, BlockDevTier, Clevis, Compare, CreateAction, DeleteAction, DevUuid,
Diff, EncryptedDevice, EncryptionInfo, FilesystemUuid, GrowAction, Key, KeyDescription,
Name, OffsetDirection, PoolDiff, PoolEncryptionInfo, PoolUuid, PropChangeAction,
RegenAction, RenameAction, SetCreateAction, SetDeleteAction, SizedKeyMemory,
Reencrypt, RegenAction, RenameAction, SetCreateAction, SetDeleteAction, SizedKeyMemory,
StratFilesystemDiff, StratPoolDiff, StratSigblockVersion, UnlockMethod,
},
},
Expand Down Expand Up @@ -1230,6 +1230,14 @@ impl Pool for StratPool {
}
}

#[pool_mutating_action("NoRequests")]
fn reencrypt_pool(&mut self) -> StratisResult<Reencrypt> {
self.thin_pool.suspend()?;
let encrypt_res = self.backstore.reencrypt();
self.thin_pool.resume()?;
encrypt_res.map(|_| 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
9 changes: 9 additions & 0 deletions src/engine/types/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -838,3 +838,12 @@ impl<T> EngineAction for PropChangeAction<T> {
}
}
}

/// Return value indicating a successful reencrypt operation on the pool
pub struct Reencrypt;

impl Display for Reencrypt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Reencryption operation was completed successfully")
}
}
5 changes: 3 additions & 2 deletions src/engine/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ pub use crate::engine::{
types::{
actions::{
Clevis, CreateAction, DeleteAction, EncryptedDevice, EngineAction, GrowAction, Key,
MappingCreateAction, MappingDeleteAction, PropChangeAction, RegenAction, RenameAction,
SetCreateAction, SetDeleteAction, SetUnlockAction, StartAction, StopAction, ToDisplay,
MappingCreateAction, MappingDeleteAction, PropChangeAction, Reencrypt, RegenAction,
RenameAction, SetCreateAction, SetDeleteAction, SetUnlockAction, StartAction,
StopAction, ToDisplay,
},
diff::{
Compare, Diff, PoolDiff, StratBlockDevDiff, StratFilesystemDiff, StratPoolDiff,
Expand Down

0 comments on commit aba7ead

Please sign in to comment.