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 Sep 17, 2024
1 parent ef5d7a6 commit 5e9aadb
Show file tree
Hide file tree
Showing 20 changed files with 730 additions and 154 deletions.
2 changes: 2 additions & 0 deletions src/dbus_api/pool/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod pool_3_3;
mod pool_3_5;
mod pool_3_6;
mod pool_3_7;
mod pool_3_8;
pub mod prop_conv;
mod shared;

Expand Down Expand Up @@ -293,6 +294,7 @@ pub fn create_dbus_pool<'a>(
.add_m(pool_3_0::rename_method(&f))
.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_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
32 changes: 32 additions & 0 deletions src/dbus_api/pool/pool_3_8/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

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

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

pub fn encrypt_pool_method(f: &Factory<MTSync<TData>, TData>) -> Method<MTSync<TData>, TData> {
f.method("EncryptPool", (), encrypt_pool)
// Optional key description of key in the kernel keyring
// b: true if the pool should be encrypted and able to be
// unlocked with a passphrase associated with this key description.
// s: key description
//
// Rust representation: (bool, String)
.in_arg(("key_desc", "(bs)"))
// Optional Clevis information for binding on initialization.
// b: true if the pool should be encrypted and able to be unlocked
// using Clevis.
// s: pin name
// s: JSON config for Clevis use
//
// Rust representation: (bool, (String, String))
.in_arg(("clevis_info", "(b(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"))
}
99 changes: 99 additions & 0 deletions src/dbus_api/pool/pool_3_8/methods.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::iter::once;

use dbus::Message;
use dbus_tree::{MTSync, MethodInfo, MethodResult};

use crate::{
dbus_api::{
types::{DbusErrorEnum, TData, OK_STRING},
util::{engine_to_dbus_err_tuple, get_next_arg, tuple_to_option},
},
engine::{CreateAction, EncryptionInfo, KeyDescription, PoolEncryptionInfo},
stratis::StratisError,
};

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

let key_desc_tuple: (bool, String) = get_next_arg(&mut iter, 0)?;
let clevis_tuple: (bool, (String, String)) = get_next_arg(&mut iter, 0)?;

let dbus_context = m.tree.get_data();
let object_path = m.path.get_name();
let return_message = message.method_return();
let default_return: (bool, Vec<String>) = (false, Vec::new());

let key_desc = match tuple_to_option(key_desc_tuple) {
Some(kds) => match KeyDescription::try_from(kds) {
Ok(kd) => Some(kd),
Err(e) => {
let (rc, rs) = engine_to_dbus_err_tuple(&e);
return Ok(vec![return_message.append3(default_return, rc, rs)]);
}
},
None => None,
};
let clevis_info = match tuple_to_option(clevis_tuple) {
Some((pin, json_string)) => match serde_json::from_str(json_string.as_str()) {
Ok(j) => Some((pin, j)),
Err(e) => {
let (rc, rs) = engine_to_dbus_err_tuple(&StratisError::Serde(e));
return Ok(vec![return_message.append3(default_return, rc, rs)]);
}
},
None => None,
};
let encryption_info = match EncryptionInfo::from_options((key_desc, clevis_info)) {
Some(enc) => enc,
None => {
let (rc, rs) = (
DbusErrorEnum::ERROR as u16,
"Either a key description or Clevis info is required for this method".to_string(),
);
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, &encryption_info),
dbus_context,
pool_path.get_name()
);
let msg = match result {
Ok(CreateAction::Created(_)) => {
let pool_enc_info = PoolEncryptionInfo::from(once(&encryption_info));
dbus_context
.push_pool_key_desc_change(pool_path.get_name(), Some(pool_enc_info.clone()));
dbus_context.push_pool_clevis_info_change(pool_path.get_name(), Some(pool_enc_info));
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])
}
8 changes: 8 additions & 0 deletions src/dbus_api/pool/pool_3_8/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

mod api;
mod methods;

