From 42ead5befc501a9cfe3514e50a1487c1943478d3 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Wed, 21 May 2025 13:53:33 +0200 Subject: [PATCH 01/19] feat!(stackable-certs): Support adding SAN entries --- crates/stackable-certs/src/ca/consts.rs | 6 +- crates/stackable-certs/src/ca/mod.rs | 118 ++++++++++++++++++------ crates/stackable-webhook/src/tls.rs | 9 +- 3 files changed, 102 insertions(+), 31 deletions(-) diff --git a/crates/stackable-certs/src/ca/consts.rs b/crates/stackable-certs/src/ca/consts.rs index 600bb5f7c..125a63a05 100644 --- a/crates/stackable-certs/src/ca/consts.rs +++ b/crates/stackable-certs/src/ca/consts.rs @@ -1,5 +1,7 @@ +use stackable_operator::time::Duration; + /// The default CA validity time span of one hour (3600 seconds). -pub const DEFAULT_CA_VALIDITY_SECONDS: u64 = 3600; +pub const DEFAULT_CA_VALIDITY: Duration = Duration::from_hours_unchecked(1); /// The root CA subject name containing only the common name. -pub const ROOT_CA_SUBJECT: &str = "CN=Stackable Data Platform Internal CA"; +pub const SDP_ROOT_CA_SUBJECT: &str = "CN=Stackable Data Platform Internal CA"; diff --git a/crates/stackable-certs/src/ca/mod.rs b/crates/stackable-certs/src/ca/mod.rs index 7c793d4f8..6334272a0 100644 --- a/crates/stackable-certs/src/ca/mod.rs +++ b/crates/stackable-certs/src/ca/mod.rs @@ -1,6 +1,6 @@ //! Contains types and functions to generate and sign certificate authorities //! (CAs). -use std::str::FromStr; +use std::{fmt::Debug, str::FromStr}; use const_oid::db::rfc5280::{ID_KP_CLIENT_AUTH, ID_KP_SERVER_AUTH}; use k8s_openapi::api::core::v1::Secret; @@ -9,9 +9,10 @@ use snafu::{OptionExt, ResultExt, Snafu}; use stackable_operator::{client::Client, commons::secret::SecretReference, time::Duration}; use tracing::{debug, instrument}; use x509_cert::{ + Certificate, builder::{Builder, CertificateBuilder, Profile}, - der::{DecodePem, pem::LineEnding, referenced::OwnedToRef}, - ext::pkix::{AuthorityKeyIdentifier, ExtendedKeyUsage}, + der::{DecodePem, asn1::Ia5String, pem::LineEnding, referenced::OwnedToRef}, + ext::pkix::{AuthorityKeyIdentifier, ExtendedKeyUsage, SubjectAltName, name::GeneralName}, name::Name, serial_number::SerialNumber, spki::{EncodePublicKey, SubjectPublicKeyInfoOwned}, @@ -66,14 +67,20 @@ pub enum Error { #[snafu(display("failed to parse AuthorityKeyIdentifier"))] ParseAuthorityKeyIdentifier { source: x509_cert::der::Error }, + + #[snafu(display("The subject alternative DNS name \"{dns_name}\" is not a Ia5String"))] + SaDnsNameNotAIa5String { + dns_name: String, + source: x509_cert::der::Error, + }, } /// Custom implementation of [`std::cmp::PartialEq`] because some inner types /// don't implement it. /// -/// Note that this implementation is restritced to testing because there is a +/// Note that this implementation is restricted to testing because there is a /// variant that is impossible to compare, and will cause a panic if it is -/// attemped. +/// attempted. #[cfg(test)] impl PartialEq for Error { fn eq(&self, other: &Self) -> bool { @@ -170,7 +177,7 @@ where /// These parameters include: /// /// - a randomly generated serial number - /// - a default validity of one hour (see [`DEFAULT_CA_VALIDITY_SECONDS`]) + /// - a default validity of one hour (see [`DEFAULT_CA_VALIDITY`]) /// /// The CA contains the public half of the provided `signing_key` and is /// signed by the private half of said key. @@ -181,9 +188,8 @@ where #[instrument(name = "create_certificate_authority", skip(signing_key_pair))] pub fn new(signing_key_pair: S) -> Result { let serial_number = rand::random::(); - let validity = Duration::from_secs(DEFAULT_CA_VALIDITY_SECONDS); - Self::new_with(signing_key_pair, serial_number, validity) + Self::new_with(signing_key_pair, serial_number, DEFAULT_CA_VALIDITY) } /// Creates a new CA certificate. @@ -200,8 +206,8 @@ where // We don't allow customization of the CA subject by callers. Every CA // created by us should contain the same subject consisting a common set // of distinguished names (DNs). - let subject = Name::from_str(ROOT_CA_SUBJECT).context(ParseSubjectSnafu { - subject: ROOT_CA_SUBJECT, + let subject = Name::from_str(SDP_ROOT_CA_SUBJECT).context(ParseSubjectSnafu { + subject: SDP_ROOT_CA_SUBJECT, })?; let spki_pem = signing_key_pair @@ -267,15 +273,16 @@ where /// authentication, because they include [`ID_KP_CLIENT_AUTH`] and /// [`ID_KP_SERVER_AUTH`] in the extended key usage extension. /// - /// It is also possible to directly greate RSA or ECDSA-based leaf + /// It is also possible to directly create RSA or ECDSA-based leaf /// certificates using [`CertificateAuthority::generate_rsa_leaf_certificate`] /// and [`CertificateAuthority::generate_ecdsa_leaf_certificate`]. #[instrument(skip(self, key_pair))] - pub fn generate_leaf_certificate( + pub fn generate_leaf_certificate<'a, T>( &mut self, key_pair: T, name: &str, scope: &str, + subject_alterative_dns_names: impl IntoIterator + Debug, validity: Duration, ) -> Result> where @@ -301,10 +308,6 @@ where let spki = SubjectPublicKeyInfoOwned::from_pem(spki_pem.as_bytes()) .context(DecodeSpkiFromPemSnafu)?; - // The leaf certificate can be used for WWW client and server - // authentication. This is a base requirement for TLS certs. - let eku = ExtendedKeyUsage(vec![ID_KP_CLIENT_AUTH, ID_KP_SERVER_AUTH]); - let signer = self.certificate_pair.key_pair.signing_key(); let mut builder = CertificateBuilder::new( Profile::Leaf { @@ -325,9 +328,27 @@ where ) .context(CreateCertificateBuilderSnafu)?; - // Again, add the extension created above. + // The leaf certificate can be used for WWW client and server + // authentication. This is a base requirement for TLS certs. builder - .add_extension(&eku) + .add_extension(&ExtendedKeyUsage(vec![ + ID_KP_CLIENT_AUTH, + ID_KP_SERVER_AUTH, + ])) + .context(AddCertificateExtensionSnafu)?; + + let sans = subject_alterative_dns_names + .into_iter() + .map(|dns_name| { + Ok(GeneralName::DnsName(Ia5String::new(dns_name).context( + SaDnsNameNotAIa5StringSnafu { + dns_name: dns_name.to_string(), + }, + )?)) + }) + .collect::, Error>>()?; + builder + .add_extension(&SubjectAltName(sans)) .context(AddCertificateExtensionSnafu)?; debug!("create and sign leaf certificate"); @@ -344,14 +365,15 @@ where /// See [`CertificateAuthority::generate_leaf_certificate`] for more /// information. #[instrument(skip(self))] - pub fn generate_rsa_leaf_certificate( + pub fn generate_rsa_leaf_certificate<'a>( &mut self, name: &str, scope: &str, + subject_alterative_dns_names: impl IntoIterator + Debug, validity: Duration, ) -> Result> { let key = rsa::SigningKey::new().context(GenerateRsaSigningKeySnafu)?; - self.generate_leaf_certificate(key, name, scope, validity) + self.generate_leaf_certificate(key, name, scope, subject_alterative_dns_names, validity) } /// Generates an ECDSAasync -based leaf certificate which is signed by this CA. @@ -359,14 +381,15 @@ where /// See [`CertificateAuthority::generate_leaf_certificate`] for more /// information. #[instrument(skip(self))] - pub fn generate_ecdsa_leaf_certificate( + pub fn generate_ecdsa_leaf_certificate<'a>( &mut self, name: &str, scope: &str, + subject_alterative_dns_names: impl IntoIterator + Debug, validity: Duration, ) -> Result> { let key = ecdsa::SigningKey::new().context(GenerateEcdsaSigningKeySnafu)?; - self.generate_leaf_certificate(key, name, scope, validity) + self.generate_leaf_certificate(key, name, scope, subject_alterative_dns_names, validity) } /// Create a [`CertificateAuthority`] from a Kubernetes [`Secret`]. @@ -443,6 +466,11 @@ where Self::from_secret(secret, key_certificate, key_private_key) } + + /// Returns the ca certificate. + pub fn ca_cert(&self) -> &Certificate { + &self.certificate_pair.certificate + } } impl CertificateAuthority { @@ -468,19 +496,57 @@ fn format_leaf_certificate_subject(name: &str, scope: &str) -> Result { #[cfg(test)] mod tests { + use const_oid::ObjectIdentifier; + use super::*; + const TEST_CERT_LIFETIME: Duration = Duration::from_hours_unchecked(1); + const TEST_SAN: &str = "airflow-0.airflow.default.svc.cluster.local"; + #[tokio::test] async fn rsa_key_generation() { let mut ca = CertificateAuthority::new_rsa().unwrap(); - ca.generate_rsa_leaf_certificate("Airflow", "pod", Duration::from_secs(3600)) - .unwrap(); + let cert = ca + .generate_rsa_leaf_certificate("Airflow", "pod", [TEST_SAN], TEST_CERT_LIFETIME) + .expect("RSA certificate generation failed"); + + assert_cert_attributes(cert.certificate()); } #[tokio::test] async fn ecdsa_key_generation() { let mut ca = CertificateAuthority::new_ecdsa().unwrap(); - ca.generate_ecdsa_leaf_certificate("Airflow", "pod", Duration::from_secs(3600)) - .unwrap(); + let cert = ca + .generate_ecdsa_leaf_certificate("Airflow", "pod", [TEST_SAN], TEST_CERT_LIFETIME) + .expect("ecdsa certificate generation failed"); + + assert_cert_attributes(cert.certificate()); + } + + fn assert_cert_attributes(cert: &Certificate) { + let cert = &cert.tbs_certificate; + // Test subject + assert_eq!( + cert.subject, + Name::from_str("CN=Airflow Certificate for pod").unwrap() + ); + + // Test SAN extension is present + let extensions = cert.extensions.as_ref().expect("cert had no extension"); + assert!( + extensions + .iter() + .any(|ext| ext.extn_id == ObjectIdentifier::new_unwrap("2.5.29.17")) + ); + + // Test lifetime + let not_before = cert.validity.not_before.to_system_time(); + let not_after = cert.validity.not_after.to_system_time(); + assert_eq!( + not_after + .duration_since(not_before) + .expect("Failed to calculate duration between notBefore and notAfter"), + *TEST_CERT_LIFETIME + ); } } diff --git a/crates/stackable-webhook/src/tls.rs b/crates/stackable-webhook/src/tls.rs index 92d9eb345..2aad52ee4 100644 --- a/crates/stackable-webhook/src/tls.rs +++ b/crates/stackable-webhook/src/tls.rs @@ -8,8 +8,11 @@ use hyper::{body::Incoming, service::service_fn}; use hyper_util::rt::{TokioExecutor, TokioIo}; use opentelemetry::trace::{FutureExt, SpanKind}; use snafu::{ResultExt, Snafu}; -use stackable_certs::{CertificatePairError, ca::CertificateAuthority, keys::rsa}; -use stackable_operator::time::Duration; +use stackable_certs::{ + CertificatePairError, + ca::{CertificateAuthority, DEFAULT_CA_VALIDITY}, + keys::rsa, +}; use tokio::net::TcpListener; use tokio_rustls::{ TlsAcceptor, @@ -106,7 +109,7 @@ impl TlsServer { CertificateAuthority::new_rsa().context(CreateCertificateAuthoritySnafu)?; let leaf_certificate = certificate_authority - .generate_rsa_leaf_certificate("Leaf", "webhook", Duration::from_secs(3600)) + .generate_rsa_leaf_certificate("Leaf", "webhook", [], DEFAULT_CA_VALIDITY) .context(GenerateLeafCertificateSnafu)?; let certificate_der = leaf_certificate From 45a6a1bd501959bbfad8a9a4bb3816bd8e46ba6d Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Wed, 21 May 2025 13:54:35 +0200 Subject: [PATCH 02/19] changelog --- crates/stackable-certs/CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/stackable-certs/CHANGELOG.md b/crates/stackable-certs/CHANGELOG.md index 0f69fa198..109d1a6ec 100644 --- a/crates/stackable-certs/CHANGELOG.md +++ b/crates/stackable-certs/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added + +- BREAKING: `CertificateAuthority::generate_leaf_certificate` (and `generate_rsa_leaf_certificate` and `generate_ecdsa_leaf_certificate`) + now take an additional parameter `subject_alterative_dns_names`. The passed SANs are added to the generated certificate, + this is needed for basically all modern TLS certificate validations when used with HTTPS. + Pass an empty list (`[]`) to keep the existing behavior ([#1044]). +- BREAKING: The constant `DEFAULT_CA_VALIDITY_SECONDS` has been renamed to `DEFAULT_CA_VALIDITY` and now is of type `stackable_operator::time::Duration`. + Also, the constant `ROOT_CA_SUBJECT` has been renamed to `SDP_ROOT_CA_SUBJECT` ([#1044]). +- Added the function `CertificateAuthority::ca_cert` to easily get the CA `Certificate` ([#1044]). + ## [0.3.1] - 2024-07-10 ### Changed @@ -11,6 +21,7 @@ All notable changes to this project will be documented in this file. - Bump rust-toolchain to 1.79.0 ([#822]). [#822]: https://github.com/stackabletech/operator-rs/pull/822 +[#1044]: https://github.com/stackabletech/operator-rs/pull/1044 ## [0.3.0] - 2024-05-08 From 2b1785908c508747a2c9a26fff28e360fcbc082c Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Tue, 17 Jun 2025 15:04:53 +0200 Subject: [PATCH 03/19] changelog --- crates/stackable-certs/CHANGELOG.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/stackable-certs/CHANGELOG.md b/crates/stackable-certs/CHANGELOG.md index 109d1a6ec..b21924788 100644 --- a/crates/stackable-certs/CHANGELOG.md +++ b/crates/stackable-certs/CHANGELOG.md @@ -9,10 +9,10 @@ All notable changes to this project will be documented in this file. - BREAKING: `CertificateAuthority::generate_leaf_certificate` (and `generate_rsa_leaf_certificate` and `generate_ecdsa_leaf_certificate`) now take an additional parameter `subject_alterative_dns_names`. The passed SANs are added to the generated certificate, this is needed for basically all modern TLS certificate validations when used with HTTPS. - Pass an empty list (`[]`) to keep the existing behavior ([#1044]). + Pass an empty list (`[]`) to keep the existing behavior ([#XXXX]). - BREAKING: The constant `DEFAULT_CA_VALIDITY_SECONDS` has been renamed to `DEFAULT_CA_VALIDITY` and now is of type `stackable_operator::time::Duration`. - Also, the constant `ROOT_CA_SUBJECT` has been renamed to `SDP_ROOT_CA_SUBJECT` ([#1044]). -- Added the function `CertificateAuthority::ca_cert` to easily get the CA `Certificate` ([#1044]). + Also, the constant `ROOT_CA_SUBJECT` has been renamed to `SDP_ROOT_CA_SUBJECT` ([#XXXX]). +- Added the function `CertificateAuthority::ca_cert` to easily get the CA `Certificate` ([#XXXX]). ## [0.3.1] - 2024-07-10 @@ -21,7 +21,6 @@ All notable changes to this project will be documented in this file. - Bump rust-toolchain to 1.79.0 ([#822]). [#822]: https://github.com/stackabletech/operator-rs/pull/822 -[#1044]: https://github.com/stackabletech/operator-rs/pull/1044 ## [0.3.0] - 2024-05-08 From d4d2d0d2c3f4c8ba91b48541290b87fc1b7487ea Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Tue, 17 Jun 2025 15:07:06 +0200 Subject: [PATCH 04/19] Improve error --- crates/stackable-certs/src/ca/mod.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/stackable-certs/src/ca/mod.rs b/crates/stackable-certs/src/ca/mod.rs index 6334272a0..9ddf06003 100644 --- a/crates/stackable-certs/src/ca/mod.rs +++ b/crates/stackable-certs/src/ca/mod.rs @@ -68,9 +68,11 @@ pub enum Error { #[snafu(display("failed to parse AuthorityKeyIdentifier"))] ParseAuthorityKeyIdentifier { source: x509_cert::der::Error }, - #[snafu(display("The subject alternative DNS name \"{dns_name}\" is not a Ia5String"))] - SaDnsNameNotAIa5String { - dns_name: String, + #[snafu(display( + "failed to parse subject alternative DNS name \"{subject_alternative_dns_name}\" as a Ia5 string" + ))] + ParseSubjectAlternativeDnsName { + subject_alternative_dns_name: String, source: x509_cert::der::Error, }, } @@ -341,8 +343,8 @@ where .into_iter() .map(|dns_name| { Ok(GeneralName::DnsName(Ia5String::new(dns_name).context( - SaDnsNameNotAIa5StringSnafu { - dns_name: dns_name.to_string(), + ParseSubjectAlternativeDnsNameSnafu { + subject_alternative_dns_name: dns_name.to_string(), }, )?)) }) From e2901ab537a31121307044d293f17a0ad3859919 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Tue, 17 Jun 2025 15:08:24 +0200 Subject: [PATCH 05/19] changelog --- crates/stackable-certs/CHANGELOG.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/stackable-certs/CHANGELOG.md b/crates/stackable-certs/CHANGELOG.md index b21924788..7195726a4 100644 --- a/crates/stackable-certs/CHANGELOG.md +++ b/crates/stackable-certs/CHANGELOG.md @@ -9,10 +9,10 @@ All notable changes to this project will be documented in this file. - BREAKING: `CertificateAuthority::generate_leaf_certificate` (and `generate_rsa_leaf_certificate` and `generate_ecdsa_leaf_certificate`) now take an additional parameter `subject_alterative_dns_names`. The passed SANs are added to the generated certificate, this is needed for basically all modern TLS certificate validations when used with HTTPS. - Pass an empty list (`[]`) to keep the existing behavior ([#XXXX]). + Pass an empty list (`[]`) to keep the existing behavior ([#1057]). - BREAKING: The constant `DEFAULT_CA_VALIDITY_SECONDS` has been renamed to `DEFAULT_CA_VALIDITY` and now is of type `stackable_operator::time::Duration`. - Also, the constant `ROOT_CA_SUBJECT` has been renamed to `SDP_ROOT_CA_SUBJECT` ([#XXXX]). -- Added the function `CertificateAuthority::ca_cert` to easily get the CA `Certificate` ([#XXXX]). + Also, the constant `ROOT_CA_SUBJECT` has been renamed to `SDP_ROOT_CA_SUBJECT` ([#1057]). +- Added the function `CertificateAuthority::ca_cert` to easily get the CA `Certificate` ([#1057]). ## [0.3.1] - 2024-07-10 @@ -21,6 +21,7 @@ All notable changes to this project will be documented in this file. - Bump rust-toolchain to 1.79.0 ([#822]). [#822]: https://github.com/stackabletech/operator-rs/pull/822 +[#1057]: https://github.com/stackabletech/operator-rs/pull/1057 ## [0.3.0] - 2024-05-08 From 67cba1800f53c40d7b26e08aa2c5f91d5f55bd96 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Tue, 24 Jun 2025 09:20:04 +0200 Subject: [PATCH 06/19] Update crates/stackable-certs/src/ca/mod.rs Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> --- crates/stackable-certs/src/ca/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/stackable-certs/src/ca/mod.rs b/crates/stackable-certs/src/ca/mod.rs index 9ddf06003..5344bc910 100644 --- a/crates/stackable-certs/src/ca/mod.rs +++ b/crates/stackable-certs/src/ca/mod.rs @@ -342,11 +342,12 @@ where let sans = subject_alterative_dns_names .into_iter() .map(|dns_name| { - Ok(GeneralName::DnsName(Ia5String::new(dns_name).context( + let ia5_dns_name = Ia5String::new(dns_name).context( ParseSubjectAlternativeDnsNameSnafu { subject_alternative_dns_name: dns_name.to_string(), }, - )?)) + )?; + Ok(GeneralName::DnsName(ia5_dns_name)) }) .collect::, Error>>()?; builder From 4dc617fea9d569ebca971de95cf97a1b70a711f0 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Tue, 24 Jun 2025 09:21:02 +0200 Subject: [PATCH 07/19] Update crates/stackable-certs/CHANGELOG.md Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> --- crates/stackable-certs/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/stackable-certs/CHANGELOG.md b/crates/stackable-certs/CHANGELOG.md index 7195726a4..1573d2a8f 100644 --- a/crates/stackable-certs/CHANGELOG.md +++ b/crates/stackable-certs/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file. - BREAKING: `CertificateAuthority::generate_leaf_certificate` (and `generate_rsa_leaf_certificate` and `generate_ecdsa_leaf_certificate`) now take an additional parameter `subject_alterative_dns_names`. The passed SANs are added to the generated certificate, - this is needed for basically all modern TLS certificate validations when used with HTTPS. + this is needed when the HTTPS server is accessible on multiple DNS names and/or IPs. Pass an empty list (`[]`) to keep the existing behavior ([#1057]). - BREAKING: The constant `DEFAULT_CA_VALIDITY_SECONDS` has been renamed to `DEFAULT_CA_VALIDITY` and now is of type `stackable_operator::time::Duration`. Also, the constant `ROOT_CA_SUBJECT` has been renamed to `SDP_ROOT_CA_SUBJECT` ([#1057]). From b5faf6daabe5a950e3adae431aabb86bb9511693 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Tue, 24 Jun 2025 09:23:25 +0200 Subject: [PATCH 08/19] Update crates/stackable-certs/CHANGELOG.md Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> --- crates/stackable-certs/CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/stackable-certs/CHANGELOG.md b/crates/stackable-certs/CHANGELOG.md index 1573d2a8f..cd31c3118 100644 --- a/crates/stackable-certs/CHANGELOG.md +++ b/crates/stackable-certs/CHANGELOG.md @@ -10,8 +10,9 @@ All notable changes to this project will be documented in this file. now take an additional parameter `subject_alterative_dns_names`. The passed SANs are added to the generated certificate, this is needed when the HTTPS server is accessible on multiple DNS names and/or IPs. Pass an empty list (`[]`) to keep the existing behavior ([#1057]). -- BREAKING: The constant `DEFAULT_CA_VALIDITY_SECONDS` has been renamed to `DEFAULT_CA_VALIDITY` and now is of type `stackable_operator::time::Duration`. - Also, the constant `ROOT_CA_SUBJECT` has been renamed to `SDP_ROOT_CA_SUBJECT` ([#1057]). +- BREAKING: Constants have been renamed/retyped ([#1057]): + - `DEFAULT_CA_VALIDITY_SECONDS` has been renamed to `DEFAULT_CA_VALIDITY` and now is of type `stackable_operator::time::Duration`. + - `ROOT_CA_SUBJECT` has been renamed to `SDP_ROOT_CA_SUBJECT`. - Added the function `CertificateAuthority::ca_cert` to easily get the CA `Certificate` ([#1057]). ## [0.3.1] - 2024-07-10 From a58677c00d83237514abf4365145661d5ddfadc2 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Wed, 25 Jun 2025 10:01:31 +0200 Subject: [PATCH 09/19] lets try different formatting --- crates/stackable-certs/src/ca/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/stackable-certs/src/ca/mod.rs b/crates/stackable-certs/src/ca/mod.rs index 5344bc910..9048e2e36 100644 --- a/crates/stackable-certs/src/ca/mod.rs +++ b/crates/stackable-certs/src/ca/mod.rs @@ -342,11 +342,10 @@ where let sans = subject_alterative_dns_names .into_iter() .map(|dns_name| { - let ia5_dns_name = Ia5String::new(dns_name).context( - ParseSubjectAlternativeDnsNameSnafu { + let ia5_dns_name = + Ia5String::new(dns_name).context(ParseSubjectAlternativeDnsNameSnafu { subject_alternative_dns_name: dns_name.to_string(), - }, - )?; + })?; Ok(GeneralName::DnsName(ia5_dns_name)) }) .collect::, Error>>()?; From 49faa9586c897c8167f415282f4365b76d085bc5 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Wed, 25 Jun 2025 10:32:18 +0200 Subject: [PATCH 10/19] changelog --- crates/stackable-certs/CHANGELOG.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/crates/stackable-certs/CHANGELOG.md b/crates/stackable-certs/CHANGELOG.md index cd31c3118..666fa47c8 100644 --- a/crates/stackable-certs/CHANGELOG.md +++ b/crates/stackable-certs/CHANGELOG.md @@ -6,14 +6,21 @@ All notable changes to this project will be documented in this file. ### Added -- BREAKING: `CertificateAuthority::generate_leaf_certificate` (and `generate_rsa_leaf_certificate` and `generate_ecdsa_leaf_certificate`) - now take an additional parameter `subject_alterative_dns_names`. The passed SANs are added to the generated certificate, - this is needed when the HTTPS server is accessible on multiple DNS names and/or IPs. - Pass an empty list (`[]`) to keep the existing behavior ([#1057]). +- Add the function `CertificateAuthority::ca_cert` to easily get the CA `Certificate` ([#1057]). + +### Changed + +- BREAKING: The functions `generate_leaf_certificate`, `generate_rsa_leaf_certificate` and + `generate_ecdsa_leaf_certificate` of `CertificateAuthority` accept an additional parameter + `subject_alterative_dns_names` ([#1057]). + - The passed SANs are added to the generated certificate, this is needed when the HTTPS server is + accessible on multiple DNS names and/or IPs. + - Pass an empty list (`[]`) to keep the existing behavior. - BREAKING: Constants have been renamed/retyped ([#1057]): - `DEFAULT_CA_VALIDITY_SECONDS` has been renamed to `DEFAULT_CA_VALIDITY` and now is of type `stackable_operator::time::Duration`. - `ROOT_CA_SUBJECT` has been renamed to `SDP_ROOT_CA_SUBJECT`. -- Added the function `CertificateAuthority::ca_cert` to easily get the CA `Certificate` ([#1057]). + +[#1057]: https://github.com/stackabletech/operator-rs/pull/1057 ## [0.3.1] - 2024-07-10 @@ -22,7 +29,6 @@ All notable changes to this project will be documented in this file. - Bump rust-toolchain to 1.79.0 ([#822]). [#822]: https://github.com/stackabletech/operator-rs/pull/822 -[#1057]: https://github.com/stackabletech/operator-rs/pull/1057 ## [0.3.0] - 2024-05-08 From fc835d125a4ba408f9b8d20a98b85bc3faccc4c4 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Wed, 25 Jun 2025 10:33:03 +0200 Subject: [PATCH 11/19] Update crates/stackable-certs/src/ca/mod.rs Co-authored-by: Techassi --- crates/stackable-certs/src/ca/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/stackable-certs/src/ca/mod.rs b/crates/stackable-certs/src/ca/mod.rs index 9048e2e36..fe2b6dc02 100644 --- a/crates/stackable-certs/src/ca/mod.rs +++ b/crates/stackable-certs/src/ca/mod.rs @@ -69,7 +69,7 @@ pub enum Error { ParseAuthorityKeyIdentifier { source: x509_cert::der::Error }, #[snafu(display( - "failed to parse subject alternative DNS name \"{subject_alternative_dns_name}\" as a Ia5 string" + "failed to parse subject alternative DNS name {subject_alternative_dns_name:?} as a Ia5 string" ))] ParseSubjectAlternativeDnsName { subject_alternative_dns_name: String, From d1d61407a11bb86ddb1e0ca6f5b5f2a854dc9eee Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Wed, 25 Jun 2025 10:34:11 +0200 Subject: [PATCH 12/19] Update crates/stackable-certs/src/ca/mod.rs Co-authored-by: Techassi --- crates/stackable-certs/src/ca/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/stackable-certs/src/ca/mod.rs b/crates/stackable-certs/src/ca/mod.rs index fe2b6dc02..e6ca385d3 100644 --- a/crates/stackable-certs/src/ca/mod.rs +++ b/crates/stackable-certs/src/ca/mod.rs @@ -534,7 +534,7 @@ mod tests { ); // Test SAN extension is present - let extensions = cert.extensions.as_ref().expect("cert had no extension"); + let extensions = cert.extensions.as_ref().expect("cert must have extensions"); assert!( extensions .iter() From 3962a16004b668a4c53423d697185826743d6962 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Wed, 25 Jun 2025 10:37:25 +0200 Subject: [PATCH 13/19] expect message --- crates/stackable-certs/src/ca/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/stackable-certs/src/ca/mod.rs b/crates/stackable-certs/src/ca/mod.rs index e6ca385d3..ecd5c1ad7 100644 --- a/crates/stackable-certs/src/ca/mod.rs +++ b/crates/stackable-certs/src/ca/mod.rs @@ -547,7 +547,7 @@ mod tests { assert_eq!( not_after .duration_since(not_before) - .expect("Failed to calculate duration between notBefore and notAfter"), + .expect("notBefore must be before notAfter"), *TEST_CERT_LIFETIME ); } From a81cf5b147f99b52129d5c07bef6a3d0260aeacb Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Wed, 25 Jun 2025 10:44:55 +0200 Subject: [PATCH 14/19] expect instead of snafu --- crates/stackable-certs/src/ca/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/stackable-certs/src/ca/mod.rs b/crates/stackable-certs/src/ca/mod.rs index ecd5c1ad7..4b54fabfd 100644 --- a/crates/stackable-certs/src/ca/mod.rs +++ b/crates/stackable-certs/src/ca/mod.rs @@ -208,9 +208,8 @@ where // We don't allow customization of the CA subject by callers. Every CA // created by us should contain the same subject consisting a common set // of distinguished names (DNs). - let subject = Name::from_str(SDP_ROOT_CA_SUBJECT).context(ParseSubjectSnafu { - subject: SDP_ROOT_CA_SUBJECT, - })?; + let subject = Name::from_str(SDP_ROOT_CA_SUBJECT) + .expect("The SDP_ROOT_CA_SUBJECT must be a valid subject"); let spki_pem = signing_key_pair .verifying_key() From ca488cdf14bfc8f1a4f888afcb73f53dd52cd622 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Wed, 25 Jun 2025 12:47:14 +0200 Subject: [PATCH 15/19] Update crates/stackable-certs/src/ca/mod.rs Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> --- crates/stackable-certs/src/ca/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/stackable-certs/src/ca/mod.rs b/crates/stackable-certs/src/ca/mod.rs index 4b54fabfd..3ca5dccdb 100644 --- a/crates/stackable-certs/src/ca/mod.rs +++ b/crates/stackable-certs/src/ca/mod.rs @@ -509,7 +509,7 @@ mod tests { let mut ca = CertificateAuthority::new_rsa().unwrap(); let cert = ca .generate_rsa_leaf_certificate("Airflow", "pod", [TEST_SAN], TEST_CERT_LIFETIME) - .expect("RSA certificate generation failed"); + .expect("Must be able to generate an RSA certificate. Perhaps there was an RNG failure"); assert_cert_attributes(cert.certificate()); } From 408c33f5b03d95399f78fd401f81d2a029fec2c7 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Wed, 25 Jun 2025 12:51:37 +0200 Subject: [PATCH 16/19] lowercase error message --- crates/stackable-certs/src/ca/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/stackable-certs/src/ca/mod.rs b/crates/stackable-certs/src/ca/mod.rs index 3ca5dccdb..9265698f8 100644 --- a/crates/stackable-certs/src/ca/mod.rs +++ b/crates/stackable-certs/src/ca/mod.rs @@ -209,7 +209,7 @@ where // created by us should contain the same subject consisting a common set // of distinguished names (DNs). let subject = Name::from_str(SDP_ROOT_CA_SUBJECT) - .expect("The SDP_ROOT_CA_SUBJECT must be a valid subject"); + .expect("the SDP_ROOT_CA_SUBJECT must be a valid subject"); let spki_pem = signing_key_pair .verifying_key() From 98acd6cf942191bdd631d60f6102107c3a8f6768 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Wed, 25 Jun 2025 12:55:46 +0200 Subject: [PATCH 17/19] Airflow -> Product --- crates/stackable-certs/src/ca/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/stackable-certs/src/ca/mod.rs b/crates/stackable-certs/src/ca/mod.rs index 9265698f8..f443621f4 100644 --- a/crates/stackable-certs/src/ca/mod.rs +++ b/crates/stackable-certs/src/ca/mod.rs @@ -502,13 +502,13 @@ mod tests { use super::*; const TEST_CERT_LIFETIME: Duration = Duration::from_hours_unchecked(1); - const TEST_SAN: &str = "airflow-0.airflow.default.svc.cluster.local"; + const TEST_SAN: &str = "product-0.product.default.svc.cluster.local"; #[tokio::test] async fn rsa_key_generation() { let mut ca = CertificateAuthority::new_rsa().unwrap(); let cert = ca - .generate_rsa_leaf_certificate("Airflow", "pod", [TEST_SAN], TEST_CERT_LIFETIME) + .generate_rsa_leaf_certificate("Product", "pod", [TEST_SAN], TEST_CERT_LIFETIME) .expect("Must be able to generate an RSA certificate. Perhaps there was an RNG failure"); assert_cert_attributes(cert.certificate()); @@ -518,7 +518,7 @@ mod tests { async fn ecdsa_key_generation() { let mut ca = CertificateAuthority::new_ecdsa().unwrap(); let cert = ca - .generate_ecdsa_leaf_certificate("Airflow", "pod", [TEST_SAN], TEST_CERT_LIFETIME) + .generate_ecdsa_leaf_certificate("Product", "pod", [TEST_SAN], TEST_CERT_LIFETIME) .expect("ecdsa certificate generation failed"); assert_cert_attributes(cert.certificate()); @@ -529,7 +529,7 @@ mod tests { // Test subject assert_eq!( cert.subject, - Name::from_str("CN=Airflow Certificate for pod").unwrap() + Name::from_str("CN=Product Certificate for pod").unwrap() ); // Test SAN extension is present From 972c7d6aebe5114a9b05ff2b32a0b2a385a500f7 Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Wed, 25 Jun 2025 13:37:17 +0200 Subject: [PATCH 18/19] fmt --- crates/stackable-certs/src/ca/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/stackable-certs/src/ca/mod.rs b/crates/stackable-certs/src/ca/mod.rs index f443621f4..6941aee4c 100644 --- a/crates/stackable-certs/src/ca/mod.rs +++ b/crates/stackable-certs/src/ca/mod.rs @@ -509,7 +509,9 @@ mod tests { let mut ca = CertificateAuthority::new_rsa().unwrap(); let cert = ca .generate_rsa_leaf_certificate("Product", "pod", [TEST_SAN], TEST_CERT_LIFETIME) - .expect("Must be able to generate an RSA certificate. Perhaps there was an RNG failure"); + .expect( + "Must be able to generate an RSA certificate. Perhaps there was an RNG failure", + ); assert_cert_attributes(cert.certificate()); } From 76ea5b29d9fa09df2cc3a5f4969bd540fb3f299d Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Wed, 25 Jun 2025 14:05:08 +0200 Subject: [PATCH 19/19] Update crates/stackable-certs/src/ca/mod.rs Co-authored-by: Techassi --- crates/stackable-certs/src/ca/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/stackable-certs/src/ca/mod.rs b/crates/stackable-certs/src/ca/mod.rs index 6941aee4c..b2e464b45 100644 --- a/crates/stackable-certs/src/ca/mod.rs +++ b/crates/stackable-certs/src/ca/mod.rs @@ -521,7 +521,9 @@ mod tests { let mut ca = CertificateAuthority::new_ecdsa().unwrap(); let cert = ca .generate_ecdsa_leaf_certificate("Product", "pod", [TEST_SAN], TEST_CERT_LIFETIME) - .expect("ecdsa certificate generation failed"); + .expect( + "Must be able to generate an ECDSA certificate. Perhaps there was an RNG failure", + ); assert_cert_attributes(cert.certificate()); }