From 60f4fbf4f25764dbd94b7f8146d927ddc684514d Mon Sep 17 00:00:00 2001 From: Manuel de Brito Fontes Date: Tue, 19 Jul 2016 11:10:18 -0400 Subject: [PATCH] Allow leading * in ingress hostname --- pkg/apis/extensions/validation/validation.go | 36 +++++++-- .../extensions/validation/validation_test.go | 76 +++++++++++++++++++ pkg/util/validation/validation.go | 21 +++++ pkg/util/validation/validation_test.go | 26 +++++++ 4 files changed, 151 insertions(+), 8 deletions(-) diff --git a/pkg/apis/extensions/validation/validation.go b/pkg/apis/extensions/validation/validation.go index 9476b98479c..a15fb523b21 100644 --- a/pkg/apis/extensions/validation/validation.go +++ b/pkg/apis/extensions/validation/validation.go @@ -323,6 +323,20 @@ func validateIngressTLS(spec *extensions.IngressSpec, fldPath *field.Path) field allErrs := field.ErrorList{} // TODO: Perform a more thorough validation of spec.TLS.Hosts that takes // the wildcard spec from RFC 6125 into account. + for _, itls := range spec.TLS { + for i, host := range itls.Hosts { + if strings.Contains(host, "*") { + for _, msg := range validation.IsWildcardDNS1123Subdomain(host) { + allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("hosts"), host, msg)) + } + continue + } + for _, msg := range validation.IsDNS1123Subdomain(host) { + allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("hosts"), host, msg)) + } + } + } + return allErrs } @@ -358,21 +372,27 @@ func ValidateIngressStatusUpdate(ingress, oldIngress *extensions.Ingress) field. return allErrs } -func validateIngressRules(IngressRules []extensions.IngressRule, fldPath *field.Path) field.ErrorList { +func validateIngressRules(ingressRules []extensions.IngressRule, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} - if len(IngressRules) == 0 { + if len(ingressRules) == 0 { return append(allErrs, field.Required(fldPath, "")) } - for i, ih := range IngressRules { + for i, ih := range ingressRules { if len(ih.Host) > 0 { - // TODO: Ports and ips are allowed in the host part of a url - // according to RFC 3986, consider allowing them. - for _, msg := range validation.IsDNS1123Subdomain(ih.Host) { - allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, msg)) - } if isIP := (net.ParseIP(ih.Host) != nil); isIP { allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, "must be a DNS name, not an IP address")) } + // TODO: Ports and ips are allowed in the host part of a url + // according to RFC 3986, consider allowing them. + if strings.Contains(ih.Host, "*") { + for _, msg := range validation.IsWildcardDNS1123Subdomain(ih.Host) { + allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, msg)) + } + continue + } + for _, msg := range validation.IsDNS1123Subdomain(ih.Host) { + allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, msg)) + } } allErrs = append(allErrs, validateIngressRuleValue(&ih.IngressRuleValue, fldPath.Index(0))...) } diff --git a/pkg/apis/extensions/validation/validation_test.go b/pkg/apis/extensions/validation/validation_test.go index 92fca26cf72..33d742d942a 100644 --- a/pkg/apis/extensions/validation/validation_test.go +++ b/pkg/apis/extensions/validation/validation_test.go @@ -796,6 +796,82 @@ func TestValidateIngress(t *testing.T) { errorCases[badPathErr] = badRegexPath errorCases[badHostIPErr] = badHostIP + wildcardHost := "foo.*.bar.com" + badWildcard := newValid() + badWildcard.Spec.Rules[0].Host = wildcardHost + badWildcardErr := fmt.Sprintf("spec.rules[0].host: Invalid value: '%v'", wildcardHost) + errorCases[badWildcardErr] = badWildcard + + for k, v := range errorCases { + errs := ValidateIngress(&v) + if len(errs) == 0 { + t.Errorf("expected failure for %q", k) + } else { + s := strings.Split(k, ":") + err := errs[0] + if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) { + t.Errorf("unexpected error: %q, expected: %q", err, k) + } + } + } +} + +func TestValidateIngressTLS(t *testing.T) { + defaultBackend := extensions.IngressBackend{ + ServiceName: "default-backend", + ServicePort: intstr.FromInt(80), + } + + newValid := func() extensions.Ingress { + return extensions.Ingress{ + ObjectMeta: api.ObjectMeta{ + Name: "foo", + Namespace: api.NamespaceDefault, + }, + Spec: extensions.IngressSpec{ + Backend: &extensions.IngressBackend{ + ServiceName: "default-backend", + ServicePort: intstr.FromInt(80), + }, + Rules: []extensions.IngressRule{ + { + Host: "foo.bar.com", + IngressRuleValue: extensions.IngressRuleValue{ + HTTP: &extensions.HTTPIngressRuleValue{ + Paths: []extensions.HTTPIngressPath{ + { + Path: "/foo", + Backend: defaultBackend, + }, + }, + }, + }, + }, + }, + }, + Status: extensions.IngressStatus{ + LoadBalancer: api.LoadBalancerStatus{ + Ingress: []api.LoadBalancerIngress{ + {IP: "127.0.0.1"}, + }, + }, + }, + } + } + + errorCases := map[string]extensions.Ingress{} + + wildcardHost := "foo.*.bar.com" + badWildcardTLS := newValid() + badWildcardTLS.Spec.Rules[0].Host = "*.foo.bar.com" + badWildcardTLS.Spec.TLS = []extensions.IngressTLS{ + { + Hosts: []string{wildcardHost}, + }, + } + badWildcardTLSErr := fmt.Sprintf("spec.tls[0].hosts: Invalid value: '%v'", wildcardHost) + errorCases[badWildcardTLSErr] = badWildcardTLS + for k, v := range errorCases { errs := ValidateIngress(&v) if len(errs) == 0 { diff --git a/pkg/util/validation/validation.go b/pkg/util/validation/validation.go index 7b66693d1c3..aac2357d74f 100644 --- a/pkg/util/validation/validation.go +++ b/pkg/util/validation/validation.go @@ -139,6 +139,27 @@ func IsDNS1035Label(value string) []string { return errs } +// wildcard definition - RFC 1034 section 4.3.3. +// examples: +// - valid: *.bar.com, *.foo.bar.com +// - invalid: *.*.bar.com, *.foo.*.com, *bar.com, f*.bar.com, * +const wildcardDNF1123SubdomainFmt = "\\*\\." + dns1123SubdomainFmt + +// IsWildcardDNS1123Subdomain tests for a string that conforms to the definition of a +// wildcard subdomain in DNS (RFC 1034 section 4.3.3). +func IsWildcardDNS1123Subdomain(value string) []string { + wildcardDNS1123SubdomainRegexp := regexp.MustCompile("^\\*\\." + dns1123SubdomainFmt + "$") + + var errs []string + if len(value) > DNS1123SubdomainMaxLength { + errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength)) + } + if !wildcardDNS1123SubdomainRegexp.MatchString(value) { + errs = append(errs, RegexError(wildcardDNF1123SubdomainFmt, "*.example.com")) + } + return errs +} + const cIdentifierFmt string = "[A-Za-z_][A-Za-z0-9_]*" var cIdentifierRegexp = regexp.MustCompile("^" + cIdentifierFmt + "$") diff --git a/pkg/util/validation/validation_test.go b/pkg/util/validation/validation_test.go index 587895d1fde..a0e512888f3 100644 --- a/pkg/util/validation/validation_test.go +++ b/pkg/util/validation/validation_test.go @@ -407,3 +407,29 @@ func TestIsConfigMapKey(t *testing.T) { } } } + +func TestIsWildcardDNS1123Subdomain(t *testing.T) { + goodValues := []string{ + "*.example.com", + "*.bar.com", + "*.foo.bar.com", + } + for _, val := range goodValues { + if errs := IsWildcardDNS1123Subdomain(val); len(errs) != 0 { + t.Errorf("expected no errors for %q: %v", val, errs) + } + } + + badValues := []string{ + "*.*.bar.com", + "*.foo.*.com", + "*bar.com", + "f*.bar.com", + "*", + } + for _, val := range badValues { + if errs := IsWildcardDNS1123Subdomain(val); len(errs) == 0 { + t.Errorf("expected errors for %q", val) + } + } +}