11//! Dynamically provisions and picks Certificate Authorities.
22
3- use std:: { collections:: BTreeMap , fmt:: Display } ;
3+ use std:: { collections:: BTreeMap , ffi :: OsStr , fmt:: Display , path :: Path } ;
44
55use openssl:: {
66 asn1:: { Asn1Integer , Asn1Time } ,
@@ -34,7 +34,7 @@ use tracing::{info, info_span, warn};
3434
3535use crate :: {
3636 backend:: SecretBackendError ,
37- crd:: CertificateKeyGeneration ,
37+ crd:: { AdditionalTrustRoot , CertificateKeyGeneration } ,
3838 utils:: { asn1time_to_offsetdatetime, Asn1TimeParseError , Unloggable } ,
3939} ;
4040
@@ -55,8 +55,8 @@ pub enum Error {
5555 #[ snafu( display( "failed to generate certificate key" ) ) ]
5656 GenerateKey { source : openssl:: error:: ErrorStack } ,
5757
58- #[ snafu( display( "failed to load CA {secret}" ) ) ]
59- FindCa {
58+ #[ snafu( display( "failed to load {secret}" ) ) ]
59+ FindSecret {
6060 source : kube:: Error ,
6161 secret : ObjectRef < Secret > ,
6262 } ,
@@ -77,6 +77,12 @@ pub enum Error {
7777 secret : ObjectRef < Secret > ,
7878 } ,
7979
80+ #[ snafu( display( "unsupported certificate format in key {key:?} of {secret}; supported extensions: .cer, .cert, .crt, .pem" ) ) ]
81+ UnsupportedCertificateFormat {
82+ key : String ,
83+ secret : ObjectRef < Secret > ,
84+ } ,
85+
8086 #[ snafu( display( "failed to parse CA lifetime from key {key:?} of {secret}" ) ) ]
8187 ParseLifetime {
8288 source : Asn1TimeParseError ,
@@ -106,9 +112,10 @@ impl SecretBackendError for Error {
106112 match self {
107113 Error :: GenerateKey { .. } => tonic:: Code :: Internal ,
108114 Error :: MissingCertificate { .. } => tonic:: Code :: FailedPrecondition ,
109- Error :: FindCa { .. } => tonic:: Code :: Unavailable ,
115+ Error :: FindSecret { .. } => tonic:: Code :: Unavailable ,
110116 Error :: CaNotFoundAndGenDisabled { .. } => tonic:: Code :: FailedPrecondition ,
111117 Error :: LoadCertificate { .. } => tonic:: Code :: FailedPrecondition ,
118+ Error :: UnsupportedCertificateFormat { .. } => tonic:: Code :: InvalidArgument ,
112119 Error :: ParseLifetime { .. } => tonic:: Code :: FailedPrecondition ,
113120 Error :: BuildCertificate { .. } => tonic:: Code :: FailedPrecondition ,
114121 Error :: SerializeCertificate { .. } => tonic:: Code :: FailedPrecondition ,
@@ -293,20 +300,22 @@ impl CertificateAuthority {
293300#[ derive( Debug ) ]
294301pub struct Manager {
295302 certificate_authorities : Vec < CertificateAuthority > ,
303+ additional_trusted_certificates : Vec < X509 > ,
296304}
297305
298306impl Manager {
299307 pub async fn load_or_create (
300308 client : & stackable_operator:: client:: Client ,
301309 secret_ref : & SecretReference ,
310+ additional_trust_roots : & [ AdditionalTrustRoot ] ,
302311 config : & Config ,
303312 ) -> Result < Self > {
304313 // Use entry API rather than apply so that we crash and retry on conflicts (to avoid creating spurious certs that we throw away immediately)
305314 let secrets_api = & client. get_api :: < Secret > ( & secret_ref. namespace ) ;
306315 let ca_secret = secrets_api
307316 . entry ( & secret_ref. name )
308317 . await
309- . with_context ( |_| FindCaSnafu { secret : secret_ref } ) ?;
318+ . with_context ( |_| FindSecretSnafu { secret : secret_ref } ) ?;
310319 let mut update_ca_secret = false ;
311320 let mut certificate_authorities = match & ca_secret {
312321 Entry :: Occupied ( ca_secret) => {
@@ -441,11 +450,67 @@ impl Manager {
441450 return SaveRequestedButForbiddenSnafu . fail ( ) ;
442451 }
443452 }
453+
454+ let mut additional_trusted_certificates = vec ! [ ] ;
455+ for AdditionalTrustRoot { secret } in additional_trust_roots {
456+ additional_trusted_certificates
457+ . extend ( Self :: read_certificates_from_secret ( client, secret) . await ?) ;
458+ }
459+
444460 Ok ( Self {
445461 certificate_authorities,
462+ additional_trusted_certificates,
446463 } )
447464 }
448465
466+ /// Read certificates from the given Secret
467+ ///
468+ /// The keys are assumed to be filenames and their extensions denote the expected format of the
469+ /// certificate.
470+ async fn read_certificates_from_secret (
471+ client : & stackable_operator:: client:: Client ,
472+ secret_ref : & SecretReference ,
473+ ) -> Result < Vec < X509 > > {
474+ let mut certificates = vec ! [ ] ;
475+
476+ let secrets_api = & client. get_api :: < Secret > ( & secret_ref. namespace ) ;
477+ let secret = secrets_api
478+ . get ( & secret_ref. name )
479+ . await
480+ . with_context ( |_| FindSecretSnafu { secret : secret_ref } ) ?;
481+
482+ let secret_data = secret. data . unwrap_or_default ( ) ;
483+ for ( key, ByteString ( value) ) in & secret_data {
484+ let extension = Path :: new ( key) . extension ( ) . and_then ( OsStr :: to_str) ;
485+ let certs = match extension {
486+ Some ( "pem" ) => X509 :: stack_from_pem ( value) ,
487+ Some ( "cer" ) | Some ( "cert" ) | Some ( "crt" ) => X509 :: from_der ( value)
488+ . map ( |cert| vec ! [ cert] )
489+ . or ( X509 :: stack_from_pem ( value) ) ,
490+ _ => {
491+ return UnsupportedCertificateFormatSnafu {
492+ key,
493+ secret : secret_ref,
494+ }
495+ . fail ( ) ;
496+ }
497+ }
498+ . context ( LoadCertificateSnafu {
499+ key,
500+ secret : secret_ref,
501+ } ) ?;
502+ info ! (
503+ "Add the certificate(s) {certs:?} from the key [{key}] of [{secret_ref}] to the additional trust roots." ,
504+ certs = certs,
505+ secret_ref = secret_ref,
506+ key = key,
507+ ) ;
508+ certificates. extend ( certs) ;
509+ }
510+
511+ Ok ( certificates)
512+ }
513+
449514 /// Get an appropriate [`CertificateAuthority`] for signing a given certificate.
450515 pub fn find_certificate_authority_for_signing (
451516 & self ,
@@ -467,5 +532,6 @@ impl Manager {
467532 self . certificate_authorities
468533 . iter ( )
469534 . map ( |ca| & ca. certificate )
535+ . chain ( & self . additional_trusted_certificates )
470536 }
471537}
0 commit comments