Skip to content

Commit

Permalink
Merge pull request #382 from rikatz/ingress-fake-cert
Browse files Browse the repository at this point in the history
Ingress Fake Certificate generation
  • Loading branch information
aledbf authored Mar 7, 2017
2 parents da4203b + e107e2b commit 251990d
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 47 deletions.
21 changes: 1 addition & 20 deletions core/pkg/ingress/controller/backend_ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
31 changes: 20 additions & 11 deletions core/pkg/ingress/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package controller

import (
"fmt"
"os"
"reflect"
"sort"
"strconv"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}
}
Expand Down
70 changes: 54 additions & 16 deletions core/pkg/net/ssl/ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}

Expand All @@ -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 {
Expand All @@ -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
}

0 comments on commit 251990d

Please sign in to comment.