Skip to content

Commit

Permalink
Merge pull request kubernetes-retired#1054 from aledbf/cert-checksum
Browse files Browse the repository at this point in the history
[nginx-ingress-controller] Add ssl certificate checksum to template
  • Loading branch information
bprashanth committed May 26, 2016
2 parents d0401b3 + c4228a1 commit 23d16a4
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 30 deletions.
24 changes: 10 additions & 14 deletions controllers/nginx/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -733,20 +733,21 @@ func (lbc *loadBalancerController) createServers(data []interface{}) map[string]
servers[host] = &nginx.Server{Name: host, Locations: locs}
}

if pemFile, ok := pems[host]; ok {
if ngxCert, ok := pems[host]; ok {
server := servers[host]
server.SSL = true
server.SSLCertificate = pemFile
server.SSLCertificateKey = pemFile
server.SSLCertificate = ngxCert.PemFileName
server.SSLCertificateKey = ngxCert.PemFileName
server.SSLPemChecksum = ngxCert.PemSHA
}
}
}

return servers
}

func (lbc *loadBalancerController) getPemsFromIngress(data []interface{}) map[string]string {
pems := make(map[string]string)
func (lbc *loadBalancerController) getPemsFromIngress(data []interface{}) map[string]nginx.SSLCert {
pems := make(map[string]nginx.SSLCert)

for _, ingIf := range data {
ing := ingIf.(*extensions.Ingress)
Expand All @@ -769,12 +770,7 @@ func (lbc *loadBalancerController) getPemsFromIngress(data []interface{}) map[st
continue
}

pemFileName, err := lbc.nginx.AddOrUpdateCertAndKey(fmt.Sprintf("%v-%v", ing.Namespace, secretName), string(cert), string(key))
if err != nil {
glog.Errorf("No valid SSL certificate found in secret %v: %v", secretName, err)
continue
}
cn, err := lbc.nginx.CheckSSLCertificate(pemFileName)
ngxCert, err := lbc.nginx.AddOrUpdateCertAndKey(fmt.Sprintf("%v-%v", ing.Namespace, secretName), string(cert), string(key))
if err != nil {
glog.Errorf("No valid SSL certificate found in secret %v: %v", secretName, err)
continue
Expand All @@ -786,14 +782,14 @@ func (lbc *loadBalancerController) getPemsFromIngress(data []interface{}) map[st
continue
}

pems["_"] = pemFileName
pems["_"] = ngxCert
glog.Infof("Using the secret %v as source for the default SSL certificate", secretName)
continue
}

for _, host := range tls.Hosts {
if isHostValid(host, cn) {
pems[host] = pemFileName
if isHostValid(host, ngxCert.CN) {
pems[host] = ngxCert
} else {
glog.Warningf("SSL Certificate stored in secret %v is not valid for the host %v defined in the Ingress rule %v", secretName, host, ing.Name)
}
Expand Down
2 changes: 2 additions & 0 deletions controllers/nginx/nginx.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ http {
server {
listen 80{{ if $cfg.useProxyProtocol }} proxy_protocol{{ end }};
{{ if $server.SSL }}listen 443{{ if $cfg.useProxyProtocol }} proxy_protocol{{ end }} ssl http2;
{{/* comment PEM sha is required to detect changes in the generated configuration and force a reload */}}
# PEM sha: {{ $server.SSLPemChecksum }}
ssl_certificate {{ $server.SSLCertificate }};
ssl_certificate_key {{ $server.SSLCertificateKey }};{{ end }}

Expand Down
1 change: 1 addition & 0 deletions controllers/nginx/nginx/nginx.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ type Server struct {
SSL bool
SSLCertificate string
SSLCertificateKey string
SSLPemChecksum string
}

// ServerByName sorts server by name
Expand Down
49 changes: 43 additions & 6 deletions controllers/nginx/nginx/ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ limitations under the License.
package nginx

import (
"crypto/sha1"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"fmt"
"io/ioutil"
Expand All @@ -26,28 +28,52 @@ import (
"github.com/golang/glog"
)

// SSLCert describes a SSL certificate to be used in NGINX
type SSLCert struct {
CertFileName string
KeyFileName string
// PemFileName contains the path to the file with the certificate and key concatenated
PemFileName string
// PemSHA contains the sha1 of the pem file.
// This is used to detect changes in the secret that contains the certificates
PemSHA string
// CN contains all the common names defined in the SSL certificate
CN []string
}

// AddOrUpdateCertAndKey creates a .pem file wth the cert and the key with the specified name
func (nginx *Manager) AddOrUpdateCertAndKey(name string, cert string, key string) (string, error) {
func (nginx *Manager) AddOrUpdateCertAndKey(name string, cert string, key string) (SSLCert, error) {
pemFileName := sslDirectory + "/" + name + ".pem"

pem, err := os.Create(pemFileName)
if err != nil {
return "", fmt.Errorf("Couldn't create pem file %v: %v", pemFileName, err)
return SSLCert{}, fmt.Errorf("Couldn't create pem file %v: %v", pemFileName, err)
}
defer pem.Close()

_, err = pem.WriteString(fmt.Sprintf("%v\n%v", cert, key))
if err != nil {
return "", fmt.Errorf("Couldn't write to pem file %v: %v", pemFileName, err)
return SSLCert{}, fmt.Errorf("Couldn't write to pem file %v: %v", pemFileName, err)
}

cn, err := nginx.commonNames(pemFileName)
if err != nil {
return SSLCert{}, err
}

return pemFileName, nil
return SSLCert{
CertFileName: cert,
KeyFileName: key,
PemFileName: pemFileName,
PemSHA: nginx.pemSHA1(pemFileName),
CN: cn,
}, nil
}

// CheckSSLCertificate checks if the certificate and key file are valid
// commonNames checks if the certificate and key file are valid
// returning the result of the validation and the list of hostnames
// contained in the common name/s
func (nginx *Manager) CheckSSLCertificate(pemFileName string) ([]string, error) {
func (nginx *Manager) commonNames(pemFileName string) ([]string, error) {
pemCerts, err := ioutil.ReadFile(pemFileName)
if err != nil {
return []string{}, err
Expand Down Expand Up @@ -92,3 +118,14 @@ func (nginx *Manager) SearchDHParamFile(baseDir string) string {
glog.Warning("no file dhparam.pem found in secrets")
return ""
}

func (nginx *Manager) pemSHA1(filename string) string {
hasher := sha1.New()
s, err := ioutil.ReadFile(filename)
if err != nil {
return ""
}

hasher.Write(s)
return hex.EncodeToString(hasher.Sum(nil))
}
15 changes: 5 additions & 10 deletions controllers/nginx/nginx/ssl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,25 +44,20 @@ func TestAddOrUpdateCertAndKey(t *testing.T) {
ngx := &Manager{}

name := fmt.Sprintf("test-%v", time.Now().UnixNano())
pemPath, err := ngx.AddOrUpdateCertAndKey(name, string(dCrt), string(dKey))
ngxCert, err := ngx.AddOrUpdateCertAndKey(name, string(dCrt), string(dKey))
if err != nil {
t.Fatalf("unexpected error checking SSL certificate: %v", err)
}

if pemPath == "" {
if ngxCert.PemFileName == "" {
t.Fatalf("expected path to pem file but returned empty")
}

cnames, err := ngx.CheckSSLCertificate(pemPath)
if err != nil {
t.Fatalf("unexpected error checking SSL certificate: %v", err)
}

if len(cnames) == 0 {
if len(ngxCert.CN) == 0 {
t.Fatalf("expected at least one cname but none returned")
}

if cnames[0] != "echoheaders" {
t.Fatalf("expected cname echoheaders but %v returned", cnames[0])
if ngxCert.CN[0] != "echoheaders" {
t.Fatalf("expected cname echoheaders but %v returned", ngxCert.CN[0])
}
}

0 comments on commit 23d16a4

Please sign in to comment.