Skip to content

Commit d575301

Browse files
committed
Bump trustee to v0.20.0 & Synchronize Trustee Resources via KBS API
All trustee resources, including the resource policy, attestation policy, RV, LUKS key, and AK, are now synchronized using the KBS API. The main motivation for this is replacing the patch mechanism that causes a restart of the trustee deployment. Also, this abstraction over the trustee backend simplifies the process of upgrading to future versions of trustee. Config: - Update kbs-config.toml to v0.20.0 format - Store reference values in dedicated ConfigMap (trustee-rv-data) instead of trustee-data API-driven resource management: - Add KBS API client functions: send_secret, delete_secret, register_ak, sync_resource_policy, sync_attestation_policy, sync_reference_values - Replace volume-mount approach for secrets with KBS API calls - Add trustee deployment reconciler that syncs policies, reference values, and secrets when deployment becomes available Dependencies: - Add kbs-client and jsonwebtoken crates - Bump trustee image tag to v0.20.0 Signed-off-by: Roy Kaufman <rkaufman@redhat.com>
1 parent d0b2f79 commit d575301

12 files changed

Lines changed: 1682 additions & 536 deletions

Cargo.lock

Lines changed: 1184 additions & 83 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ OPERATOR_IMAGE ?= $(REGISTRY)/trusted-cluster-operator:$(TAG)
2828
COMPUTE_PCRS_IMAGE=$(REGISTRY)/compute-pcrs:$(TAG)
2929
REG_SERVER_IMAGE=$(REGISTRY)/registration-server:$(TAG)
3030
ATTESTATION_KEY_REGISTER_IMAGE=$(REGISTRY)/attestation-key-register:$(TAG)
31-
TRUSTEE_IMAGE ?= quay.io/trusted-execution-clusters/key-broker-service:v0.17.0
31+
32+
TRUSTEE_IMAGE ?= quay.io/trusted-execution-clusters/key-broker-service:v0.20.0
3233
TEST_IMAGE ?= quay.io/trusted-execution-clusters/fedora-coreos-kubevirt:42.20260622
34+
3335
# tagged as 42.20251012.2.0
3436
APPROVED_IMAGE ?= quay.io/trusted-execution-clusters/fedora-coreos@sha256:6997f51fd27d1be1b5fc2e6cc3ebf16c17eb94d819b5d44ea8d6cf5f826ee773
3537

operator/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ serde_json.workspace = true
3131
thiserror = "2.0.18"
3232
tokio.workspace = true
3333
toml = "1.1.2"
34+
kbs-client = {git = "https://github.com/confidential-containers/trustee.git", rev = "e65897a9ad4eb3ac69fa2ec75ed831200eb2acd7", default-features = false, features = ["native-tls"] }
35+
jsonwebtoken = { version = "10.4.0", default-features = false, features = ["use_pem"] }
36+
jsonwebtoken-openssl = "1.0.0"
3437

3538
[dev-dependencies]
3639
http.workspace = true

