Skip to content

Commit

Permalink
Add online encrypt 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 3afb144 commit 4c49652
Show file tree
Hide file tree
Showing 24 changed files with 737 additions and 136 deletions.
7 changes: 1 addition & 6 deletions src/dbus_api/api/manager_3_8/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
blockdev::create_dbus_blockdev,
filesystem::create_dbus_filesystem,
pool::create_dbus_pool,
types::{DbusErrorEnum, TData, OK_STRING},
types::{DbusErrorEnum, EncryptionInfos, TData, OK_STRING},
util::{engine_to_dbus_err_tuple, get_next_arg, tuple_to_option},
},
engine::{
Expand All @@ -29,11 +29,6 @@ use crate::{
stratis::StratisError,
};

type EncryptionInfos<'a> = (
Vec<((bool, u32), &'a str)>,
Vec<((bool, u32), &'a str, &'a str)>,
);

pub fn start_pool(m: &MethodInfo<'_, MTSync<TData>, TData>) -> MethodResult {
let base_path = m.path.get_name();
let message: &Message = m.msg;
Expand Down
1 change: 1 addition & 0 deletions src/dbus_api/pool/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,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_7::get_fs_metadata_method(&f))
.add_m(pool_3_8::encrypt_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
2 changes: 1 addition & 1 deletion src/dbus_api/pool/pool_3_0/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ pub fn add_cachedevs_method(f: &Factory<MTSync<TData>, TData>) -> Method<MTSync<
pub fn encrypted_property(f: &Factory<MTSync<TData>, TData>) -> Property<MTSync<TData>, TData> {
f.property::<bool, _>(consts::POOL_ENCRYPTED_PROP, ())
.access(Access::Read)
.emits_changed(EmitsChangedSignal::Const)
.emits_changed(EmitsChangedSignal::True)
.on_get(get_pool_encrypted)
}

Expand Down
31 changes: 29 additions & 2 deletions src/dbus_api/pool/pool_3_8/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use crate::dbus_api::{
consts,
pool_3_8::{
methods::{
bind_clevis, bind_keyring, rebind_clevis, rebind_keyring, unbind_clevis,
unbind_keyring,
bind_clevis, bind_keyring, encrypt_pool, rebind_clevis, rebind_keyring,
unbind_clevis, unbind_keyring,
},
props::{get_pool_clevis_infos, get_pool_key_descs},
},
Expand Down Expand Up @@ -101,3 +101,30 @@ pub fn clevis_infos_property(f: &Factory<MTSync<TData>, TData>) -> Property<MTSy
.emits_changed(EmitsChangedSignal::True)
.on_get(get_pool_clevis_infos)
}

pub fn encrypt_pool_method(f: &Factory<MTSync<TData>, TData>) -> Method<MTSync<TData>, TData> {
f.method("EncryptPool", (), encrypt_pool)
// Optional key descriptions of key in the kernel keyring
// a: array of zero or more elements
// b: true if a token slot is specified
// i: token slot
// s: key description
//
// Rust representation: Vec<((bool, u32), String)>
.in_arg(("key_desc", "a((bu)s)"))
// Optional Clevis infos for binding on initialization.
// a: array of zero or more elements
// b: true if a token slot is specified
// i: token slot
// s: pin name
// s: JSON config for Clevis use
//
// Rust representation: Vec<((bool, u32), String, String)>
.in_arg(("clevis_info", "a((bu)ss)"))
// b: true if pool was newly encrypted
//
// Rust representation: bool
.out_arg(("results", "b"))
.out_arg(("return_code", "q"))
.out_arg(("return_string", "s"))
}
122 changes: 119 additions & 3 deletions src/dbus_api/pool/pool_3_8/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@

use dbus::Message;
use dbus_tree::{MTSync, MethodInfo, MethodResult};
use serde_json::Value;
use either::Either;
use serde_json::{from_str, Value};

use crate::{
dbus_api::{
types::{DbusErrorEnum, TData, OK_STRING},
types::{DbusErrorEnum, EncryptionInfos, TData, OK_STRING},
util::{engine_to_dbus_err_tuple, get_next_arg, tuple_to_option},
},
engine::{CreateAction, DeleteAction, KeyDescription, OptionalTokenSlotInput, RenameAction},
engine::{
CreateAction, DeleteAction, InputEncryptionInfo, KeyDescription, OptionalTokenSlotInput,
RenameAction,
},
stratis::StratisError,
};

Expand Down Expand Up @@ -397,3 +401,115 @@ pub fn rebind_clevis(m: &MethodInfo<'_, MTSync<TData>, TData>) -> MethodResult {
};
Ok(vec![msg])
}

pub fn encrypt_pool(m: &MethodInfo<'_, MTSync<TData>, TData>) -> MethodResult {
let message: &Message = m.msg;
let mut iter = message.iter_init();

let (key_desc_array, clevis_array): EncryptionInfos<'_> =
(get_next_arg(&mut iter, 0)?, get_next_arg(&mut iter, 1)?);

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 key_descs =
match key_desc_array
.into_iter()
.try_fold(Vec::new(), |mut vec, (ts_opt, kd_str)| {
let token_slot = tuple_to_option(ts_opt);
let kd = KeyDescription::try_from(kd_str.to_string())?;
vec.push((token_slot, kd));
Ok(vec)
}) {
Ok(kds) => kds,
Err(e) => {
let (rc, rs) = engine_to_dbus_err_tuple(&e);
return Ok(vec![return_message.append3(default_return, rc, rs)]);
}
};

let clevis_infos =
match clevis_array
.into_iter()
.try_fold(Vec::new(), |mut vec, (ts_opt, pin, json_str)| {
let token_slot = tuple_to_option(ts_opt);
let json = from_str(json_str)?;
vec.push((token_slot, (pin.to_owned(), json)));
Ok(vec)
}) {
Ok(cis) => cis,
Err(e) => {
let (rc, rs) = engine_to_dbus_err_tuple(&e);
return Ok(vec![return_message.append3(default_return, rc, rs)]);
}
};

let ei = match InputEncryptionInfo::new(key_descs, clevis_infos) {
Ok(Some(opt)) => opt,
Ok(None) => {
let (rc, rs) = engine_to_dbus_err_tuple(&StratisError::Msg(
"Need at least one unlock method to encrypt pool".to_string(),
));
return Ok(vec![return_message.append3(default_return, rc, rs)]);
}
Err(e) => {
let (rc, rs) = engine_to_dbus_err_tuple(&e);
return Ok(vec![return_message.append3(default_return, rc, rs)]);
}
};

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 (name, _, pool) = guard.as_mut_tuple();

let result = handle_action!(
pool.encrypt_pool(&name, pool_uuid, &ei),
dbus_context,
pool_path.get_name()
);
let msg = match result {
Ok(CreateAction::Created(_)) => {
let encryption_info = match pool.encryption_info().clone() {
Some(Either::Left(ei)) => ei,
Some(Either::Right(_)) => {
unreachable!("online reencryption disabled on metadata V1")
}
None => unreachable!("Must have succeeded"),
};
if encryption_info.all_key_descriptions().count() > 0 {
dbus_context.push_pool_key_desc_change(
pool_path.get_name(),
Some(Either::Left((true, encryption_info.clone()))),
);
}
if encryption_info.all_clevis_infos().count() > 0 {
dbus_context.push_pool_clevis_info_change(
pool_path.get_name(),
Some(Either::Left((true, encryption_info.clone()))),
);
}
dbus_context.push_pool_encryption_status_change(pool_path.get_name(), true);
return_message.append3(true, DbusErrorEnum::OK as u16, OK_STRING.to_string())
}
Ok(CreateAction::Identity) => {
return_message.append3(false, 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])
}
5 changes: 3 additions & 2 deletions src/dbus_api/pool/pool_3_8/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod methods;
mod props;

pub use api::{
bind_clevis_method, bind_keyring_method, clevis_infos_property, key_descs_property,
rebind_clevis_method, rebind_keyring_method, unbind_clevis_method, unbind_keyring_method,
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,
};
63 changes: 63 additions & 0 deletions src/dbus_api/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,65 @@ impl DbusTreeHandler {
}
}

/// Send a signal indicating that the pool encryption status has changed.
fn handle_pool_encryption_change(&self, path: Path<'static>, new_encryption: bool) {
if let Err(e) = self.property_changed_invalidated_signal(
&path,
prop_hashmap!(
consts::POOL_INTERFACE_NAME_3_0 => {
Vec::new(),
consts::POOL_ENCRYPTED_PROP.to_string() =>
box_variant!(new_encryption)
},
consts::POOL_INTERFACE_NAME_3_1 => {
Vec::new(),
consts::POOL_ENCRYPTED_PROP.to_string() =>
box_variant!(new_encryption)
},
consts::POOL_INTERFACE_NAME_3_2 => {
Vec::new(),
consts::POOL_ENCRYPTED_PROP.to_string() =>
box_variant!(new_encryption)
},
consts::POOL_INTERFACE_NAME_3_3 => {
Vec::new(),
consts::POOL_ENCRYPTED_PROP.to_string() =>
box_variant!(new_encryption)
},
consts::POOL_INTERFACE_NAME_3_4 => {
Vec::new(),
consts::POOL_ENCRYPTED_PROP.to_string() =>
box_variant!(new_encryption)
},
consts::POOL_INTERFACE_NAME_3_5 => {
Vec::new(),
consts::POOL_ENCRYPTED_PROP.to_string() =>
box_variant!(new_encryption)
},
consts::POOL_INTERFACE_NAME_3_6 => {
Vec::new(),
consts::POOL_ENCRYPTED_PROP.to_string() =>
box_variant!(new_encryption)
},
consts::POOL_INTERFACE_NAME_3_7 => {
Vec::new(),
consts::POOL_ENCRYPTED_PROP.to_string() =>
box_variant!(new_encryption)
},
consts::POOL_INTERFACE_NAME_3_8 => {
Vec::new(),
consts::POOL_ENCRYPTED_PROP.to_string() =>
box_variant!(new_encryption)
}
),
) {
warn!(
"Failed to send a signal over D-Bus indicating blockdev total physical size change: {}",
e
);
}
}

/// Send a signal indicating that the pool overprovisioning mode has changed.
fn handle_pool_overprov_mode_change(&self, path: Path<'static>, new_mode: bool) {
if let Err(e) = self.property_changed_invalidated_signal(
Expand Down Expand Up @@ -1445,6 +1504,10 @@ impl DbusTreeHandler {
self.handle_blockdev_total_physical_size_change(path, new_total_physical_size);
Ok(true)
}
DbusAction::PoolEncryptionChange(path, encryption_change) => {
self.handle_pool_encryption_change(path, encryption_change);
Ok(true)
}
DbusAction::PoolForegroundChange(item, new_used, new_alloc, new_size, new_no_space) => {
self.handle_pool_foreground_change(
item,
Expand Down
20 changes: 20 additions & 0 deletions src/dbus_api/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ use crate::{
},
};

/// Type for encryption input for multiple token slots.
pub type EncryptionInfos<'a> = (
Vec<((bool, u32), &'a str)>,
Vec<((bool, u32), &'a str, &'a str)>,
);

/// Type for lockable D-Bus tree object.
pub type LockableTree = Lockable<Arc<RwLock<Tree<MTSync<TData>, TData>>>>;

Expand Down Expand Up @@ -116,6 +122,7 @@ pub enum DbusAction {
FsOriginChange(Path<'static>, Option<FilesystemUuid>),
FsSizeLimitChange(Path<'static>, Option<Sectors>),
FsMergeScheduledChange(Path<'static>, bool),
PoolEncryptionChange(Path<'static>, bool),
FsBackgroundChange(
FilesystemUuid,
SignalChange<Option<Bytes>>,
Expand Down Expand Up @@ -477,6 +484,19 @@ impl DbusContext {
}
}

/// Send changed signal for changed encryption status of pool.
pub fn push_pool_encryption_status_change(&self, path: &Path<'static>, encrypted: bool) {
if let Err(e) = self
.sender
.send(DbusAction::PoolEncryptionChange(path.clone(), encrypted))
{
warn!(
"Encryption status change event could not be sent to the processing thread; no signal will be sent out for the encryption status state change: {}",
e,
)
}
}

/// Send changed signal for changed pool properties when blockdevs are
/// added.
pub fn push_pool_foreground_change(
Expand Down
22 changes: 15 additions & 7 deletions src/engine/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ use crate::{
structures::{AllLockReadGuard, AllLockWriteGuard, SomeLockReadGuard, SomeLockWriteGuard},
types::{
ActionAvailability, BlockDevTier, Clevis, CreateAction, DeleteAction, DevUuid,
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,
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,
},
},
stratis::StratisResult,
Expand Down Expand Up @@ -388,6 +388,14 @@ pub trait Pool: Debug + Send + Sync {
limit: Option<Bytes>,
) -> StratisResult<PropChangeAction<Option<Sectors>>>;

/// Encrypted an unencrypted pool.
fn encrypt_pool(
&mut self,
name: &Name,
pool_uuid: PoolUuid,
encryption_info: &InputEncryptionInfo,
) -> StratisResult<CreateAction<EncryptedDevice>>;

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

Expand Down
Loading

0 comments on commit 4c49652

Please sign in to comment.