Skip to content

Commit

Permalink
Merge pull request #577 from jcmoraisjr/jm-use-proto
Browse files Browse the repository at this point in the history
Add use-forwarded-proto config key
  • Loading branch information
jcmoraisjr authored May 11, 2020
2 parents ae30cf2 + abebe87 commit 5831677
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 27 deletions.
21 changes: 15 additions & 6 deletions docs/content/en/docs/configuration/keys.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ The table below describes all supported configuration keys.
| [`timeout-tunnel`](#timeout) | time with suffix | Backend | `1h` |
| [`tls-alpn`](#tls-alpn) | TLS ALPN advertisement | Global | `h2,http/1.1` |
| [`use-chroot`](#security) | [true\|false] | Global | `false` |
| [`use-forwarded-proto`](#fronting-proxy-port) | [true\|false] | Global | `true` |
| [`use-haproxy-user`](#security) | [true\|false] | Global | `false` |
| [`use-htx`](#use-htx) | [true\|false] | Global | `false` |
| [`use-proxy-protocol`](#proxy-protocol) | [true\|false] | Global | `false` |
Expand Down Expand Up @@ -925,10 +926,11 @@ See also:

## Fronting proxy port

| Configuration key | Scope | Default | Since |
|-----------------------|----------|---------|--------|
| `fronting-proxy-port` | `Global` | | `v0.8` |
| `https-to-http-port` | `Global` | | |
| Configuration key | Scope | Default | Since |
|-----------------------|----------|---------|---------|
| `fronting-proxy-port` | `Global` | | `v0.8` |
| `https-to-http-port` | `Global` | | |
| `use-forwarded-proto` | `Global` | `true` | `v0.10` |

A port number to listen to http requests from a fronting proxy that does the ssl
offload, eg haproxy ingress behind a cloud load balancers that manages the TLS
Expand All @@ -937,8 +939,15 @@ certificates. `https-to-http-port` is an alias to `fronting-proxy-port`.
`fronting-proxy-port` and [`http-port`](#bind-port) can share the same port number, see below
what changes in the behaviour.

Requests made to `fronting-proxy-port` port number evaluate the `X-Forwarded-Proto`
header to decide how to handle the request:
`use-forwarded-proto` defines if haproxy should use `X-Forwarded-Proto` header to decide
how to handle requests made to `fronting-proxy-port` port number.

If `use-forwarded-proto` is `false`, the request takes the `https` route and is handled as if
`X-Forwarded-Proto` header is `https`, see below. The actual header content is ignored by
haproxy and forwarded to the backend if provided.

If `use-forwarded-proto` is `true`, the default value, requests made to `fronting-proxy-port`
port number evaluate the `X-Forwarded-Proto` header to decide how to handle the request:

* If `X-Forwarded-Proto` header is `https`:
* HAProxy will handle the request just like the ssl-offload was made by HAProxy itself - HSTS header is provided if configured and
Expand Down
1 change: 1 addition & 0 deletions pkg/converters/ingress/annotations/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ func (c *updater) buildGlobalHTTPStoHTTP(d *globalData) {
}
// TODO Change all `ToHTTP` naming to `FrontingProxy`
d.global.Bind.FrontingBind = bind
d.global.Bind.FrontingUseProto = d.mapper.Get(ingtypes.GlobalUseForwardedProto).Bool()
// Socket ID should be a high number to avoid colision
// between the same socket ID from distinct frontends
// TODO match socket and frontend ID in the backend
Expand Down
1 change: 1 addition & 0 deletions pkg/converters/ingress/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func createDefaults() map[string]string {
types.GlobalTimeoutClientFin: "50s",
types.GlobalTimeoutStop: "10m",
types.GlobalTLSALPN: "h2,http/1.1",
types.GlobalUseForwardedProto: "true",
types.GlobalUseHTX: "true",
}
}
1 change: 1 addition & 0 deletions pkg/converters/ingress/types/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ const (
GlobalTimeoutStop = "timeout-stop"
GlobalTLSALPN = "tls-alpn"
GlobalUseChroot = "use-chroot"
GlobalUseForwardedProto = "use-forwarded-proto"
GlobalUseHAProxyUser = "use-haproxy-user"
GlobalUseHTX = "use-htx"
GlobalUseProxyProtocol = "use-proxy-protocol"
Expand Down
187 changes: 178 additions & 9 deletions pkg/haproxy/instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -741,9 +741,9 @@ empty/ default_empty_8080`)
c.logger.CompareLogging(defaultLogging)
}

func TestInstanceToHTTPSocket(t *testing.T) {
func TestInstanceFrontingProxyUseProto(t *testing.T) {
testCases := []struct {
toHTTPBind string
frontingBind string
domain string
expectedFront string
expectedMap string
Expand All @@ -753,8 +753,8 @@ func TestInstanceToHTTPSocket(t *testing.T) {
}{
// 0
{
toHTTPBind: ":8000",
domain: "d1.local",
frontingBind: ":8000",
domain: "d1.local",
expectedFront: `
mode http
bind :80
Expand Down Expand Up @@ -788,8 +788,8 @@ func TestInstanceToHTTPSocket(t *testing.T) {
},
// 1
{
toHTTPBind: ":8000",
domain: "*.d1.local",
frontingBind: ":8000",
domain: "*.d1.local",
expectedFront: `
mode http
bind :80
Expand Down Expand Up @@ -831,8 +831,8 @@ func TestInstanceToHTTPSocket(t *testing.T) {
},
// 2
{
toHTTPBind: ":80",
domain: "d1.local",
frontingBind: ":80",
domain: "d1.local",
expectedFront: `
mode http
bind :80
Expand Down Expand Up @@ -887,8 +887,9 @@ func TestInstanceToHTTPSocket(t *testing.T) {
}
h.TLS.CAHash = "1"
h.TLS.CAFilename = "/var/haproxy/ssl/ca.pem"
c.config.Global().Bind.FrontingBind = test.toHTTPBind
c.config.Global().Bind.FrontingBind = test.frontingBind
c.config.Global().Bind.FrontingSockID = 11
c.config.Global().Bind.FrontingUseProto = true

c.Update()
c.checkConfig(`
Expand Down Expand Up @@ -937,6 +938,174 @@ frontend _front001
}
}

func TestInstanceFrontingProxyIgnoreProto(t *testing.T) {
testCases := []struct {
frontingBind string
domain string
expectedFront string
expectedMap string
expectedRegexMap string
expectedACL string
expectedSetvar string
}{
// 0
{
frontingBind: ":8000",
domain: "d1.local",
expectedFront: `
mode http
bind :80
bind :8000 id 11
http-request set-var(req.base) base,lower,regsub(:[0-9]+/,/)
http-request set-var(req.backend) var(req.base),map_beg(/etc/haproxy/maps/_global_http_front.map)
use_backend %[var(req.backend)] if { var(req.backend) -m found }`,
expectedMap: "d1.local/ d1_app_8080",
expectedACL: `
acl tls-has-crt ssl_c_used
acl tls-need-crt ssl_fc_sni -i -f /etc/haproxy/maps/_front001_no_crt.list
acl tls-host-need-crt var(req.host) -i -f /etc/haproxy/maps/_front001_no_crt.list
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 /etc/haproxy/maps/_front001_inv_crt.list`,
expectedSetvar: `
http-request set-var(req.path) path
http-request set-var(req.snibase) ssl_fc_sni,concat(,req.path),lower
http-request set-var(req.snibackend) var(req.snibase),map_beg(/etc/haproxy/maps/_front001_sni.map)
http-request set-var(req.snibackend) var(req.base),map_beg(/etc/haproxy/maps/_front001_sni.map) if !{ var(req.snibackend) -m found } !tls-has-crt !tls-host-need-crt
http-request set-var(req.tls_nocrt_redir) ssl_fc_sni,lower,map(/etc/haproxy/maps/_front001_no_crt_redir.map,_internal) if !tls-has-crt tls-need-crt
http-request set-var(req.tls_invalidcrt_redir) ssl_fc_sni,lower,map(/etc/haproxy/maps/_front001_inv_crt_redir.map,_internal) if tls-has-invalid-crt tls-check-crt`,
},
// 1
{
frontingBind: ":8000",
domain: "*.d1.local",
expectedFront: `
mode http
bind :80
bind :8000 id 11
http-request set-var(req.base) base,lower,regsub(:[0-9]+/,/)
http-request set-var(req.backend) var(req.base),map_beg(/etc/haproxy/maps/_global_http_front.map)
http-request set-var(req.backend) var(req.base),map_reg(/etc/haproxy/maps/_global_http_front_regex.map) if !{ var(req.backend) -m found }
use_backend %[var(req.backend)] if { var(req.backend) -m found }`,
expectedRegexMap: `^[^.]+\.d1\.local/ d1_app_8080`,
expectedACL: `
acl tls-has-crt ssl_c_used
acl tls-need-crt ssl_fc_sni -i -f /etc/haproxy/maps/_front001_no_crt.list
acl tls-need-crt ssl_fc_sni -i -m reg -f /etc/haproxy/maps/_front001_no_crt_regex.list
acl tls-host-need-crt var(req.host) -i -f /etc/haproxy/maps/_front001_no_crt.list
acl tls-host-need-crt var(req.host) -i -m reg -f /etc/haproxy/maps/_front001_no_crt_regex.list
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 /etc/haproxy/maps/_front001_inv_crt.list
acl tls-check-crt ssl_fc_sni -i -m reg -f /etc/haproxy/maps/_front001_inv_crt_regex.list`,
expectedSetvar: `
http-request set-var(req.path) path
http-request set-var(req.snibase) ssl_fc_sni,concat(,req.path),lower
http-request set-var(req.snibackend) var(req.snibase),map_beg(/etc/haproxy/maps/_front001_sni.map)
http-request set-var(req.snibackend) var(req.snibase),map_reg(/etc/haproxy/maps/_front001_sni_regex.map) if !{ var(req.snibackend) -m found }
http-request set-var(req.snibackend) var(req.base),map_beg(/etc/haproxy/maps/_front001_sni.map) if !{ var(req.snibackend) -m found } !tls-has-crt !tls-host-need-crt
http-request set-var(req.snibackend) var(req.base),map_reg(/etc/haproxy/maps/_front001_sni_regex.map) if !{ var(req.snibackend) -m found } !tls-has-crt !tls-host-need-crt
http-request set-var(req.tls_nocrt_redir) ssl_fc_sni,lower,map(/etc/haproxy/maps/_front001_no_crt_redir.map,_internal) if !tls-has-crt tls-need-crt
http-request set-var(req.tls_invalidcrt_redir) ssl_fc_sni,lower,map(/etc/haproxy/maps/_front001_inv_crt_redir.map,_internal) if tls-has-invalid-crt tls-check-crt`,
},
// 2
{
frontingBind: ":80",
domain: "d1.local",
expectedFront: `
mode http
bind :80
http-request set-var(req.base) base,lower,regsub(:[0-9]+/,/)
http-request set-var(req.backend) var(req.base),map_beg(/etc/haproxy/maps/_global_http_front.map)
use_backend %[var(req.backend)] if { var(req.backend) -m found }`,
expectedMap: "d1.local/ d1_app_8080",
expectedACL: `
acl tls-has-crt ssl_c_used
acl tls-need-crt ssl_fc_sni -i -f /etc/haproxy/maps/_front001_no_crt.list
acl tls-host-need-crt var(req.host) -i -f /etc/haproxy/maps/_front001_no_crt.list
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 /etc/haproxy/maps/_front001_inv_crt.list`,
expectedSetvar: `
http-request set-var(req.path) path
http-request set-var(req.snibase) ssl_fc_sni,concat(,req.path),lower
http-request set-var(req.snibackend) var(req.snibase),map_beg(/etc/haproxy/maps/_front001_sni.map)
http-request set-var(req.snibackend) var(req.base),map_beg(/etc/haproxy/maps/_front001_sni.map) if !{ var(req.snibackend) -m found } !tls-has-crt !tls-host-need-crt
http-request set-var(req.tls_nocrt_redir) ssl_fc_sni,lower,map(/etc/haproxy/maps/_front001_no_crt_redir.map,_internal) if !tls-has-crt tls-need-crt
http-request set-var(req.tls_invalidcrt_redir) ssl_fc_sni,lower,map(/etc/haproxy/maps/_front001_inv_crt_redir.map,_internal) if tls-has-invalid-crt tls-check-crt`,
},
}
for _, test := range testCases {
c := setup(t)

var h *hatypes.Host
var b *hatypes.Backend

b = c.config.Backends().AcquireBackend("d1", "app", "8080")
h = c.config.Hosts().AcquireHost(test.domain)
h.AddPath(b, "/")
b.Endpoints = []*hatypes.Endpoint{endpointS1}
b.HSTS = []*hatypes.BackendConfigHSTS{
{
Paths: hatypes.NewBackendPaths(b.FindHostPath(test.domain + "/")),
Config: hatypes.HSTS{
Enabled: true,
MaxAge: 15768000,
Subdomains: true,
Preload: true,
},
},
}
h.TLS.CAHash = "1"
h.TLS.CAFilename = "/var/haproxy/ssl/ca.pem"
c.config.Global().Bind.FrontingBind = test.frontingBind
c.config.Global().Bind.FrontingSockID = 11
c.config.Global().Bind.FrontingUseProto = false

c.Update()
c.checkConfig(`
<<global>>
<<defaults>>
backend d1_app_8080
mode http
acl local-offload ssl_fc
http-request set-header X-SSL-Client-CN %{+Q}[ssl_c_s_dn(cn)] if local-offload
http-request set-header X-SSL-Client-DN %{+Q}[ssl_c_s_dn] if local-offload
http-request set-header X-SSL-Client-SHA1 %{+Q}[ssl_c_sha1,hex] if local-offload
http-response set-header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload"
server s1 172.17.0.11:8080 weight 100
<<backends-default>>
frontend _front_http` + test.expectedFront + `
default_backend _error404
frontend _front001
mode http
bind :443 ssl alpn h2,http/1.1 crt-list /etc/haproxy/maps/_front001_bind_crt.list ca-ignore-err all crt-ignore-err all
http-request set-var(req.base) base,lower,regsub(:[0-9]+/,/)
http-request set-var(req.hostbackend) var(req.base),map_beg(/etc/haproxy/maps/_front001_host.map)
http-request set-var(req.host) hdr(host),lower,regsub(:[0-9]+$,)
http-request set-header X-Forwarded-Proto https
http-request del-header X-SSL-Client-CN
http-request del-header X-SSL-Client-DN
http-request del-header X-SSL-Client-SHA1
http-request del-header X-SSL-Client-Cert` + test.expectedACL + test.expectedSetvar + `
http-request use-service lua.send-421 if tls-has-crt { ssl_fc_has_sni } !{ ssl_fc_sni,strcmp(req.host) eq 0 }
http-request use-service lua.send-496 if { var(req.tls_nocrt_redir) _internal }
http-request use-service lua.send-421 if !tls-has-crt tls-host-need-crt
http-request use-service lua.send-495 if { var(req.tls_invalidcrt_redir) _internal }
use_backend %[var(req.hostbackend)] if { var(req.hostbackend) -m found }
use_backend %[var(req.snibackend)] if { var(req.snibackend) -m found }
default_backend _error404
<<support>>
`)
c.checkMap("_global_http_front.map", test.expectedMap)
if test.expectedRegexMap != "" {
c.checkMap("_global_http_front_regex.map", test.expectedRegexMap)
}
c.logger.CompareLogging(defaultLogging)
c.teardown()
}
}

func TestInstanceTCPBackend(t *testing.T) {
testCases := []struct {
doconfig func(c *testConfig)
Expand Down
13 changes: 7 additions & 6 deletions pkg/haproxy/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,13 @@ type Global struct {

// GlobalBindConfig ...
type GlobalBindConfig struct {
AcceptProxy bool
HTTPBind string
HTTPSBind string
TCPBindIP string
FrontingBind string
FrontingSockID int
AcceptProxy bool
HTTPBind string
HTTPSBind string
TCPBindIP string
FrontingBind string
FrontingSockID int
FrontingUseProto bool
}

// ProcsConfig ...
Expand Down
Loading

0 comments on commit 5831677

Please sign in to comment.