diff --git a/factory/ca.go b/factory/ca.go index 5dfa863..8a357b6 100644 --- a/factory/ca.go +++ b/factory/ca.go @@ -6,6 +6,7 @@ import ( "fmt" "io/ioutil" "os" + "time" "github.com/rancher/dynamiclistener/cert" ) @@ -16,7 +17,7 @@ func GenCA() (*x509.Certificate, crypto.Signer, error) { return nil, nil, err } - caCert, err := NewSelfSignedCACert(caKey, "dynamiclistener-ca", "dynamiclistener-org") + caCert, err := NewSelfSignedCACert(caKey, fmt.Sprintf("dynamiclistener-ca@%d", time.Now().Unix()), "dynamiclistener-org") if err != nil { return nil, nil, err } diff --git a/factory/gen.go b/factory/gen.go index b59f527..e620c5f 100644 --- a/factory/gen.go +++ b/factory/gen.go @@ -154,6 +154,10 @@ func (t *TLS) generateCert(secret *v1.Secret, cn ...string) (*v1.Secret, bool, e secret = &v1.Secret{} } + if err := t.Verify(secret); err != nil { + logrus.Warnf("unable to verify existing certificate: %v - signing operation may change certificate issuer", err) + } + secret = populateCN(secret, cn...) privateKey, err := getPrivateKey(secret) @@ -171,7 +175,7 @@ func (t *TLS) generateCert(secret *v1.Secret, cn ...string) (*v1.Secret, bool, e return nil, false, err } - certBytes, keyBytes, err := Marshal(newCert, privateKey) + keyBytes, certBytes, err := MarshalChain(privateKey, newCert, t.CACert) if err != nil { return nil, false, err } @@ -187,6 +191,29 @@ func (t *TLS) generateCert(secret *v1.Secret, cn ...string) (*v1.Secret, bool, e return secret, true, nil } +func (t *TLS) Verify(secret *v1.Secret) error { + certsPem := secret.Data[v1.TLSCertKey] + if len(certsPem) == 0 { + return nil + } + + certificates, err := cert.ParseCertsPEM(certsPem) + if err != nil || len(certificates) == 0 { + return err + } + + verifyOpts := x509.VerifyOptions{ + Roots: x509.NewCertPool(), + KeyUsages: []x509.ExtKeyUsage{ + x509.ExtKeyUsageAny, + }, + } + verifyOpts.Roots.AddCert(t.CACert) + + _, 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) } @@ -250,14 +277,33 @@ func getPrivateKey(secret *v1.Secret) (crypto.Signer, error) { return NewPrivateKey() } +// MarshalChain returns given key and certificates as byte slices. +func MarshalChain(privateKey crypto.Signer, certs ...*x509.Certificate) (keyBytes, certChainBytes []byte, err error) { + keyBytes, err = cert.MarshalPrivateKeyToPEM(privateKey) + if err != nil { + return nil, nil, err + } + + for _, cert := range certs { + if cert != nil { + certBlock := pem.Block{ + Type: CertificateBlockType, + Bytes: cert.Raw, + } + certChainBytes = append(certChainBytes, pem.EncodeToMemory(&certBlock)...) + } + } + return keyBytes, certChainBytes, nil +} + // Marshal returns the given cert and key as byte slices. -func Marshal(x509Cert *x509.Certificate, privateKey crypto.Signer) ([]byte, []byte, error) { +func Marshal(x509Cert *x509.Certificate, privateKey crypto.Signer) (certBytes, keyBytes []byte, err error) { certBlock := pem.Block{ Type: CertificateBlockType, Bytes: x509Cert.Raw, } - keyBytes, err := cert.MarshalPrivateKeyToPEM(privateKey) + keyBytes, err = cert.MarshalPrivateKeyToPEM(privateKey) if err != nil { return nil, nil, err } diff --git a/storage/kubernetes/ca.go b/storage/kubernetes/ca.go index d23ac4f..9584da4 100644 --- a/storage/kubernetes/ca.go +++ b/storage/kubernetes/ca.go @@ -56,7 +56,7 @@ func createAndStoreClientCert(secrets v1controller.SecretClient, namespace strin return nil, err } - certPem, keyPem, err := factory.Marshal(cert, key) + keyPem, certPem, err := factory.MarshalChain(key, cert, caCert) if err != nil { return nil, err }