Skip to content

Commit

Permalink
Merge pull request #78 from knoppiks/multiple-ca-certs
Browse files Browse the repository at this point in the history
Allow multiple (intermediate) CA certs
  • Loading branch information
dereknola committed Aug 11, 2023
2 parents 4c1ac9b + 6cc9a67 commit e6585da
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 21 deletions.
47 changes: 37 additions & 10 deletions factory/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,28 @@ 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()
if err != nil {
return nil, nil, err
}
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
}
Expand All @@ -53,14 +63,22 @@ 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)
if err != nil {
return nil, nil, err
}
return chain[0], signer, nil
}

func LoadCAChain(caPem, caKey []byte) ([]*x509.Certificate, crypto.Signer, error) {
key, err := cert.ParsePrivateKeyPEM(caKey)
if err != nil {
return nil, nil, err
Expand All @@ -70,15 +88,24 @@ 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)
if err != nil {
return nil, nil, err
}
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
Expand All @@ -88,5 +115,5 @@ func LoadCerts(certFile, keyFile string) (*x509.Certificate, crypto.Signer, erro
return nil, nil, err
}

return LoadCA(caPem, caKey)
return LoadCAChain(caPem, caKey)
}
10 changes: 6 additions & 4 deletions factory/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ var (
)

type TLS struct {
CACert *x509.Certificate
CACert []*x509.Certificate
CAKey crypto.Signer
CN string
Organization []string
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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 {
Expand Down
5 changes: 5 additions & 0 deletions listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ type SetFactory interface {
SetFactory(tls TLSFactory)
}

// Deprecated: Use NewListenerWithChain 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 NewListenerWithChain(l, storage, []*x509.Certificate{caCert}, caKey, config)
}

func NewListenerWithChain(l net.Listener, storage TLSStorage, caCert []*x509.Certificate, caKey crypto.Signer, config Config) (net.Listener, http.Handler, error) {
if config.CN == "" {
config.CN = "dynamic"
}
Expand Down
18 changes: 12 additions & 6 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
)

type ListenOpts struct {
CAChain []*x509.Certificate
// Deprecated: Use CAChain instead
CA *x509.Certificate
CAKey crypto.Signer
Storage dynamiclistener.TLSStorage
Expand Down Expand Up @@ -132,21 +134,25 @@ 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
}

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 == "" {
Expand All @@ -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 {
Expand Down
11 changes: 10 additions & 1 deletion storage/kubernetes/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,21 @@ 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)
if err != nil {
return nil, nil, err
}
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) {
Expand Down

0 comments on commit e6585da

Please sign in to comment.