Add ssl certificate checksum to template

This commit is contained in:
Manuel de Brito Fontes 2016-05-24 11:02:29 -03:00
parent 8a652e94f5
commit c4228a150f
5 changed files with 61 additions and 30 deletions

View file

@ -733,11 +733,12 @@ func (lbc *loadBalancerController) createServers(data []interface{}) map[string]
servers[host] = &nginx.Server{Name: host, Locations: locs} servers[host] = &nginx.Server{Name: host, Locations: locs}
} }
if pemFile, ok := pems[host]; ok { if ngxCert, ok := pems[host]; ok {
server := servers[host] server := servers[host]
server.SSL = true server.SSL = true
server.SSLCertificate = pemFile server.SSLCertificate = ngxCert.PemFileName
server.SSLCertificateKey = pemFile server.SSLCertificateKey = ngxCert.PemFileName
server.SSLPemChecksum = ngxCert.PemSHA
} }
} }
} }
@ -745,8 +746,8 @@ func (lbc *loadBalancerController) createServers(data []interface{}) map[string]
return servers return servers
} }
func (lbc *loadBalancerController) getPemsFromIngress(data []interface{}) map[string]string { func (lbc *loadBalancerController) getPemsFromIngress(data []interface{}) map[string]nginx.SSLCert {
pems := make(map[string]string) pems := make(map[string]nginx.SSLCert)
for _, ingIf := range data { for _, ingIf := range data {
ing := ingIf.(*extensions.Ingress) ing := ingIf.(*extensions.Ingress)
@ -769,12 +770,7 @@ func (lbc *loadBalancerController) getPemsFromIngress(data []interface{}) map[st
continue continue
} }
pemFileName, err := lbc.nginx.AddOrUpdateCertAndKey(fmt.Sprintf("%v-%v", ing.Namespace, secretName), string(cert), string(key)) 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
}
cn, err := lbc.nginx.CheckSSLCertificate(pemFileName)
if err != nil { if err != nil {
glog.Errorf("No valid SSL certificate found in secret %v: %v", secretName, err) glog.Errorf("No valid SSL certificate found in secret %v: %v", secretName, err)
continue continue
@ -786,14 +782,14 @@ func (lbc *loadBalancerController) getPemsFromIngress(data []interface{}) map[st
continue continue
} }
pems["_"] = pemFileName pems["_"] = ngxCert
glog.Infof("Using the secret %v as source for the default SSL certificate", secretName) glog.Infof("Using the secret %v as source for the default SSL certificate", secretName)
continue continue
} }
for _, host := range tls.Hosts { for _, host := range tls.Hosts {
if isHostValid(host, cn) { if isHostValid(host, ngxCert.CN) {
pems[host] = pemFileName pems[host] = ngxCert
} else { } 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) 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)
} }

View file

@ -155,6 +155,8 @@ http {
server { server {
listen 80{{ if $cfg.useProxyProtocol }} proxy_protocol{{ end }}; listen 80{{ if $cfg.useProxyProtocol }} proxy_protocol{{ end }};
{{ if $server.SSL }}listen 443{{ if $cfg.useProxyProtocol }} proxy_protocol{{ end }} ssl http2; {{ 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 {{ $server.SSLCertificate }};
ssl_certificate_key {{ $server.SSLCertificateKey }};{{ end }} ssl_certificate_key {{ $server.SSLCertificateKey }};{{ end }}

View file

@ -69,6 +69,7 @@ type Server struct {
SSL bool SSL bool
SSLCertificate string SSLCertificate string
SSLCertificateKey string SSLCertificateKey string
SSLPemChecksum string
} }
// ServerByName sorts server by name // ServerByName sorts server by name

View file

@ -17,7 +17,9 @@ limitations under the License.
package nginx package nginx
import ( import (
"crypto/sha1"
"crypto/x509" "crypto/x509"
"encoding/hex"
"encoding/pem" "encoding/pem"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -26,28 +28,52 @@ import (
"github.com/golang/glog" "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 // 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" pemFileName := sslDirectory + "/" + name + ".pem"
pem, err := os.Create(pemFileName) pem, err := os.Create(pemFileName)
if err != nil { 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() defer pem.Close()
_, err = pem.WriteString(fmt.Sprintf("%v\n%v", cert, key)) _, err = pem.WriteString(fmt.Sprintf("%v\n%v", cert, key))
if err != nil { 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)
} }
return pemFileName, nil cn, err := nginx.commonNames(pemFileName)
if err != nil {
return SSLCert{}, err
} }
// CheckSSLCertificate checks if the certificate and key file are valid return SSLCert{
CertFileName: cert,
KeyFileName: key,
PemFileName: pemFileName,
PemSHA: nginx.pemSHA1(pemFileName),
CN: cn,
}, nil
}
// commonNames checks if the certificate and key file are valid
// returning the result of the validation and the list of hostnames // returning the result of the validation and the list of hostnames
// contained in the common name/s // 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) pemCerts, err := ioutil.ReadFile(pemFileName)
if err != nil { if err != nil {
return []string{}, err return []string{}, err
@ -92,3 +118,14 @@ func (nginx *Manager) SearchDHParamFile(baseDir string) string {
glog.Warning("no file dhparam.pem found in secrets") glog.Warning("no file dhparam.pem found in secrets")
return "" 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))
}

View file

@ -44,25 +44,20 @@ func TestAddOrUpdateCertAndKey(t *testing.T) {
ngx := &Manager{} ngx := &Manager{}
name := fmt.Sprintf("test-%v", time.Now().UnixNano()) 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 { if err != nil {
t.Fatalf("unexpected error checking SSL certificate: %v", err) t.Fatalf("unexpected error checking SSL certificate: %v", err)
} }
if pemPath == "" { if ngxCert.PemFileName == "" {
t.Fatalf("expected path to pem file but returned empty") t.Fatalf("expected path to pem file but returned empty")
} }
cnames, err := ngx.CheckSSLCertificate(pemPath) if len(ngxCert.CN) == 0 {
if err != nil {
t.Fatalf("unexpected error checking SSL certificate: %v", err)
}
if len(cnames) == 0 {
t.Fatalf("expected at least one cname but none returned") t.Fatalf("expected at least one cname but none returned")
} }
if cnames[0] != "echoheaders" { if ngxCert.CN[0] != "echoheaders" {
t.Fatalf("expected cname echoheaders but %v returned", cnames[0]) t.Fatalf("expected cname echoheaders but %v returned", ngxCert.CN[0])
} }
} }