Skip to content
Open
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
8 changes: 8 additions & 0 deletions api/v1alpha1/conditions.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,12 @@ const (
AttestationKeyApprovedCondition string = "Approved"
AttestationKeyRegistrationReason string = "Registration"
AttestationKeyMachineApprovedReason string = "MachineCreated"

// Conditions for the Machine
MachineKeyProvisionedCondition string = "KeyProvisioned"
MachineKeyProvisionedReason string = "SecretMounted"
MachineKeyNotProvisionedReason string = "ProvisioningFailed"
MachineAkApprovedCondition string = "AttestationKeyApproved"
MachineAkApprovedReason string = "KeyApproved"
MachineAkNotApprovedReason string = "NoKeyMatched"
)
8 changes: 8 additions & 0 deletions lib/src/conditions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,11 @@ pub const NOT_COMMITTED_REASON_PENDING: &str = "PodPending";
pub const ATTESTATION_KEY_APPROVED_CONDITION: &str = "Approved";
pub const ATTESTATION_KEY_REGISTRATION_REASON: &str = "Registration";
pub const ATTESTATION_KEY_MACHINE_APPROVE: &str = "MachineCreated";

pub const MACHINE_KEY_PROVISIONED_CONDITION: &str = "KeyProvisioned";
pub const MACHINE_KEY_PROVISIONED_REASON: &str = "SecretMounted";
pub const MACHINE_KEY_NOT_PROVISIONED_REASON: &str = "ProvisioningFailed";

pub const MACHINE_AK_APPROVED_CONDITION: &str = "AttestationKeyApproved";
pub const MACHINE_AK_APPROVED_REASON: &str = "KeyApproved";
pub const MACHINE_AK_NOT_APPROVED_REASON: &str = "NoKeyMatched";
6 changes: 6 additions & 0 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ impl Conditions for ApprovedImageStatus {
}
}

impl Conditions for MachineStatus {
fn conditions(&self) -> &Option<Vec<Condition>> {
&self.conditions
}
}

pub fn transition_time<S: Conditions>(
existing_status: &Option<S>,
type_: &str,
Expand Down
56 changes: 44 additions & 12 deletions operator/src/attestation_key_register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ use std::{collections::BTreeMap, sync::Arc, time::Duration};

use trusted_cluster_operator_lib::conditions::ATTESTATION_KEY_MACHINE_APPROVE;
use trusted_cluster_operator_lib::endpoints::*;
use trusted_cluster_operator_lib::{AttestationKey, AttestationKeyStatus, Machine, update_status};
use trusted_cluster_operator_lib::{AttestationKey, Machine};

use crate::conditions::attestation_key_approved_condition;
use crate::conditions::{attestation_key_approved_condition, machine_ak_approved_condition};
use crate::trustee;
use operator::{ControllerError, LONG_REQUEUE, TLS_DIR, controller_error_policy};
use operator::{create_or_info_if_exists, read_certificate, upsert_condition};
use operator::{create_or_info_if_exists, patch_status_condition, read_certificate};

/// Shared context for the three attestation-key controllers.
/// Stores give local cache access to avoid repeated API-server reads.
Expand Down Expand Up @@ -225,6 +225,19 @@ async fn machine_reconcile(
return Ok(LONG_REQUEUE);
}
}

let machine_name = machine.metadata.name.clone().unwrap_or_default();
let condition =
machine_ak_approved_condition(false, machine.metadata.generation, &machine.status);
patch_status_condition::<Machine, _>(
ctx.client.clone(),
&machine_name,
&machine.status,
condition,
"attestation-key-register",
)
.await?;

Ok(LONG_REQUEUE)
}

Expand All @@ -233,15 +246,20 @@ async fn approve_ak(ak: &AttestationKey, machine: &Machine, ctx: &AkContextData)
let client = &ctx.client;
let aks: Api<AttestationKey> = Api::default_namespaced(client.clone());

let generation = ak.metadata.generation;
let approve_reason = ATTESTATION_KEY_MACHINE_APPROVE;
let condition = attestation_key_approved_condition(approve_reason, generation, &ak.status);
let mut conditions = ak.status.as_ref().and_then(|s| s.conditions.clone());
let changed = upsert_condition(&mut conditions, condition);

if changed {
let status = AttestationKeyStatus { conditions };
update_status!(aks, &name, status)?;
let condition = attestation_key_approved_condition(
ATTESTATION_KEY_MACHINE_APPROVE,
ak.metadata.generation,
&ak.status,
);
if patch_status_condition::<AttestationKey, _>(
client.clone(),
&name,
&ak.status,
condition,
"attestation-key-register",
)
.await?
{
info!("Approved attestation key {name}");
}

Expand Down Expand Up @@ -300,6 +318,20 @@ async fn approve_ak(ak: &AttestationKey, machine: &Machine, ctx: &AkContextData)
info!("Created secret {secret_name} for attestation key {name} with finalizer");
}

let machine_condition =
machine_ak_approved_condition(true, machine.metadata.generation, &machine.status);
if patch_status_condition::<Machine, _>(
client.clone(),
&machine_name,
&machine.status,
machine_condition,
"attestation-key-register",
)
.await?
{
info!("Set AttestationKeyApproved condition on Machine {machine_name}");
}

Ok(())
}

