Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
375 changes: 371 additions & 4 deletions Cargo.lock

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
[workspace]
members = ["compute-pcrs", "crds", "operator", "manifest-gen"]
members = ["compute-pcrs", "crds", "manifest-gen", "operator", "rv-store"]
resolver = "3"

[workspace.package]
edition = "2024"

[workspace.dependencies]
anyhow = "1.0.99"
clap = "4.5.41"
chrono = "0.4.41"
clap = { version = "4.5.41", features = ["derive"] }
compute-pcrs-lib = { git = "https://github.com/confidential-clusters/compute-pcrs" }
env_logger = "0.11.8"
k8s-openapi = { version = "0.25.0", features = ["v1_33"] }
kube = "1.1.0"
kube = { version = "1.1.0", features = ["derive", "runtime"] }
log = "0.4.27"
serde = "1.0.219"
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.141"
tokio = "1.46.1"
tokio = { version = "1.46.1", features = ["macros", "rt-multi-thread"] }
11 changes: 5 additions & 6 deletions compute-pcrs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ description = "A cocl-operator optimized compute-pcrs interface"

[dependencies]
anyhow.workspace = true
chrono = "0.4.41"
clap = { workspace = true, features = ["derive"] }
compute-pcrs-lib = { git = "https://github.com/confidential-clusters/compute-pcrs", version = "0.1.0" }
chrono.workspace = true
clap.workspace = true
compute-pcrs-lib.workspace = true
k8s-openapi.workspace = true
kube.workspace = true
log.workspace = true
serde = { workspace = true, features = ["derive"] }
rv-store = { path = "../rv-store" }
serde_json.workspace = true
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
tokio.workspace = true
5 changes: 3 additions & 2 deletions compute-pcrs/Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ FROM ghcr.io/confidential-clusters/compute-pcrs/buildroot AS builder
WORKDIR /compute-pcrs
COPY Cargo.toml Cargo.lock .
COPY compute-pcrs compute-pcrs
# Hack: Set compute-pcrs as sole member to avoid needing to copy other crates.
COPY rv-store rv-store
# Hack: Set required crates as only members to avoid needing to copy other crates.
# In that case, a rebuild would be triggered upon any change in those crates.
RUN sed -i 's/members =.*/members = ["compute-pcrs"]/' Cargo.toml && \
RUN sed -i 's/members =.*/members = ["compute-pcrs", "rv-store"]/' Cargo.toml && \
git clone --depth 1 https://github.com/confidential-clusters/reference-values && \
cargo build --release -p compute-pcrs

Expand Down
97 changes: 29 additions & 68 deletions compute-pcrs/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,12 @@
use anyhow::Result;
use chrono::{DateTime, TimeDelta, Utc};
use anyhow::{Context, Result};
use chrono::Utc;
use clap::Parser;
use compute_pcrs_lib::*;
use k8s_openapi::api::core::v1::ConfigMap;
use kube::api::{ObjectMeta, PostParams};
use kube::{Api, Client};
use log::info;
use serde::{Serialize, Serializer};
use kube::{Api, Client, api::PostParams};
use std::collections::BTreeMap;

fn primitive_date_time_to_str<S>(d: &DateTime<Utc>, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(&d.format("%Y-%m-%dT%H:%M:%SZ").to_string())
}

/// Sync with Trustee
/// reference_value_provider_service::reference_value::ReferenceValue
/// (cannot import directly because its expiration doesn't serialize
/// right)
#[derive(Serialize)]
struct ReferenceValue {
pub version: String,
pub name: String,
#[serde(serialize_with = "primitive_date_time_to_str")]
pub expiration: DateTime<Utc>,
pub value: serde_json::Value,
}
use rv_store::*;

