Skip to content
Draft
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file.

- Support configuring the name of the key in the ConfigMap/Secret, in which the PEM encoded CA certificate of the Truststore should be placed.
This is e.g. needed to be able to use the generated Secret within an OpenShift Ingress ([#679]).
- Support adding domain components to the subject DN of TLS certificates with the volume annotation
`secrets.stackable.tech/backend.autotls.cert.domain-components-in-subject-dn` ([#708]).

### Changed

Expand All @@ -19,6 +21,7 @@ All notable changes to this project will be documented in this file.

[#693]: https://github.com/stackabletech/secret-operator/pull/693
[#706]: https://github.com/stackabletech/secret-operator/pull/706
[#708]: https://github.com/stackabletech/secret-operator/pull/708

## [26.3.0] - 2026-03-16

Expand Down
21 changes: 21 additions & 0 deletions rust/operator-binary/src/backend/auto_tls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,21 @@ impl SecretBackend for TlsGenerate {
}
}

let domain_components = if selector.autotls_cert_domain_components_in_subject_dn {
[
Some(pod_info.pod_name.as_str()),
pod_info.service_name.as_deref(),
Some(&pod_info.namespace),
Some("svc"),
]
.into_iter()
.flatten()
.chain(pod_info.kubernetes_cluster_domain.split('.'))
.collect()
} else {
vec![]
};

let pod_cert = X509Builder::new()
.and_then(|mut x509| {
let subject_name = X509NameBuilder::new()
Expand All @@ -363,6 +378,12 @@ impl SecretBackend for TlsGenerate {
Nid::COMMONNAME,
"generated certificate for pod",
)?;
for domain_component in domain_components {
name.append_entry_by_nid(
Nid::DOMAINCOMPONENT,
domain_component,
)?;
}
Ok(name)
})?
.build();
Expand Down
17 changes: 17 additions & 0 deletions rust/operator-binary/src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ pub struct SecretVolumeSelector {
)]
pub autotls_cert_jitter_factor: f64,

#[serde(
rename = "secrets.stackable.tech/backend.autotls.cert.domain-components-in-subject-dn",
deserialize_with = "SecretVolumeSelector::deserialize_str_as_bool",
default
)]
pub autotls_cert_domain_components_in_subject_dn: bool,

/// The TLS cert lifetime (when using the [`cert_manager`] backend).
///
/// The format is documented in <https://docs.stackable.tech/home/nightly/concepts/duration>.
Expand Down Expand Up @@ -301,6 +308,16 @@ impl SecretVolumeSelector {
)
})
}

fn deserialize_str_as_bool<'de, D: Deserializer<'de>>(de: D) -> Result<bool, D::Error> {
let str = String::deserialize(de)?;
str.parse().map_err(|_| {
<D::Error as serde::de::Error>::invalid_value(
Unexpected::Str(&str),
&"a string containing a boolean",
)
})
}
}

#[derive(Debug)]
Expand Down
6 changes: 6 additions & 0 deletions rust/operator-binary/src/backend/pod_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ pub enum FromPodError {
#[derive(Debug)]
pub struct PodInfo {
pub pod_ips: Vec<IpAddr>,
pub pod_name: String,
pub service_name: Option<String>,
pub namespace: String,
pub node_name: String,
pub node_ips: Vec<IpAddr>,
pub listener_addresses: Option<ListenerAddresses>,
Expand All @@ -119,6 +121,8 @@ impl PodInfo {
scopes: &[SecretScope],
) -> Result<Self, FromPodError> {
use from_pod_error::*;
let pod_name = pod.metadata.name.clone().context(NoPodNameSnafu)?;
let namespace = pod.metadata.namespace.clone().context(NoNamespaceSnafu)?;
let node_name = pod
.spec
.as_ref()
Expand Down Expand Up @@ -150,7 +154,9 @@ impl PodInfo {
.context(from_pod_error::IllegalAddressSnafu { address: ip })
})
.collect::<Result<_, _>>()?,
pod_name,
service_name: pod.spec.as_ref().and_then(|spec| spec.subdomain.clone()),
namespace,
node_name,
node_ips: node
.status
Expand Down
24 changes: 23 additions & 1 deletion tests/templates/kuttl/tls/10_consumer.yaml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ spec:
CERT_NAME=tls.crt
CA_NAME=ca.crt
{% endif %}
- |

set -euo pipefail
ls -la /stackable/tls-3d
ls -la /stackable/tls-42h
Expand Down Expand Up @@ -67,11 +67,18 @@ spec:
assert_trusted_roots_contain "cert 4b"
assert_trusted_roots_contain "cert 5"
assert_trusted_roots_contain "cert 6"

echo Test subject DN with domain components

openssl x509 -in /stackable/tls-domain-components/tls.crt -subject -noout | \
grep "subject=CN=generated certificate for pod, DC=tls-consumer-.*, DC=$NAMESPACE, DC=svc, DC=.*"
volumeMounts:
- mountPath: /stackable/tls-3d
name: tls-3d
- mountPath: /stackable/tls-42h
name: tls-42h
- mountPath: /stackable/tls-domain-components
name: tls-domain-components
volumes:
- name: tls-3d
ephemeral:
Expand Down Expand Up @@ -111,6 +118,21 @@ spec:
resources:
requests:
storage: "1"
- name: tls-domain-components
ephemeral:
volumeClaimTemplate:
metadata:
annotations:
secrets.stackable.tech/class: tls-$NAMESPACE
secrets.stackable.tech/scope: pod
secrets.stackable.tech/backend.autotls.cert.domain-components-in-subject-dn: "true"
spec:
storageClassName: secrets.stackable.tech
accessModes:
- ReadWriteOnce
resources:
requests:
storage: "1"
securityContext:
runAsUser: 1000
runAsGroup: 1000
Expand Down
Loading