From 63f0f1bfeeeb5e7612d0c2b656a9cfeb3ae85018 Mon Sep 17 00:00:00 2001 From: ChengyuZhu6 Date: Tue, 16 Jul 2024 11:50:52 +0800 Subject: [PATCH] cdh: Add secure mount interface for block device Add secure mount interface for block device. Fixed: #540 -- part II Signed-off-by: ChengyuZhu6 --- Cargo.lock | 1 + confidential-data-hub/storage/Cargo.toml | 1 + confidential-data-hub/storage/src/error.rs | 3 + .../src/volume_type/blockdevice/error.rs | 29 +++++ .../src/volume_type/blockdevice/mod.rs | 107 ++++++++++++++++++ .../storage/src/volume_type/mod.rs | 9 +- 6 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 confidential-data-hub/storage/src/volume_type/blockdevice/error.rs create mode 100644 confidential-data-hub/storage/src/volume_type/blockdevice/mod.rs diff --git a/Cargo.lock b/Cargo.lock index c768c1ecc..2a3b93965 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5762,6 +5762,7 @@ dependencies = [ "anyhow", "async-trait", "base64 0.21.7", + "kms", "log", "rand", "rstest", diff --git a/confidential-data-hub/storage/Cargo.toml b/confidential-data-hub/storage/Cargo.toml index 9fc1e8773..bce3553ec 100644 --- a/confidential-data-hub/storage/Cargo.toml +++ b/confidential-data-hub/storage/Cargo.toml @@ -10,6 +10,7 @@ anyhow.workspace = true async-trait.workspace = true base64.workspace = true log.workspace = true +kms = { path = "../kms", default-features = false } rand = { workspace = true, optional = true } secret = { path = "../secret" } serde.workspace = true diff --git a/confidential-data-hub/storage/src/error.rs b/confidential-data-hub/storage/src/error.rs index 113ed610e..6693b4375 100644 --- a/confidential-data-hub/storage/src/error.rs +++ b/confidential-data-hub/storage/src/error.rs @@ -15,6 +15,9 @@ pub enum Error { #[error("Error when mounting Aliyun OSS")] AliyunOssError(#[from] volume_type::aliyun::error::AliyunError), + #[error("Error when mounting Block device")] + BlockDeviceError(#[from] volume_type::blockdevice::error::BlockDeviceError), + #[error("Failed to recognize the storage type")] StorageTypeNotRecognized(#[from] strum::ParseError), } diff --git a/confidential-data-hub/storage/src/volume_type/blockdevice/error.rs b/confidential-data-hub/storage/src/volume_type/blockdevice/error.rs new file mode 100644 index 000000000..dd11fda41 --- /dev/null +++ b/confidential-data-hub/storage/src/volume_type/blockdevice/error.rs @@ -0,0 +1,29 @@ +// Copyright (c) 2024 Intel +// +// SPDX-License-Identifier: Apache-2.0 +// + +use thiserror::Error; + +pub type Result = std::result::Result; + +#[derive(Error, Debug)] +pub enum BlockDeviceError { + #[error("Error when getting encrypt/decrypt keys")] + GetKeysFailure(#[from] anyhow::Error), + + #[error("LUKS decryption mount failed")] + LUKSfsMountFailed, + + #[error("I/O error")] + IOError(#[from] std::io::Error), + + #[error("Failed to mount block device")] + BlockDeviceMountFailed, + + #[error("Serialize/Deserialize failed")] + SerdeError(#[from] serde_json::Error), + + #[error("Failed to recognize the storage type")] + StorageTypeNotRecognized(#[from] strum::ParseError), +} diff --git a/confidential-data-hub/storage/src/volume_type/blockdevice/mod.rs b/confidential-data-hub/storage/src/volume_type/blockdevice/mod.rs new file mode 100644 index 000000000..22f1dc723 --- /dev/null +++ b/confidential-data-hub/storage/src/volume_type/blockdevice/mod.rs @@ -0,0 +1,107 @@ +// Copyright (c) 2024 Intel +// +// SPDX-License-Identifier: Apache-2.0 +// +pub mod error; + +use super::SecureMount; +use anyhow::Context; +use async_trait::async_trait; +use error::{BlockDeviceError, Result}; +use kms::{Annotations, ProviderSettings}; +use log::{debug, error}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use strum::{Display, EnumString}; + +#[derive(EnumString, Serialize, Deserialize, Display, Debug, PartialEq, Eq)] +pub enum BlockDeviceEncryptType { + #[strum(serialize = "luks")] + LUKS, +} + +#[derive(Serialize, Deserialize, PartialEq, Debug)] +pub struct BlockDeviceParameters { + /// The device number, formatted as "MAJ:MIN". + #[serde(rename = "deviceId")] + pub device_id: String, + + /// The encryption type. Currently, only LUKS is supported. + #[serde(rename = "encryptType")] + pub encryption_type: BlockDeviceEncryptType, + + /// Encryption key. If not set, generate a random 4096-byte key + #[serde(rename = "encryptKey")] + pub encryption_key: Option, + + /// Indicates whether to enable dm-integrity. + #[serde(rename = "dataIntegrity")] + pub data_integrity: String, +} +pub(crate) struct BlockDevice; + +#[async_trait] +pub trait Encryptor { + async fn encrypt_and_mount( + &self, + parameters: BlockDeviceParameters, + mount_point: &str, + ) -> Result<()>; +} + +async fn get_plaintext_key(resource: &str) -> anyhow::Result> { + if resource.starts_with("sealed.") { + debug!("detected sealed secret"); + let unsealed = secret::unseal_secret(resource.as_bytes()).await?; + return Ok(unsealed); + } + + if resource.starts_with("kbs://") { + let secret = kms::new_getter("kbs", ProviderSettings::default()) + .await? + .get_secret(resource, &Annotations::default()) + .await + .map_err(|e| { + error!("get keys from kbs failed: {e}"); + BlockDeviceError::GetKeysFailure(e.into()) + })?; + return Ok(secret); + } + + Err(BlockDeviceError::GetKeysFailure(anyhow::anyhow!("unknown resource scheme")).into()) +} + +impl BlockDevice { + async fn real_mount( + &self, + options: &HashMap, + _flags: &[String], + mount_point: &str, + ) -> Result<()> { + // construct BlockDeviceParameters + let parameters = serde_json::to_string(options)?; + let bd_parameter: BlockDeviceParameters = serde_json::from_str(¶meters)?; + + Ok(()) + } +} + +#[async_trait] +impl SecureMount for BlockDevice { + /// Mount the block device to the given `mount_point``. + /// + /// If `bd.encrypt_type` is set to `LUKS`, the device will be formated as a LUKS-encrypted device. + /// Then use cryptsetup open the device and mount it to `mount_point` as plaintext. + /// + /// This is a wrapper for inner function to convert error type. + async fn mount( + &self, + options: &HashMap, + flags: &[String], + mount_point: &str, + ) -> super::Result<()> { + self.real_mount(options, flags, mount_point) + .await + .map_err(|e| e.into()) + } +} diff --git a/confidential-data-hub/storage/src/volume_type/mod.rs b/confidential-data-hub/storage/src/volume_type/mod.rs index 7a89d06b4..76f27dc28 100644 --- a/confidential-data-hub/storage/src/volume_type/mod.rs +++ b/confidential-data-hub/storage/src/volume_type/mod.rs @@ -5,7 +5,7 @@ #[cfg(feature = "aliyun")] pub mod aliyun; - +pub mod blockdevice; use std::{collections::HashMap, str::FromStr}; use crate::Result; @@ -20,6 +20,7 @@ pub enum Volume { #[cfg(feature = "aliyun")] #[strum(serialize = "alibaba-cloud-oss")] AliOss, + BlockDevice, } /// Indicating a mount point and its parameters. @@ -61,6 +62,12 @@ impl Storage { .await?; Ok(self.mount_point.clone()) } + Volume::BlockDevice => { + let bd = blockdevice::BlockDevice {}; + bd.mount(&self.options, &self.flags, &self.mount_point) + .await?; + Ok(self.mount_point.clone()) + } } } }