diff --git a/pkg/controller/config.go b/pkg/controller/config.go index f08dbd2b6..e559cb91d 100644 --- a/pkg/controller/config.go +++ b/pkg/controller/config.go @@ -31,6 +31,7 @@ import ( api "k8s.io/api/core/v1" "os" "regexp" + "sort" "strings" ) @@ -172,23 +173,25 @@ func (cfg *haConfig) createHAProxyServers() { isDefaultServer := server.Hostname == "_" isCACert := server.CertificateAuth.AuthSSLCert.CAFileName != "" haServer := types.HAProxyServer{ - IsDefaultServer: isDefaultServer, - IsCACert: isCACert, - UseHTTP: server.SSLCertificate == "" || !sslRedirect || isDefaultServer, - UseHTTPS: server.SSLCertificate != "" || isDefaultServer, - Hostname: server.Hostname, - HostnameLabel: labelizeHostname(server.Hostname), - HostnameSocket: sockHostname(labelizeHostname(server.Hostname)), - ACLLabel: labelizeACL(server.Hostname), - SSLCertificate: server.SSLCertificate, - SSLPemChecksum: server.SSLPemChecksum, - RootLocation: haRootLocation, - Locations: haLocations, - SSLRedirect: sslRedirect, - HSTS: serverHSTS(server), - HasRateLimit: serverHasRateLimit(server), - CertificateAuth: server.CertificateAuth, - Alias: server.Alias, + IsDefaultServer: isDefaultServer, + IsCACert: isCACert, + UseHTTP: server.SSLCertificate == "" || !sslRedirect || isDefaultServer, + UseHTTPS: server.SSLCertificate != "" || isDefaultServer, + Hostname: server.Hostname, + HostnameIsWildcard: idHasWildcard(server.Hostname), + HostnameLabel: labelizeHostname(server.Hostname), + HostnameSocket: sockHostname(labelizeHostname(server.Hostname)), + ACLLabel: labelizeACL(server.Hostname), + SSLCertificate: server.SSLCertificate, + SSLPemChecksum: server.SSLPemChecksum, + RootLocation: haRootLocation, + Locations: haLocations, + SSLRedirect: sslRedirect, + HSTS: serverHSTS(server), + HasRateLimit: serverHasRateLimit(server), + CertificateAuth: server.CertificateAuth, + Alias: server.Alias, + AliasIsRegex: idHasRegex(server.Alias), } if isDefaultServer { haDefaultServer = &haServer @@ -196,6 +199,23 @@ func (cfg *haConfig) createHAProxyServers() { haServers = append(haServers, &haServer) } } + sort.SliceStable(haServers, func(i, j int) bool { + // Move hosts without wildcard and alias without regex to the top, + // following are hosts without wildcard whose alias has regex, and + // finally with the least precedence are hosts with wildcards + a, b := 0, 0 + if haServers[i].HostnameIsWildcard { + a = 2 + } else if haServers[i].AliasIsRegex { + a = 1 + } + if haServers[j].HostnameIsWildcard { + b = 2 + } else if haServers[j].AliasIsRegex { + b = 1 + } + return a < b + }) cfg.haServers = haServers cfg.haDefaultServer = haDefaultServer } @@ -275,6 +295,19 @@ func sockHostname(hostname string) string { return hostname } +var ( + regexHasWildcard = regexp.MustCompile(`^\*\.`) + regexIsValidIdent = regexp.MustCompile(`^[a-zA-Z0-9\-.]+$`) +) + +func idHasWildcard(identifier string) bool { + return regexHasWildcard.MatchString(identifier) +} + +func idHasRegex(identifier string) bool { + return identifier != "" && !regexIsValidIdent.MatchString(identifier) +} + // This could be improved creating a list of auth secrets (or even configMaps) // on Ingress and saving usr(s)/pwd in auth.BasicDigest struct func (cfg *haConfig) createUserlists() { diff --git a/pkg/controller/template.go b/pkg/controller/template.go index 2eb20b36f..1eaa109bd 100644 --- a/pkg/controller/template.go +++ b/pkg/controller/template.go @@ -71,12 +71,6 @@ var funcMap = gotemplate.FuncMap{ rtn := regexp.MustCompile(`\.`).ReplaceAllLiteralString(hostname, "\\.") return "^" + rtn + "(:[0-9]+)?$" }, - "isWildcardHostname": func(identifier string) bool { - return regexp.MustCompile(`^\*\.`).MatchString(identifier) - }, - "isRegexHostname": func(identifier string) bool { - return !regexp.MustCompile(`^[a-zA-Z0-9\-.]+$`).MatchString(identifier) - }, "sizeSuffix": func(size string) string { value, err := utils.SizeSuffixToInt64(size) if err != nil { diff --git a/pkg/types/types.go b/pkg/types/types.go index 98ee29920..0f5684358 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -100,23 +100,25 @@ type ( // HAProxyServer and HAProxyLocation build some missing pieces // from ingress.Server used by HAProxy HAProxyServer struct { - IsDefaultServer bool `json:"isDefaultServer"` - IsCACert bool `json:"isCACert"` - UseHTTP bool `json:"useHTTP"` - UseHTTPS bool `json:"useHTTPS"` - Hostname string `json:"hostname"` - HostnameLabel string `json:"hostnameLabel"` - HostnameSocket string `json:"hostnameSocket"` - ACLLabel string `json:"aclLabel"` - SSLCertificate string `json:"sslCertificate"` - SSLPemChecksum string `json:"sslPemChecksum"` - RootLocation *HAProxyLocation `json:"defaultLocation"` - Locations []*HAProxyLocation `json:"locations,omitempty"` - SSLRedirect bool `json:"sslRedirect"` - HSTS *hsts.Config `json:"hsts"` - HasRateLimit bool `json:"hasRateLimit"` - CertificateAuth authtls.AuthSSLConfig `json:"certificateAuth,omitempty"` - Alias string `json:"alias,omitempty"` + IsDefaultServer bool `json:"isDefaultServer"` + IsCACert bool `json:"isCACert"` + UseHTTP bool `json:"useHTTP"` + UseHTTPS bool `json:"useHTTPS"` + Hostname string `json:"hostname"` + HostnameIsWildcard bool `json:"hostnameIsWildcard"` + HostnameLabel string `json:"hostnameLabel"` + HostnameSocket string `json:"hostnameSocket"` + ACLLabel string `json:"aclLabel"` + SSLCertificate string `json:"sslCertificate"` + SSLPemChecksum string `json:"sslPemChecksum"` + RootLocation *HAProxyLocation `json:"defaultLocation"` + Locations []*HAProxyLocation `json:"locations,omitempty"` + SSLRedirect bool `json:"sslRedirect"` + HSTS *hsts.Config `json:"hsts"` + HasRateLimit bool `json:"hasRateLimit"` + CertificateAuth authtls.AuthSSLConfig `json:"certificateAuth,omitempty"` + Alias string `json:"alias,omitempty"` + AliasIsRegex bool `json:"aliasIsRegex"` } // HAProxyLocation has location data as a HAProxy friendly syntax HAProxyLocation struct { diff --git a/rootfs/etc/haproxy/template/haproxy.tmpl b/rootfs/etc/haproxy/template/haproxy.tmpl index 672b99824..ecf10358b 100644 --- a/rootfs/etc/haproxy/template/haproxy.tmpl +++ b/rootfs/etc/haproxy/template/haproxy.tmpl @@ -399,13 +399,13 @@ frontend httpfront-{{ if $isShared }}shared-frontend{{ else if $isDefault }}defa {{- $fetch := .p3 }} {{- $needport := .p4 }} {{- if ne $server.ACLLabel "" }} -{{- if isWildcardHostname $server.Hostname }} +{{- if $server.HostnameIsWildcard }} acl {{ $server.ACLLabel }} {{ $fetch }} -m reg -i {{ hostnameRegex $server.Hostname }} {{- else }} acl {{ $server.ACLLabel }} {{ $fetch }} -i {{ $server.Hostname }}{{ if $needport }} {{ $server.Hostname }}:80 {{ $server.Hostname }}:443{{ if and $cfg.HTTPStoHTTPPort (ne $cfg.HTTPStoHTTPPort 80) }} {{ $server.Hostname }}:{{ $cfg.HTTPStoHTTPPort }}{{ end }}{{ end }} {{- end }} {{- if ne $server.Alias "" }} -{{- if isRegexHostname $server.Alias }} +{{- if $server.AliasIsRegex }} acl {{ $server.ACLLabel }} {{ $fetch }} -m reg -i '{{ aliasRegex $server.Alias }}' {{- else }} acl {{ $server.ACLLabel }} {{ $fetch }} -i {{ $server.Alias }}{{ if $needport }} {{ $server.Alias }}:80 {{ $server.Alias }}:443{{ end }}