Allow multiple CA Certificates (#4556)

This commit is contained in:
Manuel Alejandro de Brito Fontes 2019-09-13 09:22:24 -03:00 committed by GitHub
parent fe4f178db1
commit 55820ef1e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 47 additions and 46 deletions

View file

@ -17,8 +17,6 @@ limitations under the License.
package store package store
import ( import (
"crypto/sha1"
"encoding/hex"
"fmt" "fmt"
"strings" "strings"
@ -107,11 +105,17 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
} }
if len(ca) > 0 { if len(ca) > 0 {
caCert, err := ssl.CheckCACert(ca)
if err != nil {
return nil, fmt.Errorf("parsing CA certificate: %v", err)
}
path, err := ssl.StoreSSLCertOnDisk(nsSecName, sslCert) path, err := ssl.StoreSSLCertOnDisk(nsSecName, sslCert)
if err != nil { if err != nil {
return nil, fmt.Errorf("error while storing certificate and key: %v", err) return nil, fmt.Errorf("error while storing certificate and key: %v", err)
} }
sslCert.CACertificate = caCert
sslCert.CAFileName = path sslCert.CAFileName = path
sslCert.CASHA = file.SHA1(path) sslCert.CASHA = file.SHA1(path)
@ -125,7 +129,6 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
if err != nil { if err != nil {
return nil, fmt.Errorf("error configuring CRL certificate: %v", err) return nil, fmt.Errorf("error configuring CRL certificate: %v", err)
} }
} }
} }
@ -170,11 +173,6 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
sslCert.Name = secret.Name sslCert.Name = secret.Name
sslCert.Namespace = secret.Namespace sslCert.Namespace = secret.Namespace
hasher := sha1.New()
hasher.Write(sslCert.Certificate.Raw)
sslCert.PemSHA = hex.EncodeToString(hasher.Sum(nil))
// the default SSL certificate needs to be present on disk // the default SSL certificate needs to be present on disk
if secretName == s.defaultSSLCertificate { if secretName == s.defaultSSLCertificate {
path, err := ssl.StoreSSLCertOnDisk(nsSecName, sslCert) path, err := ssl.StoreSSLCertOnDisk(nsSecName, sslCert)

View file

@ -30,6 +30,8 @@ type SSLCert struct {
Certificate *x509.Certificate `json:"-"` Certificate *x509.Certificate `json:"-"`
CACertificate []*x509.Certificate `json:"-"`
// CAFileName contains the path to the file with the root certificate // CAFileName contains the path to the file with the root certificate
CAFileName string `json:"caFileName"` CAFileName string `json:"caFileName"`

View file

@ -20,10 +20,12 @@ import (
"bytes" "bytes"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/sha1"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/asn1" "encoding/asn1"
"encoding/hex"
"encoding/pem" "encoding/pem"
"errors" "errors"
"fmt" "fmt"
@ -63,21 +65,6 @@ func getPemFileName(fullSecretName string) (string, string) {
return fmt.Sprintf("%v/%v", file.DefaultSSLDirectory, pemName), pemName return fmt.Sprintf("%v/%v", file.DefaultSSLDirectory, pemName), pemName
} }
func verifyPemCertAgainstRootCA(pemCert *x509.Certificate, ca []byte) error {
bundle := x509.NewCertPool()
bundle.AppendCertsFromPEM(ca)
opts := x509.VerifyOptions{
Roots: bundle,
}
_, err := pemCert.Verify(opts)
if err != nil {
return err
}
return nil
}
// CreateSSLCert validates cert and key, extracts common names and returns corresponding SSLCert object // CreateSSLCert validates cert and key, extracts common names and returns corresponding SSLCert object
func CreateSSLCert(cert, key []byte, uid string) (*ingress.SSLCert, error) { func CreateSSLCert(cert, key []byte, uid string) (*ingress.SSLCert, error) {
var pemCertBuffer bytes.Buffer var pemCertBuffer bytes.Buffer
@ -138,8 +125,12 @@ func CreateSSLCert(cert, key []byte, uid string) (*ingress.SSLCert, error) {
} }
} }
hasher := sha1.New()
hasher.Write(pemCert.Raw)
return &ingress.SSLCert{ return &ingress.SSLCert{
Certificate: pemCert, Certificate: pemCert,
PemSHA: hex.EncodeToString(hasher.Sum(nil)),
CN: cn.List(), CN: cn.List(),
ExpireTime: pemCert.NotAfter, ExpireTime: pemCert.NotAfter,
PemCertKey: pemCertBuffer.String(), PemCertKey: pemCertBuffer.String(),
@ -150,25 +141,41 @@ func CreateSSLCert(cert, key []byte, uid string) (*ingress.SSLCert, error) {
// CreateCACert is similar to CreateSSLCert but it creates instance of SSLCert only based on given ca after // CreateCACert is similar to CreateSSLCert but it creates instance of SSLCert only based on given ca after
// parsing and validating it // parsing and validating it
func CreateCACert(ca []byte) (*ingress.SSLCert, error) { func CreateCACert(ca []byte) (*ingress.SSLCert, error) {
pemCABlock, _ := pem.Decode(ca) caCert, err := CheckCACert(ca)
if pemCABlock == nil {
return nil, fmt.Errorf("no valid PEM formatted block found")
}
// If the first certificate does not start with 'BEGIN CERTIFICATE' it's invalid and must not be used.
if pemCABlock.Type != "CERTIFICATE" {
return nil, fmt.Errorf("no certificate PEM data found, make sure certificate content starts with 'BEGIN CERTIFICATE'")
}
pemCert, err := x509.ParseCertificate(pemCABlock.Bytes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &ingress.SSLCert{ return &ingress.SSLCert{
Certificate: pemCert, CACertificate: caCert,
}, nil }, nil
} }
// CheckCACert validates a byte array containing one or more CA certificate/s
func CheckCACert(caBytes []byte) ([]*x509.Certificate, error) {
certs := []*x509.Certificate{}
var block *pem.Block
for {
block, caBytes = pem.Decode(caBytes)
if block == nil {
break
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
certs = append(certs, cert)
}
if len(certs) == 0 {
return nil, fmt.Errorf("error decoding CA certificate/s")
}
return certs, nil
}
// StoreSSLCertOnDisk creates a .pem file with content PemCertKey from the given sslCert // StoreSSLCertOnDisk creates a .pem file with content PemCertKey from the given sslCert
// and sets relevant remaining fields of sslCert object // and sets relevant remaining fields of sslCert object
func StoreSSLCertOnDisk(name string, sslCert *ingress.SSLCert) (string, error) { func StoreSSLCertOnDisk(name string, sslCert *ingress.SSLCert) (string, error) {
@ -185,27 +192,21 @@ func StoreSSLCertOnDisk(name string, sslCert *ingress.SSLCert) (string, error) {
// ConfigureCACertWithCertAndKey appends ca into existing PEM file consisting of cert and key // ConfigureCACertWithCertAndKey appends ca into existing PEM file consisting of cert and key
// and sets relevant fields in sslCert object // and sets relevant fields in sslCert object
func ConfigureCACertWithCertAndKey(name string, ca []byte, sslCert *ingress.SSLCert) error { func ConfigureCACertWithCertAndKey(name string, ca []byte, sslCert *ingress.SSLCert) error {
err := verifyPemCertAgainstRootCA(sslCert.Certificate, ca)
if err != nil {
oe := fmt.Sprintf("failed to verify certificate chain: \n\t%s\n", err)
return errors.New(oe)
}
var buffer bytes.Buffer var buffer bytes.Buffer
_, err = buffer.Write([]byte(sslCert.PemCertKey)) _, err := buffer.Write([]byte(sslCert.PemCertKey))
if err != nil { if err != nil {
return fmt.Errorf("could not append newline to cert file %v: %v", sslCert.PemFileName, err) return fmt.Errorf("could not append newline to cert file %v: %v", sslCert.CAFileName, err)
} }
_, err = buffer.Write([]byte("\n")) _, err = buffer.Write([]byte("\n"))
if err != nil { if err != nil {
return fmt.Errorf("could not append newline to cert file %v: %v", sslCert.PemFileName, err) return fmt.Errorf("could not append newline to cert file %v: %v", sslCert.CAFileName, err)
} }
_, err = buffer.Write(ca) _, err = buffer.Write(ca)
if err != nil { if err != nil {
return fmt.Errorf("could not write ca data to cert file %v: %v", sslCert.PemFileName, err) return fmt.Errorf("could not write ca data to cert file %v: %v", sslCert.CAFileName, err)
} }
return ioutil.WriteFile(sslCert.CAFileName, buffer.Bytes(), 0644) return ioutil.WriteFile(sslCert.CAFileName, buffer.Bytes(), 0644)

View file

@ -176,7 +176,7 @@ func TestConfigureCACert(t *testing.T) {
if sslCert.CAFileName != "" { if sslCert.CAFileName != "" {
t.Fatalf("expected CAFileName to be empty") t.Fatalf("expected CAFileName to be empty")
} }
if sslCert.Certificate == nil { if sslCert.CACertificate == nil {
t.Fatalf("expected Certificate to be set") t.Fatalf("expected Certificate to be set")
} }
@ -221,7 +221,7 @@ fUNCdMGmr8FVF6IzTNYGmCuk/C4=
if sslCert.CRLFileName != "" { if sslCert.CRLFileName != "" {
t.Fatalf("expected CRLFileName to be empty") t.Fatalf("expected CRLFileName to be empty")
} }
if sslCert.Certificate == nil { if sslCert.CACertificate == nil {
t.Fatalf("expected Certificate to be set") t.Fatalf("expected Certificate to be set")
} }