added support for tcp proxyProtocol v1&v2 to backend
This commit is contained in:
parent
520fcf82ae
commit
84b125bdde
28 changed files with 388 additions and 83 deletions
|
@ -183,6 +183,7 @@
|
||||||
- "traefik.tcp.routers.tcprouter1.tls.passthrough=true"
|
- "traefik.tcp.routers.tcprouter1.tls.passthrough=true"
|
||||||
- "traefik.tcp.services.tcpservice01.loadbalancer.terminationdelay=42"
|
- "traefik.tcp.services.tcpservice01.loadbalancer.terminationdelay=42"
|
||||||
- "traefik.tcp.services.tcpservice01.loadbalancer.server.port=foobar"
|
- "traefik.tcp.services.tcpservice01.loadbalancer.server.port=foobar"
|
||||||
|
- "traefik.tcp.services.tcpservice01.loadbalancer.proxyprotocol.version=42"
|
||||||
- "traefik.udp.routers.udprouter0.entrypoints=foobar, foobar"
|
- "traefik.udp.routers.udprouter0.entrypoints=foobar, foobar"
|
||||||
- "traefik.udp.routers.udprouter0.service=foobar"
|
- "traefik.udp.routers.udprouter0.service=foobar"
|
||||||
- "traefik.udp.routers.udprouter1.entrypoints=foobar, foobar"
|
- "traefik.udp.routers.udprouter1.entrypoints=foobar, foobar"
|
||||||
|
|
|
@ -343,6 +343,8 @@
|
||||||
[tcp.services.TCPService01]
|
[tcp.services.TCPService01]
|
||||||
[tcp.services.TCPService01.loadBalancer]
|
[tcp.services.TCPService01.loadBalancer]
|
||||||
terminationDelay = 42
|
terminationDelay = 42
|
||||||
|
[tcp.services.TCPService01.loadBalancer.proxyProtocol]
|
||||||
|
version = 42
|
||||||
|
|
||||||
[[tcp.services.TCPService01.loadBalancer.servers]]
|
[[tcp.services.TCPService01.loadBalancer.servers]]
|
||||||
address = "foobar"
|
address = "foobar"
|
||||||
|
|
|
@ -387,6 +387,8 @@ tcp:
|
||||||
TCPService01:
|
TCPService01:
|
||||||
loadBalancer:
|
loadBalancer:
|
||||||
terminationDelay: 42
|
terminationDelay: 42
|
||||||
|
proxyProtocol:
|
||||||
|
version: 42
|
||||||
servers:
|
servers:
|
||||||
- address: foobar
|
- address: foobar
|
||||||
- address: foobar
|
- address: foobar
|
||||||
|
|
|
@ -247,6 +247,7 @@
|
||||||
| `traefik/tcp/routers/TCPRouter1/tls/domains/1/sans/1` | `foobar` |
|
| `traefik/tcp/routers/TCPRouter1/tls/domains/1/sans/1` | `foobar` |
|
||||||
| `traefik/tcp/routers/TCPRouter1/tls/options` | `foobar` |
|
| `traefik/tcp/routers/TCPRouter1/tls/options` | `foobar` |
|
||||||
| `traefik/tcp/routers/TCPRouter1/tls/passthrough` | `true` |
|
| `traefik/tcp/routers/TCPRouter1/tls/passthrough` | `true` |
|
||||||
|
| `traefik/tcp/services/TCPService01/loadBalancer/proxyProtocol/version` | `42` |
|
||||||
| `traefik/tcp/services/TCPService01/loadBalancer/servers/0/address` | `foobar` |
|
| `traefik/tcp/services/TCPService01/loadBalancer/servers/0/address` | `foobar` |
|
||||||
| `traefik/tcp/services/TCPService01/loadBalancer/servers/1/address` | `foobar` |
|
| `traefik/tcp/services/TCPService01/loadBalancer/servers/1/address` | `foobar` |
|
||||||
| `traefik/tcp/services/TCPService01/loadBalancer/terminationDelay` | `42` |
|
| `traefik/tcp/services/TCPService01/loadBalancer/terminationDelay` | `42` |
|
||||||
|
|
|
@ -177,6 +177,7 @@
|
||||||
"traefik.tcp.routers.tcprouter1.tls.options": "foobar",
|
"traefik.tcp.routers.tcprouter1.tls.options": "foobar",
|
||||||
"traefik.tcp.routers.tcprouter1.tls.passthrough": "true",
|
"traefik.tcp.routers.tcprouter1.tls.passthrough": "true",
|
||||||
"traefik.tcp.services.tcpservice01.loadbalancer.terminationdelay": "42",
|
"traefik.tcp.services.tcpservice01.loadbalancer.terminationdelay": "42",
|
||||||
|
"traefik.tcp.services.tcpservice01.loadbalancer.proxyprotocol.version": "42",
|
||||||
"traefik.tcp.services.tcpservice01.loadbalancer.server.port": "foobar",
|
"traefik.tcp.services.tcpservice01.loadbalancer.server.port": "foobar",
|
||||||
"traefik.udp.routers.udprouter0.entrypoints": "foobar, foobar",
|
"traefik.udp.routers.udprouter0.entrypoints": "foobar, foobar",
|
||||||
"traefik.udp.routers.udprouter0.service": "foobar",
|
"traefik.udp.routers.udprouter0.service": "foobar",
|
||||||
|
|
|
@ -381,6 +381,14 @@ You can declare TCP Routers and/or Services using tags.
|
||||||
traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100
|
traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100
|
||||||
```
|
```
|
||||||
|
|
||||||
|
??? info "`traefik.tcp.services.<service_name>.loadbalancer.proxyprotocol.version`"
|
||||||
|
|
||||||
|
See [PROXY protocol](../services/index.md#proxy-protocol) for more information.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
traefik.tcp.services.mytcpservice.loadbalancer.proxyprotocol.version=1
|
||||||
|
```
|
||||||
|
|
||||||
### UDP
|
### UDP
|
||||||
|
|
||||||
You can declare UDP Routers and/or Services using tags.
|
You can declare UDP Routers and/or Services using tags.
|
||||||
|
|
|
@ -527,6 +527,14 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
- "traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100"
|
- "traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
??? info "`traefik.tcp.services.<service_name>.loadbalancer.proxyprotocol.version`"
|
||||||
|
|
||||||
|
See [PROXY protocol](../services/index.md#proxy-protocol) for more information.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "traefik.tcp.services.mytcpservice.loadbalancer.proxyprotocol.version=1"
|
||||||
|
```
|
||||||
|
|
||||||
### UDP
|
### UDP
|
||||||
|
|
||||||
You can declare UDP Routers and/or Services using labels.
|
You can declare UDP Routers and/or Services using labels.
|
||||||
|
|
|
@ -388,6 +388,14 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100
|
traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100
|
||||||
```
|
```
|
||||||
|
|
||||||
|
??? info "`traefik.tcp.services.<service_name>.loadbalancer.proxyprotocol.version`"
|
||||||
|
|
||||||
|
See [PROXY protocol](../services/index.md#proxy-protocol) for more information.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
traefik.tcp.services.mytcpservice.loadbalancer.proxyprotocol.version=1
|
||||||
|
```
|
||||||
|
|
||||||
### UDP
|
### UDP
|
||||||
|
|
||||||
You can declare UDP Routers and/or Services using tags.
|
You can declare UDP Routers and/or Services using tags.
|
||||||
|
|
|
@ -1090,22 +1090,24 @@ Register the `IngressRouteTCP` [kind](../../reference/dynamic-configuration/kube
|
||||||
port: 8080 # [6]
|
port: 8080 # [6]
|
||||||
weight: 10 # [7]
|
weight: 10 # [7]
|
||||||
terminationDelay: 400 # [8]
|
terminationDelay: 400 # [8]
|
||||||
tls: # [9]
|
proxyProtocol: # [9]
|
||||||
secretName: supersecret # [10]
|
version: 1 # [10]
|
||||||
options: # [11]
|
tls: # [11]
|
||||||
name: opt # [12]
|
secretName: supersecret # [12]
|
||||||
namespace: default # [13]
|
options: # [13]
|
||||||
certResolver: foo # [14]
|
name: opt # [14]
|
||||||
domains: # [15]
|
namespace: default # [15]
|
||||||
- main: example.net # [16]
|
certResolver: foo # [16]
|
||||||
sans: # [17]
|
domains: # [17]
|
||||||
|
- main: example.net # [18]
|
||||||
|
sans: # [19]
|
||||||
- a.example.net
|
- a.example.net
|
||||||
- b.example.net
|
- b.example.net
|
||||||
passthrough: false # [18]
|
passthrough: false # [20]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Ref | Attribute | Purpose |
|
| Ref | Attribute | Purpose |
|
||||||
|------|--------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|------|--------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| [1] | `entryPoints` | List of [entrypoints](../routers/index.md#entrypoints_1) names |
|
| [1] | `entryPoints` | List of [entrypoints](../routers/index.md#entrypoints_1) names |
|
||||||
| [2] | `routes` | List of routes |
|
| [2] | `routes` | List of routes |
|
||||||
| [3] | `routes[n].match` | Defines the [rule](../routers/index.md#rule_1) corresponding to an underlying router |
|
| [3] | `routes[n].match` | Defines the [rule](../routers/index.md#rule_1) corresponding to an underlying router |
|
||||||
|
@ -1113,17 +1115,19 @@ Register the `IngressRouteTCP` [kind](../../reference/dynamic-configuration/kube
|
||||||
| [5] | `services[n].name` | Defines the name of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) |
|
| [5] | `services[n].name` | Defines the name of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) |
|
||||||
| [6] | `services[n].port` | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) |
|
| [6] | `services[n].port` | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) |
|
||||||
| [7] | `services[n].weight` | Defines the weight to apply to the server load balancing |
|
| [7] | `services[n].weight` | Defines the weight to apply to the server load balancing |
|
||||||
| [8] | `services[n].terminationDelay` | corresponds to the deadline that the proxy sets, after one of its connected peers indicates it has closed the writing capability of its connection, to close the reading capability as well, hence fully terminating the connection.<br/>It is a duration in milliseconds, defaulting to 100. A negative value means an infinite deadline (i.e. the reading capability is never closed). |
|
| [8] | `services[n].terminationDelay` | corresponds to the deadline that the proxy sets, after one of its connected peers indicates it has closed the writing capability of its connection, to close the reading capability as well, hence fully terminating the connection. It is a duration in milliseconds, defaulting to 100. A negative value means an infinite deadline (i.e. the reading capability is never closed). |
|
||||||
| [9] | `tls` | Defines [TLS](../routers/index.md#tls_1) certificate configuration |
|
| [9] | `proxyProtocol` | Defines the [PROXY protocol](../services/index.md#proxy-protocol) configuration |
|
||||||
| [10] | `tls.secretName` | Defines the [secret](https://kubernetes.io/docs/concepts/configuration/secret/) name used to store the certificate (in the `IngressRoute` namespace) |
|
| [10] | `version` | Defines the [PROXY protocol](../services/index.md#proxy-protocol) version |
|
||||||
| [11] | `tls.options` | Defines the reference to a [TLSOption](#kind-tlsoption) |
|
| [11] | `tls` | Defines [TLS](../routers/index.md#tls_1) certificate configuration |
|
||||||
| [12] | `options.name` | Defines the [TLSOption](#kind-tlsoption) name |
|
| [12] | `tls.secretName` | Defines the [secret](https://kubernetes.io/docs/concepts/configuration/secret/) name used to store the certificate (in the `IngressRoute` namespace) |
|
||||||
| [13] | `options.namespace` | Defines the [TLSOption](#kind-tlsoption) namespace |
|
| [13] | `tls.options` | Defines the reference to a [TLSOption](#kind-tlsoption) |
|
||||||
| [14] | `tls.certResolver` | Defines the reference to a [CertResolver](../routers/index.md#certresolver_1) |
|
| [14] | `options.name` | Defines the [TLSOption](#kind-tlsoption) name |
|
||||||
| [15] | `tls.domains` | List of [domains](../routers/index.md#domains_1) |
|
| [15] | `options.namespace` | Defines the [TLSOption](#kind-tlsoption) namespace |
|
||||||
| [16] | `domains[n].main` | Defines the main domain name |
|
| [16] | `tls.certResolver` | Defines the reference to a [CertResolver](../routers/index.md#certresolver_1) |
|
||||||
| [17] | `domains[n].sans` | List of SANs (alternative domains) |
|
| [17] | `tls.domains` | List of [domains](../routers/index.md#domains_1) |
|
||||||
| [18] | `tls.passthrough` | If `true`, delegates the TLS termination to the backend |
|
| [18] | `domains[n].main` | Defines the main domain name |
|
||||||
|
| [19] | `domains[n].sans` | List of SANs (alternative domains) |
|
||||||
|
| [20] | `tls.passthrough` | If `true`, delegates the TLS termination to the backend |
|
||||||
|
|
||||||
??? example "Declaring an IngressRouteTCP"
|
??? example "Declaring an IngressRouteTCP"
|
||||||
|
|
||||||
|
|
|
@ -385,6 +385,14 @@ You can declare TCP Routers and/or Services using KV.
|
||||||
|-------------------------------------------------------------------|-------|
|
|-------------------------------------------------------------------|-------|
|
||||||
| `traefik/tcp/services/mytcpservice/loadbalancer/terminationdelay` | `100` |
|
| `traefik/tcp/services/mytcpservice/loadbalancer/terminationdelay` | `100` |
|
||||||
|
|
||||||
|
??? info "`traefik/tcp/services/<service_name>/loadbalancer/proxyprotocol/version`"
|
||||||
|
|
||||||
|
See [PROXY protocol](../services/index.md#proxy-protocol) for more information.
|
||||||
|
|
||||||
|
| Key (Path) | Value |
|
||||||
|
|------------------------------------------------------------------------|-------|
|
||||||
|
| `traefik/tcp/services/mytcpservice/loadbalancer/proxyprotocol/version` | `1` |
|
||||||
|
|
||||||
??? info "`traefik/tcp/services/<service_name>/weighted/services/<n>/name`"
|
??? info "`traefik/tcp/services/<service_name>/weighted/services/<n>/name`"
|
||||||
|
|
||||||
| Key (Path) | Value |
|
| Key (Path) | Value |
|
||||||
|
|
|
@ -421,6 +421,14 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
"traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay": "100"
|
"traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay": "100"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
??? info "`traefik.tcp.services.<service_name>.loadbalancer.proxyprotocol.version`"
|
||||||
|
|
||||||
|
See [PROXY protocol](../services/index.md#proxy-protocol) for more information.
|
||||||
|
|
||||||
|
```json
|
||||||
|
"traefik.tcp.services.mytcpservice.loadbalancer.proxyprotocol.version": "1"
|
||||||
|
```
|
||||||
|
|
||||||
### UDP
|
### UDP
|
||||||
|
|
||||||
You can declare UDP Routers and/or Services using labels.
|
You can declare UDP Routers and/or Services using labels.
|
||||||
|
|
|
@ -424,6 +424,14 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
- "traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100"
|
- "traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
??? info "`traefik.tcp.services.<service_name>.loadbalancer.proxyprotocol.version`"
|
||||||
|
|
||||||
|
See [PROXY protocol](../services/index.md#proxy-protocol) for more information.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- "traefik.tcp.services.mytcpservice.loadbalancer.proxyprotocol.version=1"
|
||||||
|
```
|
||||||
|
|
||||||
### UDP
|
### UDP
|
||||||
|
|
||||||
You can declare UDP Routers and/or Services using labels.
|
You can declare UDP Routers and/or Services using labels.
|
||||||
|
|
|
@ -991,6 +991,39 @@ The `address` option (IP:Port) point to a specific instance.
|
||||||
- address: "xx.xx.xx.xx:xx"
|
- address: "xx.xx.xx.xx:xx"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### PROXY Protocol
|
||||||
|
|
||||||
|
Traefik supports [PROXY Protocol](https://www.haproxy.org/download/2.0/doc/proxy-protocol.txt) version 1 and 2 on TCP Services.
|
||||||
|
It can be enabled by setting `proxyProtocol` on the load balancer.
|
||||||
|
|
||||||
|
Below are the available options for the PROXY protocol:
|
||||||
|
|
||||||
|
- `version` specifies the version of the protocol to be used. Either `1` or `2`.
|
||||||
|
|
||||||
|
!!! info "Version"
|
||||||
|
|
||||||
|
Specifying a version is optional. By default the version 2 will be used.
|
||||||
|
|
||||||
|
??? example "A Service with Proxy Protocol v1 -- Using the [File Provider](../../providers/file.md)"
|
||||||
|
|
||||||
|
```toml tab="TOML"
|
||||||
|
## Dynamic configuration
|
||||||
|
[tcp.services]
|
||||||
|
[tcp.services.my-service.loadBalancer]
|
||||||
|
[tcp.services.my-service.loadBalancer.proxyProtocol]
|
||||||
|
version = 1
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="YAML"
|
||||||
|
## Dynamic configuration
|
||||||
|
tcp:
|
||||||
|
services:
|
||||||
|
my-service:
|
||||||
|
loadBalancer:
|
||||||
|
proxyProtocol:
|
||||||
|
version: 1
|
||||||
|
```
|
||||||
|
|
||||||
#### Termination Delay
|
#### Termination Delay
|
||||||
|
|
||||||
As a proxy between a client and a server, it can happen that either side (e.g. client side) decides to terminate its writing capability on the connection (i.e. issuance of a FIN packet).
|
As a proxy between a client and a server, it can happen that either side (e.g. client side) decides to terminate its writing capability on the connection (i.e. issuance of a FIN packet).
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -14,7 +14,6 @@ require (
|
||||||
github.com/abbot/go-http-auth v0.0.0-00010101000000-000000000000
|
github.com/abbot/go-http-auth v0.0.0-00010101000000-000000000000
|
||||||
github.com/abronan/valkeyrie v0.0.0-20200127174252-ef4277a138cd
|
github.com/abronan/valkeyrie v0.0.0-20200127174252-ef4277a138cd
|
||||||
github.com/aws/aws-sdk-go v1.30.20
|
github.com/aws/aws-sdk-go v1.30.20
|
||||||
github.com/c0va23/go-proxyprotocol v0.9.1
|
|
||||||
github.com/cenkalti/backoff/v4 v4.0.2
|
github.com/cenkalti/backoff/v4 v4.0.2
|
||||||
github.com/containerd/containerd v1.3.2 // indirect
|
github.com/containerd/containerd v1.3.2 // indirect
|
||||||
github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd
|
github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd
|
||||||
|
@ -63,6 +62,7 @@ require (
|
||||||
github.com/openzipkin/zipkin-go v0.2.2
|
github.com/openzipkin/zipkin-go v0.2.2
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/philhofer/fwd v1.0.0 // indirect
|
github.com/philhofer/fwd v1.0.0 // indirect
|
||||||
|
github.com/pires/go-proxyproto v0.3.1
|
||||||
github.com/pmezard/go-difflib v1.0.0
|
github.com/pmezard/go-difflib v1.0.0
|
||||||
github.com/prometheus/client_golang v1.3.0
|
github.com/prometheus/client_golang v1.3.0
|
||||||
github.com/prometheus/client_model v0.1.0
|
github.com/prometheus/client_model v0.1.0
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -141,8 +141,6 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
|
||||||
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
github.com/c0va23/go-proxyprotocol v0.9.1 h1:5BCkp0fDJOhzzH1lhjUgHhmZz9VvRMMif1U2D31hb34=
|
|
||||||
github.com/c0va23/go-proxyprotocol v0.9.1/go.mod h1:TNjUV+llvk8TvWJxlPYAeAYZgSzT/iicNr3nWBWX320=
|
|
||||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
|
@ -680,6 +678,8 @@ github.com/pierrec/lz4 v0.0.0-20190327172049-315a67e90e41/go.mod h1:3/3N9NVKO0je
|
||||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
|
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||||
|
github.com/pires/go-proxyproto v0.3.1 h1:eWb52zeDUbSUDBV+8aVCfOy0pnEG6DrDW3cJ/WKdQsk=
|
||||||
|
github.com/pires/go-proxyproto v0.3.1/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
|
|
@ -34,7 +34,7 @@ func (s *ProxyProtocolSuite) TestProxyProtocolTrusted(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
defer s.killCmd(cmd)
|
defer s.killCmd(cmd)
|
||||||
|
|
||||||
err = try.GetRequest("http://"+haproxyIP+"/whoami", 500*time.Millisecond,
|
err = try.GetRequest("http://"+haproxyIP+"/whoami", 1*time.Second,
|
||||||
try.StatusCodeIs(http.StatusOK),
|
try.StatusCodeIs(http.StatusOK),
|
||||||
try.BodyContains("X-Forwarded-For: "+gatewayIP))
|
try.BodyContains("X-Forwarded-For: "+gatewayIP))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
@ -57,7 +57,7 @@ func (s *ProxyProtocolSuite) TestProxyProtocolV2Trusted(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
defer s.killCmd(cmd)
|
defer s.killCmd(cmd)
|
||||||
|
|
||||||
err = try.GetRequest("http://"+haproxyIP+":81/whoami", 500*time.Millisecond,
|
err = try.GetRequest("http://"+haproxyIP+":81/whoami", 1*time.Second,
|
||||||
try.StatusCodeIs(http.StatusOK),
|
try.StatusCodeIs(http.StatusOK),
|
||||||
try.BodyContains("X-Forwarded-For: "+gatewayIP))
|
try.BodyContains("X-Forwarded-For: "+gatewayIP))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
@ -79,7 +79,7 @@ func (s *ProxyProtocolSuite) TestProxyProtocolNotTrusted(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
defer s.killCmd(cmd)
|
defer s.killCmd(cmd)
|
||||||
|
|
||||||
err = try.GetRequest("http://"+haproxyIP+"/whoami", 500*time.Millisecond,
|
err = try.GetRequest("http://"+haproxyIP+"/whoami", 1*time.Second,
|
||||||
try.StatusCodeIs(http.StatusOK),
|
try.StatusCodeIs(http.StatusOK),
|
||||||
try.BodyContains("X-Forwarded-For: "+haproxyIP))
|
try.BodyContains("X-Forwarded-For: "+haproxyIP))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
@ -101,7 +101,7 @@ func (s *ProxyProtocolSuite) TestProxyProtocolV2NotTrusted(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
defer s.killCmd(cmd)
|
defer s.killCmd(cmd)
|
||||||
|
|
||||||
err = try.GetRequest("http://"+haproxyIP+":81/whoami", 500*time.Millisecond,
|
err = try.GetRequest("http://"+haproxyIP+":81/whoami", 1*time.Second,
|
||||||
try.StatusCodeIs(http.StatusOK),
|
try.StatusCodeIs(http.StatusOK),
|
||||||
try.BodyContains("X-Forwarded-For: "+haproxyIP))
|
try.BodyContains("X-Forwarded-For: "+haproxyIP))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
|
@ -73,6 +73,7 @@ type TCPServersLoadBalancer struct {
|
||||||
// connection. It is a duration in milliseconds, defaulting to 100. A negative value
|
// connection. It is a duration in milliseconds, defaulting to 100. A negative value
|
||||||
// means an infinite deadline (i.e. the reading capability is never closed).
|
// means an infinite deadline (i.e. the reading capability is never closed).
|
||||||
TerminationDelay *int `json:"terminationDelay,omitempty" toml:"terminationDelay,omitempty" yaml:"terminationDelay,omitempty"`
|
TerminationDelay *int `json:"terminationDelay,omitempty" toml:"terminationDelay,omitempty" yaml:"terminationDelay,omitempty"`
|
||||||
|
ProxyProtocol *ProxyProtocol `json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty" file:"allowEmpty"`
|
||||||
Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"`
|
Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,3 +107,15 @@ type TCPServer struct {
|
||||||
Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty" label:"-"`
|
Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty" label:"-"`
|
||||||
Port string `toml:"-" json:"-" yaml:"-"`
|
Port string `toml:"-" json:"-" yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
|
// ProxyProtocol holds the ProxyProtocol configuration.
|
||||||
|
type ProxyProtocol struct {
|
||||||
|
Version int `json:"version,omitempty" toml:"version,omitempty" yaml:"version,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefaults Default values for a ProxyProtocol.
|
||||||
|
func (p *ProxyProtocol) SetDefaults() {
|
||||||
|
p.Version = 2
|
||||||
|
}
|
||||||
|
|
|
@ -880,6 +880,22 @@ func (in *PassTLSClientCert) DeepCopy() *PassTLSClientCert {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ProxyProtocol) DeepCopyInto(out *ProxyProtocol) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyProtocol.
|
||||||
|
func (in *ProxyProtocol) DeepCopy() *ProxyProtocol {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ProxyProtocol)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *RateLimit) DeepCopyInto(out *RateLimit) {
|
func (in *RateLimit) DeepCopyInto(out *RateLimit) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
@ -1373,6 +1389,11 @@ func (in *TCPServersLoadBalancer) DeepCopyInto(out *TCPServersLoadBalancer) {
|
||||||
*out = new(int)
|
*out = new(int)
|
||||||
**out = **in
|
**out = **in
|
||||||
}
|
}
|
||||||
|
if in.ProxyProtocol != nil {
|
||||||
|
in, out := &in.ProxyProtocol, &out.ProxyProtocol
|
||||||
|
*out = new(ProxyProtocol)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
if in.Servers != nil {
|
if in.Servers != nil {
|
||||||
in, out := &in.Servers, &out.Servers
|
in, out := &in.Servers, &out.Servers
|
||||||
*out = make([]TCPServer, len(*in))
|
*out = make([]TCPServer, len(*in))
|
||||||
|
|
|
@ -182,8 +182,10 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
"traefik.tcp.routers.Router1.tls.passthrough": "false",
|
"traefik.tcp.routers.Router1.tls.passthrough": "false",
|
||||||
"traefik.tcp.services.Service0.loadbalancer.server.Port": "42",
|
"traefik.tcp.services.Service0.loadbalancer.server.Port": "42",
|
||||||
"traefik.tcp.services.Service0.loadbalancer.TerminationDelay": "42",
|
"traefik.tcp.services.Service0.loadbalancer.TerminationDelay": "42",
|
||||||
|
"traefik.tcp.services.Service0.loadbalancer.proxyProtocol.version": "42",
|
||||||
"traefik.tcp.services.Service1.loadbalancer.server.Port": "42",
|
"traefik.tcp.services.Service1.loadbalancer.server.Port": "42",
|
||||||
"traefik.tcp.services.Service1.loadbalancer.TerminationDelay": "42",
|
"traefik.tcp.services.Service1.loadbalancer.TerminationDelay": "42",
|
||||||
|
"traefik.tcp.services.Service1.loadbalancer.proxyProtocol": "true",
|
||||||
|
|
||||||
"traefik.udp.routers.Router0.entrypoints": "foobar, fiibar",
|
"traefik.udp.routers.Router0.entrypoints": "foobar, fiibar",
|
||||||
"traefik.udp.routers.Router0.service": "foobar",
|
"traefik.udp.routers.Router0.service": "foobar",
|
||||||
|
@ -233,6 +235,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
TerminationDelay: func(i int) *int { return &i }(42),
|
TerminationDelay: func(i int) *int { return &i }(42),
|
||||||
|
ProxyProtocol: &dynamic.ProxyProtocol{Version: 42},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Service1": {
|
"Service1": {
|
||||||
|
@ -243,6 +246,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
TerminationDelay: func(i int) *int { return &i }(42),
|
TerminationDelay: func(i int) *int { return &i }(42),
|
||||||
|
ProxyProtocol: &dynamic.ProxyProtocol{Version: 2},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRouteTCP
|
||||||
|
metadata:
|
||||||
|
name: test.route
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- foo
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- match: HostSNI(`foo.com`)
|
||||||
|
services:
|
||||||
|
- name: whoamitcp
|
||||||
|
port: 8000
|
||||||
|
proxyProtocol:
|
||||||
|
version: 2
|
|
@ -140,6 +140,15 @@ func createLoadBalancerServerTCP(client Client, namespace string, service v1alph
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if service.ProxyProtocol != nil {
|
||||||
|
tcpService.LoadBalancer.ProxyProtocol = &dynamic.ProxyProtocol{}
|
||||||
|
tcpService.LoadBalancer.ProxyProtocol.SetDefaults()
|
||||||
|
|
||||||
|
if service.ProxyProtocol.Version != 0 {
|
||||||
|
tcpService.LoadBalancer.ProxyProtocol.Version = service.ProxyProtocol.Version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if service.TerminationDelay != nil {
|
if service.TerminationDelay != nil {
|
||||||
tcpService.LoadBalancer.TerminationDelay = service.TerminationDelay
|
tcpService.LoadBalancer.TerminationDelay = service.TerminationDelay
|
||||||
}
|
}
|
||||||
|
|
|
@ -2878,6 +2878,49 @@ func TestLoadIngressRoutes(t *testing.T) {
|
||||||
TLS: &dynamic.TLSConfiguration{},
|
TLS: &dynamic.TLSConfiguration{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "TCP with proxyProtocol Version",
|
||||||
|
paths: []string{"tcp/services.yml", "tcp/with_proxyprotocol.yml"},
|
||||||
|
expected: &dynamic.Configuration{
|
||||||
|
TLS: &dynamic.TLSConfiguration{},
|
||||||
|
UDP: &dynamic.UDPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.UDPRouter{},
|
||||||
|
Services: map[string]*dynamic.UDPService{},
|
||||||
|
},
|
||||||
|
TCP: &dynamic.TCPConfiguration{
|
||||||
|
Routers: map[string]*dynamic.TCPRouter{
|
||||||
|
"default-test.route-fdd3e9338e47a45efefc": {
|
||||||
|
EntryPoints: []string{"foo"},
|
||||||
|
Service: "default-test.route-fdd3e9338e47a45efefc",
|
||||||
|
Rule: "HostSNI(`foo.com`)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Services: map[string]*dynamic.TCPService{
|
||||||
|
"default-test.route-fdd3e9338e47a45efefc": {
|
||||||
|
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||||
|
Servers: []dynamic.TCPServer{
|
||||||
|
{
|
||||||
|
Address: "10.10.0.1:8000",
|
||||||
|
Port: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Address: "10.10.0.2:8000",
|
||||||
|
Port: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ProxyProtocol: &dynamic.ProxyProtocol{Version: 2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
|
ServersTransports: map[string]*dynamic.ServersTransport{},
|
||||||
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
Services: map[string]*dynamic.Service{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "TLS with tls store",
|
desc: "TLS with tls store",
|
||||||
paths: []string{"services.yml", "with_tls_store.yml"},
|
paths: []string{"services.yml", "with_tls_store.yml"},
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package v1alpha1
|
package v1alpha1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||||
"github.com/traefik/traefik/v2/pkg/types"
|
"github.com/traefik/traefik/v2/pkg/types"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
@ -58,6 +59,7 @@ type ServiceTCP struct {
|
||||||
Port int32 `json:"port"`
|
Port int32 `json:"port"`
|
||||||
Weight *int `json:"weight,omitempty"`
|
Weight *int `json:"weight,omitempty"`
|
||||||
TerminationDelay *int `json:"terminationDelay,omitempty"`
|
TerminationDelay *int `json:"terminationDelay,omitempty"`
|
||||||
|
ProxyProtocol *dynamic.ProxyProtocol `json:"proxyProtocol,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// +genclient
|
// +genclient
|
||||||
|
|
|
@ -1011,6 +1011,11 @@ func (in *ServiceTCP) DeepCopyInto(out *ServiceTCP) {
|
||||||
*out = new(int)
|
*out = new(int)
|
||||||
**out = **in
|
**out = **in
|
||||||
}
|
}
|
||||||
|
if in.ProxyProtocol != nil {
|
||||||
|
in, out := &in.ProxyProtocol, &out.ProxyProtocol
|
||||||
|
*out = new(dynamic.ProxyProtocol)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
proxyprotocol "github.com/c0va23/go-proxyprotocol"
|
"github.com/pires/go-proxyproto"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||||
"github.com/traefik/traefik/v2/pkg/ip"
|
"github.com/traefik/traefik/v2/pkg/ip"
|
||||||
|
@ -316,10 +316,10 @@ func (c *writeCloserWrapper) CloseWrite() error {
|
||||||
// implementation, if any was found within the underlying conn.
|
// implementation, if any was found within the underlying conn.
|
||||||
func writeCloser(conn net.Conn) (tcp.WriteCloser, error) {
|
func writeCloser(conn net.Conn) (tcp.WriteCloser, error) {
|
||||||
switch typedConn := conn.(type) {
|
switch typedConn := conn.(type) {
|
||||||
case *proxyprotocol.Conn:
|
case *proxyproto.Conn:
|
||||||
underlying, err := writeCloser(typedConn.Conn)
|
underlying, ok := typedConn.TCPConn()
|
||||||
if err != nil {
|
if !ok {
|
||||||
return nil, err
|
return nil, fmt.Errorf("underlying connection is not a tcp connection")
|
||||||
}
|
}
|
||||||
return &writeCloserWrapper{writeCloser: underlying, Conn: typedConn}, nil
|
return &writeCloserWrapper{writeCloser: underlying, Conn: typedConn}, nil
|
||||||
case *net.TCPConn:
|
case *net.TCPConn:
|
||||||
|
@ -356,42 +356,35 @@ func (ln tcpKeepAliveListener) Accept() (net.Conn, error) {
|
||||||
return tc, nil
|
return tc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type proxyProtocolLogger struct {
|
|
||||||
log.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// Printf force log level to debug.
|
|
||||||
func (p proxyProtocolLogger) Printf(format string, v ...interface{}) {
|
|
||||||
p.Debugf(format, v...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildProxyProtocolListener(ctx context.Context, entryPoint *static.EntryPoint, listener net.Listener) (net.Listener, error) {
|
func buildProxyProtocolListener(ctx context.Context, entryPoint *static.EntryPoint, listener net.Listener) (net.Listener, error) {
|
||||||
var sourceCheck func(addr net.Addr) (bool, error)
|
proxyListener := &proxyproto.Listener{Listener: listener}
|
||||||
|
|
||||||
if entryPoint.ProxyProtocol.Insecure {
|
if entryPoint.ProxyProtocol.Insecure {
|
||||||
sourceCheck = func(_ net.Addr) (bool, error) {
|
log.FromContext(ctx).Infof("Enabling ProxyProtocol without trusted IPs: Insecure")
|
||||||
return true, nil
|
return proxyListener, nil
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
checker, err := ip.NewChecker(entryPoint.ProxyProtocol.TrustedIPs)
|
checker, err := ip.NewChecker(entryPoint.ProxyProtocol.TrustedIPs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceCheck = func(addr net.Addr) (bool, error) {
|
proxyListener.Policy = func(upstream net.Addr) (proxyproto.Policy, error) {
|
||||||
ipAddr, ok := addr.(*net.TCPAddr)
|
ipAddr, ok := upstream.(*net.TCPAddr)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false, fmt.Errorf("type error %v", addr)
|
return proxyproto.REJECT, fmt.Errorf("type error %v", upstream)
|
||||||
}
|
}
|
||||||
|
|
||||||
return checker.ContainsIP(ipAddr.IP), nil
|
if !checker.ContainsIP(ipAddr.IP) {
|
||||||
|
log.FromContext(ctx).Debugf("IP %s is not in trusted IPs list, ignoring ProxyProtocol Headers and bypass connection", ipAddr.IP)
|
||||||
|
return proxyproto.IGNORE, nil
|
||||||
}
|
}
|
||||||
|
return proxyproto.USE, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.FromContext(ctx).Infof("Enabling ProxyProtocol for trusted IPs %v", entryPoint.ProxyProtocol.TrustedIPs)
|
log.FromContext(ctx).Infof("Enabling ProxyProtocol for trusted IPs %v", entryPoint.ProxyProtocol.TrustedIPs)
|
||||||
|
|
||||||
return proxyprotocol.NewDefaultListener(listener).
|
return proxyListener, nil
|
||||||
WithSourceChecker(sourceCheck).
|
|
||||||
WithLogger(proxyProtocolLogger{Logger: log.FromContext(ctx)}), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildListener(ctx context.Context, entryPoint *static.EntryPoint) (net.Listener, error) {
|
func buildListener(ctx context.Context, entryPoint *static.EntryPoint) (net.Listener, error) {
|
||||||
|
|
|
@ -59,7 +59,7 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
handler, err := tcp.NewProxy(server.Address, duration)
|
handler, err := tcp.NewProxy(server.Address, duration, conf.LoadBalancer.ProxyProtocol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("In service %q server %q: %v", serviceQualifiedName, server.Address, err)
|
logger.Errorf("In service %q server %q: %v", serviceQualifiedName, server.Address, err)
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package tcp
|
package tcp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pires/go-proxyproto"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||||
"github.com/traefik/traefik/v2/pkg/log"
|
"github.com/traefik/traefik/v2/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -12,16 +15,21 @@ import (
|
||||||
type Proxy struct {
|
type Proxy struct {
|
||||||
target *net.TCPAddr
|
target *net.TCPAddr
|
||||||
terminationDelay time.Duration
|
terminationDelay time.Duration
|
||||||
|
proxyProtocol *dynamic.ProxyProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProxy creates a new Proxy.
|
// NewProxy creates a new Proxy.
|
||||||
func NewProxy(address string, terminationDelay time.Duration) (*Proxy, error) {
|
func NewProxy(address string, terminationDelay time.Duration, proxyProtocol *dynamic.ProxyProtocol) (*Proxy, error) {
|
||||||
tcpAddr, err := net.ResolveTCPAddr("tcp", address)
|
tcpAddr, err := net.ResolveTCPAddr("tcp", address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Proxy{target: tcpAddr, terminationDelay: terminationDelay}, nil
|
if proxyProtocol != nil && (proxyProtocol.Version < 1 || proxyProtocol.Version > 2) {
|
||||||
|
return nil, fmt.Errorf("unknown proxyProtocol version: %d", proxyProtocol.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Proxy{target: tcpAddr, terminationDelay: terminationDelay, proxyProtocol: proxyProtocol}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeTCP forwards the connection to a service.
|
// ServeTCP forwards the connection to a service.
|
||||||
|
@ -39,8 +47,16 @@ func (p *Proxy) ServeTCP(conn WriteCloser) {
|
||||||
|
|
||||||
// maybe not needed, but just in case
|
// maybe not needed, but just in case
|
||||||
defer connBackend.Close()
|
defer connBackend.Close()
|
||||||
|
|
||||||
errChan := make(chan error)
|
errChan := make(chan error)
|
||||||
|
|
||||||
|
if p.proxyProtocol != nil && p.proxyProtocol.Version > 0 && p.proxyProtocol.Version < 3 {
|
||||||
|
header := proxyproto.HeaderProxyFromAddrs(byte(p.proxyProtocol.Version), conn.RemoteAddr(), conn.LocalAddr())
|
||||||
|
if _, err := header.WriteTo(connBackend); err != nil {
|
||||||
|
log.WithoutContext().Errorf("Error while writing proxy protocol headers to backend connection: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
go p.connCopy(conn, connBackend, errChan)
|
go p.connCopy(conn, connBackend, errChan)
|
||||||
go p.connCopy(connBackend, conn, errChan)
|
go p.connCopy(connBackend, conn, errChan)
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,17 @@ package tcp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pires/go-proxyproto"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||||
)
|
)
|
||||||
|
|
||||||
func fakeRedis(t *testing.T, listener net.Listener) {
|
func fakeRedis(t *testing.T, listener net.Listener) {
|
||||||
|
@ -16,6 +20,7 @@ func fakeRedis(t *testing.T, listener net.Listener) {
|
||||||
conn, err := listener.Accept()
|
conn, err := listener.Accept()
|
||||||
fmt.Println("Accept on server")
|
fmt.Println("Accept on server")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
withErr := false
|
withErr := false
|
||||||
buf := make([]byte, 64)
|
buf := make([]byte, 64)
|
||||||
|
@ -26,12 +31,13 @@ func fakeRedis(t *testing.T, listener net.Listener) {
|
||||||
if string(buf[:4]) == "ping" {
|
if string(buf[:4]) == "ping" {
|
||||||
time.Sleep(1 * time.Millisecond)
|
time.Sleep(1 * time.Millisecond)
|
||||||
if _, err := conn.Write([]byte("PONG")); err != nil {
|
if _, err := conn.Write([]byte("PONG")); err != nil {
|
||||||
conn.Close()
|
_ = conn.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if withErr {
|
if withErr {
|
||||||
conn.Close()
|
_ = conn.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +52,7 @@ func TestCloseWrite(t *testing.T) {
|
||||||
_, port, err := net.SplitHostPort(backendListener.Addr().String())
|
_, port, err := net.SplitHostPort(backendListener.Addr().String())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
proxy, err := NewProxy(":"+port, 10*time.Millisecond)
|
proxy, err := NewProxy(":"+port, 10*time.Millisecond, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
proxyListener, err := net.Listen("tcp", ":0")
|
proxyListener, err := net.Listen("tcp", ":0")
|
||||||
|
@ -79,3 +85,87 @@ func TestCloseWrite(t *testing.T) {
|
||||||
require.Equal(t, int64(4), n)
|
require.Equal(t, int64(4), n)
|
||||||
require.Equal(t, "PONG", buffer.String())
|
require.Equal(t, "PONG", buffer.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProxyProtocol(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
version int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "PROXY protocol v1",
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "PROXY protocol v2",
|
||||||
|
version: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
backendListener, err := net.Listen("tcp", ":0")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var version int
|
||||||
|
proxyBackendListener := proxyproto.Listener{
|
||||||
|
Listener: backendListener,
|
||||||
|
ValidateHeader: func(h *proxyproto.Header) error {
|
||||||
|
version = int(h.Version)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Policy: func(upstream net.Addr) (proxyproto.Policy, error) {
|
||||||
|
switch test.version {
|
||||||
|
case 1, 2:
|
||||||
|
return proxyproto.USE, nil
|
||||||
|
default:
|
||||||
|
return proxyproto.REQUIRE, errors.New("unsupported version")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
defer proxyBackendListener.Close()
|
||||||
|
|
||||||
|
go fakeRedis(t, &proxyBackendListener)
|
||||||
|
|
||||||
|
_, port, err := net.SplitHostPort(proxyBackendListener.Addr().String())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
proxy, err := NewProxy(":"+port, 10*time.Millisecond, &dynamic.ProxyProtocol{Version: test.version})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
proxyListener, err := net.Listen("tcp", ":0")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
conn, err := proxyListener.Accept()
|
||||||
|
require.NoError(t, err)
|
||||||
|
proxy.ServeTCP(conn.(*net.TCPConn))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, port, err = net.SplitHostPort(proxyListener.Addr().String())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
conn, err := net.Dial("tcp", ":"+port)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = conn.Write([]byte("ping\n"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = conn.(*net.TCPConn).CloseWrite()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var buf []byte
|
||||||
|
buffer := bytes.NewBuffer(buf)
|
||||||
|
n, err := io.Copy(buffer, conn)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(4), n)
|
||||||
|
assert.Equal(t, "PONG", buffer.String())
|
||||||
|
|
||||||
|
assert.Equal(t, test.version, version)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue