Skip to content

Commit

Permalink
cdh: add image pull API
Browse files Browse the repository at this point in the history
This commit adds a new API `pull_image()` for Confidential DataHub. This
new api will converge all operations related to image pull to one API,
hinting that image signature validation, auth file retrievement,
security policy enforcement will all be done inside CDH.

Related configurations is included in CDH's config.image item.

Signed-off-by: Xynnn007 <xynnn@linux.alibaba.com>
  • Loading branch information
Xynnn007 committed Jul 15, 2024
1 parent 53c2b7d commit e021335
Show file tree
Hide file tree
Showing 16 changed files with 247 additions and 51 deletions.
6 changes: 2 additions & 4 deletions .github/workflows/cdh_basic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,8 @@ jobs:
if: matrix.instance == 'ubuntu-latest'

- name: Run cargo test
uses: actions-rs/cargo@v1
with:
command: test
args: --features kbs,aliyun,sev,bin -p kms -p confidential-data-hub -p secret -p image
run: |
sudo -E cargo test --features kbs,aliyun,sev,bin,image-pull -p kms -p confidential-data-hub -p secret -p image
- name: Run cargo fmt check
uses: actions-rs/cargo@v1
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions confidential-data-hub/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ KMS_PROVIDER ?= aliyun,ehsm
DESTDIR ?= $(PREFIX)/bin
RUSTFLAGS_ARGS ?=
features ?=
IMAGE_PULL ?= false

binary_name ?=

Expand All @@ -37,6 +38,10 @@ else
binary_name = grpc-cdh
endif

ifeq ($(IMAGE_PULL), true)
features += image-pull
endif

ifeq ($(SOURCE_ARCH), ppc64le)
ARCH=powerpc64le
endif
Expand Down
6 changes: 6 additions & 0 deletions confidential-data-hub/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ RPC plugins (flag `RPC`)
| grpc | Use grpc API to serve for requests (TCP/IP socket). |
| ttrpc | Use ttrpc API to serve for requests (Unix socket). |

Advanced API

| Feature name | Note |
| ------------------- | ----------------------------------------------------------------- |
| image-pull | Support `pull_image` API |

### Configuration file

CDH will be launched by a configuration file by
Expand Down
6 changes: 5 additions & 1 deletion confidential-data-hub/hub/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ clap = { workspace = true, features = [ "derive" ], optional = true }
config = { workspace = true, optional = true }
env_logger = { workspace = true, optional = true }
image = { path = "../image", default-features = false }
image-rs = { path = "../../image-rs", default-features = false, optional = true }
kms = { path = "../kms", default-features = false }
lazy_static.workspace = true
log.workspace = true
Expand All @@ -42,7 +43,7 @@ storage.path = "../storage"
serde = { workspace = true, optional = true }
serde_json.workspace = true
thiserror.workspace = true
tokio = { workspace = true, features = [ "rt-multi-thread", "macros" ] }
tokio = { workspace = true, features = [ "rt-multi-thread", "macros", "sync" ] }
tonic = { workspace = true, optional = true }
ttrpc = { workspace = true, features = ["async"], optional = true }

Expand All @@ -69,6 +70,9 @@ sev = ["image/sev", "kms/sev", "secret/sev"]
# support eHSM stacks (KMS, ...)
ehsm = ["image/ehsm", "secret/ehsm"]

# suppot for image-pull API
image-pull = ["image-rs/kata-cc-rustls-tls"]

# Binary RPC type
bin = [ "anyhow", "attestation-agent", "cfg-if", "clap", "config", "env_logger", "serde" ]
ttrpc = ["dep:ttrpc", "protobuf", "ttrpc-codegen", "tokio/signal"]
Expand Down
3 changes: 3 additions & 0 deletions confidential-data-hub/hub/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,7 @@ pub trait DataHub {
async fn get_resource(&self, uri: String) -> Result<Vec<u8>>;

async fn secure_mount(&self, storage: Storage) -> Result<String>;

/// Pull image of image url (reference), and place the merged layers in the bundle path
async fn pull_image(&self, _image_url: &str, _bundle_path: &str) -> Result<String>;
}
14 changes: 2 additions & 12 deletions confidential-data-hub/hub/src/bin/grpc-cdh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,13 @@ use std::{env, net::SocketAddr};

use anyhow::{Context, Result};
use clap::Parser;
use confidential_data_hub::hub::Hub;
use confidential_data_hub::{hub::Hub, CdhConfig};
use log::info;
use tokio::signal::unix::{signal, SignalKind};

mod config;
mod grpc_server;
mod message;

use config::*;

const VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/version"));

