diff --git a/internal/ingress/controller/controller.go b/internal/ingress/controller/controller.go index 93acb2f530..895fc85706 100644 --- a/internal/ingress/controller/controller.go +++ b/internal/ingress/controller/controller.go @@ -1007,20 +1007,7 @@ func (n *NGINXController) createServers(data []*extensions.Ingress, continue } - tlsSecretName := "" - found := false - for _, tls := range ing.Spec.TLS { - if sets.NewString(tls.Hosts...).Has(host) { - tlsSecretName = tls.SecretName - found = true - break - } - } - - if !found { - // does not contains a TLS section but none of the host match - continue - } + tlsSecretName := extractTLSSecretName(host, ing, n.store.GetLocalSSLCert) if tlsSecretName == "" { glog.V(3).Infof("host %v is listed on tls section but secretName is empty. Using default cert", host) @@ -1174,3 +1161,41 @@ func (n *NGINXController) SetForceReload(shouldReload bool) { atomic.StoreInt32(&n.forceReload, 0) } } + +// extractTLSSecretName returns the name of the secret that +// contains a SSL certificate for a particular hostname. +// In case there is no match, an empty string is returned. +func extractTLSSecretName(host string, ing *extensions.Ingress, + getLocalSSLCert func(string) (*ingress.SSLCert, error)) string { + if ing == nil { + return "" + } + + for _, tls := range ing.Spec.TLS { + if sets.NewString(tls.Hosts...).Has(host) { + return tls.SecretName + } + } + + // contains a TLS section but none of the host match or there + // is no hosts in the TLS section. As last resort we valide + // the host against the certificate and we use it if is valid + for _, tls := range ing.Spec.TLS { + key := fmt.Sprintf("%v/%v", ing.Namespace, tls.SecretName) + cert, err := getLocalSSLCert(key) + if err != nil { + glog.Warningf("ssl certificate \"%v\" does not exist in local store", key) + continue + } + + if cert == nil { + continue + } + + if sets.NewString(cert.CN...).Has(host) { + return tls.SecretName + } + } + + return "" +} diff --git a/internal/ingress/controller/controller_test.go b/internal/ingress/controller/controller_test.go new file mode 100644 index 0000000000..e658884597 --- /dev/null +++ b/internal/ingress/controller/controller_test.go @@ -0,0 +1,131 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 controller + +import ( + "testing" + + extensions "k8s.io/api/extensions/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/ingress-nginx/internal/ingress" +) + +func TestExtractTLSSecretName(t *testing.T) { + tests := []struct { + host string + ingress *extensions.Ingress + fn func(string) (*ingress.SSLCert, error) + expName string + }{ + { + "foo.bar", + nil, + func(string) (*ingress.SSLCert, error) { + return nil, nil + }, + "", + }, + { + "foo.bar", + &extensions.Ingress{}, + func(string) (*ingress.SSLCert, error) { + return nil, nil + }, + "", + }, + { + "foo.bar", + &extensions.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: extensions.IngressSpec{ + TLS: []extensions.IngressTLS{ + {SecretName: "demo"}, + }, + Rules: []extensions.IngressRule{ + { + Host: "foo.bar", + }, + }, + }, + }, + func(string) (*ingress.SSLCert, error) { + return nil, nil + }, + "", + }, + { + "foo.bar", + &extensions.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: extensions.IngressSpec{ + TLS: []extensions.IngressTLS{ + {SecretName: "demo"}, + }, + Rules: []extensions.IngressRule{ + { + Host: "foo.bar", + }, + }, + }, + }, + func(string) (*ingress.SSLCert, error) { + return &ingress.SSLCert{ + CN: []string{"foo.bar", "example.com"}, + }, nil + }, + "demo", + }, + { + "foo.bar", + &extensions.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: extensions.IngressSpec{ + TLS: []extensions.IngressTLS{ + { + Hosts: []string{"foo.bar", "example.com"}, + SecretName: "demo", + }, + }, + Rules: []extensions.IngressRule{ + { + Host: "foo.bar", + }, + }, + }, + }, + func(string) (*ingress.SSLCert, error) { + return &ingress.SSLCert{ + CN: []string{"foo.bar", "example.com"}, + }, nil + }, + "demo", + }, + } + + for _, testCase := range tests { + name := extractTLSSecretName(testCase.host, testCase.ingress, testCase.fn) + if name != testCase.expName { + t.Errorf("expected %v as the name of the secret but got %v", testCase.expName, name) + } + } +}