From 962d4c297e11c082155661755a641b69eda79a0d Mon Sep 17 00:00:00 2001 From: Xynnn007 Date: Thu, 20 Jun 2024 15:51:11 +0800 Subject: [PATCH] cdh: add one-shot CDH One-shot CDH is a different version of CDH that will run and do the operation one time then exit. This is useful in scenarios where CDH cannot run as a long term daemon. Signed-off-by: Xynnn007 --- README.md | 3 + confidential-data-hub/Makefile | 8 +- confidential-data-hub/README.md | 5 + confidential-data-hub/hub/Cargo.toml | 4 + .../hub/src/bin/cdh-oneshot.rs | 186 ++++++++++++++++++ 5 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 confidential-data-hub/hub/src/bin/cdh-oneshot.rs diff --git a/README.md b/README.md index dbf5efc81..8c53655c2 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,9 @@ A tool for exercising CDH endpoints [CDH Go Client](confidential-data-hub/golang) A Go tool for exercising CDH endpoints +[CDH (One Shot)](confidential-data-hub/hub/src/bin/cdh-oneshot.rs) +One Shot version of CDH + [CoCo Keyprovider](attestation-agent/coco_keyprovider) Keyprovider endpoint for encrypting images diff --git a/confidential-data-hub/Makefile b/confidential-data-hub/Makefile index 35af0fd8e..2668abddc 100644 --- a/confidential-data-hub/Makefile +++ b/confidential-data-hub/Makefile @@ -14,6 +14,8 @@ $(info DEBIANOS is: $(DEBIANOS)) TARGET_DIR := ../target BIN_NAME := confidential-data-hub +ONE_SHOT ?= false + SOURCE_ARCH := $(shell uname -m) RPC ?= ttrpc ARCH ?= $(shell uname -m) @@ -27,7 +29,11 @@ features ?= binary_name ?= -ifeq ($(RPC), ttrpc) +ifeq ($(ONE_SHOT), true) + binary = --bin cdh-oneshot + features += bin + binary_name = cdh-oneshot +else ifeq ($(RPC), ttrpc) binary = --bin ttrpc-cdh features += bin,ttrpc binary_name = ttrpc-cdh diff --git a/confidential-data-hub/README.md b/confidential-data-hub/README.md index 5b1d02bc6..f307a11e8 100644 --- a/confidential-data-hub/README.md +++ b/confidential-data-hub/README.md @@ -33,6 +33,11 @@ If you don't want to include any RESOURCE_PROVIDER(s): make RESOURCE_PROVIDER=none ``` +The default CDH runs as a service daemon. If you want to build CDH to an one-shot binary (run once and exit), use flag `ONE_SHOT=true` +```shell +make ONE_SHOT=true +``` + Please refer to [Supported Features](#supported-features) for the options. ### Supported Features diff --git a/confidential-data-hub/hub/Cargo.toml b/confidential-data-hub/hub/Cargo.toml index 64bad3f55..cf9afc49b 100644 --- a/confidential-data-hub/hub/Cargo.toml +++ b/confidential-data-hub/hub/Cargo.toml @@ -22,6 +22,10 @@ required-features = ["bin", "ttrpc"] name = "grpc-cdh-tool" required-features = ["bin", "grpc"] +[[bin]] +name = "cdh-oneshot" +required-features = ["bin"] + [dependencies] anyhow = { workspace = true, optional = true } async-trait.workspace = true diff --git a/confidential-data-hub/hub/src/bin/cdh-oneshot.rs b/confidential-data-hub/hub/src/bin/cdh-oneshot.rs new file mode 100644 index 000000000..58216b905 --- /dev/null +++ b/confidential-data-hub/hub/src/bin/cdh-oneshot.rs @@ -0,0 +1,186 @@ +// Copyright (c) 2024 Alibaba Cloud +// +// SPDX-License-Identifier: Apache-2.0 +// + +//! This is a one-shot version of CDH + +#![allow(non_snake_case)] + +use base64::{engine::general_purpose::STANDARD, Engine}; +use clap::{Args, Parser, Subcommand}; +use confidential_data_hub::{hub::Hub, CdhConfig, DataHub}; +use log::warn; +use storage::volume_type::Storage; + +#[derive(Parser)] +#[command(name = "cdh_oneshot")] +#[command(bin_name = "cdh_oneshot")] +#[command(author, version, about, long_about = None)] +struct Cli { + #[command(subcommand)] + operation: Operation, + + /// CDH's config path + #[arg(short, long)] + config: Option, + + /// Retries times + #[arg(short, long, default_value = "3")] + retry: u32, +} + +#[derive(Subcommand)] +#[command(author, version, about, long_about = None)] +enum Operation { + /// Unseal the given sealed secret + UnsealSecret(UnsealSecretArgs), + + /// Unwrap the image encryption key + UnwrapKey(UnwrapKeyArgs), + + /// Get Resource from KBS + GetResource(GetResourceArgs), + + /// Secure mount + SecureMount(SecureMountArgs), + + /// Pull image + PullImage(PullImageArgs), +} + +#[derive(Args)] +#[command(author, version, about, long_about = None)] +struct UnsealSecretArgs { + /// path to the file which contains the sealed secret + #[arg(short, long)] + secret_path: String, +} + +#[derive(Args)] +#[command(author, version, about, long_about = None)] +struct UnwrapKeyArgs { + /// path to the file which contains the AnnotationPacket + #[arg(short, long)] + annotation_path: String, +} + +#[derive(Args)] +#[command(author, version, about, long_about = None)] +struct GetResourceArgs { + /// KBS Resource URI to the target resource + #[arg(short, long)] + resource_uri: String, +} + +#[derive(Args)] +#[command(author, version, about, long_about = None)] +struct SecureMountArgs { + /// path to the file which contains the Storage object. + #[arg(short, long)] + storage_path: String, +} + +#[derive(Args)] +#[command(author, version, about, long_about = None)] +struct PullImageArgs { + /// URL of the image to be pulled + #[arg(short, long)] + image_url: String, + + /// Path to store the bundle + #[arg(short, long)] + bundle_path: String, +} + +#[tokio::main] +async fn main() { + let args = Cli::parse(); + let config = CdhConfig::new(args.config).expect("failed to initialize cdh config"); + config.set_configuration_envs(); + + let cdh = Hub::new(config).await.expect("failed to start CDH"); + + let mut tried = 1; + match args.operation { + Operation::UnsealSecret(op_args) => { + let secret = tokio::fs::read(op_args.secret_path) + .await + .expect("read secret file"); + loop { + match cdh.unseal_secret(secret.clone()).await { + Ok(secret) => { + let res = STANDARD.encode(secret); + println!("{res}"); + break; + } + Err(e) => { + if tried > args.retry { + let error = format!("failed to unseal secret, {:?}", e); + panic!("{error}"); + } + warn!("Tried {tried} times... failed to unseal secret: {e}."); + tried += 1; + } + } + } + } + Operation::UnwrapKey(op_args) => { + let KeyProviderKeyWrapProtocolInput = tokio::fs::read(op_args.annotation_path) + .await + .expect("read annotation packet file"); + loop { + match cdh.unwrap_key(&KeyProviderKeyWrapProtocolInput).await { + Ok(KeyProviderKeyWrapProtocolOutput) => { + let res = STANDARD.encode(KeyProviderKeyWrapProtocolOutput); + println!("{res}"); + break; + } + Err(e) => { + if tried > args.retry { + panic!("failed to unwrap key"); + } + warn!("Tried {tried} times... failed to unwrap key: {e}."); + tried += 1; + } + } + } + } + Operation::GetResource(op_args) => loop { + match cdh.get_resource(op_args.resource_uri.clone()).await { + Ok(resource) => { + let res = STANDARD.encode(resource); + println!("{res}"); + break; + } + Err(e) => { + if tried > args.retry { + let error = format!("failed to get resource, {:?}", e); + panic!("{error}"); + } + warn!("Tried {tried} times... failed to get resource: {e}."); + tried += 1; + } + } + }, + Operation::SecureMount(op_args) => { + let storage_manifest = tokio::fs::read(op_args.storage_path) + .await + .expect("read file"); + let storage: Storage = + serde_json::from_slice(&storage_manifest).expect("deserialize Storage"); + let res = cdh + .secure_mount(storage) + .await + .expect("failed to secure mount"); + println!("mount path: {res}"); + } + Operation::PullImage(op_args) => { + let manifest_digest = cdh + .pull_image(&op_args.image_url, &op_args.bundle_path) + .await + .expect("failed to pull image"); + println!("image digest: {manifest_digest}"); + } + } +}