#[derive(Parser)]
#[command(version, about)]
Expand All @@ -44,9 +23,9 @@ struct Args {
/// Path to directory storing MokListRT, MokListTrustedRT and MokListXRT
#[arg(short, long)]
mokvars: String,
/// ConfigMap name to write to
/// Image reference
#[arg(short, long)]
configmap: String,
image: String,
/// Namespace to write ConfigMap to
#[arg(short, long)]
namespace: String,
Expand All @@ -56,53 +35,35 @@ struct Args {
async fn main() -> Result<()> {
let args = Args::parse();

let mut pcrs: Vec<_> = [
let pcrs = vec![
compute_pcr4(&args.kernels, &args.esp, false, true),
compute_pcr7(Some(&args.efivars), &args.esp, true),
compute_pcr14(&args.mokvars),
]
.iter()
.map(|pcr| (format!("pcr{}", pcr.id), pcr.value.clone()))
.collect();
pcrs.push(("svn".to_string(), "1".to_string()));

let reference_values: Vec<_> = pcrs
.iter()
.map(|(name, value)| ReferenceValue {
version: "0.1.0".to_string(),
name: format!("tpm_{name}"),
expiration: Utc::now() + TimeDelta::days(365),
value: serde_json::Value::Array(vec![serde_json::Value::String(value.to_string())]),
})
.collect();
let reference_values_json = serde_json::to_string(&reference_values)?;
let data = BTreeMap::from([(
"reference-values.json".to_string(),
reference_values_json.to_string(),
)]);

let config_map = ConfigMap {
metadata: ObjectMeta {
name: Some(args.configmap.clone()),
namespace: Some(args.namespace.clone()),
..Default::default()
},
data: Some(data),
..Default::default()
};
];

let client = Client::try_default().await?;
let config_maps: Api<ConfigMap> = Api::namespaced(client, &args.namespace);
match config_maps
.create(&PostParams::default(), &config_map)
.await
{
Ok(_) => info!("Create ConfigMap {}", args.configmap),
Err(kube::Error::Api(ae)) if ae.code == 409 => {
info!("ConfigMap {} already exists", args.configmap)
}
Err(e) => return Err(e.into()),
}

let mut image_pcrs_map = config_maps.get(PCR_CONFIG_MAP).await?;
let image_pcrs_data = image_pcrs_map
.data
.context("Image PCRs map existed, but had no data")?;
let image_pcrs_str = image_pcrs_data
.get(PCR_CONFIG_FILE)
.context("Image PCRs data existed, but had no file")?;
let mut image_pcrs: ImagePcrs = serde_json::from_str(image_pcrs_str)?;

let image_pcr = ImagePcr {
first_seen: Utc::now(),
pcrs,
};
image_pcrs.0.insert(args.image, image_pcr);

let image_pcrs_json = serde_json::to_string(&image_pcrs)?;
let data = BTreeMap::from([(PCR_CONFIG_FILE.to_string(), image_pcrs_json.to_string())]);
image_pcrs_map.data = Some(data);
config_maps
.replace(PCR_CONFIG_MAP, &PostParams::default(), &image_pcrs_map)
.await?;
Ok(())
}
4 changes: 2 additions & 2 deletions crds/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition.workspace = true

[dependencies]
k8s-openapi.workspace = true
kube = { workspace = true, features = ["derive"] }
kube.workspace = true
schemars = { version = "0.8", features = ["derive"] }
serde = { workspace = true, features = ["derive"] }
serde.workspace = true
serde_json.workspace = true
1 change: 1 addition & 0 deletions crds/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize};
namespaced,
plural = "confidentialclusters"
)]
#[serde(rename_all = "camelCase")]
pub struct ConfidentialClusterSpec {
pub trustee: Trustee,
pub pcrs_compute_image: String,
Expand Down
4 changes: 2 additions & 2 deletions manifest-gen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ edition.workspace = true

[dependencies]
anyhow.workspace = true
env_logger.workspace = true
clap = { workspace = true, features = ["derive"] }
crds = { path = "../crds" }
env_logger.workspace = true
k8s-openapi.workspace = true
kube = { workspace = true, features = ["derive"] }
kube.workspace = true
log.workspace = true
serde_yaml = "0.9"
17 changes: 15 additions & 2 deletions manifest-gen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,19 @@ fn generate_operator(args: &Args) -> Result<()> {
],
..Default::default()
},
PolicyRule {
api_groups: Some(vec!["".to_string()]),
resources: Some(vec![ConfigMap::plural(&()).to_string()]),
verbs: vec![
"create".to_string(),
"get".to_string(),
"list".to_string(),
"watch".to_string(),
"patch".to_string(),
"update".to_string(),
],
..Default::default()
},
PolicyRule {
api_groups: Some(vec![ConfidentialCluster::group(&()).to_string()]),
resources: Some(vec![ConfidentialCluster::plural(&()).to_string()]),
Expand Down Expand Up @@ -192,7 +205,7 @@ fn generate_operator(args: &Args) -> Result<()> {
let compute_pcrs_role = Role {
metadata: ObjectMeta {
name: Some(format!("{compute_pcrs_service_account_name}-role")),
namespace: Some(args.trustee_namespace.clone()),
namespace: Some(namespace.clone()),
..Default::default()
},
rules: Some(vec![PolicyRule {
Expand All @@ -213,7 +226,7 @@ fn generate_operator(args: &Args) -> Result<()> {
let compute_pcrs_role_binding = RoleBinding {
metadata: ObjectMeta {
name: Some(format!("{compute_pcrs_service_account_name}-rolebinding")),
namespace: Some(args.trustee_namespace.clone()),
namespace: Some(namespace.clone()),
..Default::default()
},
role_ref: RoleRef {
Expand Down
16 changes: 11 additions & 5 deletions operator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,22 @@ edition.workspace = true
[dependencies]
anyhow.workspace = true
base64 = "0.22.1"
chrono.workspace = true
compute-pcrs-lib.workspace = true
crds = { path = "../crds" }
env_logger = { workspace = true }
env_logger.workspace = true
futures-util = "0.3.31"
hex = "0.4.3"
json-patch = "4.0.0"
jsonptr = "0.7.1"
k8s-openapi.workspace = true
kube = { workspace = true, features = ["runtime"] }
kube.workspace = true
log.workspace = true
oci-client = "0.15.0"
oci-spec = "0.8.2"
openssl = "0.10.73"
thiserror = "2.0.12"
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
serde = { workspace = true, features = ["derive"] }
rv-store = { path = "../rv-store" }
serde.workspace = true
serde_json.workspace = true
thiserror = "2.0.12"
tokio.workspace = true
30 changes: 24 additions & 6 deletions operator/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use log::{error, info};
use thiserror::Error;

use crds::ConfidentialCluster;
mod reference_values;
mod trustee;

#[derive(Debug, Error)]
Expand All @@ -25,6 +26,8 @@ struct ContextData {
client: Client,
}

const BOOT_IMAGE: &str = "quay.io/fedora/fedora-coreos:42.20250705.3.0";

async fn list_confidential_clusters(
client: Client,
namespace: &str,
Expand Down Expand Up @@ -91,23 +94,38 @@ async fn install_trustee_configuration(client: Client, namespace: String) -> Res
Err(e) => error!("Failed to create HTTPS certificates for the KBS: {e}"),
}

trustee::launch_rv_job_controller(client.clone(), &namespace).await;

match trustee::generate_reference_values(
let rv_ctx = trustee::RvContextData {
client: client.clone(),
operator_namespace: namespace.clone(),
trustee_namespace: trustee_namespace.clone(),
pcrs_compute_image: cocl.spec.pcrs_compute_image,
rv_map: cocl.spec.trustee.reference_values.clone(),
};
reference_values::launch_rv_job_controller(rv_ctx.clone()).await;
match reference_values::create_pcrs_config_map(client.clone(), &namespace).await {
Ok(_) => info!("Created bare configmap for PCRs"),
Err(e) => error!("Failed to create the PCRs configmap: {e}"),
}
match trustee::create_reference_value_config_map(
client.clone(),
&namespace,
&trustee_namespace,
&cocl.spec.trustee.reference_values,
&cocl.spec.pcrs_compute_image,
)
.await
{
Ok(_) => info!(
"Generate configmap for the reference values: {}",
"Created bare configmap for the reference values: {}",
cocl.spec.trustee.reference_values
),
Err(e) => error!("Failed to create the reference values configmap: {e}"),
}
// TODO machine config input
match reference_values::handle_new_image(rv_ctx, BOOT_IMAGE).await {
Ok(_) => info!("Computed or retrieved reference values for image: {BOOT_IMAGE}",),
Err(e) => {
error!("Failed to compute or retrieve reference values for image {BOOT_IMAGE}: {e}",)
}
}

match trustee::generate_resource_policy(
client.clone(),
Expand Down
Loading
Loading