From 04ed2d9a481eff534fbcfa84b3bf0979a0e511b1 Mon Sep 17 00:00:00 2001 From: Manuel de Brito Fontes Date: Sat, 21 Apr 2018 18:25:53 -0300 Subject: [PATCH] Allow tls section without hosts in Ingress rule --- internal/ingress/controller/controller.go | 53 +++++-- .../ingress/controller/controller_test.go | 131 ++++++++++++++++++ 2 files changed, 170 insertions(+), 14 deletions(-) create mode 100644 internal/ingress/controller/controller_test.go diff --git a/internal/ingress/controller/controller.go b/internal/ingress/controller/controller.go index 93acb2f53..895fc8570 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 000000000..e65888459 --- /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) + } + } +}