Skip to content

feat: Support putting TrustStore information in Secret #597

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ All notable changes to this project will be documented in this file.
- Use `--file-log-rotation-period` (or `FILE_LOG_ROTATION_PERIOD`) to configure the frequency of rotation.
- Use `--console-log-format` (or `CONSOLE_LOG_FORMAT`) to set the format to `plain` (default) or `json`.
- Added TrustStore CRD for requesting CA certificate information ([#557]).
- Support exporting the CA certificate information to Secrets or ConfigMaps ([#597]).

### Changed

Expand Down Expand Up @@ -46,6 +47,7 @@ All notable changes to this project will be documented in this file.
[#587]: https://github.com/stackabletech/secret-operator/pull/587
[#591]: https://github.com/stackabletech/secret-operator/pull/591
[#594]: https://github.com/stackabletech/secret-operator/pull/594
[#597]: https://github.com/stackabletech/secret-operator/pull/597

## [25.3.0] - 2025-03-21

Expand Down
12 changes: 12 additions & 0 deletions deploy/helm/secret-operator/crds/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,18 @@ spec:
- kerberos
nullable: true
type: string
outputResource:
default: ConfigMap
description: |-
Which Kubernetes resource should be used to output the requested information to.

The trust information (such as a `ca.crt`) can be considered public information, so we put it in a `ConfigMap` by default. However, some tools (such as OpenShift routes) require it to be placed in a `Secret`, so we also support that.

Can be either `ConfigMap` or `Secret`, defaults to `ConfigMap`.
enum:
- Secret
- ConfigMap
type: string
secretClassName:
description: The name of the SecretClass that the request concerns.
type: string
Expand Down
18 changes: 18 additions & 0 deletions rust/operator-binary/src/crd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,10 +518,28 @@ pub struct TrustStoreSpec {
/// The name of the SecretClass that the request concerns.
pub secret_class_name: String,

/// Which Kubernetes resource should be used to output the requested information to.
///
/// The trust information (such as a `ca.crt`) can be considered public information, so we put
/// it in a `ConfigMap` by default. However, some tools (such as OpenShift routes) require it
/// to be placed in a `Secret`, so we also support that.
///
/// Can be either `ConfigMap` or `Secret`, defaults to `ConfigMap`.
#[serde(default)]
pub output_resource: TrustStoreOutputType,

/// The [format](DOCS_BASE_URL_PLACEHOLDER/secret-operator/secretclass#format) that the data should be converted into.
pub format: Option<SecretFormat>,
}

#[derive(Clone, Debug, Default, PartialEq, JsonSchema, Serialize, Deserialize)]
pub enum TrustStoreOutputType {
Secret,

#[default]
ConfigMap,
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
76 changes: 56 additions & 20 deletions rust/operator-binary/src/truststore_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use strum::{EnumDiscriminants, IntoStaticStr};
use crate::{
OPERATOR_NAME,
backend::{self, SecretBackendError, TrustSelector},
crd::{SearchNamespaceMatchCondition, SecretClass, TrustStore},
crd::{SearchNamespaceMatchCondition, SecretClass, TrustStore, TrustStoreOutputType},
format::{
self,
well_known::{CompatibilityOptions, NamingOptions},
Expand Down Expand Up @@ -82,6 +82,11 @@ pub async fn start(client: &stackable_operator::client::Client, watch_namespace:
watch_namespace.get_api::<PartialObjectMeta<ConfigMap>>(client),
watcher::Config::default(),
)
// TODO: merge this into the other Secret watch
.owns(
watch_namespace.get_api::<PartialObjectMeta<Secret>>(client),
watcher::Config::default(),
)
.watches(
watch_namespace.get_api::<PartialObjectMeta<ConfigMap>>(client),
watcher::Config::default(),
Expand Down Expand Up @@ -205,7 +210,14 @@ pub enum Error {
source: stackable_operator::client::Error,
config_map: ObjectRef<ConfigMap>,
},

#[snafu(display("failed to apply target {secret} for the TrustStore"))]
ApplyTrustStoreSecret {
source: stackable_operator::client::Error,
secret: ObjectRef<Secret>,
},
}

type Result<T, E = Error> = std::result::Result<T, E>;
impl ReconcilerError for Error {
fn category(&self) -> &'static str {
Expand All @@ -222,6 +234,7 @@ impl ReconcilerError for Error {
Error::FormatData { secret_class, .. } => Some(secret_class.clone().erase()),
Error::BuildOwnerReference { .. } => None,
Error::ApplyTrustStoreConfigMap { config_map, .. } => Some(config_map.clone().erase()),
Error::ApplyTrustStoreSecret { secret, .. } => Some(secret.clone().erase()),
}
}
}
Expand Down Expand Up @@ -264,7 +277,7 @@ async fn reconcile(
.get_trust_data(&selector)
.await
.context(BackendGetTrustDataSnafu)?;
let (Flattened(string_data), Flattened(binary_data)) = trust_data
let trust_file_contents = trust_data
.data
.into_files(
truststore.spec.format,
Expand All @@ -273,30 +286,53 @@ async fn reconcile(
)
.context(FormatDataSnafu {
secret_class: secret_class_ref,
})?
})?;
let (Flattened(string_data), Flattened(binary_data)) = trust_file_contents
.into_iter()
// Try to put valid UTF-8 data into `data`, but fall back to `binary_data` otherwise
// Try to put valid UTF-8 data into `string_data`, but fall back to `binary_data` otherwise
.map(|(k, v)| match String::from_utf8(v) {
Ok(v) => (Some((k, v)), None),
Err(v) => (None, Some((k, ByteString(v.into_bytes())))),
})
.collect();
let trust_cm = ConfigMap {
metadata: ObjectMetaBuilder::new()
.name_and_namespace(truststore)
.ownerreference_from_resource(truststore, None, Some(true))
.context(BuildOwnerReferenceSnafu)?
.build(),
data: Some(string_data),
binary_data: Some(binary_data),
..Default::default()
};
ctx.client
.apply_patch(CONTROLLER_NAME, &trust_cm, &trust_cm)
.await
.context(ApplyTrustStoreConfigMapSnafu {
config_map: &trust_cm,
})?;

let trust_metadata = ObjectMetaBuilder::new()
.name_and_namespace(truststore)
.ownerreference_from_resource(truststore, None, Some(true))
.context(BuildOwnerReferenceSnafu)?
.build();

match truststore.spec.output_resource {
TrustStoreOutputType::ConfigMap => {
let trust_cm = ConfigMap {
metadata: trust_metadata,
data: Some(string_data),
binary_data: Some(binary_data),
..Default::default()
};
ctx.client
.apply_patch(CONTROLLER_NAME, &trust_cm, &trust_cm)
.await
.context(ApplyTrustStoreConfigMapSnafu {
config_map: &trust_cm,
})?;
}
TrustStoreOutputType::Secret => {
let trust_secret = Secret {
metadata: trust_metadata,
string_data: Some(string_data),
data: Some(binary_data),
..Default::default()
};
ctx.client
.apply_patch(CONTROLLER_NAME, &trust_secret, &trust_secret)
.await
.context(ApplyTrustStoreSecretSnafu {
secret: &trust_secret,
})?;
}
}

Ok(controller::Action::await_change())
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
apiVersion: kuttl.dev/v1beta1
kind: TestStep
commands:
- script: envsubst '$NAMESPACE' < 02_secretclass.yaml | kubectl apply -f -
5 changes: 0 additions & 5 deletions tests/templates/kuttl/cert-manager-tls/02-secretclass.yaml

This file was deleted.

5 changes: 0 additions & 5 deletions tests/templates/kuttl/cert-manager-tls/10-consumer.yaml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
apiVersion: kuttl.dev/v1beta1
kind: TestStep
commands:
- script: envsubst '$NAMESPACE' < 10_consumer.yaml | kubectl apply -n $NAMESPACE -f -
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
commands:
- script: envsubst '$NAMESPACE' < secretclass.yaml | kubectl apply -f -
- script: envsubst '$NAMESPACE' < 01_secretclass.yaml | kubectl apply -f -
---
apiVersion: v1
kind: Secret
Expand Down
2 changes: 1 addition & 1 deletion tests/templates/kuttl/kerberos-ad/02-kinit-client.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
commands:
- script: envsubst '$NAMESPACE' < kinit-client.yaml | kubectl apply -n $NAMESPACE -f -
- script: envsubst '$NAMESPACE' < 02_kinit-client.yaml | kubectl apply -n $NAMESPACE -f -
---
apiVersion: v1
kind: Service
Expand Down
4 changes: 2 additions & 2 deletions tests/templates/kuttl/kerberos/01-install-kdc.yaml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
commands:
- script: envsubst '$NAMESPACE' < secretclass.yaml | kubectl apply -f -
- script: envsubst '$NAMESPACE' < listenerclass.yaml | kubectl apply -f -
- script: envsubst '$NAMESPACE' < 01_secretclass.yaml | kubectl apply -f -
- script: envsubst '$NAMESPACE' < 01_listenerclass.yaml | kubectl apply -f -
---
apiVersion: apps/v1
kind: StatefulSet
Expand Down
2 changes: 1 addition & 1 deletion tests/templates/kuttl/kerberos/02-kinit-client.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
commands:
- script: envsubst '$NAMESPACE' < kinit-client.yaml | kubectl apply -n $NAMESPACE -f -
- script: envsubst '$NAMESPACE' < 02_kinit-client.yaml | kubectl apply -n $NAMESPACE -f -
---
apiVersion: v1
kind: Service
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
apiVersion: kuttl.dev/v1beta1
kind: TestStep
commands:
- script: envsubst '$NAMESPACE' < 01_secretclass.yaml | kubectl --namespace=$NAMESPACE apply -f -
5 changes: 0 additions & 5 deletions tests/templates/kuttl/tls-truststore/01-secretclass.yaml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ kind: TestAssert
timeout: 5
---
apiVersion: v1
kind: ConfigMap
kind: {{ test_scenario['values']['truststore-output-resource'] }}
metadata:
name: truststore-pem
# data is validated in 03-assert.yaml
---
apiVersion: v1
kind: ConfigMap
kind: {{ test_scenario['values']['truststore-output-resource'] }}
metadata:
name: truststore-pkcs12
# data is validated in 03-assert.yaml
---
{% if test_scenario['values']['truststore-output-resource'] == 'ConfigMap' %}
apiVersion: v1
kind: ConfigMap
metadata:
Expand All @@ -26,3 +27,13 @@ data:
binaryData:
# Should stay binary since it is not legal UTF-8
actuallyBinary: aWxsZWdhbIB1dGYtOA==
{% else %}
apiVersion: v1
kind: Secret
metadata:
name: truststore-k8ssearch
data:
foo: YmFy
baz: aGVsbG8=
actuallyBinary: aWxsZWdhbIB1dGYtOA==
{% endif %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
apiVersion: kuttl.dev/v1beta1
kind: TestStep
commands:
- script: envsubst '$NAMESPACE' < 02_truststore.yaml | kubectl --namespace=$NAMESPACE apply -f -
5 changes: 0 additions & 5 deletions tests/templates/kuttl/tls-truststore/02-truststore.yaml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ metadata:
spec:
secretClassName: tls-$NAMESPACE
format: tls-pem
outputResource: {{ test_scenario['values']['truststore-output-resource'] }}
---
apiVersion: secrets.stackable.tech/v1alpha1
kind: TrustStore
Expand All @@ -15,10 +16,12 @@ metadata:
spec:
secretClassName: tls-$NAMESPACE
format: tls-pkcs12
outputResource: {{ test_scenario['values']['truststore-output-resource'] }}
---
apiVersion: secrets.stackable.tech/v1alpha1
kind: TrustStore
metadata:
name: truststore-k8ssearch
spec:
secretClassName: k8ssearch-$NAMESPACE
outputResource: {{ test_scenario['values']['truststore-output-resource'] }}
8 changes: 0 additions & 8 deletions tests/templates/kuttl/tls-truststore/03-assert.yaml

This file was deleted.

13 changes: 13 additions & 0 deletions tests/templates/kuttl/tls-truststore/03-assert.yaml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Validate certificates generated by step 02
---
apiVersion: kuttl.dev/v1beta1
kind: TestAssert
timeout: 10
commands:
{% if test_scenario['values']['truststore-output-resource'] == 'ConfigMap' %}
- script: kubectl --namespace=$NAMESPACE get configmap/truststore-pem --output=jsonpath='{.data.ca\.crt}' | openssl x509 -noout
- script: kubectl --namespace=$NAMESPACE get configmap/truststore-pkcs12 --output=jsonpath='{.binaryData.truststore\.p12}' | base64 --decode | openssl pkcs12 -noout -passin 'pass:' -legacy
{% else %}
- script: kubectl --namespace=$NAMESPACE get secret/truststore-pem --output=jsonpath='{.data.ca\.crt}' | base64 --decode | openssl x509 -noout
- script: kubectl --namespace=$NAMESPACE get secret/truststore-pkcs12 --output=jsonpath='{.data.truststore\.p12}' | base64 --decode | openssl pkcs12 -noout -passin 'pass:' -legacy
{% endif %}
5 changes: 5 additions & 0 deletions tests/templates/kuttl/tls/01-create-secretclass.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
apiVersion: kuttl.dev/v1beta1
kind: TestStep
commands:
- script: envsubst '$NAMESPACE' < 01_secretclass.yaml | kubectl apply -f -
5 changes: 0 additions & 5 deletions tests/templates/kuttl/tls/01-secretclass.yaml

This file was deleted.

5 changes: 0 additions & 5 deletions tests/templates/kuttl/tls/10-consumer.yaml

This file was deleted.

5 changes: 5 additions & 0 deletions tests/templates/kuttl/tls/10-create-consumer.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
apiVersion: kuttl.dev/v1beta1
kind: TestStep
commands:
- script: envsubst '$NAMESPACE' < 10_consumer.yaml | kubectl apply -n $NAMESPACE -f -
5 changes: 5 additions & 0 deletions tests/test-definition.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ dimensions:
values:
- false
- true
- name: truststore-output-resource
values:
- ConfigMap
- Secret
tests:
- name: kerberos
dimensions:
Expand All @@ -40,6 +44,7 @@ tests:
- name: tls-truststore
dimensions:
- openshift
- truststore-output-resource
- name: cert-manager-tls
dimensions:
- openshift
Expand Down
Loading