From 6c1b45a663579ed100e4192ed63023b707074656 Mon Sep 17 00:00:00 2001 From: Ricardo Pchevuzinske Katz Date: Tue, 28 Feb 2017 21:11:16 -0300 Subject: [PATCH 1/3] Generates a Self signed certificate for default vhost if the secret doesn't exists Generates a Self signed certificate for default vhost if the secret doesn't exists modified: core/pkg/ingress/controller/backend_ssl.go modified: core/pkg/ingress/controller/controller.go modified: core/pkg/net/ssl/ssl.go --- core/pkg/ingress/controller/backend_ssl.go | 2 +- core/pkg/ingress/controller/controller.go | 22 +++++--- core/pkg/net/ssl/ssl.go | 66 ++++++++++++++++------ 3 files changed, 66 insertions(+), 24 deletions(-) diff --git a/core/pkg/ingress/controller/backend_ssl.go b/core/pkg/ingress/controller/backend_ssl.go index 7a84a3d4f6..35e7dd59f7 100644 --- a/core/pkg/ingress/controller/backend_ssl.go +++ b/core/pkg/ingress/controller/backend_ssl.go @@ -56,7 +56,7 @@ func (ic *GenericController) syncSecret(k interface{}) error { } } else { defCert, defKey := ssl.GetFakeSSLCert() - cert, err = ssl.AddOrUpdateCertAndKey("system-snake-oil-certificate", defCert, defKey, []byte{}) + cert, err = ssl.AddOrUpdateCertAndKey("default-fake-certificate", defCert, defKey, []byte{}) if err != nil { return nil } diff --git a/core/pkg/ingress/controller/controller.go b/core/pkg/ingress/controller/controller.go index 04d69e305e..e7759803f9 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" @@ -843,13 +844,23 @@ func (ic *GenericController) createServers(data []interface{}, // 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{}) + + 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) - } else { + defCert, defKey := ssl.GetFakeSSLCert() + cert, err = ssl.AddOrUpdateCertAndKey(fakeCertificate, defCert, defKey, []byte{}) + if err != nil { + glog.Fatalf("Error generating self signed certificate: %v", err) + } defaultPemFileName = cert.PemFileName defaultPemSHA = cert.PemSHA + } else { + defaultPemFileName = fakeCertificatePath + defaultPemSHA = ssl.PemSHA1(fakeCertificatePath) } } else { defaultPemFileName = defaultCertificate.PemFileName @@ -944,9 +955,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..cd5f57beb9 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" @@ -120,14 +125,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 +167,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 +192,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 +205,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 } From 51235a38e876209954d611c65b4df6a0865a66c6 Mon Sep 17 00:00:00 2001 From: Ricardo Pchevuzinske Katz Date: Mon, 6 Mar 2017 16:29:33 -0300 Subject: [PATCH 2/3] Removes wrong secret enqueing and improve the Fake Cert generation --- core/pkg/ingress/controller/backend_ssl.go | 21 +-------------------- core/pkg/ingress/controller/controller.go | 15 ++++++++------- core/pkg/net/ssl/ssl.go | 1 + 3 files changed, 10 insertions(+), 27 deletions(-) diff --git a/core/pkg/ingress/controller/backend_ssl.go b/core/pkg/ingress/controller/backend_ssl.go index 35e7dd59f7..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("default-fake-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 e7759803f9..207861e982 100644 --- a/core/pkg/ingress/controller/controller.go +++ b/core/pkg/ingress/controller/controller.go @@ -838,26 +838,27 @@ 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 - + // 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.V(3).Infof("No Default SSL Certificate found. Generating a new one") defCert, defKey := ssl.GetFakeSSLCert() - cert, err = ssl.AddOrUpdateCertAndKey(fakeCertificate, defCert, defKey, []byte{}) + defaultCertificate, err = ssl.AddOrUpdateCertAndKey(fakeCertificate, defCert, defKey, []byte{}) if err != nil { glog.Fatalf("Error generating self signed certificate: %v", err) } - defaultPemFileName = cert.PemFileName - defaultPemSHA = cert.PemSHA + defaultPemFileName = defaultCertificate.PemFileName + defaultPemSHA = defaultCertificate.PemSHA } else { defaultPemFileName = fakeCertificatePath defaultPemSHA = ssl.PemSHA1(fakeCertificatePath) diff --git a/core/pkg/net/ssl/ssl.go b/core/pkg/net/ssl/ssl.go index cd5f57beb9..b007d8bba7 100644 --- a/core/pkg/net/ssl/ssl.go +++ b/core/pkg/net/ssl/ssl.go @@ -78,6 +78,7 @@ func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert, // 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) } From e107e2b87f61ca537c5b9b1baaad3fcaa571a6c3 Mon Sep 17 00:00:00 2001 From: Ricardo Pchevuzinske Katz Date: Mon, 6 Mar 2017 16:33:44 -0300 Subject: [PATCH 3/3] Temporary PEM Files cleanup --- core/pkg/net/ssl/ssl.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/pkg/net/ssl/ssl.go b/core/pkg/net/ssl/ssl.go index b007d8bba7..93811ecf36 100644 --- a/core/pkg/net/ssl/ssl.go +++ b/core/pkg/net/ssl/ssl.go @@ -68,11 +68,13 @@ 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") } @@ -84,6 +86,7 @@ func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert, pemCert, err := x509.ParseCertificate(pemBlock.Bytes) if err != nil { + _ = os.Remove(tempPemFile.Name()) return nil, err }