diff --git a/core/pkg/ingress/controller/backend_ssl.go b/core/pkg/ingress/controller/backend_ssl.go index 7a84a3d4f6..f67ff4743b 100644 --- a/core/pkg/ingress/controller/backend_ssl.go +++ b/core/pkg/ingress/controller/backend_ssl.go @@ -43,28 +43,9 @@ func (ic *GenericController) syncSecret(k interface{}) error { return fmt.Errorf("deferring sync till endpoints controller has synced") } - // check if the default certificate is configured - key := fmt.Sprintf("default/%v", defServerName) - _, exists := ic.sslCertTracker.Get(key) + var key string var cert *ingress.SSLCert var err error - if !exists { - if ic.cfg.DefaultSSLCertificate != "" { - cert, err = ic.getPemCertificate(ic.cfg.DefaultSSLCertificate) - if err != nil { - return err - } - } else { - defCert, defKey := ssl.GetFakeSSLCert() - cert, err = ssl.AddOrUpdateCertAndKey("system-snake-oil-certificate", defCert, defKey, []byte{}) - if err != nil { - return nil - } - } - cert.Name = defServerName - cert.Namespace = api.NamespaceDefault - ic.sslCertTracker.Add(key, cert) - } key = k.(string) diff --git a/core/pkg/ingress/controller/controller.go b/core/pkg/ingress/controller/controller.go index 04d69e305e..207861e982 100644 --- a/core/pkg/ingress/controller/controller.go +++ b/core/pkg/ingress/controller/controller.go @@ -18,6 +18,7 @@ package controller import ( "fmt" + "os" "reflect" "sort" "strconv" @@ -837,19 +838,30 @@ func (ic *GenericController) createServers(data []interface{}, CookiePath: bdef.ProxyCookiePath, } - // This adds the Default Certificate to Default Backend and also for vhosts missing the secret + // This adds the Default Certificate to Default Backend (or generates a new self signed one) var defaultPemFileName, defaultPemSHA string + + // Tries to fetch the default Certificate. If it does not exists, generate a new self signed one. defaultCertificate, err := ic.getPemCertificate(ic.cfg.DefaultSSLCertificate) - // If no default Certificate was supplied, tries to generate a new dumb one if err != nil { - var cert *ingress.SSLCert - defCert, defKey := ssl.GetFakeSSLCert() - cert, err = ssl.AddOrUpdateCertAndKey("system-snake-oil-certificate", defCert, defKey, []byte{}) + // This means the Default Secret does not exists, so we will create a new one. + fakeCertificate := "default-fake-certificate" + fakeCertificatePath := fmt.Sprintf("%v/%v.pem", ingress.DefaultSSLDirectory, fakeCertificate) + + // Only generates a new certificate if it doesn't exists physically + _, err := os.Stat(fakeCertificatePath) if err != nil { - glog.Fatalf("Error generating self signed certificate: %v", err) + glog.V(3).Infof("No Default SSL Certificate found. Generating a new one") + defCert, defKey := ssl.GetFakeSSLCert() + defaultCertificate, err = ssl.AddOrUpdateCertAndKey(fakeCertificate, defCert, defKey, []byte{}) + if err != nil { + glog.Fatalf("Error generating self signed certificate: %v", err) + } + defaultPemFileName = defaultCertificate.PemFileName + defaultPemSHA = defaultCertificate.PemSHA } else { - defaultPemFileName = cert.PemFileName - defaultPemSHA = cert.PemSHA + defaultPemFileName = fakeCertificatePath + defaultPemSHA = ssl.PemSHA1(fakeCertificatePath) } } else { defaultPemFileName = defaultCertificate.PemFileName @@ -944,9 +956,6 @@ func (ic *GenericController) createServers(data []interface{}, servers[host].SSLCertificate = cert.PemFileName servers[host].SSLPemChecksum = cert.PemSHA } - } else { - servers[host].SSLCertificate = defaultPemFileName - servers[host].SSLPemChecksum = defaultPemSHA } } } diff --git a/core/pkg/net/ssl/ssl.go b/core/pkg/net/ssl/ssl.go index ea492940f4..93811ecf36 100644 --- a/core/pkg/net/ssl/ssl.go +++ b/core/pkg/net/ssl/ssl.go @@ -17,14 +17,19 @@ limitations under the License. package ssl import ( + "crypto/rand" + "crypto/rsa" "crypto/sha1" "crypto/x509" + "crypto/x509/pkix" "encoding/hex" "encoding/pem" "errors" "fmt" "io/ioutil" + "math/big" "os" + "time" "github.com/golang/glog" @@ -63,21 +68,25 @@ func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert, pemCerts, err := ioutil.ReadFile(tempPemFile.Name()) if err != nil { + _ = os.Remove(tempPemFile.Name()) return nil, err } pemBlock, _ := pem.Decode(pemCerts) if pemBlock == nil { + _ = os.Remove(tempPemFile.Name()) return nil, fmt.Errorf("No valid PEM formatted block found") } // If the file does not start with 'BEGIN CERTIFICATE' it's invalid and must not be used. if pemBlock.Type != "CERTIFICATE" { + _ = os.Remove(tempPemFile.Name()) return nil, fmt.Errorf("Certificate %v contains invalid data, and must be created with 'kubectl create secret tls'", name) } pemCert, err := x509.ParseCertificate(pemBlock.Bytes) if err != nil { + _ = os.Remove(tempPemFile.Name()) return nil, err } @@ -120,14 +129,14 @@ func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert, return &ingress.SSLCert{ CAFileName: pemFileName, PemFileName: pemFileName, - PemSHA: pemSHA1(pemFileName), + PemSHA: PemSHA1(pemFileName), CN: cn, }, nil } return &ingress.SSLCert{ PemFileName: pemFileName, - PemSHA: pemSHA1(pemFileName), + PemSHA: PemSHA1(pemFileName), CN: cn, }, nil } @@ -162,7 +171,7 @@ func AddCertAuth(name string, ca []byte) (*ingress.SSLCert, error) { return &ingress.SSLCert{ CAFileName: caFileName, PemFileName: caFileName, - PemSHA: pemSHA1(caFileName), + PemSHA: PemSHA1(caFileName), }, nil } @@ -187,9 +196,9 @@ func SearchDHParamFile(baseDir string) string { return "" } -// pemSHA1 returns the SHA1 of a pem file. This is used to +// PemSHA1 returns the SHA1 of a pem file. This is used to // reload NGINX in case a secret with a SSL certificate changed. -func pemSHA1(filename string) string { +func PemSHA1(filename string) string { hasher := sha1.New() s, err := ioutil.ReadFile(filename) if err != nil { @@ -200,23 +209,52 @@ func pemSHA1(filename string) string { return hex.EncodeToString(hasher.Sum(nil)) } -const ( - snakeOilPem = "/etc/ssl/certs/ssl-cert-snakeoil.pem" - snakeOilKey = "/etc/ssl/private/ssl-cert-snakeoil.key" -) - -// GetFakeSSLCert returns the snake oil ssl certificate created by the command -// make-ssl-cert generate-default-snakeoil --force-overwrite +// GetFakeSSLCert creates a Self Signed Certificate +// Based in the code https://golang.org/src/crypto/tls/generate_cert.go func GetFakeSSLCert() ([]byte, []byte) { - cert, err := ioutil.ReadFile(snakeOilPem) + + var priv interface{} + var err error + + priv, err = rsa.GenerateKey(rand.Reader, 2048) + if err != nil { - return nil, nil + glog.Fatalf("failed to generate fake private key: %s", err) } - key, err := ioutil.ReadFile(snakeOilKey) + notBefore := time.Now() + // This certificate is valid for 365 days + notAfter := notBefore.Add(365 * 24 * time.Hour) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { - return nil, nil + glog.Fatalf("failed to generate fake serial number: %s", err) } + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Acme Co"}, + CommonName: "Kubernetes Ingress Controller Fake Certificate", + }, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + DNSNames: []string{"ingress.local"}, + } + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.(*rsa.PrivateKey).PublicKey, priv) + if err != nil { + glog.Fatalf("Failed to create fake certificate: %s", err) + } + + cert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + + key := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv.(*rsa.PrivateKey))}) + return cert, key }