diff --git a/pkg/converters/ingress/annotations/backend.go b/pkg/converters/ingress/annotations/backend.go index 0e255414f..fcd5b7df4 100644 --- a/pkg/converters/ingress/annotations/backend.go +++ b/pkg/converters/ingress/annotations/backend.go @@ -563,7 +563,7 @@ func (c *updater) buildBackendOAuth(d *backData) { } func (c *updater) findBackend(namespace, uriPrefix string) *hatypes.HostBackend { - for _, host := range c.haproxy.Hosts() { + for _, host := range c.haproxy.Hosts().Items { for _, path := range host.Paths { if strings.TrimRight(path.Path, "/") == uriPrefix && path.Backend.Namespace == namespace { return &path.Backend diff --git a/pkg/converters/ingress/annotations/backend_test.go b/pkg/converters/ingress/annotations/backend_test.go index 0057b5ecf..5157949c3 100644 --- a/pkg/converters/ingress/annotations/backend_test.go +++ b/pkg/converters/ingress/annotations/backend_test.go @@ -1148,7 +1148,7 @@ func TestOAuth(t *testing.T) { if test.backend != "" { b := strings.Split(test.backend, ":") backend := c.haproxy.AcquireBackend(b[0], b[1], "8080") - c.haproxy.AcquireHost("app.local").AddPath(backend, b[2]) + c.haproxy.Hosts().AcquireHost("app.local").AddPath(backend, b[2]) } c.createUpdater().buildBackendOAuth(d) c.compareObjects("oauth", i, d.backend.OAuth, test.oauthExp) diff --git a/pkg/converters/ingress/ingress.go b/pkg/converters/ingress/ingress.go index 4c863945a..5775ccca3 100644 --- a/pkg/converters/ingress/ingress.go +++ b/pkg/converters/ingress/ingress.go @@ -180,7 +180,7 @@ func (c *converter) syncIngress(ing *extensions.Ingress) { func (c *converter) syncAnnotations() { c.updater.UpdateGlobalConfig(c.haproxy, c.globalConfig) - for _, host := range c.haproxy.Hosts() { + for _, host := range c.haproxy.Hosts().Items { if ann, found := c.hostAnnotations[host]; found { c.updater.UpdateHostConfig(host, ann) } @@ -195,7 +195,7 @@ func (c *converter) syncAnnotations() { func (c *converter) addDefaultHostBackend(source *annotations.Source, fullSvcName, svcPort string, annHost, annBack map[string]string) error { hostname := "*" uri := "/" - if fr := c.haproxy.FindHost(hostname); fr != nil { + if fr := c.haproxy.Hosts().FindHost(hostname); fr != nil { if fr.FindPath(uri) != nil { return fmt.Errorf("path %s was already defined on default host", uri) } @@ -210,7 +210,7 @@ func (c *converter) addDefaultHostBackend(source *annotations.Source, fullSvcNam } func (c *converter) addHost(hostname string, source *annotations.Source, ann map[string]string) *hatypes.Host { - host := c.haproxy.AcquireHost(hostname) + host := c.haproxy.Hosts().AcquireHost(hostname) mapper, found := c.hostAnnotations[host] if !found { mapper = c.mapBuilder.NewMapper() diff --git a/pkg/converters/ingress/ingress_test.go b/pkg/converters/ingress/ingress_test.go index b5934f2d0..9d857db31 100644 --- a/pkg/converters/ingress/ingress_test.go +++ b/pkg/converters/ingress/ingress_test.go @@ -1362,11 +1362,11 @@ func convertHost(hafronts ...*hatypes.Host) []hostMock { } func (c *testConfig) compareConfigFront(expected string) { - c.compareText(_yamlMarshal(convertHost(c.hconfig.Hosts()...)), expected) + c.compareText(_yamlMarshal(convertHost(c.hconfig.Hosts().Items...)), expected) } func (c *testConfig) compareConfigDefaultFront(expected string) { - host := c.hconfig.DefaultHost() + host := c.hconfig.Hosts().DefaultHost() if host != nil { c.compareText(_yamlMarshal(convertHost(host)[0]), expected) } else { diff --git a/pkg/haproxy/config.go b/pkg/haproxy/config.go index 00e92aa55..83a1fc3ef 100644 --- a/pkg/haproxy/config.go +++ b/pkg/haproxy/config.go @@ -21,7 +21,6 @@ import ( "reflect" "sort" "strconv" - "strings" "github.com/jcmoraisjr/haproxy-ingress/pkg/haproxy/template" hatypes "github.com/jcmoraisjr/haproxy-ingress/pkg/haproxy/types" @@ -30,24 +29,22 @@ import ( // Config ... type Config interface { AcquireTCPBackend(servicename string, port int) *hatypes.TCPBackend - AcquireHost(hostname string) *hatypes.Host - FindHost(hostname string) *hatypes.Host AcquireBackend(namespace, name, port string) *hatypes.Backend FindBackend(namespace, name, port string) *hatypes.Backend ConfigDefaultBackend(defaultBackend *hatypes.Backend) ConfigDefaultX509Cert(filename string) AddUserlist(name string, users []hatypes.User) *hatypes.Userlist FindUserlist(name string) *hatypes.Userlist - FrontendGroup() *hatypes.FrontendGroup - BuildFrontendGroup() error - BuildBackendMaps() error - DefaultHost() *hatypes.Host + Frontend() *hatypes.Frontend + SyncConfig() + WriteFrontendMaps() error + WriteBackendMaps() error DefaultBackend() *hatypes.Backend AcmeData() *hatypes.AcmeData Acme() *hatypes.Acme Global() *hatypes.Global TCPBackends() []*hatypes.TCPBackend - Hosts() []*hatypes.Host + Hosts() *hatypes.Hosts Backends() []*hatypes.Backend Userlists() []*hatypes.Userlist Equals(other Config) bool @@ -58,15 +55,14 @@ type config struct { acmeData *hatypes.AcmeData // haproxy internal state acme *hatypes.Acme - fgroup *hatypes.FrontendGroup mapsTemplate *template.Config mapsDir string global *hatypes.Global + frontend *hatypes.Frontend + hosts *hatypes.Hosts tcpbackends []*hatypes.TCPBackend - hosts []*hatypes.Host backends []*hatypes.Backend userlists []*hatypes.Userlist - defaultHost *hatypes.Host defaultBackend *hatypes.Backend defaultX509Cert string } @@ -85,6 +81,8 @@ func createConfig(options options) *config { acmeData: &hatypes.AcmeData{}, acme: &hatypes.Acme{}, global: &hatypes.Global{}, + frontend: &hatypes.Frontend{Name: "_front001"}, + hosts: &hatypes.Hosts{}, mapsTemplate: mapsTemplate, mapsDir: options.mapsDir, } @@ -112,40 +110,6 @@ func (c *config) AcquireTCPBackend(servicename string, port int) *hatypes.TCPBac return backend } -func (c *config) AcquireHost(hostname string) *hatypes.Host { - if host := c.FindHost(hostname); host != nil { - return host - } - host := createHost(hostname) - if host.Hostname != "*" { - c.hosts = append(c.hosts, host) - sort.Slice(c.hosts, func(i, j int) bool { - return c.hosts[i].Hostname < c.hosts[j].Hostname - }) - } else { - c.defaultHost = host - } - return host -} - -func (c *config) FindHost(hostname string) *hatypes.Host { - if hostname == "*" && c.defaultHost != nil { - return c.defaultHost - } - for _, f := range c.hosts { - if f.Hostname == hostname { - return f - } - } - return nil -} - -func createHost(hostname string) *hatypes.Host { - return &hatypes.Host{ - Hostname: hostname, - } -} - func (c *config) sortBackends() { sort.Slice(c.backends, func(i, j int) bool { if c.backends[i] == c.defaultBackend { @@ -227,233 +191,237 @@ func (c *config) FindUserlist(name string) *hatypes.Userlist { return nil } -func (c *config) FrontendGroup() *hatypes.FrontendGroup { - return c.fgroup +func (c *config) Frontend() *hatypes.Frontend { + return c.frontend } -func (c *config) BuildFrontendGroup() error { - // tested thanks to instance_test templating tests - // ideas to make a nice test or a nice refactor are welcome - frontends, sslpassthrough, defaultBind := hatypes.BuildRawFrontends(c.hosts) - fgroupMaps := hatypes.CreateMaps() - fgroup := &hatypes.FrontendGroup{ - Frontends: frontends, - HasSSLPassthrough: len(sslpassthrough) > 0, - DefaultBind: defaultBind, - Maps: fgroupMaps, - HTTPFrontsMap: fgroupMaps.AddMap(c.mapsDir + "/_global_http_front.map"), - HTTPRootRedirMap: fgroupMaps.AddMap(c.mapsDir + "/_global_http_root_redir.map"), - HTTPSRedirMap: fgroupMaps.AddMap(c.mapsDir + "/_global_https_redir.map"), - SSLPassthroughMap: fgroupMaps.AddMap(c.mapsDir + "/_global_sslpassthrough.map"), - VarNamespaceMap: fgroupMaps.AddMap(c.mapsDir + "/_global_k8s_ns.map"), - } - if c.global.Bind.HasFrontingProxy() { - bind := hatypes.NewFrontendBind(nil) - bind.Socket = c.global.Bind.FrontingBind - bind.ID = c.global.Bind.FrontingSockID - bind.AcceptProxy = c.global.Bind.AcceptProxy - fgroup.ToHTTPBind = bind - } - if fgroup.HasTCPProxy() { - // More than one HAProxy's frontend or bind, or using ssl-passthrough config, - // so need a `mode tcp` frontend with `inspect-delay` and `req.ssl_sni` - var i int - for _, frontend := range frontends { - i++ - bindName := fmt.Sprintf("%s_socket", frontend.Name) - bind := &frontend.Bind - bind.Name = bindName - bind.Socket = fmt.Sprintf("unix@/var/run/%s.sock", bindName) - bind.AcceptProxy = true - bind.ALPN = c.global.SSL.ALPN - } +// SyncConfig does final synchronization, just before write +// maps and config files to disk. These tasks should be done +// during ingress, services and endpoint parsing, but most of +// them need to start after all objects are parsed. +func (c *config) SyncConfig() { + if c.hosts.HasSSLPassthrough() { + // using ssl-passthrough config, so need a `mode tcp` + // frontend with `inspect-delay` and `req.ssl_sni` + bindName := fmt.Sprintf("%s_socket", c.frontend.Name) + c.frontend.BindName = bindName + c.frontend.BindSocket = fmt.Sprintf("unix@/var/run/%s.sock", bindName) + c.frontend.AcceptProxy = true } else { // One single HAProxy's frontend and bind - bind := &frontends[0].Bind - bind.Name = "_public" - bind.Socket = c.global.Bind.HTTPSBind - bind.AcceptProxy = c.global.Bind.AcceptProxy - bind.ALPN = c.global.SSL.ALPN + c.frontend.BindName = "_public" + c.frontend.BindSocket = c.global.Bind.HTTPSBind + c.frontend.AcceptProxy = c.global.Bind.AcceptProxy + } + for _, host := range c.hosts.Items { + if host.SSLPassthrough { + // no action if ssl-passthrough + continue + } + if host.HasTLSAuth() { + for _, path := range host.Paths { + backend := c.FindBackend(path.Backend.Namespace, path.Backend.Name, path.Backend.Port) + if backend != nil { + backend.TLS.HasTLSAuth = true + } + } + } + if c.global.StrictHost && host.FindPath("/") == nil { + var back *hatypes.Backend + defaultHost := c.hosts.DefaultHost() + if defaultHost != nil { + if path := defaultHost.FindPath("/"); path != nil { + hback := path.Backend + back = c.FindBackend(hback.Namespace, hback.Name, hback.Port) + } + } + if back == nil { + // TODO c.defaultBackend can be nil; create a valid + // _error404 backend, remove `if nil` from host.AddPath() + // and from `for range host.Paths` on map building. + back = c.defaultBackend + } + host.AddPath(back, "/") + } } - for _, frontend := range frontends { - mapsPrefix := c.mapsDir + "/" + frontend.Name - frontend.Maps = hatypes.CreateMaps() - frontend.HostBackendsMap = frontend.Maps.AddMap(mapsPrefix + "_host.map") - frontend.RootRedirMap = frontend.Maps.AddMap(mapsPrefix + "_root_redir.map") - frontend.MaxBodySizeMap = frontend.Maps.AddMap(mapsPrefix + "_max_body_size.map") - frontend.SNIBackendsMap = frontend.Maps.AddMap(mapsPrefix + "_sni.map") - frontend.TLSInvalidCrtErrorList = frontend.Maps.AddMap(mapsPrefix + "_inv_crt.list") - frontend.TLSInvalidCrtErrorPagesMap = frontend.Maps.AddMap(mapsPrefix + "_inv_crt_redir.map") - frontend.TLSNoCrtErrorList = frontend.Maps.AddMap(mapsPrefix + "_no_crt.list") - frontend.TLSNoCrtErrorPagesMap = frontend.Maps.AddMap(mapsPrefix + "_no_crt_redir.map") - frontend.Bind.CrtList = frontend.Maps.AddMap(mapsPrefix + "_bind_crt.list") - frontend.Bind.UseServerList = frontend.Maps.AddMap(mapsPrefix + "_use_server.list") +} + +// WriteFrontendMaps reads the model and writes haproxy's maps +// used in the frontend. Should be called before write the main +// config file. This func doesn't change model state, except the +// link to the frontend maps. +func (c *config) WriteFrontendMaps() error { + mapBuilder := hatypes.CreateMaps() + fmaps := &hatypes.FrontendMaps{ + HTTPFrontsMap: mapBuilder.AddMap(c.mapsDir + "/_global_http_front.map"), + HTTPRootRedirMap: mapBuilder.AddMap(c.mapsDir + "/_global_http_root_redir.map"), + HTTPSRedirMap: mapBuilder.AddMap(c.mapsDir + "/_global_https_redir.map"), + SSLPassthroughMap: mapBuilder.AddMap(c.mapsDir + "/_global_sslpassthrough.map"), + VarNamespaceMap: mapBuilder.AddMap(c.mapsDir + "/_global_k8s_ns.map"), + // + HostBackendsMap: mapBuilder.AddMap(c.mapsDir + "/_front001_host.map"), + RootRedirMap: mapBuilder.AddMap(c.mapsDir + "/_front001_root_redir.map"), + MaxBodySizeMap: mapBuilder.AddMap(c.mapsDir + "/_front001_max_body_size.map"), + SNIBackendsMap: mapBuilder.AddMap(c.mapsDir + "/_front001_sni.map"), + TLSInvalidCrtErrorList: mapBuilder.AddMap(c.mapsDir + "/_front001_inv_crt.list"), + TLSInvalidCrtErrorPagesMap: mapBuilder.AddMap(c.mapsDir + "/_front001_inv_crt_redir.map"), + TLSNoCrtErrorList: mapBuilder.AddMap(c.mapsDir + "/_front001_no_crt.list"), + TLSNoCrtErrorPagesMap: mapBuilder.AddMap(c.mapsDir + "/_front001_no_crt_redir.map"), + // + CrtList: mapBuilder.AddMap(c.mapsDir + "/_front001_bind_crt.list"), + UseServerList: mapBuilder.AddMap(c.mapsDir + "/_front001_use_server.list"), } + fmaps.CrtList.AppendItem(c.defaultX509Cert) // Some maps use yes/no answers instead of a list with found/missing keys // This approach avoid overlap: // 1. match with path_beg/map_beg, /path has a feature and a declared /path/sub doesn't have // 2. *.host.domain wildcard/alias/alias-regex has a feature and a declared sub.host.domain doesn't have yesno := map[bool]string{true: "yes", false: "no"} - for _, sslpassHost := range sslpassthrough { - rootPath := sslpassHost.FindPath("/") - if rootPath == nil { - return fmt.Errorf("missing root path on host %s", sslpassHost.Hostname) - } - fgroup.SSLPassthroughMap.AppendHostname(sslpassHost.Hostname, rootPath.Backend.ID) - fgroup.HTTPSRedirMap.AppendHostname(sslpassHost.Hostname+"/", yesno[sslpassHost.HTTPPassthroughBackend == ""]) - if sslpassHost.HTTPPassthroughBackend != "" { - fgroup.HTTPFrontsMap.AppendHostname(sslpassHost.Hostname+"/", sslpassHost.HTTPPassthroughBackend) + for _, host := range c.hosts.Items { + if host.SSLPassthrough { + rootPath := host.FindPath("/") + if rootPath == nil { + // Cannot use this hostname if the root path wasn't declared. + // Silently skipping beucase we have not a logger here. + // However this skip should never happen because root path + // validation already happens in the annotation parsing phase. + continue + } + fmaps.SSLPassthroughMap.AppendHostname(host.Hostname, rootPath.Backend.ID) + fmaps.HTTPSRedirMap.AppendHostname(host.Hostname+"/", yesno[host.HTTPPassthroughBackend == ""]) + if host.HTTPPassthroughBackend != "" { + fmaps.HTTPFrontsMap.AppendHostname(host.Hostname+"/", host.HTTPPassthroughBackend) + } + // ssl-passthrough is as simple as that, jump to the next host + continue } - } - for _, f := range frontends { - for _, host := range f.Hosts { - if c.global.StrictHost && host.FindPath("/") == nil { - var back *hatypes.Backend - if c.defaultHost != nil { - if path := c.defaultHost.FindPath("/"); path != nil { - hback := path.Backend - back = c.FindBackend(hback.Namespace, hback.Name, hback.Port) - } - } - if back == nil { - // TODO c.defaultBackend can be nil; create a valid - // _error404 backend, remove `if nil` from host.AddPath() - // and from `for range host.Paths` below - back = c.defaultBackend - } - host.AddPath(back, "/") + // + // Starting here to the end of this for loop has only HTTP/L7 map configuration + // + // TODO implement deny 413 and move all MaxBodySize stuff to backend + maxBodySizes := map[string]int64{} + for _, path := range host.Paths { + backend := c.FindBackend(path.Backend.Namespace, path.Backend.Name, path.Backend.Port) + base := host.Hostname + path.Path + hasSSLRedirect := false + if host.TLS.HasTLS() && backend != nil { + hasSSLRedirect = backend.HasSSLRedirectHostpath(base) } - // TODO implement deny 413 and move all MaxBodySize stuff to backend - maxBodySizes := map[string]int64{} - for _, path := range host.Paths { - backend := c.FindBackend(path.Backend.Namespace, path.Backend.Name, path.Backend.Port) - base := host.Hostname + path.Path - hasSSLRedirect := false - if host.TLS.HasTLS() && backend != nil { - hasSSLRedirect = backend.HasSSLRedirectHostpath(base) - } - // TODO use only root path if all uri has the same conf - fgroup.HTTPSRedirMap.AppendHostname(host.Hostname+path.Path, yesno[hasSSLRedirect]) - var aliasName, aliasRegex string - // TODO warn in logs about ignoring alias name due to hostname colision - if host.Alias.AliasName != "" && c.FindHost(host.Alias.AliasName) == nil { - aliasName = host.Alias.AliasName + path.Path - } - if host.Alias.AliasRegex != "" { - aliasRegex = host.Alias.AliasRegex + path.Path - } - back := path.Backend.ID - if host.HasTLSAuth() { - f.SNIBackendsMap.AppendHostname(base, back) - f.SNIBackendsMap.AppendAliasName(aliasName, back) - f.SNIBackendsMap.AppendAliasRegex(aliasRegex, back) - if backend != nil { - backend.TLS.HasTLSAuth = true - } - } else { - f.HostBackendsMap.AppendHostname(base, back) - f.HostBackendsMap.AppendAliasName(aliasName, back) - f.HostBackendsMap.AppendAliasRegex(aliasRegex, back) - } - if backend != nil { - if maxBodySize := backend.MaxBodySizeHostpath(base); maxBodySize > 0 { - maxBodySizes[base] = maxBodySize - } - } - if !hasSSLRedirect || c.global.Bind.HasFrontingProxy() { - fgroup.HTTPFrontsMap.AppendHostname(base, back) - } - var ns string - if host.VarNamespace { - ns = path.Backend.Namespace - } else { - ns = "-" - } - fgroup.VarNamespaceMap.AppendHostname(base, ns) + // TODO use only root path if all uri has the same conf + fmaps.HTTPSRedirMap.AppendHostname(host.Hostname+path.Path, yesno[hasSSLRedirect]) + var aliasName, aliasRegex string + // TODO warn in logs about ignoring alias name due to hostname colision + if host.Alias.AliasName != "" && c.hosts.FindHost(host.Alias.AliasName) == nil { + aliasName = host.Alias.AliasName + path.Path } - // TODO implement deny 413 and move all MaxBodySize stuff to backend - if len(maxBodySizes) > 0 { - // add all paths of the same host to avoid overlap - // 0 (zero) means unlimited - for _, path := range host.Paths { - base := host.Hostname + path.Path - f.MaxBodySizeMap.AppendHostname(base, strconv.FormatInt(maxBodySizes[base], 10)) - } + if host.Alias.AliasRegex != "" { + aliasRegex = host.Alias.AliasRegex + path.Path } + backendID := path.Backend.ID if host.HasTLSAuth() { - f.TLSInvalidCrtErrorList.AppendHostname(host.Hostname, "") - if !host.TLS.CAVerifyOptional { - f.TLSNoCrtErrorList.AppendHostname(host.Hostname, "") - } - page := host.TLS.CAErrorPage - if page != "" { - f.TLSInvalidCrtErrorPagesMap.AppendHostname(host.Hostname, page) - if !host.TLS.CAVerifyOptional { - f.TLSNoCrtErrorPagesMap.AppendHostname(host.Hostname, page) - } + fmaps.SNIBackendsMap.AppendHostname(base, backendID) + fmaps.SNIBackendsMap.AppendAliasName(aliasName, backendID) + fmaps.SNIBackendsMap.AppendAliasRegex(aliasRegex, backendID) + } else { + fmaps.HostBackendsMap.AppendHostname(base, backendID) + fmaps.HostBackendsMap.AppendAliasName(aliasName, backendID) + fmaps.HostBackendsMap.AppendAliasRegex(aliasRegex, backendID) + } + if backend != nil { + if maxBodySize := backend.MaxBodySizeHostpath(base); maxBodySize > 0 { + maxBodySizes[base] = maxBodySize } } - // TODO wildcard/alias/alias-regex hostname can overlap - // a configured domain which doesn't have rootRedirect - if host.RootRedirect != "" { - fgroup.HTTPRootRedirMap.AppendHostname(host.Hostname, host.RootRedirect) - f.RootRedirMap.AppendHostname(host.Hostname, host.RootRedirect) + if !hasSSLRedirect || c.global.Bind.HasFrontingProxy() { + fmaps.HTTPFrontsMap.AppendHostname(base, backendID) } + var ns string + if host.VarNamespace { + ns = path.Backend.Namespace + } else { + ns = "-" + } + fmaps.VarNamespaceMap.AppendHostname(base, ns) } - f.Bind.CrtList.AppendItem(c.defaultX509Cert) - for _, tls := range f.Bind.TLS { - crtFile := tls.CrtFilename - if (crtFile == "" || crtFile == c.defaultX509Cert) && tls.CAFilename == "" { - // default cert without client cert auth, ignore - continue + // TODO implement deny 413 and move all MaxBodySize stuff to backend + if len(maxBodySizes) > 0 { + // add all paths of the same host to avoid overlap + // 0 (zero) means unlimited + for _, path := range host.Paths { + base := host.Hostname + path.Path + fmaps.MaxBodySizeMap.AppendHostname(base, strconv.FormatInt(maxBodySizes[base], 10)) } - if crtFile == "" { - crtFile = c.defaultX509Cert + } + if host.HasTLSAuth() { + fmaps.TLSInvalidCrtErrorList.AppendHostname(host.Hostname, "") + if !host.TLS.CAVerifyOptional { + fmaps.TLSNoCrtErrorList.AppendHostname(host.Hostname, "") + } + page := host.TLS.CAErrorPage + if page != "" { + fmaps.TLSInvalidCrtErrorPagesMap.AppendHostname(host.Hostname, page) + if !host.TLS.CAVerifyOptional { + fmaps.TLSNoCrtErrorPagesMap.AppendHostname(host.Hostname, page) + } } + } + // TODO wildcard/alias/alias-regex hostname can overlap + // a configured domain which doesn't have rootRedirect + if host.RootRedirect != "" { + fmaps.HTTPRootRedirMap.AppendHostname(host.Hostname, host.RootRedirect) + fmaps.RootRedirMap.AppendHostname(host.Hostname, host.RootRedirect) + } + fmaps.UseServerList.AppendHostname(host.Hostname, "") + // + tls := host.TLS + crtFile := tls.TLSFilename + if crtFile == "" { + crtFile = c.defaultX509Cert + } + if crtFile != c.defaultX509Cert || tls.CAFilename != "" { + // has custom cert or tls auth + // + // TODO optimization: distinct hostnames that shares crt, ca and crl + // can be combined into a single line. Note that this is usually the exception. + // TODO this NEED its own template file. var crtListConfig string - hostnames := strings.Join(tls.Hostnames, " ") if tls.CAFilename == "" { - crtListConfig = fmt.Sprintf("%s %s", crtFile, hostnames) + crtListConfig = fmt.Sprintf("%s %s", crtFile, host.Hostname) } else { - // TODO this NEED its own template file var crl string if tls.CRLFilename != "" { crl = " crl-file " + tls.CRLFilename } - crtListConfig = fmt.Sprintf("%s [ca-file %s%s verify optional] %s", crtFile, tls.CAFilename, crl, hostnames) + crtListConfig = fmt.Sprintf("%s [ca-file %s%s verify optional] %s", crtFile, tls.CAFilename, crl, host.Hostname) } - f.Bind.CrtList.AppendItem(crtListConfig) - } - for _, host := range f.Hosts { - f.Bind.UseServerList.AppendHostname(host.Hostname, "") + fmaps.CrtList.AppendItem(crtListConfig) } } - if err := writeMaps(fgroup.Maps, c.mapsTemplate); err != nil { + if err := writeMaps(mapBuilder, c.mapsTemplate); err != nil { return err } - for _, f := range frontends { - if err := writeMaps(f.Maps, c.mapsTemplate); err != nil { - return err - } - } - c.fgroup = fgroup + c.frontend.Maps = fmaps return nil } -func (c *config) BuildBackendMaps() error { +// WriteBackendMaps reads the model and writes haproxy's maps +// used in the backends. Should be called before write the main +// config file. This func doesn't change model state, except the +// link to the backend maps. +func (c *config) WriteBackendMaps() error { // TODO rename HostMap types to HAProxyMap - maps := hatypes.CreateMaps() + mapBuilder := hatypes.CreateMaps() for _, backend := range c.backends { mapsPrefix := c.mapsDir + "/_back_" + backend.ID if backend.NeedACL() { - pathsMap := maps.AddMap(mapsPrefix + "_idpath.map") + pathsMap := mapBuilder.AddMap(mapsPrefix + "_idpath.map") for _, path := range backend.Paths { pathsMap.AppendPath(path.Hostpath, path.ID) } backend.PathsMap = pathsMap } } - return writeMaps(maps, c.mapsTemplate) + return writeMaps(mapBuilder, c.mapsTemplate) } func writeMaps(maps *hatypes.HostsMaps, template *template.Config) error { @@ -470,10 +438,6 @@ func writeMaps(maps *hatypes.HostsMaps, template *template.Config) error { return nil } -func (c *config) DefaultHost() *hatypes.Host { - return c.defaultHost -} - func (c *config) DefaultBackend() *hatypes.Backend { return c.defaultBackend } @@ -494,7 +458,7 @@ func (c *config) TCPBackends() []*hatypes.TCPBackend { return c.tcpbackends } -func (c *config) Hosts() []*hatypes.Host { +func (c *config) Hosts() *hatypes.Hosts { return c.hosts } diff --git a/pkg/haproxy/config_test.go b/pkg/haproxy/config_test.go index 9a361a8eb..e0b3f0555 100644 --- a/pkg/haproxy/config_test.go +++ b/pkg/haproxy/config_test.go @@ -22,28 +22,26 @@ import ( func TestEmptyFrontend(t *testing.T) { c := createConfig(options{}) - if err := c.BuildFrontendGroup(); err != nil { + if err := c.WriteFrontendMaps(); err != nil { t.Errorf("error creating frontends: %v", err) } - if fg := c.FrontendGroup(); fg == nil { - t.Error("expected FrontendGroup != nil") + if maps := c.frontend.Maps; maps == nil { + t.Error("expected frontend.Maps != nil") } - c.AcquireHost("empty") - if err := c.BuildFrontendGroup(); err != nil { + c.hosts.AcquireHost("empty") + if err := c.WriteFrontendMaps(); err != nil { t.Errorf("error creating frontends: %v", err) } - fg := c.FrontendGroup() - if fg == nil { - t.Error("expected FrontendGroup != nil") - } else if len(fg.Frontends) == 0 { - t.Error("expected at least one frontend") + maps := c.frontend.Maps + if maps == nil { + t.Error("expected frontend.Maps != nil") } } func TestAcquireHostDiff(t *testing.T) { c := createConfig(options{}) - f1 := c.AcquireHost("h1") - f2 := c.AcquireHost("h2") + f1 := c.hosts.AcquireHost("h1") + f2 := c.hosts.AcquireHost("h2") if f1.Hostname != "h1" { t.Errorf("expected %v but was %v", "h1", f1.Hostname) } @@ -54,8 +52,8 @@ func TestAcquireHostDiff(t *testing.T) { func TestAcquireHostSame(t *testing.T) { c := createConfig(options{}) - f1 := c.AcquireHost("h1") - f2 := c.AcquireHost("h1") + f1 := c.hosts.AcquireHost("h1") + f2 := c.hosts.AcquireHost("h1") if f1 != f2 { t.Errorf("expected same host but was different") } @@ -103,18 +101,18 @@ func TestEqual(t *testing.T) { if !c1.Equals(c2) { t.Error("c1 and c2 should be equals (with backends)") } - h1 := c1.AcquireHost("d") + h1 := c1.hosts.AcquireHost("d") h1.AddPath(b1, "/") if c1.Equals(c2) { t.Error("c1 and c2 should not be equals (hosts on one side)") } - h2 := c2.AcquireHost("d") + h2 := c2.hosts.AcquireHost("d") h2.AddPath(b2, "/") if !c1.Equals(c2) { t.Error("c1 and c2 should be equals (with hosts)") } - err1 := c1.BuildFrontendGroup() - err2 := c2.BuildFrontendGroup() + err1 := c1.WriteFrontendMaps() + err2 := c2.WriteFrontendMaps() if err1 != nil { t.Errorf("error building c1: %v", err1) } diff --git a/pkg/haproxy/instance.go b/pkg/haproxy/instance.go index c97f43e01..51f4e7562 100644 --- a/pkg/haproxy/instance.go +++ b/pkg/haproxy/instance.go @@ -264,12 +264,13 @@ func (i *instance) haproxyUpdate(timer *utils.Timer) { // - i.metrics.UpdateSuccessful() should be called only if haproxy is reloaded or cfg is validated // defer i.rotateConfig() - if err := i.curConfig.BuildFrontendGroup(); err != nil { + i.curConfig.SyncConfig() + if err := i.curConfig.WriteFrontendMaps(); err != nil { i.logger.Error("error building configuration group: %v", err) i.metrics.IncUpdateNoop() return } - if err := i.curConfig.BuildBackendMaps(); err != nil { + if err := i.curConfig.WriteBackendMaps(); err != nil { i.logger.Error("error building backend maps: %v", err) i.metrics.IncUpdateNoop() return @@ -326,27 +327,27 @@ func (i *instance) haproxyUpdate(timer *utils.Timer) { func (i *instance) updateCertExpiring() { // TODO move to dynupdate when dynamic crt update is implemented if i.oldConfig == nil { - for _, curHost := range i.curConfig.Hosts() { + for _, curHost := range i.curConfig.Hosts().Items { if curHost.TLS.HasTLS() { i.metrics.SetCertExpireDate(curHost.Hostname, curHost.TLS.TLSCommonName, &curHost.TLS.TLSNotAfter) } } return } - for _, oldHost := range i.oldConfig.Hosts() { + for _, oldHost := range i.oldConfig.Hosts().Items { if !oldHost.TLS.HasTLS() { continue } - curHost := i.curConfig.FindHost(oldHost.Hostname) + curHost := i.curConfig.Hosts().FindHost(oldHost.Hostname) if curHost == nil || oldHost.TLS.TLSCommonName != curHost.TLS.TLSCommonName { i.metrics.SetCertExpireDate(oldHost.Hostname, oldHost.TLS.TLSCommonName, nil) } } - for _, curHost := range i.curConfig.Hosts() { + for _, curHost := range i.curConfig.Hosts().Items { if !curHost.TLS.HasTLS() { continue } - oldHost := i.oldConfig.FindHost(curHost.Hostname) + oldHost := i.oldConfig.Hosts().FindHost(curHost.Hostname) if oldHost == nil || oldHost.TLS.TLSCommonName != curHost.TLS.TLSCommonName || oldHost.TLS.TLSNotAfter != curHost.TLS.TLSNotAfter { i.metrics.SetCertExpireDate(curHost.Hostname, curHost.TLS.TLSCommonName, &curHost.TLS.TLSNotAfter) } diff --git a/pkg/haproxy/instance_test.go b/pkg/haproxy/instance_test.go index e39f93b7d..76fff27a3 100644 --- a/pkg/haproxy/instance_test.go +++ b/pkg/haproxy/instance_test.go @@ -514,7 +514,7 @@ func TestBackends(t *testing.T) { b = c.config.AcquireBackend("d1", "app", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS1} - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") for _, p := range test.path { h.AddPath(b, p) } @@ -563,7 +563,7 @@ func TestInstanceBare(t *testing.T) { b = c.config.AcquireBackend("d1", "app", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS1} - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") h.AddPath(b, "/") c.Update() @@ -615,7 +615,7 @@ func TestInstanceGlobalBind(t *testing.T) { b = c.config.AcquireBackend("d1", "app", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS1} - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") h.AddPath(b, "/") c.config.Global().Bind = test.bind @@ -659,7 +659,7 @@ func TestInstanceEmpty(t *testing.T) { c := setup(t) defer c.teardown() - c.config.AcquireHost("empty").AddPath(c.config.AcquireBackend("default", "empty", "8080"), "/") + c.config.Hosts().AcquireHost("empty").AddPath(c.config.AcquireBackend("default", "empty", "8080"), "/") c.Update() c.checkConfig(` @@ -843,7 +843,7 @@ func TestInstanceToHTTPSocket(t *testing.T) { var b *hatypes.Backend b = c.config.AcquireBackend("d1", "app", "8080") - h = c.config.AcquireHost(test.domain) + h = c.config.Hosts().AcquireHost(test.domain) h.AddPath(b, "/") b.Endpoints = []*hatypes.Endpoint{endpointS1} b.HSTS = []*hatypes.BackendConfigHSTS{ @@ -1011,7 +1011,7 @@ func TestInstanceDefaultHost(t *testing.T) { var b *hatypes.Backend b = c.config.AcquireBackend("d1", "app", "8080") - h = c.config.AcquireHost("*") + h = c.config.Hosts().AcquireHost("*") h.AddPath(b, "/") h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem" h.TLS.TLSHash = "0" @@ -1020,7 +1020,7 @@ func TestInstanceDefaultHost(t *testing.T) { h.VarNamespace = true b = c.config.AcquireBackend("d2", "app", "8080") - h = c.config.AcquireHost("d2.local") + h = c.config.Hosts().AcquireHost("d2.local") h.AddPath(b, "/app") h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem" h.TLS.TLSHash = "0" @@ -1092,7 +1092,7 @@ func TestInstanceStrictHost(t *testing.T) { b = c.config.AcquireBackend("d1", "app", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS1} - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") h.AddPath(b, "/path") c.config.Global().StrictHost = true @@ -1131,12 +1131,12 @@ func TestInstanceStrictHostDefaultHost(t *testing.T) { b = c.config.AcquireBackend("d1", "app", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS1} - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") h.AddPath(b, "/path") b = c.config.AcquireBackend("d2", "app", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS21} - h = c.config.AcquireHost("*") + h = c.config.Hosts().AcquireHost("*") h.AddPath(b, "/") c.config.Global().StrictHost = true @@ -1175,7 +1175,7 @@ d1.local/ d2_app_8080 c.logger.CompareLogging(defaultLogging) } -func TestInstanceSingleFrontendSingleBind(t *testing.T) { +func TestInstanceFrontend(t *testing.T) { c := setup(t) defer c.teardown() @@ -1187,7 +1187,7 @@ func TestInstanceSingleFrontendSingleBind(t *testing.T) { var b *hatypes.Backend b = c.config.AcquireBackend("d1", "app", "8080") - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") h.AddPath(b, "/") b.SSLRedirect = b.CreateConfigBool(true) b.Endpoints = []*hatypes.Endpoint{endpointS1} @@ -1196,7 +1196,7 @@ func TestInstanceSingleFrontendSingleBind(t *testing.T) { h.TLS.TLSHash = "1" b = c.config.AcquireBackend("d2", "app", "8080") - h = c.config.AcquireHost("d2.local") + h = c.config.Hosts().AcquireHost("d2.local") h.AddPath(b, "/app") b.SSLRedirect = b.CreateConfigBool(true) b.Endpoints = []*hatypes.Endpoint{endpointS1} @@ -1262,7 +1262,7 @@ d2.local/app - c.logger.CompareLogging(defaultLogging) } -func TestInstanceSingleFrontendTwoBindsCA(t *testing.T) { +func TestInstanceFrontendCA(t *testing.T) { c := setup(t) defer c.teardown() @@ -1274,7 +1274,7 @@ func TestInstanceSingleFrontendTwoBindsCA(t *testing.T) { var b *hatypes.Backend b = c.config.AcquireBackend("d", "app", "8080") - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") h.AddPath(b, "/") h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem" h.TLS.TLSHash = "0" @@ -1282,7 +1282,7 @@ func TestInstanceSingleFrontendTwoBindsCA(t *testing.T) { h.TLS.CAHash = "1" h.TLS.CAErrorPage = "http://d1.local/error.html" - h = c.config.AcquireHost("d2.local") + h = c.config.Hosts().AcquireHost("d2.local") h.AddPath(b, "/") h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem" h.TLS.TLSHash = "0" @@ -1391,7 +1391,7 @@ func TestInstanceSomePaths(t *testing.T) { var b *hatypes.Backend b = c.config.AcquireBackend("d", "app0", "8080") - h = c.config.AcquireHost("d.local") + h = c.config.Hosts().AcquireHost("d.local") h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem" h.TLS.TLSHash = "0" h.AddPath(b, "/") @@ -1469,7 +1469,7 @@ func TestInstanceCustomFrontend(t *testing.T) { b = c.config.AcquireBackend("d1", "app", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS1} - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") h.AddPath(b, "/") c.config.Global().CustomFrontend = []string{ "# new header", @@ -1522,7 +1522,7 @@ func TestInstanceSSLRedirect(t *testing.T) { b = c.config.AcquireBackend("d1", "app-api", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS1} - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem" h.TLS.TLSHash = "0" h.AddPath(b, "/") @@ -1530,7 +1530,7 @@ func TestInstanceSSLRedirect(t *testing.T) { b = c.config.AcquireBackend("d2", "app-front", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS21} - h = c.config.AcquireHost("d2.local") + h = c.config.Hosts().AcquireHost("d2.local") h.TLS.TLSFilename = "" h.TLS.TLSHash = "" h.AddPath(b, "/") @@ -1579,14 +1579,14 @@ func TestInstanceSSLPassthrough(t *testing.T) { var b *hatypes.Backend b = c.config.AcquireBackend("d2", "app", "8080") - h = c.config.AcquireHost("d2.local") + h = c.config.Hosts().AcquireHost("d2.local") h.AddPath(b, "/") b.SSLRedirect = b.CreateConfigBool(true) b.Endpoints = []*hatypes.Endpoint{endpointS31} h.SSLPassthrough = true b = c.config.AcquireBackend("d3", "app-ssl", "8443") - h = c.config.AcquireHost("d3.local") + h = c.config.Hosts().AcquireHost("d3.local") h.AddPath(b, "/") b.Endpoints = []*hatypes.Endpoint{endpointS41s} h.SSLPassthrough = true @@ -1657,7 +1657,7 @@ func TestInstanceRootRedirect(t *testing.T) { var b *hatypes.Backend b = c.config.AcquireBackend("d1", "app", "8080") - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem" h.TLS.TLSHash = "0" h.AddPath(b, "/") @@ -1666,7 +1666,7 @@ func TestInstanceRootRedirect(t *testing.T) { b.Endpoints = []*hatypes.Endpoint{endpointS1} b = c.config.AcquireBackend("d2", "app", "8080") - h = c.config.AcquireHost("d2.local") + h = c.config.Hosts().AcquireHost("d2.local") h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem" h.TLS.TLSHash = "0" h.AddPath(b, "/app1") @@ -1746,20 +1746,20 @@ func TestInstanceAlias(t *testing.T) { b = c.config.AcquireBackend("d1", "app", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS1} - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") h.AddPath(b, "/") h.Alias.AliasName = "*.d1.local" b = c.config.AcquireBackend("d2", "app", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS21} - h = c.config.AcquireHost("d2.local") + h = c.config.Hosts().AcquireHost("d2.local") h.AddPath(b, "/") h.Alias.AliasName = "sub.d2.local" h.Alias.AliasRegex = "^[a-z]+\\.d2\\.local$" b = c.config.AcquireBackend("d3", "app", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS31} - h = c.config.AcquireHost("d3.local") + h = c.config.Hosts().AcquireHost("d3.local") h.AddPath(b, "/") h.Alias.AliasRegex = ".*d3\\.local$" @@ -1824,7 +1824,7 @@ func TestInstanceMaxBody(t *testing.T) { b = c.config.AcquireBackend("d1", "app", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS1} - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") h.AddPath(b, "/") h.AddPath(b, "/app") b.MaxBodySize = []*hatypes.BackendConfigInt{{ @@ -1834,7 +1834,7 @@ func TestInstanceMaxBody(t *testing.T) { b = c.config.AcquireBackend("d2", "app", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS21} - h = c.config.AcquireHost("d2.local") + h = c.config.Hosts().AcquireHost("d2.local") h.AddPath(b, "/") c.Update() @@ -1878,7 +1878,7 @@ func TestInstanceSyslog(t *testing.T) { b = c.config.AcquireBackend("d1", "app", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS1} - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") h.AddPath(b, "/") syslog := &c.config.Global().Syslog @@ -1971,13 +1971,13 @@ func TestDNS(t *testing.T) { b = c.config.AcquireBackend("d1", "app", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS21, endpointS22} b.Resolver = "k8s" - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") h.AddPath(b, "/") b = c.config.AcquireBackend("d2", "app", "http") b.Endpoints = []*hatypes.Endpoint{endpointS21, endpointS22} b.Resolver = "k8s" - h = c.config.AcquireHost("d2.local") + h = c.config.Hosts().AcquireHost("d2.local") h.AddPath(b, "/") c.Update() @@ -2079,7 +2079,7 @@ userlist default_auth2 b = c.config.AcquireBackend("d1", "app", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS1} - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") h.AddPath(b, "/") h.AddPath(b, "/admin") @@ -2166,7 +2166,7 @@ frontend _front_http b = c.config.AcquireBackend("d1", "app", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS1} - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") h.AddPath(b, "/") acme := c.config.Acme() @@ -2387,7 +2387,7 @@ func TestModSecurity(t *testing.T) { b = c.config.AcquireBackend("d1", "app", "8080") b.Endpoints = []*hatypes.Endpoint{endpointS1} - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") if test.path == "" { test.path = "/" } @@ -2444,15 +2444,15 @@ func TestInstanceWildcardHostname(t *testing.T) { var b *hatypes.Backend b = c.config.AcquireBackend("d1", "app", "8080") - h = c.config.AcquireHost("d1.local") + h = c.config.Hosts().AcquireHost("d1.local") h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem" h.TLS.TLSHash = "0" h.AddPath(b, "/") - h = c.config.AcquireHost("*.app.d1.local") + h = c.config.Hosts().AcquireHost("*.app.d1.local") h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem" h.TLS.TLSHash = "0" h.AddPath(b, "/") - h = c.config.AcquireHost("*.sub.d1.local") + h = c.config.Hosts().AcquireHost("*.sub.d1.local") h.TLS.TLSFilename = "/var/haproxy/ssl/certs/default.pem" h.TLS.TLSHash = "0" h.AddPath(b, "/") @@ -2464,7 +2464,7 @@ func TestInstanceWildcardHostname(t *testing.T) { b.Endpoints = []*hatypes.Endpoint{endpointS1} b = c.config.AcquireBackend("d2", "app", "8080") - h = c.config.AcquireHost("*.d2.local") + h = c.config.Hosts().AcquireHost("*.d2.local") h.AddPath(b, "/") h.RootRedirect = "/app" b.SSLRedirect = b.CreateConfigBool(false) diff --git a/pkg/haproxy/types/frontend.go b/pkg/haproxy/types/frontend.go index 591b1c5c6..49e9bf72a 100644 --- a/pkg/haproxy/types/frontend.go +++ b/pkg/haproxy/types/frontend.go @@ -122,163 +122,12 @@ func (hm *HostsMaps) AddMap(filename string) *HostsMap { return hmap } -// HasTCPProxy ... -func (fg *FrontendGroup) HasTCPProxy() bool { - // short-circuit saves: - // len(fg.Frontend) may be zero only if fg.HasSSLPassthrough is true - return fg.HasSSLPassthrough || len(fg.Frontends) > 1 -} - -// HasVarNamespace ... -func (fg *FrontendGroup) HasVarNamespace() bool { - for _, f := range fg.Frontends { - for _, host := range f.Hosts { - if host.VarNamespace { - return true - } - } - } - return false +// HasMaxBody ... +func (f *Frontend) HasMaxBody() bool { + return f.Maps.MaxBodySizeMap.HasHost() } // String ... func (f *Frontend) String() string { return fmt.Sprintf("%+v", *f) } - -// HasTLSAuth ... -func (f *Frontend) HasTLSAuth() bool { - for _, host := range f.Hosts { - if host.HasTLSAuth() { - return true - } - } - return false -} - -// HasInvalidErrorPage ... -func (f *Frontend) HasInvalidErrorPage() bool { - for _, host := range f.Hosts { - if host.TLS.CAErrorPage != "" { - return true - } - } - return false -} - -// HasNoCrtErrorPage ... -func (f *Frontend) HasNoCrtErrorPage() bool { - // Use currently the same attribute - return f.HasInvalidErrorPage() -} - -// HasTLSMandatory ... -func (f *Frontend) HasTLSMandatory() bool { - for _, host := range f.Hosts { - if host.HasTLSAuth() && !host.TLS.CAVerifyOptional { - return true - } - } - return false -} - -// HasMaxBody ... -func (f *Frontend) HasMaxBody() bool { - return f.MaxBodySizeMap.HasHost() -} - -// BuildRawFrontends ... -func BuildRawFrontends(hosts []*Host) (frontends []*Frontend, sslpassthrough []*Host, defaultBind *BindConfig) { - // creating frontends and ssl-passthrough hosts - for _, host := range hosts { - if host.SSLPassthrough { - // ssl-passthrough does not use a frontend - sslpassthrough = append(sslpassthrough, host) - continue - } - frontend := findMatchingFrontend(frontends, host) - if frontend == nil { - frontend = NewFrontend(host) - frontends = append(frontends, frontend) - } - frontend.Hosts = append(frontend.Hosts, host) - } - var hostCount int - // creating binds - for _, frontend := range frontends { - frontend.Bind = NewFrontendBind(frontend.Hosts) - if defaultBind == nil || hostCount > len(frontend.Hosts) { - defaultBind = &frontend.Bind - hostCount = len(frontend.Hosts) - } - } - // configuring the default bind - if defaultBind == nil { - var frontend *Frontend - frontend = NewFrontend(nil) - frontend.Bind = NewFrontendBind(nil) - defaultBind = &frontend.Bind - frontends = append(frontends, frontend) - } - // naming frontends - var i int - for _, frontend := range frontends { - i++ - frontend.Name = fmt.Sprintf("_front%03d", i) - } - // sorting frontends - sort.Slice(frontends, func(i, j int) bool { - return frontends[i].Name < frontends[j].Name - }) - return frontends, sslpassthrough, defaultBind -} - -func findMatchingFrontend(frontends []*Frontend, host *Host) *Frontend { - for _, frontend := range frontends { - if frontend.match(host) { - return frontend - } - } - return nil -} - -// NewFrontend and Frontend.Match should always sinchronize its attributes -func NewFrontend(host *Host) *Frontend { - return &Frontend{} -} - -// NewFrontendBind ... -func NewFrontendBind(hosts []*Host) BindConfig { - var tls []*BindTLSConfig - for _, host := range hosts { - cfg := findTLSConfig(tls, &host.TLS) - if cfg == nil { - cfg = &BindTLSConfig{ - CAFilename: host.TLS.CAFilename, - CAHash: host.TLS.CAHash, - CRLFilename: host.TLS.CRLFilename, - CRLHash: host.TLS.CRLHash, - CrtFilename: host.TLS.TLSFilename, - CrtHash: host.TLS.TLSHash, - } - tls = append(tls, cfg) - } - cfg.Hostnames = append(cfg.Hostnames, host.Hostname) - } - return BindConfig{ - TLS: tls, - } -} - -func findTLSConfig(bindTLS []*BindTLSConfig, hostTLS *HostTLSConfig) *BindTLSConfig { - for _, tls := range bindTLS { - if tls.CAHash == hostTLS.CAHash && tls.CrtHash == hostTLS.TLSHash { - return tls - } - } - return nil -} - -func (f *Frontend) match(host *Host) bool { - return true -} diff --git a/pkg/haproxy/types/frontend_test.go b/pkg/haproxy/types/frontend_test.go index fa7310fdf..67047c2b0 100644 --- a/pkg/haproxy/types/frontend_test.go +++ b/pkg/haproxy/types/frontend_test.go @@ -18,9 +18,6 @@ package types import ( "testing" - - "github.com/kylelemons/godebug/diff" - yaml "gopkg.in/yaml.v2" ) func TestAppendHostname(t *testing.T) { @@ -65,35 +62,3 @@ func TestAppendHostname(t *testing.T) { } } } - -func TestBuildFrontendEmpty(t *testing.T) { - frontends, _, _ := BuildRawFrontends([]*Host{}) - if len(frontends) != 1 { - t.Errorf("expected len(frontends) == 1, but was %d", len(frontends)) - } -} - -func TestBuildSSLPassthrough(t *testing.T) { - h1 := &Host{Hostname: "h1.local"} - h2 := &Host{Hostname: "h2.local", SSLPassthrough: true} - testCases := []struct { - hosts []*Host - expected []*Host - }{ - // 0 - { - hosts: []*Host{h1, h2}, - expected: []*Host{h2}, - }, - } - for i, test := range testCases { - _, sslpassthrough, _ := BuildRawFrontends(test.hosts) - actualRaw, _ := yaml.Marshal(sslpassthrough) - expectedRaw, _ := yaml.Marshal(test.expected) - actual := string(actualRaw) - expected := string(expectedRaw) - if actual != expected { - t.Errorf("sslpassthrough '%d' actual and expected differs:\n%v", i, diff.Diff(actual, expected)) - } - } -} diff --git a/pkg/haproxy/types/host.go b/pkg/haproxy/types/host.go index d8e46ba45..53e46b328 100644 --- a/pkg/haproxy/types/host.go +++ b/pkg/haproxy/types/host.go @@ -21,6 +21,119 @@ import ( "sort" ) +// AcquireHost ... +func (h *Hosts) AcquireHost(hostname string) *Host { + if host := h.FindHost(hostname); host != nil { + return host + } + host := createHost(hostname) + if host.Hostname != "*" { + h.Items = append(h.Items, host) + sort.Slice(h.Items, func(i, j int) bool { + return h.Items[i].Hostname < h.Items[j].Hostname + }) + } else { + h.defaultHost = host + } + return host +} + +// FindHost ... +func (h *Hosts) FindHost(hostname string) *Host { + if hostname == "*" && h.defaultHost != nil { + return h.defaultHost + } + for _, f := range h.Items { + if f.Hostname == hostname { + return f + } + } + return nil +} + +func createHost(hostname string) *Host { + return &Host{ + Hostname: hostname, + } +} + +// DefaultHost ... +func (h *Hosts) DefaultHost() *Host { + return h.defaultHost +} + +// HasSSLPassthrough ... +func (h *Hosts) HasSSLPassthrough() bool { + // TODO this is just another HasXXX() or FindXXX() which iterates over + // thousands of items to find an answer. Sometimes this is done more + // than once. This need to be improved. + // We can find this answer (regarding HasSSLPassthrough) on + // ssl-passthrough map but it is (currently) built on instance.update() + // which would need some knowledge and synchronization from the caller. + for _, host := range h.Items { + if host.SSLPassthrough { + return true + } + } + return false +} + +// HasHTTP ... +func (h *Hosts) HasHTTP() bool { + for _, host := range h.Items { + if !host.SSLPassthrough { + return true + } + } + return false +} + +// HasInvalidErrorPage ... +func (h *Hosts) HasInvalidErrorPage() bool { + for _, host := range h.Items { + if host.TLS.CAErrorPage != "" { + return true + } + } + return false +} + +// HasNoCrtErrorPage ... +func (h *Hosts) HasNoCrtErrorPage() bool { + // Use currently the same attribute + return h.HasInvalidErrorPage() +} + +// HasTLSAuth ... +func (h *Hosts) HasTLSAuth() bool { + for _, host := range h.Items { + if host.HasTLSAuth() { + return true + } + } + return false +} + +// HasTLSMandatory ... +func (h *Hosts) HasTLSMandatory() bool { + for _, host := range h.Items { + if host.HasTLSAuth() && !host.TLS.CAVerifyOptional { + return true + } + } + return false +} + +// HasVarNamespace ... +func (h *Hosts) HasVarNamespace() bool { + for _, host := range h.Items { + if host.VarNamespace { + return true + } + } + return false +} + // FindPath ... func (h *Host) FindPath(path string) *HostPath { for _, p := range h.Paths { diff --git a/pkg/haproxy/types/types.go b/pkg/haproxy/types/types.go index e28f02240..bb95b5be8 100644 --- a/pkg/haproxy/types/types.go +++ b/pkg/haproxy/types/types.go @@ -246,29 +246,14 @@ type HostsMaps struct { Items []*HostsMap } -// FrontendGroup ... -type FrontendGroup struct { - Frontends []*Frontend - // - DefaultBind *BindConfig - HasSSLPassthrough bool - ToHTTPBind BindConfig - // - Maps *HostsMaps +// FrontendMaps ... +type FrontendMaps struct { HTTPFrontsMap *HostsMap HTTPRootRedirMap *HostsMap HTTPSRedirMap *HostsMap SSLPassthroughMap *HostsMap VarNamespaceMap *HostsMap -} - -// Frontend ... -type Frontend struct { - Name string - Bind BindConfig - Hosts []*Host // - Maps *HostsMaps HostBackendsMap *HostsMap MaxBodySizeMap *HostsMap RootRedirMap *HostsMap @@ -277,31 +262,25 @@ type Frontend struct { TLSInvalidCrtErrorPagesMap *HostsMap TLSNoCrtErrorList *HostsMap TLSNoCrtErrorPagesMap *HostsMap + // + CrtList *HostsMap + UseServerList *HostsMap } -// BindConfig ... -type BindConfig struct { - Name string - Socket string - ID int - // +// Frontend ... +type Frontend struct { + Name string + Maps *FrontendMaps + BindName string + BindSocket string + BindID int AcceptProxy bool - ALPN string - TLS []*BindTLSConfig - // - UseServerList *HostsMap - CrtList *HostsMap } -// BindTLSConfig ... -type BindTLSConfig struct { - CAFilename string - CAHash string - CRLFilename string - CRLHash string - CrtFilename string - CrtHash string - Hostnames []string +// Hosts ... +type Hosts struct { + Items []*Host + defaultHost *Host } // Host ... diff --git a/rootfs/etc/haproxy/template/haproxy.tmpl b/rootfs/etc/haproxy/template/haproxy.tmpl index 9484d1e53..a9674ccdd 100644 --- a/rootfs/etc/haproxy/template/haproxy.tmpl +++ b/rootfs/etc/haproxy/template/haproxy.tmpl @@ -587,8 +587,10 @@ backend _error404 http-request use-service lua.send-404 {{- end }} -{{- $fgroup := $cfg.FrontendGroup }} -{{- if $fgroup }} +{{- $frontend := $cfg.Frontend }} +{{- $hosts := $cfg.Hosts }} +{{- $fmaps := $frontend.Maps }} +{{- if $fmaps }} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @@ -597,8 +599,7 @@ backend _error404 # # FRONTENDS # # # -{{- $frontends := $fgroup.Frontends }} -{{- if $fgroup.HasTCPProxy }} +{{- if $hosts.HasSSLPassthrough }} # # # # # # # # # # # # # # # # # # # # # @@ -623,14 +624,14 @@ listen _front__tls tcp-request inspect-delay 5s {{- /*------------------------------------*/}} -{{- if $fgroup.HasSSLPassthrough }} +{{- if $hosts.HasSSLPassthrough }} tcp-request content set-var(req.sslpassback) - {{- "" }} req.ssl_sni,lower,map({{ $fgroup.SSLPassthroughMap.MatchFile }}) + {{- "" }} req.ssl_sni,lower,map({{ $fmaps.SSLPassthroughMap.MatchFile }}) {{- end }} -{{- if $fgroup.SSLPassthroughMap.HasRegex }} +{{- if $fmaps.SSLPassthroughMap.HasRegex }} {{- /*** TODO map_reg() running on every request ***/}} tcp-request content set-var(req.sslpassregback) - {{- "" }} req.ssl_sni,lower,map_reg({{ $fgroup.SSLPassthroughMap.RegexFile }}) + {{- "" }} req.ssl_sni,lower,map_reg({{ $fmaps.SSLPassthroughMap.RegexFile }}) {{- "" }} if !{ var(req.sslpassback) -m found } {{- end }} @@ -638,34 +639,28 @@ listen _front__tls tcp-request content accept if { req.ssl_hello_type 1 } {{- /*------------------------------------*/}} -{{- if $fgroup.HasSSLPassthrough }} +{{- if $hosts.HasSSLPassthrough }} use_backend %[var(req.sslpassback)] if { var(req.sslpassback) -m found } {{- end }} -{{- range $frontend := $frontends }} -{{- if $frontend.Hosts }} -{{- $bind := $frontend.Bind }} - ## {{ $bind.Name }} - use-server _server{{ $bind.Name }} if { req.ssl_sni -i -f {{ $bind.UseServerList.MatchFile }} } - server _server{{ $bind.Name }} {{ $bind.Socket }} send-proxy-v2 weight 0 -{{- end }} +{{- if $hosts.HasHTTP }} + ## {{ $frontend.BindName }} + use-server _server{{ $frontend.BindName }} if { req.ssl_sni -i -f {{ $fmaps.UseServerList.MatchFile }} } + server _server{{ $frontend.BindName }} {{ $frontend.BindSocket }} send-proxy-v2 weight 0 {{- end }} {{- /*------------------------------------*/}} -{{- if $fgroup.SSLPassthroughMap.HasRegex }} +{{- if $fmaps.SSLPassthroughMap.HasRegex }} use_backend %[var(req.sslpassback)] if { var(req.sslpassback) -m found } {{- end }} -{{- range $frontend := $frontends }} -{{- $bind := $frontend.Bind }} -{{- if $bind.UseServerList.HasRegex }} - ## {{ $bind.Name }} wildcard - use-server _server{{ $bind.Name }}_wildcard if { req.ssl_sni -i -m reg -f {{ $bind.UseServerList.RegexFile }} } - server _server{{ $bind.Name }}_wildcard {{ $bind.Socket }} send-proxy-v2 weight 0 -{{- end }} +{{- if $fmaps.UseServerList.HasRegex }} + ## {{ $frontend.BindName }} wildcard + use-server _server{{ $frontend.BindName }}_wildcard if { req.ssl_sni -i -m reg -f {{ $fmaps.UseServerList.RegexFile }} } + server _server{{ $frontend.BindName }}_wildcard {{ $frontend.BindSocket }} send-proxy-v2 weight 0 {{- end }} {{- /*------------------------------------*/}} # default backend - server _default_server{{ $fgroup.DefaultBind.Name }} {{ $fgroup.DefaultBind.Socket }} send-proxy-v2 + server _default_server{{ $frontend.BindName }} {{ $frontend.BindSocket }} send-proxy-v2 {{- end }} {{- $hasFrontingProxy := $global.Bind.HasFrontingProxy }} @@ -676,15 +671,14 @@ listen _front__tls # frontend _front_http mode http -{{- $frontingBind := $fgroup.ToHTTPBind }} {{- $hasPlainHTTPSocket := not $global.Bind.ShareHTTPPort }} {{- if and $global.Bind.HTTPBind $hasPlainHTTPSocket }} bind {{ $global.Bind.HTTPBind }}{{ if $global.Bind.AcceptProxy }} accept-proxy{{ end }} {{- end }} -{{- if $frontingBind.Socket }} - bind {{ $frontingBind.Socket }} - {{- if and $hasPlainHTTPSocket $frontingBind.ID }} id {{ $frontingBind.ID }}{{ end }} - {{- if $frontingBind.AcceptProxy }} accept-proxy{{ end }} +{{- if $global.Bind.FrontingBind }} + bind {{ $global.Bind.FrontingBind }} + {{- if and $hasPlainHTTPSocket $global.Bind.FrontingSockID }} id {{ $global.Bind.FrontingSockID }}{{ end }} + {{- if $global.Bind.AcceptProxy }} accept-proxy{{ end }} {{- end }} {{- /*------------------------------------*/}} @@ -722,9 +716,9 @@ frontend _front_http {{- /*------------------------------------*/}} {{- $acmeexclusive := and $cfg.Acme.Enabled (not $cfg.Acme.Shared) }} -{{- if $fgroup.HTTPSRedirMap.HasRegex }} +{{- if $fmaps.HTTPSRedirMap.HasRegex }} http-request set-var(req.redir) - {{- "" }} var(req.base),map_beg({{ $fgroup.HTTPSRedirMap.MatchFile }}) + {{- "" }} var(req.base),map_beg({{ $fmaps.HTTPSRedirMap.MatchFile }}) {{- if $hasFrontingProxy }} if !fronting-proxy{{ end }} http-request redirect scheme https {{- if $global.SSL.RedirectCode }} code {{ $global.SSL.RedirectCode }}{{ end }} @@ -736,23 +730,23 @@ frontend _front_http {{- "" }} if{{ if $acmeexclusive }} !acme-challenge{{ end }} {{- if $hasFrontingProxy }} !fronting-proxy{{ end }} {{- "" }} !{ var(req.redir) -m found } - {{- "" }} { var(req.base),map_reg({{ $fgroup.HTTPSRedirMap.RegexFile }}) yes } + {{- "" }} { var(req.base),map_reg({{ $fmaps.HTTPSRedirMap.RegexFile }}) yes } {{- else }} http-request redirect scheme https {{- if $global.SSL.RedirectCode }} code {{ $global.SSL.RedirectCode }}{{ end }} {{- "" }} if{{ if $acmeexclusive }} !acme-challenge{{ end }} {{- if $hasFrontingProxy }} !fronting-proxy{{ end }} - {{- "" }} { var(req.base),map_beg({{ $fgroup.HTTPSRedirMap.MatchFile }}) yes } + {{- "" }} { var(req.base),map_beg({{ $fmaps.HTTPSRedirMap.MatchFile }}) yes } {{- end }} {{- /*------------------------------------*/}} -{{- if $fgroup.HTTPRootRedirMap.HasHost }} +{{- if $fmaps.HTTPRootRedirMap.HasHost }} http-request set-var(req.host) hdr(host),lower,regsub(:[0-9]+$,) http-request set-var(req.rootredir) - {{- "" }} var(req.host),map({{ $fgroup.HTTPRootRedirMap.MatchFile }}) -{{- if $fgroup.HTTPRootRedirMap.HasRegex }} + {{- "" }} var(req.host),map({{ $fmaps.HTTPRootRedirMap.MatchFile }}) +{{- if $fmaps.HTTPRootRedirMap.HasRegex }} http-request set-var(req.rootredir) - {{- "" }} var(req.host),map_reg({{ $fgroup.HTTPRootRedirMap.RegexFile }}) if !{ var(req.rootredir) -m found } + {{- "" }} var(req.host),map_reg({{ $fmaps.HTTPRootRedirMap.RegexFile }}) if !{ var(req.rootredir) -m found } {{- end }} http-request redirect location %[var(req.rootredir)] {{- "" }} if{{ if $acmeexclusive }} !acme-challenge{{ end }} @@ -760,12 +754,12 @@ frontend _front_http {{- end }} {{- /*------------------------------------*/}} -{{- if $fgroup.HasVarNamespace }} +{{- if $hosts.HasVarNamespace }} http-request set-var(txn.namespace) - {{- "" }} var(req.base),map_beg({{ $fgroup.VarNamespaceMap.MatchFile }},-) -{{- if $fgroup.VarNamespaceMap.HasRegex }} + {{- "" }} var(req.base),map_beg({{ $fmaps.VarNamespaceMap.MatchFile }},-) +{{- if $fmaps.VarNamespaceMap.HasRegex }} http-request set-var(txn.namespace) - {{- "" }} var(req.base),map_reg({{ $fgroup.VarNamespaceMap.RegexFile }},-) + {{- "" }} var(req.base),map_reg({{ $fmaps.VarNamespaceMap.RegexFile }},-) {{- "" }} if { var(txn.namespace) -- - } {{- end }} {{- end }} @@ -783,10 +777,10 @@ frontend _front_http {{- if $hasFrontingProxy }} if !fronting-proxy{{ end }} {{- /*------------------------------------*/}} - http-request set-var(req.backend) var(req.base),map_beg({{ $fgroup.HTTPFrontsMap.MatchFile }}) -{{- if $fgroup.HTTPFrontsMap.HasRegex }} + http-request set-var(req.backend) var(req.base),map_beg({{ $fmaps.HTTPFrontsMap.MatchFile }}) +{{- if $fmaps.HTTPFrontsMap.HasRegex }} http-request set-var(req.backend) - {{- "" }} var(req.base),map_reg({{ $fgroup.HTTPFrontsMap.RegexFile }}) + {{- "" }} var(req.base),map_reg({{ $fmaps.HTTPFrontsMap.RegexFile }}) {{- "" }} if !{ var(req.backend) -m found } {{- end }} @@ -810,18 +804,16 @@ frontend _front_http # # # HTTPS frontend # -{{- range $frontend := $frontends }} frontend {{ $frontend.Name }} mode http {{- /*------------------------------------*/}} -{{- $bind := $frontend.Bind }} -{{- if $bind.Socket }} - bind {{ $bind.Socket }} - {{- if $bind.ID }} id {{ $bind.ID }}{{ end }} - {{- if $bind.AcceptProxy }} accept-proxy{{ end }} - {{- "" }} ssl alpn {{ $bind.ALPN }} - {{- "" }} crt-list {{ $bind.CrtList.MatchFile }} +{{- if $frontend.BindSocket }} + bind {{ $frontend.BindSocket }} + {{- if $frontend.BindID }} id {{ $frontend.BindID }}{{ end }} + {{- if $frontend.AcceptProxy }} accept-proxy{{ end }} + {{- "" }} ssl alpn {{ $global.SSL.ALPN }} + {{- "" }} crt-list {{ $fmaps.CrtList.MatchFile }} {{- "" }} ca-ignore-err all crt-ignore-err all {{- end }} @@ -835,41 +827,41 @@ frontend {{ $frontend.Name }} {{- end }} {{- /*------------------------------------*/}} -{{- if or $frontend.HasTLSAuth $frontend.HostBackendsMap.HasRegex $fgroup.HasVarNamespace $frontend.HasMaxBody }} +{{- if or $hosts.HasTLSAuth $fmaps.HostBackendsMap.HasRegex $hosts.HasVarNamespace $frontend.HasMaxBody }} http-request set-var(req.base) base,lower,regsub(:[0-9]+/,/) http-request set-var(req.hostbackend) - {{- "" }} var(req.base),map_beg({{ $frontend.HostBackendsMap.MatchFile }}) + {{- "" }} var(req.base),map_beg({{ $fmaps.HostBackendsMap.MatchFile }}) {{- else }} http-request set-var(req.hostbackend) base,lower,regsub(:[0-9]+/,/) - {{- "" }},map_beg({{ $frontend.HostBackendsMap.MatchFile }}) + {{- "" }},map_beg({{ $fmaps.HostBackendsMap.MatchFile }}) {{- end }} -{{- if $frontend.HostBackendsMap.HasRegex }} +{{- if $fmaps.HostBackendsMap.HasRegex }} http-request set-var(req.hostbackend) - {{- "" }} var(req.base),map_reg({{ $frontend.HostBackendsMap.RegexFile }}) + {{- "" }} var(req.base),map_reg({{ $fmaps.HostBackendsMap.RegexFile }}) {{- "" }} if !{ var(req.hostbackend) -m found } {{- end }} {{- /*------------------------------------*/}} -{{- if or $frontend.HasTLSAuth $frontend.RootRedirMap.HasHost }} +{{- if or $hosts.HasTLSAuth $fmaps.RootRedirMap.HasHost }} http-request set-var(req.host) hdr(host),lower,regsub(:[0-9]+$,) {{- end }} -{{- if $frontend.RootRedirMap.HasHost }} +{{- if $fmaps.RootRedirMap.HasHost }} http-request set-var(req.rootredir) - {{- "" }} var(req.host),map({{ $frontend.RootRedirMap.MatchFile }}) -{{- if $frontend.RootRedirMap.HasRegex }} + {{- "" }} var(req.host),map({{ $fmaps.RootRedirMap.MatchFile }}) +{{- if $fmaps.RootRedirMap.HasRegex }} http-request set-var(req.rootredir) - {{- "" }} var(req.host),map_reg({{ $frontend.RootRedirMap.RegexFile }}) if !{ var(req.rootredir) -m found } + {{- "" }} var(req.host),map_reg({{ $fmaps.RootRedirMap.RegexFile }}) if !{ var(req.rootredir) -m found } {{- end }} http-request redirect location %[var(req.rootredir)] if { path / } { var(req.rootredir) -m found } {{- end }} {{- /*------------------------------------*/}} -{{- if $fgroup.HasVarNamespace }} +{{- if $hosts.HasVarNamespace }} http-request set-var(txn.namespace) - {{- "" }} var(req.base),map_beg({{ $fgroup.VarNamespaceMap.MatchFile }},-) -{{- if $fgroup.VarNamespaceMap.HasRegex }} + {{- "" }} var(req.base),map_beg({{ $fmaps.VarNamespaceMap.MatchFile }},-) +{{- if $fmaps.VarNamespaceMap.HasRegex }} http-request set-var(txn.namespace) - {{- "" }} var(req.base),map_reg({{ $fgroup.VarNamespaceMap.RegexFile }},-) + {{- "" }} var(req.base),map_reg({{ $fmaps.VarNamespaceMap.RegexFile }},-) {{- "" }} if { var(txn.namespace) -- - } {{- end }} {{- end }} @@ -883,76 +875,76 @@ frontend {{ $frontend.Name }} {{- /*------------------------------------*/}} {{- if $frontend.HasMaxBody }} - http-request set-var(req.maxbody) var(req.base),map_beg_int({{ $frontend.MaxBodySizeMap.MatchFile }},0) + http-request set-var(req.maxbody) var(req.base),map_beg_int({{ $fmaps.MaxBodySizeMap.MatchFile }},0) {{- end }} {{- /*------------------------------------*/}} -{{- if $frontend.HasTLSAuth }} -{{- $mandatory := $frontend.HasTLSMandatory }} +{{- if $hosts.HasTLSAuth }} +{{- $mandatory := $hosts.HasTLSMandatory }} acl tls-has-crt ssl_c_used {{- if $mandatory }} - acl tls-need-crt ssl_fc_sni -i -f {{ $frontend.TLSNoCrtErrorList.MatchFile }} -{{- if $frontend.TLSNoCrtErrorList.HasRegex }} - acl tls-need-crt ssl_fc_sni -i -m reg -f {{ $frontend.TLSNoCrtErrorList.RegexFile }} + acl tls-need-crt ssl_fc_sni -i -f {{ $fmaps.TLSNoCrtErrorList.MatchFile }} +{{- if $fmaps.TLSNoCrtErrorList.HasRegex }} + acl tls-need-crt ssl_fc_sni -i -m reg -f {{ $fmaps.TLSNoCrtErrorList.RegexFile }} {{- end }} {{- end }} - acl tls-host-need-crt var(req.host) -i -f {{ $frontend.TLSNoCrtErrorList.MatchFile }} -{{- if $frontend.TLSNoCrtErrorList.HasRegex }} - acl tls-host-need-crt var(req.host) -i -m reg -f {{ $frontend.TLSNoCrtErrorList.RegexFile }} + acl tls-host-need-crt var(req.host) -i -f {{ $fmaps.TLSNoCrtErrorList.MatchFile }} +{{- if $fmaps.TLSNoCrtErrorList.HasRegex }} + acl tls-host-need-crt var(req.host) -i -m reg -f {{ $fmaps.TLSNoCrtErrorList.RegexFile }} {{- end }} acl tls-has-invalid-crt ssl_c_ca_err gt 0 acl tls-has-invalid-crt ssl_c_err gt 0 - acl tls-check-crt ssl_fc_sni -i -f {{ $frontend.TLSInvalidCrtErrorList.MatchFile }} -{{- if $frontend.TLSInvalidCrtErrorList.HasRegex }} - acl tls-check-crt ssl_fc_sni -i -m reg -f {{ $frontend.TLSInvalidCrtErrorList.RegexFile }} + acl tls-check-crt ssl_fc_sni -i -f {{ $fmaps.TLSInvalidCrtErrorList.MatchFile }} +{{- if $fmaps.TLSInvalidCrtErrorList.HasRegex }} + acl tls-check-crt ssl_fc_sni -i -m reg -f {{ $fmaps.TLSInvalidCrtErrorList.RegexFile }} {{- end }} http-request set-var(req.path) path http-request set-var(req.snibase) ssl_fc_sni,concat(,req.path),lower -{{- if $frontend.SNIBackendsMap.HasRegex }} +{{- if $fmaps.SNIBackendsMap.HasRegex }} http-request set-var(req.snibackend) var(req.snibase) - {{- "" }},map_beg({{ $frontend.SNIBackendsMap.MatchFile }}) + {{- "" }},map_beg({{ $fmaps.SNIBackendsMap.MatchFile }}) http-request set-var(req.snibackend) var(req.snibase) - {{- "" }},map_reg({{ $frontend.SNIBackendsMap.RegexFile }}) + {{- "" }},map_reg({{ $fmaps.SNIBackendsMap.RegexFile }}) {{- "" }} if !{ var(req.snibackend) -m found } http-request set-var(req.snibackend) var(req.base) - {{- "" }},map_beg({{ $frontend.SNIBackendsMap.MatchFile }}) + {{- "" }},map_beg({{ $fmaps.SNIBackendsMap.MatchFile }}) {{- "" }} if !{ var(req.snibackend) -m found } {{- "" }} !tls-has-crt !tls-host-need-crt http-request set-var(req.snibackend) var(req.base) - {{- "" }},map_reg({{ $frontend.SNIBackendsMap.RegexFile }}) + {{- "" }},map_reg({{ $fmaps.SNIBackendsMap.RegexFile }}) {{- "" }} if !{ var(req.snibackend) -m found } {{- "" }} !tls-has-crt !tls-host-need-crt {{- else }} http-request set-var(req.snibackend) var(req.snibase) - {{- "" }},map_beg({{ $frontend.SNIBackendsMap.MatchFile }}) + {{- "" }},map_beg({{ $fmaps.SNIBackendsMap.MatchFile }}) http-request set-var(req.snibackend) var(req.base) - {{- "" }},map_beg({{ $frontend.SNIBackendsMap.MatchFile }}) + {{- "" }},map_beg({{ $fmaps.SNIBackendsMap.MatchFile }}) {{- "" }} if !{ var(req.snibackend) -m found } {{- "" }} !tls-has-crt !tls-host-need-crt {{- end }} {{- if $mandatory }} http-request set-var(req.tls_nocrt_redir) ssl_fc_sni,lower - {{- "" }},map({{ $frontend.TLSNoCrtErrorPagesMap.MatchFile }},_internal) + {{- "" }},map({{ $fmaps.TLSNoCrtErrorPagesMap.MatchFile }},_internal) {{- "" }} if !tls-has-crt tls-need-crt -{{- if $frontend.TLSNoCrtErrorPagesMap.HasRegex }} +{{- if $fmaps.TLSNoCrtErrorPagesMap.HasRegex }} http-request set-var(req.tls_nocrt_redir) ssl_fc_sni,lower - {{- "" }},map_reg({{ $frontend.TLSNoCrtErrorPagesMap.RegexFile }},_internal) + {{- "" }},map_reg({{ $fmaps.TLSNoCrtErrorPagesMap.RegexFile }},_internal) {{- "" }} if { var(req.tls_nocrt_redir) _internal } {{- end }} {{- end }} http-request set-var(req.tls_invalidcrt_redir) ssl_fc_sni,lower - {{- "" }},map({{ $frontend.TLSInvalidCrtErrorPagesMap.MatchFile }},_internal) + {{- "" }},map({{ $fmaps.TLSInvalidCrtErrorPagesMap.MatchFile }},_internal) {{- "" }} if tls-has-invalid-crt tls-check-crt -{{- if $frontend.TLSInvalidCrtErrorPagesMap.HasRegex }} +{{- if $fmaps.TLSInvalidCrtErrorPagesMap.HasRegex }} http-request set-var(req.tls_invalidcrt_redir) ssl_fc_sni,lower - {{- "" }},map_reg({{ $frontend.TLSInvalidCrtErrorPagesMap.RegexFile }},_internal) + {{- "" }},map_reg({{ $fmaps.TLSInvalidCrtErrorPagesMap.RegexFile }},_internal) {{- "" }} if { var(req.tls_invalidcrt_redir) _internal } {{- end }} -{{- if and $mandatory $frontend.HasNoCrtErrorPage }} +{{- if and $mandatory $hosts.HasNoCrtErrorPage }} http-request redirect location %[var(req.tls_nocrt_redir)] code 303 {{- "" }} if { var(req.tls_nocrt_redir) -m found } !{ var(req.tls_nocrt_redir) _internal } {{- end }} -{{- if $frontend.HasInvalidErrorPage }} +{{- if $hosts.HasInvalidErrorPage }} http-request redirect location %[var(req.tls_invalidcrt_redir)] code 303 {{- "" }} if { var(req.tls_invalidcrt_redir) -m found } !{ var(req.tls_invalidcrt_redir) _internal } {{- end }} @@ -968,10 +960,10 @@ frontend {{ $frontend.Name }} http-request use-service lua.send-413 if {{- "" }} !{ var(req.maxbody) 0 } { req.body_size,sub(req.maxbody) gt 0 } {{- end }} -{{- if $frontend.HasTLSAuth }} +{{- if $hosts.HasTLSAuth }} http-request use-service lua.send-421 if {{- "" }} tls-has-crt { ssl_fc_has_sni } !{ ssl_fc_sni,strcmp(req.host) eq 0 } -{{- if $frontend.HasTLSMandatory }} +{{- if $hosts.HasTLSMandatory }} http-request use-service lua.send-496 if {{- "" }} { var(req.tls_nocrt_redir) _internal } {{- /* HTTP 421 instead of 404 if missing sni or the provided one doesn't read client crt. */}} @@ -986,21 +978,21 @@ frontend {{ $frontend.Name }} {{- /*------------------------------------*/}} use_backend %[var(req.hostbackend)] {{- "" }} if { var(req.hostbackend) -m found } -{{- if $frontend.HasTLSAuth }} +{{- if $hosts.HasTLSAuth }} use_backend %[var(req.snibackend)] {{- "" }} if { var(req.snibackend) -m found } {{- end }} {{- template "defaultbackend" map $cfg }} -{{- end }} -{{- end }}{{/* if $fgroup */}} +{{- end }}{{/* if $fmaps */}} {{- /*------------------------------------*/}} {{- /*------------------------------------*/}} {{- define "defaultbackend" }} {{- $cfg := .p1 }} -{{- if $cfg.DefaultHost }} -{{- range $path := $cfg.DefaultHost.Paths }} +{{- $hosts := $cfg.Hosts }} +{{- if $hosts.DefaultHost }} +{{- range $path := $hosts.DefaultHost.Paths }} use_backend {{ $path.Backend.ID }} {{- if ne $path.Path "/" }} if { path_beg {{ $path.Path }} }{{ end }} {{- end }}