pub use api::encrypt_pool_method;
63 changes: 63 additions & 0 deletions src/dbus_api/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,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 @@ -1379,6 +1438,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
14 changes: 14 additions & 0 deletions src/dbus_api/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ pub enum DbusAction {
BlockdevTotalPhysicalSizeChange(Path<'static>, Sectors),
FsOriginChange(Path<'static>),
FsSizeLimitChange(Path<'static>, Option<Sectors>),
PoolEncryptionChange(Path<'static>, bool),
FsBackgroundChange(
FilesystemUuid,
SignalChange<Option<Bytes>>,
Expand Down Expand Up @@ -465,6 +466,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
19 changes: 14 additions & 5 deletions src/engine/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ use crate::{
structures::{AllLockReadGuard, AllLockWriteGuard, SomeLockReadGuard, SomeLockWriteGuard},
types::{
ActionAvailability, BlockDevTier, Clevis, CreateAction, DeleteAction, DevUuid,
EncryptionInfo, FilesystemUuid, GrowAction, Key, KeyDescription, LockedPoolsInfo,
MappingCreateAction, MappingDeleteAction, Name, PoolDiff, PoolEncryptionInfo,
PoolIdentifier, PoolUuid, RegenAction, RenameAction, ReportType, SetCreateAction,
SetDeleteAction, SetUnlockAction, StartAction, StopAction, StoppedPoolsInfo,
StratFilesystemDiff, StratSigblockVersion, UdevEngineEvent, UnlockMethod,
EncryptedDevice, EncryptionInfo, FilesystemUuid, GrowAction, Key, KeyDescription,
LockedPoolsInfo, MappingCreateAction, MappingDeleteAction, Name, PoolDiff,
PoolEncryptionInfo, PoolIdentifier, PoolUuid, RegenAction, RenameAction, ReportType,
SetCreateAction, SetDeleteAction, SetUnlockAction, StartAction, StopAction,
StoppedPoolsInfo, StratFilesystemDiff, StratSigblockVersion, UdevEngineEvent,
UnlockMethod,
},
},
stratis::StratisResult,
Expand Down Expand Up @@ -342,6 +343,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: &EncryptionInfo,
) -> 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
14 changes: 12 additions & 2 deletions src/engine/sim_engine/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ use crate::{
structures::Table,
types::{
ActionAvailability, BlockDevTier, Clevis, CreateAction, DeleteAction, DevUuid,
EncryptionInfo, FilesystemUuid, GrowAction, Key, KeyDescription, Name, PoolDiff,
PoolEncryptionInfo, PoolUuid, RegenAction, RenameAction, SetCreateAction,
EncryptedDevice, EncryptionInfo, FilesystemUuid, GrowAction, Key, KeyDescription, Name,
PoolDiff, PoolEncryptionInfo, PoolUuid, RegenAction, RenameAction, SetCreateAction,
SetDeleteAction, StratSigblockVersion,
},
PropChangeAction,
Expand Down Expand Up @@ -746,6 +746,16 @@ impl Pool for SimPool {
}
}

fn encrypt_pool(
&mut self,
_: &Name,
_: PoolUuid,
enc: &EncryptionInfo,
) -> StratisResult<CreateAction<EncryptedDevice>> {
self.encryption_info = Some(enc.clone());
Ok(CreateAction::Created(EncryptedDevice))
}

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: 19 additions & 1 deletion src/engine/strat_engine/backstore/backstore/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

// Code to handle the backing store of a pool.

use std::{cmp, collections::HashMap, iter::once, path::PathBuf};
use std::{cmp, collections::HashMap, iter::once, path::Path, path::PathBuf};

use chrono::{DateTime, Utc};
use either::Either;
Expand All @@ -29,6 +29,7 @@ use crate::{
names::{format_backstore_ids, CacheRole},
serde_structs::{BackstoreSave, CapSave, PoolFeatures, PoolSave, Recordable},
shared::bds_to_bdas,
thinpool::ThinPool,
types::BDARecordResult,
writing::wipe_sectors,
},
Expand Down Expand Up @@ -1108,6 +1109,23 @@ impl Backstore {
self.data_tier.grow(dev)
}

pub fn encrypt(
&mut self,
pool_uuid: PoolUuid,
thinpool: &mut ThinPool<Self>,
encryption_info: &EncryptionInfo,
) -> StratisResult<()> {
let (dm_name, _) = format_backstore_ids(pool_uuid, CacheRole::Cache);
let handle = CryptHandle::encrypt(
pool_uuid,
thinpool,
Path::new(&format!("/dev/mapper/{}", &dm_name.to_string())),
encryption_info,
)?;
self.enc = Some(Either::Right(handle));
Ok(())
}

/// A summary of block sizes
pub fn block_size_summary(&self, tier: BlockDevTier) -> Option<BlockSizeSummary> {
match tier {
Expand Down
Loading

0 comments on commit 5e9aadb

Please sign in to comment.