#[derive(Debug, Parser)]
Expand All @@ -35,7 +32,6 @@ async fn main() -> Result<()> {
let cli = Cli::parse();

let config = CdhConfig::new(cli.config)?;
config.set_configuration_envs();

let cdh_socket = config.socket.parse::<SocketAddr>()?;

Expand All @@ -44,13 +40,7 @@ async fn main() -> Result<()> {
config.socket
);

let credentials = config
.credentials
.iter()
.map(|it| (it.path.clone(), it.resource_uri.clone()))
.collect();

let cdh = Hub::new(credentials).await.context("start CDH")?;
let cdh = Hub::new(config).await.context("start CDH")?;

let mut interrupt = signal(SignalKind::interrupt())?;
let mut hangup = signal(SignalKind::hangup())?;
Expand Down
19 changes: 5 additions & 14 deletions confidential-data-hub/hub/src/bin/ttrpc-cdh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::{env, path::Path, sync::Arc};

use anyhow::{anyhow, Context, Result};
use clap::Parser;
use confidential_data_hub::CdhConfig;
use log::info;
use protos::{
api_ttrpc::{
Expand All @@ -21,13 +22,10 @@ use tokio::{
use ttrpc::r#async::Server as TtrpcServer;
use ttrpc_server::Server;

mod config;
mod message;
mod protos;
mod ttrpc_server;

use config::*;

const UNIX_SOCKET_PREFIX: &str = "unix://";

const VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/version"));
Expand Down Expand Up @@ -56,7 +54,6 @@ async fn main() -> Result<()> {
let cli = Cli::parse();

let config = CdhConfig::new(cli.config)?;
config.set_configuration_envs();

let unix_socket_path = config
.socket
Expand All @@ -66,16 +63,10 @@ async fn main() -> Result<()> {
create_socket_parent_directory(unix_socket_path).await?;
clean_previous_sock_file(unix_socket_path).await?;

let credentials = config
.credentials
.iter()
.map(|it| (it.path.clone(), it.resource_uri.clone()))
.collect();

let sealed_secret_service = ttrpc_service!(create_sealed_secret_service, &credentials);
let get_resource_service = ttrpc_service!(create_get_resource_service, &credentials);
let key_provider_service = ttrpc_service!(create_key_provider_service, &credentials);
let secure_mount_service = ttrpc_service!(create_secure_mount_service, &credentials);
let sealed_secret_service = ttrpc_service!(create_sealed_secret_service, &config);
let get_resource_service = ttrpc_service!(create_get_resource_service, &config);
let key_provider_service = ttrpc_service!(create_key_provider_service, &config);
let secure_mount_service = ttrpc_service!(create_secure_mount_service, &config);

let mut server = TtrpcServer::new()
.bind(&config.socket)
Expand Down
12 changes: 6 additions & 6 deletions confidential-data-hub/hub/src/bin/ttrpc_server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
// SPDX-License-Identifier: Apache-2.0
//

use std::{collections::HashMap, error::Error as _, sync::Arc};
use std::{error::Error as _, sync::Arc};

use anyhow::Result;
use async_trait::async_trait;
use confidential_data_hub::{hub::Hub, DataHub};
use confidential_data_hub::{hub::Hub, CdhConfig, DataHub};
use lazy_static::lazy_static;
use log::{debug, error};
use storage::volume_type::Storage;
Expand Down Expand Up @@ -35,18 +35,18 @@ lazy_static! {
pub struct Server;

impl Server {
async fn init(credentials: &HashMap<String, String>) -> Result<()> {
async fn init(config: &CdhConfig) -> Result<()> {
let mut writer = HUB.write().await;
if writer.is_none() {
let hub = Hub::new(credentials.to_owned()).await?;
let hub = Hub::new(config.clone()).await?;
*writer = Some(hub);
}

Ok(())
}

pub async fn new(credentials: &HashMap<String, String>) -> Result<Self> {
Self::init(credentials).await?;
pub async fn new(config: &CdhConfig) -> Result<Self> {
Self::init(config).await?;
Ok(Self)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ cfg_if::cfg_if! {
}
}

#[derive(Deserialize, Debug, PartialEq)]
#[derive(Clone, Deserialize, Debug, PartialEq)]
pub struct KbsConfig {
pub name: String,

Expand All @@ -41,19 +41,79 @@ impl KbsConfig {
}
}

#[derive(Deserialize, Debug, PartialEq)]
#[derive(Clone, Deserialize, Debug, PartialEq)]
pub struct Credential {
pub resource_uri: String,
pub path: String,
}

#[derive(Deserialize, Debug, PartialEq)]
#[cfg(feature = "image-pull")]
#[derive(Clone, Deserialize, Debug, PartialEq)]
pub struct ImageConfiguration {
/// Whether to enable image security validation
///
/// This defaults to `false`.
pub security_validate: bool,

/// Use `auth.json` control
/// ///
/// This defaults to `false`.
pub auth: bool,

/// Maximum number of concurrent downloads to perform during image pull.
///
/// This defaults to [`image_rs::config::DEFAULT_MAX_CONCURRENT_DOWNLOAD`].
pub max_concurrent_download: usize,

/// sigstore config file for simple signing
///
/// This defaults to [`image_rs::config::SIG_STORE_CONFIG_DEFAULT_FILE`].
pub sigstore_config_uri: String,

/// Path to `Policy.json`
///
/// This defaults to [`image_rs::config::POLICY_FILE_PATH`].
pub policy_uri: String,

/// Path to the auth file
///
/// This defaults to [`image_rs::config::AUTH_FILE_PATH`].
pub auth_uri: String,

/// Proxy that will be used to pull image
pub https_proxy: String,

/// No proxy env that will be used to pull image
pub no_proxy: String,
}

#[cfg(feature = "image-pull")]
impl Default for ImageConfiguration {
fn default() -> Self {
Self {
security_validate: false,
auth: false,
sigstore_config_uri: image_rs::config::SIG_STORE_CONFIG_DEFAULT_FILE.to_string(),
policy_uri: image_rs::config::POLICY_FILE_PATH.to_string(),
auth_uri: image_rs::config::AUTH_FILE_PATH.to_string(),
max_concurrent_download: image_rs::config::DEFAULT_MAX_CONCURRENT_DOWNLOAD,
https_proxy: String::new(),
no_proxy: String::new(),
}
}
}

#[derive(Clone, Deserialize, Debug, PartialEq)]
pub struct CdhConfig {
pub kbc: KbsConfig,

#[serde(default)]
pub credentials: Vec<Credential>,

#[cfg(feature = "image-pull")]
#[serde(default)]
pub image: ImageConfiguration,

pub socket: String,
}

Expand Down Expand Up @@ -82,6 +142,9 @@ impl CdhConfig {
kbc: KbsConfig::new()?,
credentials: Vec::new(),
socket: DEFAULT_CDH_SOCKET_ADDR.into(),

#[cfg(feature = "image-pull")]
image: ImageConfiguration::default(),
}
}
};
Expand Down Expand Up @@ -171,6 +234,16 @@ socket = "unix:///run/confidential-containers/cdh.sock"
name = "offline_fs_kbc"
url = ""
kbs_cert = ""
[image]
security_validate = false
auth = false
max_concurrent_download = 3
sigstore_config_uri = "kbs:///default/sigstore-config/test"
policy_uri = "kbs:///default/security-policy/test"
auth_uri = "kbs:///default/credential/test"
https_proxy = ""
no_proxy = ""
"#,
true
)]
Expand Down Expand Up @@ -199,6 +272,16 @@ kbs_cert = ""
[[credentials]]
resource_uri = "kbs:///default/1/1"
path = "/run/confidential-containers/cdh/kms-credential/aliyun/config.toml"
[image]
security_validate = false
auth = false
max_concurrent_download = 3
sigstore_config_uri = "kbs:///default/sigstore-config/test"
policy_uri = "kbs:///default/security-policy/test"
auth_uri = "kbs:///default/credential/test"
https_proxy = ""
no_proxy = ""
"#,
true
)]
Expand Down Expand Up @@ -227,6 +310,9 @@ path = "/run/confidential-containers/cdh/kms-credential/aliyun/config.toml"
},
credentials: Vec::new(),
socket: DEFAULT_CDH_SOCKET_ADDR.into(),

#[cfg(feature = "image-pull")]
image: crate::ImageConfiguration::default(),
};
assert_eq!(config, expected);

Expand All @@ -238,6 +324,7 @@ path = "/run/confidential-containers/cdh/kms-credential/aliyun/config.toml"
let config = CdhConfig::new(None).unwrap_err();
let expected = anyhow!("Config file /byenv not found.");
assert_eq!(format!("{config}"), format!("{expected}"));
env::remove_var("CDH_CONFIG_PATH");

let config = CdhConfig::new(Some("/thing".into())).unwrap_err();
let expected = anyhow!("Config file /thing not found.");
Expand Down
6 changes: 6 additions & 0 deletions confidential-data-hub/hub/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,10 @@ pub enum Error {

#[error("secure mount failed")]
SecureMount(#[from] storage::Error),

#[error("image pull failed")]
ImagePull {
#[source]
source: anyhow::Error,
},
}
Loading

0 comments on commit e021335

Please sign in to comment.