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
import (
"crypto/sha1"
"encoding/hex"
"fmt"
"strings"
@ -107,11 +105,17 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
}
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)
if err != nil {
return nil, fmt.Errorf("error while storing certificate and key: %v", err)
}
sslCert.CACertificate = caCert
sslCert.CAFileName = path
sslCert.CASHA = file.SHA1(path)
@ -125,7 +129,6 @@ func (s *k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error
if err != nil {
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.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
if secretName == s.defaultSSLCertificate {
path, err := ssl.StoreSSLCertOnDisk(nsSecName, sslCert)

View file

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

View file

@ -20,10 +20,12 @@ import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
@ -63,21 +65,6 @@ func getPemFileName(fullSecretName string) (string, string) {
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
func CreateSSLCert(cert, key []byte, uid string) (*ingress.SSLCert, error) {
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{
Certificate: pemCert,
PemSHA: hex.EncodeToString(hasher.Sum(nil)),
CN: cn.List(),
ExpireTime: pemCert.NotAfter,
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
// parsing and validating it
func CreateCACert(ca []byte) (*ingress.SSLCert, error) {
pemCABlock, _ := pem.Decode(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)
caCert, err := CheckCACert(ca)
if err != nil {
return nil, err
}
return &ingress.SSLCert{
Certificate: pemCert,
CACertificate: caCert,
}, 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
// and sets relevant remaining fields of sslCert object
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
// and sets relevant fields in sslCert object
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
_, err = buffer.Write([]byte(sslCert.PemCertKey))
_, err := buffer.Write([]byte(sslCert.PemCertKey))
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"))
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)
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)

View file

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