diff --git a/controllers/nginx-third-party/controller.go b/controllers/nginx-third-party/controller.go index f8031e55d..b3fb50650 100644 --- a/controllers/nginx-third-party/controller.go +++ b/controllers/nginx-third-party/controller.go @@ -396,10 +396,20 @@ func (lbc *loadBalancerController) getPemsFromIngress(data []interface{}) map[st continue } + cn, err := lbc.nginx.CheckSSLCertificate(secretName) + if err != nil { + glog.Warningf("No valid SSL certificate found in secret %v", secretName) + continue + } + pemFileName := lbc.nginx.AddOrUpdateCertAndKey(secretName, string(cert), string(key)) for _, host := range tls.Hosts { - pems[host] = pemFileName + if isHostValid(host, cn) { + pems[host] = pemFileName + } 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) + } } } } diff --git a/controllers/nginx-third-party/nginx/nginx.go b/controllers/nginx-third-party/nginx/nginx.go index 1ccdb21ef..c7278ab90 100644 --- a/controllers/nginx-third-party/nginx/nginx.go +++ b/controllers/nginx-third-party/nginx/nginx.go @@ -16,13 +16,6 @@ limitations under the License. package nginx -import ( - "fmt" - "os" - - "github.com/golang/glog" -) - // IngressNGINXConfig describes an NGINX configuration type IngressNGINXConfig struct { Upstreams []Upstream @@ -35,7 +28,7 @@ type Upstream struct { Backends []UpstreamServer } -// UpstreamByNameServers Upstream sorter by name +// UpstreamByNameServers sorts upstreams by name type UpstreamByNameServers []*Upstream func (c UpstreamByNameServers) Len() int { return len(c) } @@ -50,7 +43,7 @@ type UpstreamServer struct { Port string } -// UpstreamServerByAddrPort UpstreamServer sorter by address and port +// UpstreamServerByAddrPort sorts upstream servers by address and port type UpstreamServerByAddrPort []UpstreamServer func (c UpstreamServerByAddrPort) Len() int { return len(c) } @@ -76,7 +69,7 @@ type Server struct { SSLCertificateKey string } -// ServerByName Server sorter by name +// ServerByName sorts server by name type ServerByName []*Server func (c ServerByName) Len() int { return len(c) } @@ -91,7 +84,7 @@ type Location struct { Upstream Upstream } -// LocationByPath Location sorter by path +// LocationByPath sorts location by path type LocationByPath []Location func (c LocationByPath) Len() int { return len(c) } @@ -112,21 +105,3 @@ func NewUpstream(name string) *Upstream { Backends: []UpstreamServer{}, } } - -// AddOrUpdateCertAndKey creates a .pem file wth the cert and the key with the specified name -func (nginx *NginxManager) AddOrUpdateCertAndKey(name string, cert string, key string) string { - pemFileName := sslDirectory + "/" + name + ".pem" - - pem, err := os.Create(pemFileName) - if err != nil { - glog.Fatalf("Couldn't create pem file %v: %v", pemFileName, err) - } - defer pem.Close() - - _, err = pem.WriteString(fmt.Sprintf("%v\n%v", key, cert)) - if err != nil { - glog.Fatalf("Couldn't write to pem file %v: %v", pemFileName, err) - } - - return pemFileName -} diff --git a/controllers/nginx-third-party/nginx/ssl.go b/controllers/nginx-third-party/nginx/ssl.go new file mode 100644 index 000000000..b76b001f2 --- /dev/null +++ b/controllers/nginx-third-party/nginx/ssl.go @@ -0,0 +1,72 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package nginx + +import ( + "crypto/x509" + "encoding/pem" + "fmt" + "io/ioutil" + "os" + + "github.com/golang/glog" +) + +// AddOrUpdateCertAndKey creates a .pem file wth the cert and the key with the specified name +func (nginx *NginxManager) AddOrUpdateCertAndKey(name string, cert string, key string) string { + pemFileName := sslDirectory + "/" + name + ".pem" + + pem, err := os.Create(pemFileName) + if err != nil { + glog.Fatalf("Couldn't create pem file %v: %v", pemFileName, err) + } + defer pem.Close() + + _, err = pem.WriteString(fmt.Sprintf("%v\n%v", key, cert)) + if err != nil { + glog.Fatalf("Couldn't write to pem file %v: %v", pemFileName, err) + } + + return pemFileName +} + +// CheckSSLCertificate 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 *NginxManager) CheckSSLCertificate(secretName string) ([]string, error) { + pemFileName := sslDirectory + "/" + secretName + ".pem" + pemCerts, err := ioutil.ReadFile(pemFileName) + if err != nil { + return []string{}, err + } + + var block *pem.Block + block, pemCerts = pem.Decode(pemCerts) + + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return []string{}, err + } + + cn := []string{cert.Subject.CommonName} + if len(cert.DNSNames) > 0 { + cn = append(cn, cert.DNSNames...) + } + + glog.V(2).Infof("DNS %v %v\n", cn, len(cn)) + return cn, nil +} diff --git a/controllers/nginx-third-party/nginx/template.go b/controllers/nginx-third-party/nginx/template.go index 8d0a077a3..c334f6bcd 100644 --- a/controllers/nginx-third-party/nginx/template.go +++ b/controllers/nginx-third-party/nginx/template.go @@ -83,7 +83,7 @@ func (ngx *NginxManager) writeCfg(cfg *nginxConfiguration, upstreams []*Upstream return false, err } - changed, err := checkChanges(ngx.ConfigFile, buffer) + changed, err := ngx.needsReload(buffer) if err != nil { return false, err } diff --git a/controllers/nginx-third-party/nginx/utils.go b/controllers/nginx-third-party/nginx/utils.go index 9e0ccc723..c68939c81 100644 --- a/controllers/nginx-third-party/nginx/utils.go +++ b/controllers/nginx-third-party/nginx/utils.go @@ -120,7 +120,8 @@ func toMap(iface interface{}) (map[string]interface{}, bool) { return map[string]interface{}{}, false } -func checkChanges(filename string, data *bytes.Buffer) (bool, error) { +func (ngx *NginxManager) needsReload(data *bytes.Buffer) (bool, error) { + filename := ngx.ConfigFile in, err := os.Open(filename) if err != nil { return false, err @@ -141,7 +142,8 @@ func checkChanges(filename string, data *bytes.Buffer) (bool, error) { dData, err := diff(src, res) if err != nil { - return false, fmt.Errorf("computing diff: %s", err) + glog.Errorf("error computing diff: %s", err) + return true, nil } if glog.V(2) { diff --git a/controllers/nginx-third-party/utils.go b/controllers/nginx-third-party/utils.go index 314e4c7d7..a7ca3fac3 100644 --- a/controllers/nginx-third-party/utils.go +++ b/controllers/nginx-third-party/utils.go @@ -175,3 +175,40 @@ func getTCPServices(kubeClient *unversioned.Client, tcpServices string) []nginx. return svcs } + +func isHostValid(host string, cns []string) bool { + for _, cn := range cns { + if matchHostnames(cn, host) { + return true + } + } + + return false +} + +func matchHostnames(pattern, host string) bool { + host = strings.TrimSuffix(host, ".") + pattern = strings.TrimSuffix(pattern, ".") + + if len(pattern) == 0 || len(host) == 0 { + return false + } + + patternParts := strings.Split(pattern, ".") + hostParts := strings.Split(host, ".") + + if len(patternParts) != len(hostParts) { + return false + } + + for i, patternPart := range patternParts { + if i == 0 && patternPart == "*" { + continue + } + if patternPart != hostParts[i] { + return false + } + } + + return true +}