operator/src/attestation_key_register.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ async fn secret_reconcile(
331331
match ev {
332332
Event::Apply(_secret) => {
333333
// On creation/update, just update the trustee deployment volumes
334-
trustee::update_attestation_keys(&ctx)
334+
trustee::update_attestation_keys(ctx.client.clone())
335335
.await
336336
.map(|_| Action::await_change())
337337
.map_err(|e| {
@@ -345,7 +345,7 @@ async fn secret_reconcile(
345345
"AttestationKey secret {secret_name} is being deleted, updating trustee deployment volumes"
346346
);
347347
// Update trustee deployment - secrets with deletion_timestamp will be filtered out
348-
trustee::update_attestation_keys(&ctx)
348+
trustee::update_attestation_keys(ctx.client.clone())
349349
.await
350350
.map(|_| Action::await_change())
351351
.map_err(|e| {

operator/src/kbs-config.toml

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,35 @@
22
sockets = ["0.0.0.0:8080"]
33

44
[admin]
5-
type = "DenyAll"
5+
authorization_mode = "AuthenticatedAuthorization"
6+
7+
[admin.authentication.bearer_jwt]
8+
identity_providers = [
9+
{ public_key_uri = "/opt/trustee/keys/public.pub" }
10+
]
11+
12+
[admin.authorization.regex_acl]
13+
acls = [{ role = "admin", allowed_endpoints = "^/kbs/.+$" }]
614

715
[attestation_token]
8-
insecure_key = true
9-
attestation_token_type = "CoCo"
16+
insecure_header_jwk = true
1017

1118
[attestation_service]
1219
type = "coco_as_builtin"
13-
work_dir = "/opt/trustee"
14-
policy_engine = "opa"
20+
timeout = 5
1521

1622
[attestation_service.attestation_token_broker]
17-
type = "Ear"
18-
policy_dir = "/opt/trustee/policies"
19-
20-
[attestation_service.attestation_token_config]
2123
duration_min = 5
2224

2325
[attestation_service.rvps_config]
2426
type = "BuiltIn"
2527

26-
[attestation_service.rvps_config.storage]
27-
type = "LocalJson"
28-
file_path = "/opt/trustee/reference-values.json"
29-
30-
3128
[[plugins]]
3229
name = "resource"
33-
type = "LocalFs"
34-
dir_path = "/opt/trustee/kbs-repository"
30+
storage_backend_type = "kvstorage"
31+
32+
[storage_backend]
33+
storage_type = "LocalJson"
3534

36-
[policy_engine]
37-
policy_path = "/opt/trustee/policy.rego"
35+
[storage_backend.backends.local_json]
36+
file_dir_path = "/opt/trustee/storage"

operator/src/main.rs

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,11 @@ mod register_server;
2828
#[cfg(test)]
2929
mod test_utils;
3030
mod trustee;
31-
3231
use crate::conditions::*;
3332
use operator::*;
3433

3534
/// Default tag for Trustee image
36-
const TRUSTEE_VERSION: &str = "v0.17.0";
35+
const TRUSTEE_VERSION: &str = "v0.20.0";
3736
/// Default version tag for operator-managed component images
3837
const COMPONENT_VERSION: &str = "v0.2.0";
3938
/// Default registry
@@ -167,17 +166,18 @@ async fn install_trustee_configuration(
167166
.context("Failed to create the KBS configuration configmap")?;
168167
info!("Generated configmap for the KBS configuration");
169168

170-
trustee::generate_attestation_policy(client.clone(), owner_reference.clone())
169+
reference_values::create_pcrs_config_map(client.clone())
171170
.await
172-
.context("Failed to create the attestation policy configmap")?;
173-
info!("Generated configmap for the attestation policy");
174-
175-
match trustee::generate_trustee_auth_keys_secret(client.clone(), owner_reference.clone()).await
176-
{
177-
Ok(_) => info!("Generate auth keys for the KBS API",),
178-
Err(e) => error!("Failed to create the auth keys: {e}"),
179-
}
180-
171+
.context("Failed to create the PCRs configmap")?;
172+
info!("Created bare configmap for PCRs");
173+
trustee::generate_trustee_auth_keys_secret(client.clone(), owner_reference.clone())
174+
.await
175+
.context("Failed to create the auth keys")?;
176+
info!("Generate auth keys for the KBS API");
177+
trustee::generate_rv_data(client.clone(), owner_reference.clone())
178+
.await
179+
.context("Failed to create the reference values configmap")?;
180+
info!("Created configmap for reference values");
181181
let kbs_port = cluster.spec.trustee_kbs_port;
182182
trustee::generate_kbs_service(client.clone(), owner_reference.clone(), kbs_port)
183183
.await
@@ -253,7 +253,7 @@ async fn install_attestation_key_register(
253253
#[tokio::main]
254254
async fn main() -> Result<()> {
255255
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
256-
256+
let _ = jsonwebtoken_openssl::install_default();
257257
let kube_client = Client::try_default().await?;
258258
info!("trusted execution clusters operator",);
259259

@@ -284,9 +284,9 @@ async fn main() -> Result<()> {
284284
attestation_key_register::launch_ak_controller(ak_ctx.clone()).await;
285285
attestation_key_register::launch_machine_ak_controller(ak_ctx.clone()).await;
286286
attestation_key_register::launch_secret_ak_controller(ak_ctx).await;
287-
reference_values::create_pcrs_config_map(kube_client.clone()).await?;
288287
reference_values::launch_rv_image_controller(kube_client.clone()).await;
289288
reference_values::launch_rv_job_controller(kube_client.clone()).await;
289+
trustee::launch_trustee_sync_controller(kube_client.clone()).await;
290290

291291
Controller::new(cl, watcher::Config::default())
292292
.run(reconcile, controller_error_policy, ctx)
@@ -300,7 +300,7 @@ async fn main() -> Result<()> {
300300
mod tests {
301301
use http::{Method, Request, StatusCode};
302302
use k8s_openapi::api::apps::v1::Deployment;
303-
use k8s_openapi::api::core::v1::{ConfigMap, Service};
303+
use k8s_openapi::api::core::v1::{ConfigMap, Secret, Service};
304304
use k8s_openapi::{apimachinery::pkg::apis::meta::v1::Time, jiff::Timestamp};
305305
use kube::api::ObjectList;
306306
use kube::client::Body;
@@ -442,31 +442,33 @@ mod tests {
442442
};
443443

444444
let clos = async |req: Request<Body>, ctr| {
445-
if ctr < 8 && req.method() == Method::POST {
445+
if ctr < 10 && req.method() == Method::POST {
446446
use serde_json::to_string;
447447
let resp = match ctr {
448-
// Trustee
449-
0 => to_string(&ConfigMap::default()),
450-
1 => to_string(&ConfigMap::default()),
451-
2 => to_string(&Service::default()),
452-
3 => to_string(&Deployment::default()),
453-
// Registration server
454-
4 => to_string(&Deployment::default()),
455-
5 => to_string(&Service::default()),
456-
// Attestation key register server
457-
6 => to_string(&Deployment::default()),
458-
7 => to_string(&Service::default()),
448+
// install_trustee_configuration
449+
0 => to_string(&ConfigMap::default()), // trustee-data
450+
1 => to_string(&ConfigMap::default()), // image-pcrs
451+
2 => to_string(&Secret::default()), // trustee-auth
452+
3 => to_string(&ConfigMap::default()), // trustee-rv-data
453+
4 => to_string(&Service::default()), // kbs-service
454+
5 => to_string(&Deployment::default()), // trustee-deployment
455+
// install_register_server
456+
6 => to_string(&Deployment::default()), // register-server
457+
7 => to_string(&Service::default()), // register-server-svc
458+
// install_attestation_key_register
459+
8 => to_string(&Deployment::default()), // ak-register
460+
9 => to_string(&Service::default()), // ak-register-svc
459461
_ => unreachable!("unexpected counter {ctr}"),
460462
};
461463
Ok(resp.unwrap())
462-
} else if ctr == 8 && req.method() == Method::GET {
464+
} else if ctr == 10 && req.method() == Method::GET {
463465
let object_list = ObjectList::<ApprovedImage> {
464466
items: Vec::new(),
465467
types: Default::default(),
466468
metadata: Default::default(),
467469
};
468470
Ok(serde_json::to_string(&object_list).unwrap())
469-
} else if ctr == 9 && req.method() == Method::PATCH {
471+
} else if ctr == 11 && req.method() == Method::PATCH {
470472
let body = req.into_body().collect_bytes().await.unwrap().to_vec();
471473
let body = String::from_utf8_lossy(&body);
472474
assert!(body.contains("ForeignCondition"),);
@@ -497,7 +499,7 @@ mod tests {
497499
cluster.status = Some(TrustedExecutionClusterStatus {
498500
conditions: Some(vec![pre_existing_installed, foreign_condition]),
499501
});
500-
count_check!(10, clos, |client| {
502+
count_check!(12, clos, |client| {
501503
let result = reconcile(Arc::new(cluster), Arc::new(dummy_cluster_ctx(client))).await;
502504
assert_eq!(result.unwrap(), Action::await_change());
503505
});

operator/src/reference_values.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -495,12 +495,13 @@ mod tests {
495495
Ok(serde_json::to_string(&dummy_pcrs_map()).unwrap())
496496
}
497497
(2, &Method::GET) | (3, &Method::PUT) => {
498-
assert!(req.uri().path().contains(trustee::TRUSTEE_DATA_MAP));
498+
assert!(req.uri().path().contains(trustee::TRUSTEE_RV_MAP));
499499
Ok(serde_json::to_string(&dummy_trustee_map()).unwrap())
500500
}
501+
(4, &Method::GET) => Err(StatusCode::NOT_FOUND),
501502
_ => panic!("unexpected API interaction: {req:?}, counter {ctr}"),
502503
};
503-
count_check!(4, clos, |client| {
504+
count_check!(5, clos, |client| {
504505
let job = Arc::new(dummy_job());
505506
let result = job_reconcile(job, Arc::new(client)).await.unwrap();
506507
assert_eq!(result, Action::await_change());
@@ -612,12 +613,13 @@ mod tests {
612613
Ok(serde_json::to_string(&dummy_pcrs_map()).unwrap())
613614
}
614615
(3, &Method::GET) | (4, &Method::PUT) => {
615-
assert!(req.uri().path().contains(trustee::TRUSTEE_DATA_MAP));
616+
assert!(req.uri().path().contains(trustee::TRUSTEE_RV_MAP));
616617
Ok(serde_json::to_string(&dummy_trustee_map()).unwrap())
617618
}
619+
(5, &Method::GET) => Err(StatusCode::NOT_FOUND),
618620
_ => panic!("unexpected API interaction: {req:?}, counter {ctr}"),
619621
};
620-
count_check!(5, clos, |client| {
622+
count_check!(6, clos, |client| {
621623
assert!(image_remove_reconcile(client, image, cluster).await.is_ok());
622624
});
623625
}

operator/src/register_server.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ async fn keygen_reconcile(
139139
async {
140140
let owner_reference = generate_owner_reference(&Arc::unwrap_or_clone(machine))?;
141141
trustee::generate_secret(kube_client.clone(), id, owner_reference).await?;
142-
trustee::mount_secret(kube_client, id).await
142+
trustee::send_secret(kube_client, id).await
143143
}
144144
.await
145145
.map(|_| Action::await_change())
@@ -185,10 +185,12 @@ async fn keygen_reconcile(
185185
}
186186
}
187187

188-
trustee::unmount_secret(kube_client, id)
189-
.await
190-
.map(|_| Action::await_change())
191-
.map_err(|e| finalizer::Error::<ControllerError>::CleanupFailed(e.into()))
188+
trustee::delete_secret(kube_client, id).await.map_err(|e| {
189+
finalizer::Error::<ControllerError>::CleanupFailed(
190+
anyhow!("failed to delete secret for machine {id}: {e}").into(),
191+
)
192+
})?;
193+
Ok(Action::await_change())
192194
}
193195
}
194196
})

operator/src/test_utils.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
// SPDX-License-Identifier: MIT
44

55
use compute_pcrs_lib::Pcr;
6-
use k8s_openapi::{api::core::v1::ConfigMap, jiff::Timestamp};
6+
use k8s_openapi::{
7+
api::core::v1::{ConfigMap, Secret},
8+
jiff::Timestamp,
9+
};
710
use std::collections::BTreeMap;
811

912
use crate::trustee;
@@ -41,6 +44,26 @@ pub fn dummy_trustee_map() -> ConfigMap {
4144
}
4245
}
4346

47+
pub fn dummy_trustee_auth() -> Secret {
48+
let key_pair =
49+
trustee::generate_ed25519_key_pair().expect("Failed to generate ed25519 key pair");
50+
let data = BTreeMap::from([
51+
(
52+
trustee::TRUSTEE_AUTH_PRIV_KEY.to_string(),
53+
k8s_openapi::ByteString(key_pair.private_key_pem),
54+
),
55+
(
56+
trustee::TRUSTEE_AUTH_PUB_KEY.to_string(),
57+
k8s_openapi::ByteString(key_pair.public_key_pem),
58+
),
59+
]);
60+
61+
Secret {
62+
data: Some(data),
63+
..Default::default()
64+
}
65+
}
66+
4467
pub fn dummy_pcrs_map() -> ConfigMap {
4568
let data = BTreeMap::from([(
4669
PCR_CONFIG_FILE.to_string(),

operator/src/tpm.rego

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ executables := 3 if {
99
input.tpm.pcr04 in query_reference_value("tpm_pcr4")
1010
input.tpm.pcr14 in query_reference_value("tpm_pcr14")
1111

12+
input.tpm.ak_public in query_reference_value("trusted_aks")
1213
}
1314
# Azure SNP vTPM validation
1415
executables := 3 if {

0 commit comments

Comments
 (0)