Skip to content
This repository has been archived by the owner on Apr 17, 2019. It is now read-only.

[nginx-ingress-controller] Add ssl certificate checksum to template #1054

Merged
merged 1 commit into from
May 26, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 10 additions & 14 deletions ingress/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 ingress/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 ingress/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 ingress/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 ingress/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])
}
}