From f09b4cb805e0851556614acafea9b8580ab74a3a Mon Sep 17 00:00:00 2001 From: Joao Morais Date: Wed, 17 Mar 2021 20:55:55 -0300 Subject: [PATCH] fix prefix path type if the path matches a domain Map keys are built using sysadmin/user defined hostnames and paths. We concatenate both when building the map file, and also concatenate hdr(host) and path from the incoming request. This works pretty well on beg and reg match types. Since v0.11 we have also dir (Prefix from ingress pathType) and str (Exact). The later also work well with this construction, but str (Prefix) does not behave as expected from the end user perspective. Take this map example: domain.local/ backend1 sub.domain.local/ backend2 and this request: sub.domain.local/domain.local A begin or regex match type would choose backend2, and this is the expected behavior for prefix as well, but due to how haproxy implements -m dir, backend1 would be chosen. When using dir match type, haproxy slices the request using the slash as separator. These slices tries to match a whole map entry, but without starting from the beginning. A hash # is added after the hostname and before the first slash, leaving the first item of the slice as unique. This is only needed for dir / Prefix, but will not hurt other match types and does not have any performance degradation. Now the map looks like this: domain.local#/ backend1 sub.domain.local#/ backend2 and the request is internally converted to this: sub.domain.local#/domain.local Should be merged as far as v0.11. --- pkg/haproxy/instance_test.go | 234 +++++++++++----------- pkg/haproxy/types/maps.go | 19 +- pkg/haproxy/types/maps_test.go | 42 ++-- rootfs/etc/templates/haproxy/haproxy.tmpl | 6 +- 4 files changed, 158 insertions(+), 143 deletions(-) diff --git a/pkg/haproxy/instance_test.go b/pkg/haproxy/instance_test.go index 30d4b5d89..5aea7deef 100644 --- a/pkg/haproxy/instance_test.go +++ b/pkg/haproxy/instance_test.go @@ -140,8 +140,8 @@ func TestBackends(t *testing.T) { http-response set-header Access-Control-Allow-Credentials "true" if { var(txn.pathID) path01 }`, expCheck: map[string]string{ "_back_d1_app_8080_idpath__begin.map": ` -d1.local/sub path02 -d1.local/ path01`, +d1.local#/sub path02 +d1.local#/ path01`, }, }, { @@ -176,9 +176,9 @@ d1.local/ path01`, http-response set-header Strict-Transport-Security "max-age=15768000" if https-request { var(txn.pathID) path02 path03 }`, expCheck: map[string]string{ "_back_d1_app_8080_idpath__begin.map": ` -d1.local/uri path03 -d1.local/path path02 -d1.local/ path01`, +d1.local#/uri path03 +d1.local#/path path02 +d1.local#/ path01`, }, }, { @@ -233,9 +233,9 @@ d1.local/ path01`, http-request replace-path ^/path3(.*)$ /sub2\1 if { var(txn.pathID) path03 }`, expCheck: map[string]string{ "_back_d1_app_8080_idpath__begin.map": ` -d1.local/path3 path03 -d1.local/path2 path02 -d1.local/path1 path01`, +d1.local#/path3 path03 +d1.local#/path2 path02 +d1.local#/path1 path01`, }, }, { @@ -284,9 +284,9 @@ d1.local/path1 path01`, http-request deny if { var(txn.pathID) path03 } !allow_rule_src1`, expCheck: map[string]string{ "_back_d1_app_8080_idpath__begin.map": ` -d1.local/path path03 -d1.local/app path01 -d1.local/api path02`, +d1.local#/path path03 +d1.local#/app path01 +d1.local#/api path02`, }, }, { @@ -318,14 +318,14 @@ d1.local/api path02`, expFronts: "<>", expCheck: map[string]string{ "_back_d1_app_8080_idpath__prefix_01.map": ` -d1.local/path path04`, +d1.local#/path path04`, "_back_d1_app_8080_idpath__exact_02.map": ` -d1.local/app path02`, +d1.local#/app path02`, "_back_d1_app_8080_idpath__begin.map": ` -d1.local/api path03 -d1.local/ path01`, +d1.local#/api path03 +d1.local#/ path01`, "_back_d1_app_8080_idpath__regex.map": ` -^d1\.local/api/v[0-9]+/ path05`, +^d1\.local#/api/v[0-9]+/ path05`, }, }, { @@ -350,9 +350,9 @@ d1.local/ path01`, http-request deny if { var(txn.pathID) path02 } deny_rule_src1 !deny_exception_src1`, expCheck: map[string]string{ "_back_d1_app_8080_idpath__begin.map": ` -d1.local/app3 path03 -d1.local/app2 path02 -d1.local/app1 path01`, +d1.local#/app3 path03 +d1.local#/app2 path02 +d1.local#/app1 path01`, }, }, { @@ -379,10 +379,10 @@ d1.local/app1 path01`, http-request deny if { var(txn.pathID) path04 } !deny_exception_src2`, expCheck: map[string]string{ "_back_d1_app_8080_idpath__begin.map": ` -d1.local/app4 path04 -d1.local/app3 path03 -d1.local/app2 path02 -d1.local/app1 path01`, +d1.local#/app4 path04 +d1.local#/app3 path03 +d1.local#/app2 path02 +d1.local#/app1 path01`, }, }, { @@ -404,9 +404,9 @@ d1.local/app1 path01`, http-request deny if { var(txn.pathID) path03 } !allow_rule_src1`, expCheck: map[string]string{ "_back_d1_app_8080_idpath__begin.map": ` -d1.local/path path03 -d1.local/app path01 -d1.local/api path02`, +d1.local#/path path03 +d1.local#/app path01 +d1.local#/api path02`, }, }, { @@ -462,8 +462,8 @@ d1.local/api path02`, http-request use-service lua.send-413 if { var(txn.pathID) path02 } { req.body_size,sub(2048) gt 0 }`, expCheck: map[string]string{ "_back_d1_app_8080_idpath__begin.map": ` -d1.local/app path02 -d1.local/ path01`, +d1.local#/app path02 +d1.local#/ path01`, }, }, { @@ -956,9 +956,9 @@ backend _error404 `) c.checkMap("_front_http_host__begin.map", ` -empty/ default_empty_8080`) +empty#/ default_empty_8080`) c.checkMap("_front_https_host__begin.map", ` -empty/ default_empty_8080`) +empty#/ default_empty_8080`) c.logger.CompareLogging(defaultLogging) } @@ -1013,9 +1013,9 @@ backend _error404 `) c.checkMap("_front_http_host__begin.map", ` -empty/ default_empty_8080`) +empty#/ default_empty_8080`) c.checkMap("_front_https_host__begin.map", ` -empty/ default_empty_8080`) +empty#/ default_empty_8080`) c.logger.CompareLogging(defaultLogging) } @@ -1230,13 +1230,13 @@ frontend _front_https `) c.checkMap("_front_http_host__prefix.map", ` -d1.local/app default_d1_8080`) +d1.local#/app default_d1_8080`) c.checkMap("_front_http_host__regex.map", ` -^d1\.local/api/v[0-9]+/ default_d1_8080`) +^d1\.local#/api/v[0-9]+/ default_d1_8080`) c.checkMap("_front_https_host__prefix.map", ` -d1.local/app default_d1_8080`) +d1.local#/app default_d1_8080`) c.checkMap("_front_https_host__regex.map", ` -^d1\.local/api/v[0-9]+/ default_d1_8080`) +^d1\.local#/api/v[0-9]+/ default_d1_8080`) c.logger.CompareLogging(defaultLogging) } @@ -1288,13 +1288,13 @@ func TestInstanceFrontingProxy(t *testing.T) { http-request set-header X-SSL-Client-SHA1 %{+Q}[ssl_c_sha1,hex] http-response set-header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload"` setvarBegin = ` - http-request set-var(req.snibase) ssl_fc_sni,lower,concat(,req.path) + http-request set-var(req.snibase) ssl_fc_sni,lower,concat(\#,req.path) http-request set-var(req.snibackend) var(req.snibase),lower,map_beg(/etc/haproxy/maps/_front_https_sni__begin.map) http-request set-var(req.snibackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_sni__begin.map) if !{ var(req.snibackend) -m found } !tls-has-crt !tls-host-need-crt http-request set-var(req.tls_nocrt_redir) str(_internal) if !tls-has-crt tls-need-crt http-request set-var(req.tls_invalidcrt_redir) str(_internal) if tls-has-invalid-crt tls-check-crt` setvarRegex = ` - http-request set-var(req.snibase) ssl_fc_sni,lower,concat(,req.path) + http-request set-var(req.snibase) ssl_fc_sni,lower,concat(\#,req.path) http-request set-var(req.snibackend) var(req.snibase),map_reg(/etc/haproxy/maps/_front_https_sni__regex.map) http-request set-var(req.snibackend) var(req.base),map_reg(/etc/haproxy/maps/_front_https_sni__regex.map) if !{ var(req.snibackend) -m found } !tls-has-crt !tls-host-need-crt http-request set-var(req.tls_nocrt_redir) str(_internal) if !tls-has-crt tls-need-crt @@ -1328,7 +1328,7 @@ func TestInstanceFrontingProxy(t *testing.T) { bind :80 bind :8000 id 11 acl fronting-proxy so_id 11` + frontUseProto, - expectedMap: "d1.local/ d1_app_8080", + expectedMap: "d1.local#/ d1_app_8080", expectedACLFront: aclFrontExact, expectedSetvar: setvarBegin, }, @@ -1355,7 +1355,7 @@ func TestInstanceFrontingProxy(t *testing.T) { http-request del-header X-SSL-Client-Cert if !fronting-proxy http-request set-var(req.backend) var(req.base),map_reg(/etc/haproxy/maps/_front_http_host__regex.map) use_backend %[var(req.backend)] if { var(req.backend) -m found }`, - expectedRegexMap: `^[^.]+\.d1\.local/ d1_app_8080`, + expectedRegexMap: `^[^.]+\.d1\.local#/ d1_app_8080`, expectedACLFront: aclFrontRegex, expectedSetvar: setvarRegex, }, @@ -1373,7 +1373,7 @@ func TestInstanceFrontingProxy(t *testing.T) { mode http bind :80 acl fronting-proxy hdr(X-Forwarded-Proto) -m found` + frontUseProto, - expectedMap: "d1.local/ d1_app_8080", + expectedMap: "d1.local#/ d1_app_8080", expectedACLFront: aclFrontExact, expectedSetvar: setvarBegin, }, @@ -1392,7 +1392,7 @@ func TestInstanceFrontingProxy(t *testing.T) { <> http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map) use_backend %[var(req.backend)] if { var(req.backend) -m found }`, - expectedMap: "d1.local/ d1_app_8080", + expectedMap: "d1.local#/ d1_app_8080", expectedACLFront: aclFrontExact, expectedSetvar: setvarBegin, }, @@ -1411,7 +1411,7 @@ func TestInstanceFrontingProxy(t *testing.T) { <> http-request set-var(req.backend) var(req.base),map_reg(/etc/haproxy/maps/_front_http_host__regex.map) use_backend %[var(req.backend)] if { var(req.backend) -m found }`, - expectedRegexMap: `^[^.]+\.d1\.local/ d1_app_8080`, + expectedRegexMap: `^[^.]+\.d1\.local#/ d1_app_8080`, expectedACLFront: aclFrontRegex, expectedSetvar: setvarRegex, }, @@ -1429,7 +1429,7 @@ func TestInstanceFrontingProxy(t *testing.T) { <> http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map) use_backend %[var(req.backend)] if { var(req.backend) -m found }`, - expectedMap: "d1.local/ d1_app_8080", + expectedMap: "d1.local#/ d1_app_8080", expectedACLFront: aclFrontExact, expectedSetvar: setvarBegin, }, @@ -1449,7 +1449,7 @@ func TestInstanceFrontingProxy(t *testing.T) { bind :80 bind :8000 id 11 acl fronting-proxy so_id 11` + frontUseProto, - expectedMap: "d1.local/ d1_app_8080", + expectedMap: "d1.local#/ d1_app_8080", expectedACLFront: aclFrontExact, expectedSetvar: setvarBegin, }, @@ -1468,7 +1468,7 @@ func TestInstanceFrontingProxy(t *testing.T) { mode http bind :80 acl fronting-proxy hdr(X-Forwarded-Proto) -m found` + frontUseProto, - expectedMap: "d1.local/ d1_app_8080", + expectedMap: "d1.local#/ d1_app_8080", expectedACLFront: aclFrontExact, expectedSetvar: setvarBegin, }, @@ -1487,7 +1487,7 @@ func TestInstanceFrontingProxy(t *testing.T) { <> http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map) use_backend %[var(req.backend)] if { var(req.backend) -m found }`, - expectedMap: "d1.local/ d1_app_8080", + expectedMap: "d1.local#/ d1_app_8080", expectedACLFront: aclFrontExact, expectedSetvar: setvarBegin, }, @@ -1505,7 +1505,7 @@ func TestInstanceFrontingProxy(t *testing.T) { <> http-request set-var(req.backend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_http_host__begin.map) use_backend %[var(req.backend)] if { var(req.backend) -m found }`, - expectedMap: "d1.local/ d1_app_8080", + expectedMap: "d1.local#/ d1_app_8080", expectedACLFront: aclFrontExact, expectedSetvar: setvarBegin, }, @@ -1766,16 +1766,16 @@ frontend _front_https `) c.checkMap("_front_http_host__begin.map", ` -d2.local/app d2_app_8080 +d2.local#/app d2_app_8080 `) c.checkMap("_front_bind_crt.list", ` /var/haproxy/ssl/certs/default.pem !* `) c.checkMap("_front_namespace__begin.map", ` -d2.local/app d2 +d2.local#/app d2 `) c.checkMap("_front_https_host__begin.map", ` -d2.local/app d2_app_8080 +d2.local#/app d2_app_8080 `) c.logger.CompareLogging(defaultLogging) @@ -1806,12 +1806,12 @@ backend d1_app_8080 <> `) c.checkMap("_front_http_host__begin.map", ` -d1.local/path d1_app_8080 -d1.local/ _error404 +d1.local#/path d1_app_8080 +d1.local#/ _error404 `) c.checkMap("_front_https_host__begin.map", ` -d1.local/path d1_app_8080 -d1.local/ _error404 +d1.local#/path d1_app_8080 +d1.local#/ _error404 `) c.logger.CompareLogging(defaultLogging) } @@ -1855,12 +1855,12 @@ backend d2_app_8080 <> `) c.checkMap("_front_http_host__begin.map", ` -d1.local/path d1_app_8080 -d1.local/ d2_app_8080 +d1.local#/path d1_app_8080 +d1.local#/ d2_app_8080 `) c.checkMap("_front_https_host__begin.map", ` -d1.local/path d1_app_8080 -d1.local/ d2_app_8080 +d1.local#/path d1_app_8080 +d1.local#/ d2_app_8080 `) c.logger.CompareLogging(defaultLogging) } @@ -1938,22 +1938,22 @@ frontend _front_https `) c.checkMap("_front_http_host__begin.map", ` -d1.local/ d1_app_8080 +d1.local#/ d1_app_8080 `) c.checkMap("_front_http_host__prefix.map", ` -d2.local/app d2_app_8080 +d2.local#/app d2_app_8080 `) c.checkMap("_front_https_host__begin.map", ` -d1.local/ d1_app_8080 +d1.local#/ d1_app_8080 `) c.checkMap("_front_https_host__prefix.map", ` -d2.local/app d2_app_8080 +d2.local#/app d2_app_8080 `) c.checkMap("_front_namespace__begin.map", ` -d1.local/ d1 +d1.local#/ d1 `) c.checkMap("_front_namespace__prefix.map", ` -d2.local/app - +d2.local#/app - `) c.checkMap("_front_bind_crt.list", ` @@ -2066,7 +2066,7 @@ frontend _front_https acl tls-has-invalid-crt ssl_c_verify gt 0 acl tls-check-crt ssl_fc_sni -i -m str -f /etc/haproxy/maps/_front_tls_auth__exact.list acl tls-check-crt ssl_fc_sni -i -m reg -f /etc/haproxy/maps/_front_tls_auth__regex.list - http-request set-var(req.snibase) ssl_fc_sni,lower,concat(,req.path) + http-request set-var(req.snibase) ssl_fc_sni,lower,concat(\#,req.path) http-request set-var(req.snibackend) var(req.snibase),lower,map_beg(/etc/haproxy/maps/_front_https_sni__begin.map) http-request set-var(req.snibackend) var(req.snibase),map_reg(/etc/haproxy/maps/_front_https_sni__regex.map) if !{ var(req.snibackend) -m found } http-request set-var(req.snibackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_sni__begin.map) if !{ var(req.snibackend) -m found } !tls-has-crt !tls-host-need-crt @@ -2088,14 +2088,14 @@ frontend _front_https `) c.checkMap("_front_http_host__begin.map", ` -d2.local/ d_app_8080 -d3.local/ d_app_8080 -d4.local/ d_app_8080 -d5.local/ d_app_8080 -d6.local/ d_app_8080 +d2.local#/ d_app_8080 +d3.local#/ d_app_8080 +d4.local#/ d_app_8080 +d5.local#/ d_app_8080 +d6.local#/ d_app_8080 `) c.checkMap("_front_http_host__regex.map", ` -^[^.]+\.d1\.local/ d_app_8080 +^[^.]+\.d1\.local#/ d_app_8080 `) c.checkMap("_front_bind_crt.list", ` /var/haproxy/ssl/certs/default.pem !* @@ -2107,16 +2107,16 @@ d6.local/ d_app_8080 /var/haproxy/ssl/certs/default.pem [ssl-min-ver TLSv1.0 ssl-max-ver TLSv1.2] d6.local `) c.checkMap("_front_https_host__begin.map", ` -d3.local/ d_app_8080 -d4.local/ d_app_8080 -d5.local/ d_app_8080 -d6.local/ d_app_8080 +d3.local#/ d_app_8080 +d4.local#/ d_app_8080 +d5.local#/ d_app_8080 +d6.local#/ d_app_8080 `) c.checkMap("_front_https_sni__begin.map", ` -d2.local/ d_app_8080 +d2.local#/ d_app_8080 `) c.checkMap("_front_https_sni__regex.map", ` -^[^.]+\.d1\.local/ d_app_8080 +^[^.]+\.d1\.local#/ d_app_8080 `) c.checkMap("_front_tls_needcrt__exact.list", ` d2.local @@ -2206,16 +2206,16 @@ backend default_default-backend_8080 `) c.checkMap("_front_http_host__begin.map", ` -d.local/sub d_app3_8080 -d.local/app/sub d_app2_8080 -d.local/app d_app1_8080 -d.local/ d_app0_8080 +d.local#/sub d_app3_8080 +d.local#/app/sub d_app2_8080 +d.local#/app d_app1_8080 +d.local#/ d_app0_8080 `) c.checkMap("_front_https_host__begin.map", ` -d.local/sub d_app3_8080 -d.local/app/sub d_app2_8080 -d.local/app d_app1_8080 -d.local/ d_app0_8080 +d.local#/sub d_app3_8080 +d.local#/app/sub d_app2_8080 +d.local#/app d_app1_8080 +d.local#/ d_app0_8080 `) c.logger.CompareLogging(defaultLogging) @@ -2380,8 +2380,8 @@ frontend _front_https d2.local d2_app_8080 d3.local d3_app-ssl_8443`) c.checkMap("_front_http_host__begin.map", ` -d2.local/ _redirect_https -d3.local/ d3_app-http_8080`) +d2.local#/ _redirect_https +d3.local#/ d3_app-http_8080`) c.checkMap("_front_bind_crt.list", ` /var/haproxy/ssl/certs/default.pem !* `) @@ -2458,14 +2458,14 @@ frontend _front_https `) c.checkMap("_front_http_host__regex.map", ` -^[^.]+\.d1\.local/ d1_app_8080 +^[^.]+\.d1\.local#/ d1_app_8080 `) c.checkMap("_front_http_host__begin.map", ` -d2.local/app2 d2_app_8080 -d2.local/app1 d2_app_8080 +d2.local#/app2 d2_app_8080 +d2.local#/app1 d2_app_8080 `) c.checkMap("_front_http_host__regex.map", ` -^[^.]+\.d1\.local/ d1_app_8080 +^[^.]+\.d1\.local#/ d1_app_8080 `) c.checkMap("_front_redir_fromroot__exact.map", ` d2.local /app1 @@ -2474,11 +2474,11 @@ d2.local /app1 ^[^.]+\.d1\.local$ /app `) c.checkMap("_front_https_host__begin.map", ` -d2.local/app2 d2_app_8080 -d2.local/app1 d2_app_8080 +d2.local#/app2 d2_app_8080 +d2.local#/app1 d2_app_8080 `) c.checkMap("_front_https_host__regex.map", ` -^[^.]+\.d1\.local/ d1_app_8080 +^[^.]+\.d1\.local#/ d1_app_8080 `) c.logger.CompareLogging(defaultLogging) @@ -2546,21 +2546,21 @@ frontend _front_https `) c.checkMap("_front_http_host__begin.map", ` -d1.local/ d1_app_8080 -d2.local/ d2_app_8080 -d3.local/ d3_app_8080 -sub.d2.local/ d2_app_8080 +d1.local#/ d1_app_8080 +d2.local#/ d2_app_8080 +d3.local#/ d3_app_8080 +sub.d2.local#/ d2_app_8080 `) c.checkMap("_front_https_host__begin.map", ` -d1.local/ d1_app_8080 -d2.local/ d2_app_8080 -d3.local/ d3_app_8080 -sub.d2.local/ d2_app_8080 +d1.local#/ d1_app_8080 +d2.local#/ d2_app_8080 +d3.local#/ d3_app_8080 +sub.d2.local#/ d2_app_8080 `) c.checkMap("_front_https_host__regex.map", ` -^[a-z]+\.d2\.local/ d2_app_8080 -^[^.]+\.d1\.local/ d1_app_8080 -d3\.local/ d3_app_8080 +^[a-z]+\.d2\.local#/ d2_app_8080 +^[^.]+\.d1\.local#/ d1_app_8080 +d3\.local#/ d3_app_8080 `) c.logger.CompareLogging(defaultLogging) } @@ -3190,7 +3190,7 @@ frontend _front_https acl tls-has-crt ssl_c_used acl tls-has-invalid-crt ssl_c_verify gt 0 acl tls-check-crt ssl_fc_sni -i -m reg -f /etc/haproxy/maps/_front_tls_auth__regex.list - http-request set-var(req.snibase) ssl_fc_sni,lower,concat(,req.path) + http-request set-var(req.snibase) ssl_fc_sni,lower,concat(\#,req.path) http-request set-var(req.snibackend) var(req.snibase),map_reg(/etc/haproxy/maps/_front_https_sni__regex.map) http-request set-var(req.snibackend) var(req.base),map_reg(/etc/haproxy/maps/_front_https_sni__regex.map) if !{ var(req.snibackend) -m found } !tls-has-crt http-request set-var(req.tls_invalidcrt_redir) ssl_fc_sni,lower,map_reg(/etc/haproxy/maps/_front_tls_invalidcrt_pages__regex.map,_internal) if tls-has-invalid-crt tls-check-crt @@ -3204,28 +3204,28 @@ frontend _front_https `) c.checkMap("_front_http_host__begin.map", ` -d1.local/ d1_app_8080 +d1.local#/ d1_app_8080 `) c.checkMap("_front_http_host__regex.map", ` -^[^.]+\.app\.d1\.local/ d1_app_8080 -^[^.]+\.sub\.d1\.local/ d1_app_8080 -^[^.]+\.d2\.local/ d2_app_8080 +^[^.]+\.app\.d1\.local#/ d1_app_8080 +^[^.]+\.sub\.d1\.local#/ d1_app_8080 +^[^.]+\.d2\.local#/ d2_app_8080 `) c.checkMap("_front_redir_fromroot__regex.map", ` ^[^.]+\.d2\.local$ /app `) c.checkMap("_front_https_host__begin.map", ` -d1.local/ d1_app_8080 +d1.local#/ d1_app_8080 `) c.checkMap("_front_https_host__regex.map", ` -^[^.]+\.app\.d1\.local/ d1_app_8080 -^[^.]+\.d2\.local/ d2_app_8080 +^[^.]+\.app\.d1\.local#/ d1_app_8080 +^[^.]+\.d2\.local#/ d2_app_8080 `) c.checkMap("_front_redir_fromroot__regex.map", ` ^[^.]+\.d2\.local$ /app `) c.checkMap("_front_https_sni__regex.map", ` -^[^.]+\.sub\.d1\.local/ d1_app_8080 +^[^.]+\.sub\.d1\.local#/ d1_app_8080 `) c.checkMap("_front_tls_auth__regex.list", ` ^[^.]+\.sub\.d1\.local$ @@ -3518,7 +3518,7 @@ func (c *testConfig) checkConfigFile(expected, fileName string) { http-request use-service lua.send-404`, " <>": ` http-request set-var(req.path) path http-request set-var(req.host) hdr(host),field(1,:),lower - http-request set-var(req.base) var(req.host),concat(,req.path)`, + http-request set-var(req.base) var(req.host),concat(\#,req.path)`, " <>": ` http-request set-header X-Forwarded-Proto http http-request del-header X-SSL-Client-CN http-request del-header X-SSL-Client-DN @@ -3557,7 +3557,7 @@ func (c *testConfig) checkConfigFile(expected, fileName string) { bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front_bind_crt.list ca-ignore-err all crt-ignore-err all http-request set-var(req.path) path http-request set-var(req.host) hdr(host),field(1,:),lower - http-request set-var(req.base) var(req.host),concat(,req.path) + http-request set-var(req.base) var(req.host),concat(\#,req.path) http-request set-var(req.hostbackend) var(req.base),lower,map_beg(/etc/haproxy/maps/_front_https_host__begin.map) <> use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }`, diff --git a/pkg/haproxy/types/maps.go b/pkg/haproxy/types/maps.go index 22fc70d0c..fe24290ba 100644 --- a/pkg/haproxy/types/maps.go +++ b/pkg/haproxy/types/maps.go @@ -123,7 +123,7 @@ func (hm *HostsMap) addTarget(hostname, path, target string, match MatchType) { hostname: hostname, path: path, match: match, - Key: bindHostnamePath(match, hostname, path), + Key: buildMapKey(match, hostname, path), Value: target, } matchFile := hm.rawfiles[match] @@ -136,7 +136,7 @@ func (hm *HostsMap) addTarget(hostname, path, target string, match MatchType) { hm.matchFiles = nil } -func bindHostnamePath(match MatchType, hostname, path string) string { +func buildMapKey(match MatchType, hostname, path string) string { if match == MatchRegex && hostname != "" && path != "" { // we support hostname with ^/$ boundaries // lets change the ending of the hostname @@ -147,6 +147,21 @@ func bindHostnamePath(match MatchType, hostname, path string) string { hostname = hostname + "[^/]*" } } + if path != "" { + // Fixes dir match type (Prefix from the ingress pathType) if a path or + // subpath matches a configured domain. Eg, this map: + // + // domain.local/ backend1 + // sub.domain.local/ backend2 + // + // and this request: + // + // sub.domain.local/domain.local + // + // backend2 would be chosen on beg and reg match types, but backend1 + // would be chosen if dir was used. + return hostname + "#" + path + } return hostname + path } diff --git a/pkg/haproxy/types/maps_test.go b/pkg/haproxy/types/maps_test.go index 174f46326..e22c3d4d4 100644 --- a/pkg/haproxy/types/maps_test.go +++ b/pkg/haproxy/types/maps_test.go @@ -88,7 +88,7 @@ func TestAddHostnamePathMapping(t *testing.T) { path: "/", match: MatchBegin, expmatch: MatchBegin, - expected: "example.local/", + expected: "example.local#/", }, // 1 { @@ -96,7 +96,7 @@ func TestAddHostnamePathMapping(t *testing.T) { path: "/", match: MatchBegin, expmatch: MatchRegex, - expected: "^[^.]+\\.example\\.local/", + expected: "^[^.]+\\.example\\.local#/", }, // 2 { @@ -104,7 +104,7 @@ func TestAddHostnamePathMapping(t *testing.T) { path: "/path", match: MatchBegin, expmatch: MatchRegex, - expected: "^[^.]+\\.example\\.local/path", + expected: "^[^.]+\\.example\\.local#/path", }, // 3 { @@ -112,7 +112,7 @@ func TestAddHostnamePathMapping(t *testing.T) { path: "/path", match: MatchRegex, expmatch: MatchRegex, - expected: "^example\\.local/path", + expected: "^example\\.local#/path", }, // 4 { @@ -120,7 +120,7 @@ func TestAddHostnamePathMapping(t *testing.T) { path: "/path[0-9]$", match: MatchRegex, expmatch: MatchRegex, - expected: "^example\\.local/path[0-9]$", + expected: "^example\\.local#/path[0-9]$", }, // 5 { @@ -128,7 +128,7 @@ func TestAddHostnamePathMapping(t *testing.T) { path: "/path/", match: MatchRegex, expmatch: MatchRegex, - expected: "^example\\.local/path/", + expected: "^example\\.local#/path/", }, // 6 { @@ -136,7 +136,7 @@ func TestAddHostnamePathMapping(t *testing.T) { path: "/.*path$", match: MatchRegex, expmatch: MatchRegex, - expected: "^[^.]+\\.example\\.local/.*path$", + expected: "^[^.]+\\.example\\.local#/.*path$", }, // 7 { @@ -144,7 +144,7 @@ func TestAddHostnamePathMapping(t *testing.T) { path: "/path", match: MatchPrefix, expmatch: MatchPrefix, - expected: "example.local/path", + expected: "example.local#/path", }, // 8 { @@ -152,7 +152,7 @@ func TestAddHostnamePathMapping(t *testing.T) { path: "/path.new", match: MatchPrefix, expmatch: MatchRegex, - expected: "^[^.]+\\.example\\.local/path\\.new(/.*)?", + expected: "^[^.]+\\.example\\.local#/path\\.new(/.*)?", }, // 9 { @@ -160,7 +160,7 @@ func TestAddHostnamePathMapping(t *testing.T) { path: "/path/", match: MatchPrefix, expmatch: MatchRegex, - expected: "^[^.]+\\.example\\.local/path/", + expected: "^[^.]+\\.example\\.local#/path/", }, // 10 { @@ -168,7 +168,7 @@ func TestAddHostnamePathMapping(t *testing.T) { path: "/path", match: MatchExact, expmatch: MatchExact, - expected: "example.local/path", + expected: "example.local#/path", }, // 11 { @@ -176,7 +176,7 @@ func TestAddHostnamePathMapping(t *testing.T) { path: "/path", match: MatchExact, expmatch: MatchRegex, - expected: "^[^.]+\\.example\\.local/path$", + expected: "^[^.]+\\.example\\.local#/path$", }, } for i, test := range testCases { @@ -213,7 +213,7 @@ func TestAddAliasPathMapping(t *testing.T) { path: "/", match: MatchBegin, expected: map[MatchType][]string{ - MatchBegin: {"example.local/"}, + MatchBegin: {"example.local#/"}, }, }, // 1 @@ -222,7 +222,7 @@ func TestAddAliasPathMapping(t *testing.T) { path: "/", match: MatchBegin, expected: map[MatchType][]string{ - MatchRegex: {"^[^.]+\\.example\\.local/"}, + MatchRegex: {"^[^.]+\\.example\\.local#/"}, }, }, // 2 @@ -231,7 +231,7 @@ func TestAddAliasPathMapping(t *testing.T) { path: "/", match: MatchBegin, expected: map[MatchType][]string{ - MatchRegex: {".*\\.local[^/]*/"}, + MatchRegex: {".*\\.local[^/]*#/"}, }, }, // 3 @@ -240,7 +240,7 @@ func TestAddAliasPathMapping(t *testing.T) { path: "/", match: MatchExact, expected: map[MatchType][]string{ - MatchRegex: {"^.*\\.local[^/]*/$"}, + MatchRegex: {"^.*\\.local[^/]*#/$"}, }, }, // 4 @@ -249,7 +249,7 @@ func TestAddAliasPathMapping(t *testing.T) { path: "/", match: MatchPrefix, expected: map[MatchType][]string{ - MatchRegex: {"^.*\\.local/"}, + MatchRegex: {"^.*\\.local#/"}, }, }, // 5 @@ -258,7 +258,7 @@ func TestAddAliasPathMapping(t *testing.T) { path: "/path", match: MatchPrefix, expected: map[MatchType][]string{ - MatchRegex: {"\\.local/path(/.*)?"}, + MatchRegex: {"\\.local#/path(/.*)?"}, }, }, // 6 @@ -267,7 +267,7 @@ func TestAddAliasPathMapping(t *testing.T) { path: "/path/", match: MatchPrefix, expected: map[MatchType][]string{ - MatchRegex: {"^.*\\.local/path/"}, + MatchRegex: {"^.*\\.local#/path/"}, }, }, // 7 @@ -277,8 +277,8 @@ func TestAddAliasPathMapping(t *testing.T) { path: "/path", match: MatchBegin, expected: map[MatchType][]string{ - MatchBegin: {"example.local/path"}, - MatchRegex: {"\\.local/path"}, + MatchBegin: {"example.local#/path"}, + MatchRegex: {"\\.local#/path"}, }, }, } diff --git a/rootfs/etc/templates/haproxy/haproxy.tmpl b/rootfs/etc/templates/haproxy/haproxy.tmpl index 091eb3928..6965c02fd 100644 --- a/rootfs/etc/templates/haproxy/haproxy.tmpl +++ b/rootfs/etc/templates/haproxy/haproxy.tmpl @@ -925,7 +925,7 @@ frontend _front_http {{- /*------------------------------------*/}} http-request set-var(req.path) path http-request set-var(req.host) hdr(host),field(1,:),lower - http-request set-var(req.base) var(req.host),concat(,req.path) + http-request set-var(req.base) var(req.host),concat(\#,req.path) {{- /*------------------------------------*/}} {{- $acmeexclusive := and $global.Acme.Enabled (not $global.Acme.Shared) }} @@ -1020,7 +1020,7 @@ frontend _front_https {{- if or $fmaps.RedirFromRootMap.HasHost $fmaps.HTTPSHostMap.HasHost $fmaps.HTTPSSNIMap.HasHost $fmaps.TLSAuthList.HasHost $fmaps.TLSNeedCrtList.HasHost $fmaps.VarNamespaceMap.HasHost }} http-request set-var(req.path) path http-request set-var(req.host) hdr(host),field(1,:),lower - http-request set-var(req.base) var(req.host),concat(,req.path) + http-request set-var(req.base) var(req.host),concat(\#,req.path) {{- end }} {{- /*------------------------------------*/}} @@ -1080,7 +1080,7 @@ frontend _front_https {{- end }} {{- if $fmaps.HTTPSSNIMap.HasHost }} - http-request set-var(req.snibase) ssl_fc_sni,lower,concat(,req.path) + http-request set-var(req.snibase) ssl_fc_sni,lower,concat(\#,req.path) {{- range $match := $fmaps.HTTPSSNIMap.MatchFiles }} http-request set-var(req.snibackend) var(req.snibase) {{- if $match.Lower }},lower{{ end }}