From 0be8e157210f512537bc7652da9f18f31104d214 Mon Sep 17 00:00:00 2001 From: Jack Kleeman Date: Mon, 27 Oct 2025 21:14:25 +0000 Subject: [PATCH] Support secret provider in restatecloudenvironment --- README.md | 10 ++- crd/RestateCloudEnvironment.pkl | 22 ++++++- crd/restatecloudenvironments.yaml | 16 ++++- .../reconcilers/tunnel.rs | 61 +++++++++++++------ src/resources/restatecloudenvironments.rs | 19 +++++- 5 files changed, 105 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 83abd63..2c7e41f 100644 --- a/README.md +++ b/README.md @@ -426,7 +426,8 @@ spec: | Field | Type | Description | |---|---|---| -| `secret` | `object` | **Required**. A reference to a secret in the same namespace as the operator. See details below. | +| `secret` | `object` | **Required**. A reference to a secret that will be used for registration, as well as being mounted to the tunnel pods unless a `secretProvider` is also specified. | +| `secretProvider` | `object` | A reference to a `SecretProviderClass` that should be mounted to the tunnel pods to authenticate the tunnel. A Kubernetes Secret (synced by the Secret Store CSI Driver) is still necessary for the operator to register services. See details below. | **`secret` Fields** @@ -435,6 +436,13 @@ spec: | `name` | `string` | **Required**. The name of the referenced secret. It must be in the same namespace as the operator. | | `key` | `string` | **Required**. The key to read from the referenced Secret. | +**`secretProvider` Fields** + +| Field | Type | Description | +|---|---|---| +| `secretProviderClass` | `string` | **Required**. The name of the referenced `SecretProviderClass`. It must be in the same namespace as the operator. | +| `path` | `string` | **Required**. The path that the token will be available at inside the secret volume. | + --- #### `spec.tunnel` diff --git a/crd/RestateCloudEnvironment.pkl b/crd/RestateCloudEnvironment.pkl index 0da1209..6f4fcbe 100644 --- a/crd/RestateCloudEnvironment.pkl +++ b/crd/RestateCloudEnvironment.pkl @@ -46,11 +46,18 @@ class Spec { /// Where to get credentials for communication with the Cloud environment class Authentication { - /// Configured a reference to a secret in the same namespace as the operator + /// A reference to a secret that will be used for registration, as well as being mounted to the tunnel + /// pods unless a secretProvider is also specified. secret: Secret + + /// A reference to a SecretProviderClass that should be mounted to the tunnel pods to authenticate the + /// tunnel. A Kubernetes Secret (synced by the Secret Store CSI Driver) is still necessary for the + /// operator to register services. + secretProvider: SecretProvider? } -/// Configured a reference to a secret in the same namespace as the operator +/// A reference to a secret that will be used for registration, as well as being mounted to the tunnel +/// pods unless a secretProvider is also specified. class Secret { /// The key to read from the referenced Secret key: String @@ -59,6 +66,17 @@ class Secret { name: String } +/// A reference to a SecretProviderClass that should be mounted to the tunnel pods to authenticate the +/// tunnel. A Kubernetes Secret (synced by the Secret Store CSI Driver) is still necessary for the +/// operator to register services. +class SecretProvider { + /// The path that the token will be available inside the secret volume + path: String + + /// The name of the referenced SecretProviderClass. It must be in the same namespace as the operator. + secretProviderClass: String +} + /// Optional configuration for the deployment of tunnel pods class Tunnel { /// If specified, pod affinity. Defaults to zone anti-affinity, provide {} to disable all affinity diff --git a/crd/restatecloudenvironments.yaml b/crd/restatecloudenvironments.yaml index 6c87623..be4d8bf 100644 --- a/crd/restatecloudenvironments.yaml +++ b/crd/restatecloudenvironments.yaml @@ -33,7 +33,7 @@ spec: description: Where to get credentials for communication with the Cloud environment properties: secret: - description: Configured a reference to a secret in the same namespace as the operator + description: A reference to a secret that will be used for registration, as well as being mounted to the tunnel pods unless a secretProvider is also specified. properties: key: description: The key to read from the referenced Secret @@ -45,6 +45,20 @@ spec: - key - name type: object + secretProvider: + description: A reference to a SecretProviderClass that should be mounted to the tunnel pods to authenticate the tunnel. A Kubernetes Secret (synced by the Secret Store CSI Driver) is still necessary for the operator to register services. + nullable: true + properties: + path: + description: The path that the token will be available inside the secret volume + type: string + secretProviderClass: + description: The name of the referenced SecretProviderClass. It must be in the same namespace as the operator. + type: string + required: + - path + - secretProviderClass + type: object required: - secret type: object diff --git a/src/controllers/restatecloudenvironment/reconcilers/tunnel.rs b/src/controllers/restatecloudenvironment/reconcilers/tunnel.rs index 97fae70..b7f4d72 100644 --- a/src/controllers/restatecloudenvironment/reconcilers/tunnel.rs +++ b/src/controllers/restatecloudenvironment/reconcilers/tunnel.rs @@ -4,10 +4,10 @@ use k8s_openapi::{ api::{ apps::v1::{Deployment, DeploymentSpec}, core::v1::{ - Container, ContainerPort, EnvVar, HTTPGetAction, KeyToPath, PodSecurityContext, - PodSpec, PodTemplateSpec, Probe, ResourceRequirements, SeccompProfile, - SecretVolumeSource, SecurityContext, Service, ServicePort, ServiceSpec, Volume, - VolumeMount, + CSIVolumeSource, Container, ContainerPort, EnvVar, HTTPGetAction, KeyToPath, + PodSecurityContext, PodSpec, PodTemplateSpec, Probe, ResourceRequirements, + SeccompProfile, SecretVolumeSource, SecurityContext, Service, ServicePort, ServiceSpec, + Volume, VolumeMount, }, }, apimachinery::pkg::{api::resource::Quantity, util::intstr::IntOrString}, @@ -127,6 +127,43 @@ fn tunnel_deployment( }] }; + let (bearer_token_path, secret_volume) = + if let Some(secret_provider) = &spec.authentication.secret_provider { + ( + secret_provider.path.clone(), + Volume { + name: "bearer-token".into(), + csi: Some(CSIVolumeSource { + driver: "secrets-store.csi.k8s.io".into(), + read_only: Some(true), + volume_attributes: Some(BTreeMap::from([( + "secretProviderClass".into(), + secret_provider.secret_provider_class.clone(), + )])), + ..Default::default() + }), + ..Default::default() + }, + ) + } else { + ( + "bearer-token".into(), + Volume { + name: "bearer-token".into(), + secret: Some(SecretVolumeSource { + secret_name: Some(spec.authentication.secret.name.clone()), + items: Some(vec![KeyToPath { + key: spec.authentication.secret.key.clone(), + mode: None, + path: "bearer-token".into(), + }]), + ..Default::default() + }), + ..Default::default() + }, + ) + }; + Deployment { metadata, spec: Some(DeploymentSpec { @@ -175,7 +212,7 @@ fn tunnel_deployment( volume_mounts: Some(vec![VolumeMount { mount_path: BEARER_TOKEN_MOUNT_PATH.into(), name: "bearer-token".into(), - sub_path: Some("bearer-token".into()), + sub_path: Some(bearer_token_path), read_only: Some(true), ..Default::default() }]), @@ -195,19 +232,7 @@ fn tunnel_deployment( termination_grace_period_seconds: Some(310), tolerations: tunnel.and_then(|t| t.tolerations.clone()), node_selector: tunnel.and_then(|t| t.node_selector.clone()), - volumes: Some(vec![Volume { - name: "bearer-token".into(), - secret: Some(SecretVolumeSource { - secret_name: Some(spec.authentication.secret.name.clone()), - items: Some(vec![KeyToPath { - key: spec.authentication.secret.key.clone(), - mode: None, - path: "bearer-token".into(), - }]), - ..Default::default() - }), - ..Default::default() - }]), + volumes: Some(vec![secret_volume]), ..Default::default() }), }, diff --git a/src/resources/restatecloudenvironments.rs b/src/resources/restatecloudenvironments.rs index 2fd8fa2..e408eca 100644 --- a/src/resources/restatecloudenvironments.rs +++ b/src/resources/restatecloudenvironments.rs @@ -175,10 +175,18 @@ impl RestateCloudEnvironment { } } -/// Configuration for authentication to the Cloud environment. Currently, only secret references are supported and one must be provided. +/// Configuration for authentication to the Cloud environment. A secret reference is currently required, but +/// a CSI Secret Store provider can be used to sync this secret. #[derive(Deserialize, Serialize, Clone, Debug, JsonSchema)] +#[serde(rename_all = "camelCase")] pub struct RestateCloudEnvironmentAuthentication { + /// A reference to a secret that will be used for registration, as well as being mounted to the tunnel pods + /// unless a secretProvider is also specified. pub secret: SecretReference, + /// A reference to a SecretProviderClass that should be mounted to the tunnel pods to authenticate the tunnel. + /// A Kubernetes Secret (synced by the Secret Store CSI Driver) is still necessary + /// for the operator to register services. + pub secret_provider: Option, } /// Configured a reference to a secret in the same namespace as the operator @@ -190,6 +198,15 @@ pub struct SecretReference { pub key: String, } +#[derive(Deserialize, Serialize, Clone, Debug, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct SecretProviderReference { + /// The name of the referenced SecretProviderClass. It must be in the same namespace as the operator. + pub secret_provider_class: String, + /// The path that the token will be available inside the secret volume + pub path: String, +} + #[derive(Deserialize, Serialize, Clone, Debug, CELSchema)] #[serde(rename_all = "camelCase")] pub struct TunnelSpec {