From 02304047cf4984582835dae2392c98f582eeaee1 Mon Sep 17 00:00:00 2001 From: Jonas Wagner Date: Mon, 10 Jul 2023 08:59:37 +0200 Subject: [PATCH 1/3] Enable intermediate CA Certificates Signed-off-by: Jonas Wagner --- factory/ca.go | 38 ++++++++++++++++++++++++++++---------- factory/gen.go | 10 ++++++---- listener.go | 5 +++++ server/server.go | 18 ++++++++++++------ storage/kubernetes/ca.go | 8 +++++++- 5 files changed, 58 insertions(+), 21 deletions(-) diff --git a/factory/ca.go b/factory/ca.go index 8a357b6..b2c92f6 100644 --- a/factory/ca.go +++ b/factory/ca.go @@ -25,18 +25,25 @@ func GenCA() (*x509.Certificate, crypto.Signer, error) { return caCert, caKey, nil } +// Deprecated: Use LoadOrGenCAChain instead as it supports intermediate CAs func LoadOrGenCA() (*x509.Certificate, crypto.Signer, error) { - cert, key, err := loadCA() + chain, signer, err := LoadOrGenCAChain() + return chain[0], signer, err +} + +func LoadOrGenCAChain() ([]*x509.Certificate, crypto.Signer, error) { + certs, key, err := loadCA() if err == nil { - return cert, key, nil + return certs, key, nil } - cert, key, err = GenCA() + cert, key, err := GenCA() if err != nil { return nil, nil, err } + certs = []*x509.Certificate{cert} - certBytes, keyBytes, err := Marshal(cert, key) + certBytes, keyBytes, err := MarshalChain(key, certs...) if err != nil { return nil, nil, err } @@ -53,14 +60,19 @@ func LoadOrGenCA() (*x509.Certificate, crypto.Signer, error) { return nil, nil, err } - return cert, key, nil + return certs, key, nil } -func loadCA() (*x509.Certificate, crypto.Signer, error) { - return LoadCerts("./certs/ca.pem", "./certs/ca.key") +func loadCA() ([]*x509.Certificate, crypto.Signer, error) { + return LoadCertsChain("./certs/ca.pem", "./certs/ca.key") } func LoadCA(caPem, caKey []byte) (*x509.Certificate, crypto.Signer, error) { + chain, signer, err := LoadCAChain(caPem, caKey) + return chain[0], signer, err +} + +func LoadCAChain(caPem, caKey []byte) ([]*x509.Certificate, crypto.Signer, error) { key, err := cert.ParsePrivateKeyPEM(caKey) if err != nil { return nil, nil, err @@ -70,15 +82,21 @@ func LoadCA(caPem, caKey []byte) (*x509.Certificate, crypto.Signer, error) { return nil, nil, fmt.Errorf("key is not a crypto.Signer") } - cert, err := ParseCertPEM(caPem) + certs, err := cert.ParseCertsPEM(caPem) if err != nil { return nil, nil, err } - return cert, signer, nil + return certs, signer, nil } +// Deprecated: Use LoadCertsChain instead as it supports intermediate CAs func LoadCerts(certFile, keyFile string) (*x509.Certificate, crypto.Signer, error) { + chain, signer, err := LoadCertsChain(certFile, keyFile) + return chain[0], signer, err +} + +func LoadCertsChain(certFile, keyFile string) ([]*x509.Certificate, crypto.Signer, error) { caPem, err := ioutil.ReadFile(certFile) if err != nil { return nil, nil, err @@ -88,5 +106,5 @@ func LoadCerts(certFile, keyFile string) (*x509.Certificate, crypto.Signer, erro return nil, nil, err } - return LoadCA(caPem, caKey) + return LoadCAChain(caPem, caKey) } diff --git a/factory/gen.go b/factory/gen.go index 736a4ef..7de72f1 100644 --- a/factory/gen.go +++ b/factory/gen.go @@ -33,7 +33,7 @@ var ( ) type TLS struct { - CACert *x509.Certificate + CACert []*x509.Certificate CAKey crypto.Signer CN string Organization []string @@ -178,7 +178,7 @@ func (t *TLS) generateCert(secret *v1.Secret, cn ...string) (*v1.Secret, bool, e return nil, false, err } - keyBytes, certBytes, err := MarshalChain(privateKey, newCert, t.CACert) + keyBytes, certBytes, err := MarshalChain(privateKey, append([]*x509.Certificate{newCert}, t.CACert...)...) if err != nil { return nil, false, err } @@ -226,14 +226,16 @@ func (t *TLS) Verify(secret *v1.Secret) error { x509.ExtKeyUsageAny, }, } - verifyOpts.Roots.AddCert(t.CACert) + for _, c := range t.CACert { + verifyOpts.Roots.AddCert(c) + } _, err = certificates[0].Verify(verifyOpts) return err } func (t *TLS) newCert(domains []string, ips []net.IP, privateKey crypto.Signer) (*x509.Certificate, error) { - return NewSignedCert(privateKey, t.CACert, t.CAKey, t.CN, t.Organization, domains, ips) + return NewSignedCert(privateKey, t.CACert[0], t.CAKey, t.CN, t.Organization, domains, ips) } func populateCN(secret *v1.Secret, cn ...string) *v1.Secret { diff --git a/listener.go b/listener.go index 3804e2f..cbfab1f 100644 --- a/listener.go +++ b/listener.go @@ -34,7 +34,12 @@ type SetFactory interface { SetFactory(tls TLSFactory) } +// Deprecated: Use NewListener2 instead as it supports intermediate CAs func NewListener(l net.Listener, storage TLSStorage, caCert *x509.Certificate, caKey crypto.Signer, config Config) (net.Listener, http.Handler, error) { + return NewListener2(l, storage, []*x509.Certificate{caCert}, caKey, config) +} + +func NewListener2(l net.Listener, storage TLSStorage, caCert []*x509.Certificate, caKey crypto.Signer, config Config) (net.Listener, http.Handler, error) { if config.CN == "" { config.CN = "dynamic" } diff --git a/server/server.go b/server/server.go index 2e4a9d7..6519d35 100644 --- a/server/server.go +++ b/server/server.go @@ -21,6 +21,8 @@ import ( ) type ListenOpts struct { + CAChain []*x509.Certificate + // Deprecated: Use CAChain instead CA *x509.Certificate CAKey crypto.Signer Storage dynamiclistener.TLSStorage @@ -132,7 +134,7 @@ func getTLSListener(ctx context.Context, tcp net.Listener, handler http.Handler, return nil, nil, err } - listener, dynHandler, err := dynamiclistener.NewListener(tcp, storage, caCert, caKey, opts.TLSListenerConfig) + listener, dynHandler, err := dynamiclistener.NewListener2(tcp, storage, caCert, caKey, opts.TLSListenerConfig) if err != nil { return nil, nil, err } @@ -140,13 +142,17 @@ func getTLSListener(ctx context.Context, tcp net.Listener, handler http.Handler, return listener, wrapHandler(dynHandler, handler), nil } -func getCA(opts ListenOpts) (*x509.Certificate, crypto.Signer, error) { - if opts.CA != nil && opts.CAKey != nil { - return opts.CA, opts.CAKey, nil +func getCA(opts ListenOpts) ([]*x509.Certificate, crypto.Signer, error) { + if opts.CAKey != nil { + if opts.CAChain != nil { + return opts.CAChain, opts.CAKey, nil + } else if opts.CA != nil { + return []*x509.Certificate{opts.CA}, opts.CAKey, nil + } } if opts.Secrets == nil { - return factory.LoadOrGenCA() + return factory.LoadOrGenCAChain() } if opts.CAName == "" { @@ -161,7 +167,7 @@ func getCA(opts ListenOpts) (*x509.Certificate, crypto.Signer, error) { opts.CANamespace = "kube-system" } - return kubernetes.LoadOrGenCA(opts.Secrets, opts.CANamespace, opts.CAName) + return kubernetes.LoadOrGenCAChain(opts.Secrets, opts.CANamespace, opts.CAName) } func newStorage(ctx context.Context, opts ListenOpts) dynamiclistener.TLSStorage { diff --git a/storage/kubernetes/ca.go b/storage/kubernetes/ca.go index 9584da4..6c203e1 100644 --- a/storage/kubernetes/ca.go +++ b/storage/kubernetes/ca.go @@ -11,12 +11,18 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +// Deprecated: Use LoadOrGenCAChain instead as it supports intermediate CAs func LoadOrGenCA(secrets v1controller.SecretClient, namespace, name string) (*x509.Certificate, crypto.Signer, error) { + chain, signer, err := LoadOrGenCAChain(secrets, namespace, name) + return chain[0], signer, err +} + +func LoadOrGenCAChain(secrets v1controller.SecretClient, namespace, name string) ([]*x509.Certificate, crypto.Signer, error) { secret, err := getSecret(secrets, namespace, name) if err != nil { return nil, nil, err } - return factory.LoadCA(secret.Data[v1.TLSCertKey], secret.Data[v1.TLSPrivateKeyKey]) + return factory.LoadCAChain(secret.Data[v1.TLSCertKey], secret.Data[v1.TLSPrivateKeyKey]) } func LoadOrGenClient(secrets v1controller.SecretClient, namespace, name, cn string, ca *x509.Certificate, key crypto.Signer) (*x509.Certificate, crypto.Signer, error) { From 8f13b193a1028827453c064efa89629725b6cdc7 Mon Sep 17 00:00:00 2001 From: Jonas Wagner Date: Fri, 14 Jul 2023 08:16:08 +0200 Subject: [PATCH 2/3] Use more Verbose name for Listener Co-authored-by: Brad Davidson Signed-off-by: Jonas Wagner Date: Fri, 14 Jul 2023 08:19:03 +0200 Subject: [PATCH 3/3] Prevent Panic for empty Arrays on Error Co-authored-by: Brad Davidson Signed-off-by: Jonas Wagner --- factory/ca.go | 11 ++++++++++- storage/kubernetes/ca.go | 3 +++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/factory/ca.go b/factory/ca.go index b2c92f6..fb1c537 100644 --- a/factory/ca.go +++ b/factory/ca.go @@ -28,6 +28,9 @@ func GenCA() (*x509.Certificate, crypto.Signer, error) { // Deprecated: Use LoadOrGenCAChain instead as it supports intermediate CAs func LoadOrGenCA() (*x509.Certificate, crypto.Signer, error) { chain, signer, err := LoadOrGenCAChain() + if err != nil { + return nil, nil, err + } return chain[0], signer, err } @@ -69,7 +72,10 @@ func loadCA() ([]*x509.Certificate, crypto.Signer, error) { func LoadCA(caPem, caKey []byte) (*x509.Certificate, crypto.Signer, error) { chain, signer, err := LoadCAChain(caPem, caKey) - return chain[0], signer, err + if err != nil { + return nil, nil, err + } + return chain[0], signer, nil } func LoadCAChain(caPem, caKey []byte) ([]*x509.Certificate, crypto.Signer, error) { @@ -93,6 +99,9 @@ func LoadCAChain(caPem, caKey []byte) ([]*x509.Certificate, crypto.Signer, error // Deprecated: Use LoadCertsChain instead as it supports intermediate CAs func LoadCerts(certFile, keyFile string) (*x509.Certificate, crypto.Signer, error) { chain, signer, err := LoadCertsChain(certFile, keyFile) + if err != nil { + return nil, nil, err + } return chain[0], signer, err } diff --git a/storage/kubernetes/ca.go b/storage/kubernetes/ca.go index 6c203e1..18a07e0 100644 --- a/storage/kubernetes/ca.go +++ b/storage/kubernetes/ca.go @@ -14,6 +14,9 @@ import ( // Deprecated: Use LoadOrGenCAChain instead as it supports intermediate CAs func LoadOrGenCA(secrets v1controller.SecretClient, namespace, name string) (*x509.Certificate, crypto.Signer, error) { chain, signer, err := LoadOrGenCAChain(secrets, namespace, name) + if err != nil { + return nil, nil, err + } return chain[0], signer, err }