Expand Down
58 changes: 57 additions & 1 deletion operator/src/conditions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
// SPDX-License-Identifier: MIT

use k8s_openapi::apimachinery::pkg::apis::meta::v1::Condition;
use trusted_cluster_operator_lib::{AttestationKeyStatus, TrustedExecutionClusterStatus};
use trusted_cluster_operator_lib::{
AttestationKeyStatus, MachineStatus, TrustedExecutionClusterStatus,
};
use trusted_cluster_operator_lib::{condition_status, conditions::*, transition_time};

pub fn known_trustee_address_condition(
Expand Down Expand Up @@ -55,6 +57,60 @@ pub fn installed_condition(
}
}

pub fn machine_key_provisioned_condition(
provisioned: bool,
generation: Option<i64>,
existing_status: &Option<MachineStatus>,
) -> Condition {
let (reason, message) = match provisioned {
true => (
MACHINE_KEY_PROVISIONED_REASON,
"LUKS key generated and mounted in Trustee",
),
false => (
MACHINE_KEY_NOT_PROVISIONED_REASON,
"Key provisioning failed, check operator logs for details",
),
};
let type_ = MACHINE_KEY_PROVISIONED_CONDITION;
let status = condition_status(provisioned);
Condition {
type_: type_.to_string(),
reason: reason.to_string(),
message: message.to_string(),
last_transition_time: transition_time(existing_status, type_, &status),
status,
observed_generation: generation,
}
}

pub fn machine_ak_approved_condition(
approved: bool,
generation: Option<i64>,
existing_status: &Option<MachineStatus>,
) -> Condition {
let (reason, message) = match approved {
true => (
MACHINE_AK_APPROVED_REASON,
"A matching attestation key was found and approved",
),
false => (
MACHINE_AK_NOT_APPROVED_REASON,
"No matching attestation key found",
),
};
let type_ = MACHINE_AK_APPROVED_CONDITION;
let status = condition_status(approved);
Condition {
type_: type_.to_string(),
reason: reason.to_string(),
message: message.to_string(),
last_transition_time: transition_time(existing_status, type_, &status),
status,
observed_generation: generation,
}
}

pub fn attestation_key_approved_condition(
reason: &str,
generation: Option<i64>,
Expand Down
35 changes: 35 additions & 0 deletions operator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ use std::{sync::Arc, time::Duration};
use tokio::time::timeout;

// Re-export common functions from the lib
use kube::api::{Patch, PatchParams};
use trusted_cluster_operator_lib::Conditions;
pub use trusted_cluster_operator_lib::generate_owner_reference;

#[derive(Debug, thiserror::Error)]
Expand Down Expand Up @@ -172,3 +174,36 @@ pub fn upsert_condition(
true
}
}

pub async fn patch_status_condition<K, S>(
client: Client,
name: &str,
existing_status: &Option<S>,
condition: Condition,
field_manager: &str,
) -> Result<bool>
where
K: Resource<Scope = k8s_openapi::NamespaceResourceScope>
+ Clone
+ serde::de::DeserializeOwned
+ Debug,
K::DynamicType: Default,
S: Conditions,
{
let mut conditions = existing_status
.as_ref()
.and_then(|s| s.conditions().clone());
if upsert_condition(&mut conditions, condition.clone()) {
let api: Api<K> = Api::default_namespaced(client);
let patch = Patch::Apply(serde_json::json!({
"apiVersion": K::api_version(&Default::default()),
"kind": K::kind(&Default::default()),
"status": { "conditions": [condition] }
}));
api.patch_status(name, &PatchParams::apply(field_manager), &patch)
.await
.map_err(Into::<anyhow::Error>::into)?;
return Ok(true);
}
Ok(false)
}
31 changes: 28 additions & 3 deletions operator/src/register_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use kube::{Api, Client, Resource};
use log::info;
use std::{collections::BTreeMap, sync::Arc};

use crate::conditions::machine_key_provisioned_condition;
use crate::trustee;
use operator::*;
use trusted_cluster_operator_lib::{Machine, TrustedExecutionCluster, endpoints::*};
Expand Down Expand Up @@ -135,11 +136,35 @@ async fn keygen_reconcile(
match ev {
Event::Apply(machine) => {
let kube_client = Arc::unwrap_or_clone(client);
let id = &machine.spec.id.clone();
let id = machine.spec.id.clone();
let machine_name = machine.metadata.name.clone().unwrap_or_default();
let generation = machine.metadata.generation;
let existing_status = machine.status.clone();
async {
let owner_reference = generate_owner_reference(&Arc::unwrap_or_clone(machine))?;
trustee::generate_secret(kube_client.clone(), id, owner_reference).await?;
trustee::send_secret(kube_client, id).await
let provisioning_result =
trustee::generate_secret(kube_client.clone(), &id, owner_reference).await;
let provisioning_result = match provisioning_result {
Ok(_) => trustee::send_secret(kube_client.clone(), &id).await,
Err(e) => Err(e),
};

let provisioned = provisioning_result.is_ok();
let condition = machine_key_provisioned_condition(
provisioned,
generation,
&existing_status,
);
patch_status_condition::<Machine, _>(
kube_client,
&machine_name,
&existing_status,
condition,
"register-server",
)
.await?;
provisioning_result?;
Ok::<(), anyhow::Error>(())
}
.await
.map(|_| LONG_REQUEUE)
Expand Down