mirror of
https://github.com/traefik/traefik.git
synced 2026-02-03 20:39:51 -05:00
Merge branch 'master' into healthcheck-allow-body
This commit is contained in:
commit
855751d0fd
128 changed files with 3715 additions and 2110 deletions
|
|
@ -82,6 +82,7 @@ linters:
|
|||
toolchain-pattern: go1\.\d+\.\d+$
|
||||
tool-forbidden: true
|
||||
go-version-pattern: ^1\.\d+(\.0)?$
|
||||
replace-local: true
|
||||
replace-allow-list:
|
||||
- github.com/abbot/go-http-auth
|
||||
- github.com/gorilla/mux
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -103,7 +103,7 @@ test-integration:
|
|||
#? test-gateway-api-conformance: Run the Gateway API conformance tests
|
||||
test-gateway-api-conformance: build-image-dirty
|
||||
# In case of a new Minor/Major version, the traefikVersion needs to be updated.
|
||||
GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration -v -tags gatewayAPIConformance -test.run GatewayAPIConformanceSuite -traefikVersion="v3.6" $(TESTFLAGS)
|
||||
GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration -v -tags gatewayAPIConformance -test.run GatewayAPIConformanceSuite -traefikVersion="v3.7" $(TESTFLAGS)
|
||||
|
||||
.PHONY: test-knative-conformance
|
||||
#? test-knative-conformance: Run the Knative conformance tests
|
||||
|
|
|
|||
|
|
@ -20,3 +20,97 @@ For detailed steps tailored to your environment, follow the guide for your platf
|
|||
- [Kubernetes](./kubernetes/basic.md)
|
||||
- [Docker](./docker/basic.md)
|
||||
- [Docker Swarm](./swarm/basic.md)
|
||||
|
||||
## Advanced Use Cases
|
||||
|
||||
### Exposing gRPC Services
|
||||
|
||||
Traefik Proxy supports gRPC applications without requiring specific configuration. You can expose gRPC services using either HTTP (h2c) or HTTPS.
|
||||
|
||||
??? example "Using HTTP (h2c)"
|
||||
|
||||
For unencrypted gRPC communication, configure your service to use the `h2c://` protocol scheme:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
grpc-router:
|
||||
service: grpc-service
|
||||
rule: Host(`grpc.example.com`)
|
||||
|
||||
services:
|
||||
grpc-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: h2c://backend:8080
|
||||
```
|
||||
|
||||
!!! note
|
||||
For providers with labels (Docker, Kubernetes), specify the scheme using:
|
||||
`traefik.http.services.<service-name>.loadbalancer.server.scheme=h2c`
|
||||
|
||||
??? example "Using HTTPS"
|
||||
|
||||
For encrypted gRPC communication, use standard HTTPS URLs. Traefik will use HTTP/2 over TLS to communicate with your gRPC backend:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
grpc-router:
|
||||
service: grpc-service
|
||||
rule: Host(`grpc.example.com`)
|
||||
tls: {}
|
||||
|
||||
services:
|
||||
grpc-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: https://backend:8080
|
||||
```
|
||||
|
||||
Traefik handles the protocol negotiation automatically. Configure TLS certificates for your backends using [ServersTransport](../reference/routing-configuration/http/load-balancing/serverstransport.md) if needed.
|
||||
|
||||
### Exposing WebSocket Services
|
||||
|
||||
Traefik Proxy supports WebSocket (WS) and WebSocket Secure (WSS) connections out of the box. No special configuration is required beyond standard HTTP routing.
|
||||
|
||||
??? example "Basic WebSocket"
|
||||
|
||||
Configure a router and service pointing to your WebSocket server. Traefik automatically detects and handles the WebSocket upgrade:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
websocket-router:
|
||||
rule: Host(`ws.example.com`)
|
||||
service: websocket-service
|
||||
|
||||
services:
|
||||
websocket-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: http://websocket-backend:8000
|
||||
```
|
||||
|
||||
??? example "WebSocket Secure (WSS)"
|
||||
|
||||
For encrypted WebSocket connections, enable TLS on your router. Clients connect using `wss://` while you can choose whether backends use encrypted or unencrypted connections:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
websocket-secure-router:
|
||||
rule: Host(`wss.example.com`)
|
||||
service: websocket-service
|
||||
tls: {}
|
||||
|
||||
services:
|
||||
websocket-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: http://websocket-backend:8000 # SSL termination at Traefik
|
||||
# OR
|
||||
# - url: https://websocket-backend:8443 # End-to-end encryption
|
||||
```
|
||||
|
||||
Traefik preserves WebSocket headers including `Origin`, `Sec-WebSocket-Key`, and `Sec-WebSocket-Version`. Use the [Headers middleware](../reference/routing-configuration/http/middlewares/headers.md) if you need to modify headers for origin checking or other requirements.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ The GrpcWeb middleware converts gRPC Web requests to HTTP/2 gRPC requests before
|
|||
!!! tip
|
||||
|
||||
Please note, that Traefik needs to communicate using gRPC with the backends (h2c or HTTP/2 over TLS).
|
||||
Check out the [gRPC](../../user-guides/grpc.md) user guide for more details.
|
||||
Check out [Exposing gRPC Services](../../expose/overview.md#exposing-grpc-services) for more details.
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
|
|
|
|||
|
|
@ -636,6 +636,10 @@ The `Headers` and `HeadersRegexp` matchers have been renamed to `Header` and `He
|
|||
|
||||
`PathPrefix` no longer uses regular expressions to match path prefixes.
|
||||
|
||||
`Path` and `PathPrefix` no longer support path parameter placeholders (e.g., `{id}`, `{name}`).
|
||||
Routes using placeholders like ``Path(`/route/{id}`)`` will not match in v3 syntax.
|
||||
Use `PathRegexp` instead for dynamic path segments.
|
||||
|
||||
`QueryRegexp` has been introduced to match query values using a regular expression.
|
||||
|
||||
`HeaderRegexp`, `HostRegexp`, `PathRegexp`, `QueryRegexp`, and `HostSNIRegexp` matchers now uses the [Go regexp syntax](https://golang.org/pkg/regexp/syntax/).
|
||||
|
|
@ -716,6 +720,40 @@ http:
|
|||
ruleSyntax = "v2"
|
||||
```
|
||||
|
||||
##### Migrate Path Placeholders to PathRegexp
|
||||
|
||||
In v2, `Path` and `PathPrefix` supported path parameter placeholders like `{id}` for matching dynamic path segments.
|
||||
In v3, this is no longer supported and `PathRegexp` should be used instead.
|
||||
|
||||
??? example "Migrating a route with path placeholders"
|
||||
|
||||
v2 syntax (no longer works in v3):
|
||||
|
||||
```yaml
|
||||
match: Host(`example.com`) && Path(`/products/{id}`)
|
||||
```
|
||||
|
||||
v3 syntax using `PathRegexp`:
|
||||
|
||||
```yaml
|
||||
match: Host(`example.com`) && PathRegexp(`^/products/[^/]+$`)
|
||||
```
|
||||
|
||||
For more complex patterns with multiple placeholders:
|
||||
|
||||
v2 syntax:
|
||||
|
||||
```yaml
|
||||
match: Host(`example.com`) && Path(`/users/{userId}/orders/{orderId}`)
|
||||
```
|
||||
|
||||
v3 syntax:
|
||||
|
||||
```yaml
|
||||
match: Host(`example.com`) && PathRegexp(`^/users/[^/]+/orders/[^/]+$`) ## matches any non-slash characters
|
||||
match: Host(`example.com`) && PathRegexp(`^/users/[a-zA-Z0-9_-]+/orders/[a-zA-Z0-9_-]+$`) ## restricts to alphanumeric, hyphens, and underscores
|
||||
```
|
||||
|
||||
### IPWhiteList
|
||||
|
||||
In v3, we renamed the `IPWhiteList` middleware to `IPAllowList` without changing anything to the configuration.
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ metrics:
|
|||
|
||||
_Required, Default=""_
|
||||
|
||||
Token with which to connect to InfluxDB v2.
|
||||
Token with which to connect to InfluxDB v2. It accepts either a token value or a file path to the token.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
metrics:
|
||||
|
|
|
|||
|
|
@ -363,6 +363,6 @@ providers:
|
|||
|
||||
## Full Example
|
||||
|
||||
For additional information, refer to the [full example](../user-guides/crd-acme/index.md) with Let's Encrypt.
|
||||
For additional information on exposing services with Kubernetes, refer to the [Kubernetes guide](../expose/kubernetes/basic.md).
|
||||
|
||||
{% include-markdown "includes/traefik-for-business-applications.md" %}
|
||||
|
|
|
|||
|
|
@ -222,6 +222,25 @@ spec:
|
|||
- Service
|
||||
- TraefikService
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references
|
||||
to Middleware resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of
|
||||
the referenced Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
description: |-
|
||||
Name defines the name of the referenced Kubernetes Service or TraefikService.
|
||||
|
|
@ -1232,6 +1251,25 @@ spec:
|
|||
- Service
|
||||
- TraefikService
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references to
|
||||
Middleware resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of the
|
||||
referenced Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
description: |-
|
||||
Name defines the name of the referenced Kubernetes Service or TraefikService.
|
||||
|
|
@ -2974,6 +3012,25 @@ spec:
|
|||
- Service
|
||||
- TraefikService
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references
|
||||
to Middleware resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of the
|
||||
referenced Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
description: |-
|
||||
Name defines the name of the referenced Kubernetes Service or TraefikService.
|
||||
|
|
@ -3211,6 +3268,24 @@ spec:
|
|||
Default value is -1, which means unlimited size.
|
||||
format: int64
|
||||
type: integer
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references to Middleware
|
||||
resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced Middleware
|
||||
resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
mirrorBody:
|
||||
description: |-
|
||||
MirrorBody defines whether the body of the request should be mirrored.
|
||||
|
|
@ -3298,6 +3373,25 @@ spec:
|
|||
- Service
|
||||
- TraefikService
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references
|
||||
to Middleware resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of the
|
||||
referenced Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
description: |-
|
||||
Name defines the name of the referenced Kubernetes Service or TraefikService.
|
||||
|
|
@ -3686,6 +3780,25 @@ spec:
|
|||
- Service
|
||||
- TraefikService
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references
|
||||
to Middleware resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of the
|
||||
referenced Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
description: |-
|
||||
Name defines the name of the referenced Kubernetes Service or TraefikService.
|
||||
|
|
|
|||
|
|
@ -223,6 +223,25 @@ spec:
|
|||
- Service
|
||||
- TraefikService
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references
|
||||
to Middleware resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of
|
||||
the referenced Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
description: |-
|
||||
Name defines the name of the referenced Kubernetes Service or TraefikService.
|
||||
|
|
|
|||
|
|
@ -387,6 +387,25 @@ spec:
|
|||
- Service
|
||||
- TraefikService
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references to
|
||||
Middleware resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of the
|
||||
referenced Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
description: |-
|
||||
Name defines the name of the referenced Kubernetes Service or TraefikService.
|
||||
|
|
|
|||
|
|
@ -131,6 +131,25 @@ spec:
|
|||
- Service
|
||||
- TraefikService
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references
|
||||
to Middleware resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of the
|
||||
referenced Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
description: |-
|
||||
Name defines the name of the referenced Kubernetes Service or TraefikService.
|
||||
|
|
@ -368,6 +387,24 @@ spec:
|
|||
Default value is -1, which means unlimited size.
|
||||
format: int64
|
||||
type: integer
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references to Middleware
|
||||
resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced Middleware
|
||||
resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
mirrorBody:
|
||||
description: |-
|
||||
MirrorBody defines whether the body of the request should be mirrored.
|
||||
|
|
@ -455,6 +492,25 @@ spec:
|
|||
- Service
|
||||
- TraefikService
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references
|
||||
to Middleware resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of the
|
||||
referenced Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
description: |-
|
||||
Name defines the name of the referenced Kubernetes Service or TraefikService.
|
||||
|
|
@ -843,6 +899,25 @@ spec:
|
|||
- Service
|
||||
- TraefikService
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references
|
||||
to Middleware resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of the
|
||||
referenced Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
description: |-
|
||||
Name defines the name of the referenced Kubernetes Service or TraefikService.
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
|||
| <a id="opt-metrics-influxdb2-bucket" href="#opt-metrics-influxdb2-bucket" title="#opt-metrics-influxdb2-bucket">metrics.influxdb2.bucket</a> | InfluxDB v2 bucket ID. | |
|
||||
| <a id="opt-metrics-influxdb2-org" href="#opt-metrics-influxdb2-org" title="#opt-metrics-influxdb2-org">metrics.influxdb2.org</a> | InfluxDB v2 org ID. | |
|
||||
| <a id="opt-metrics-influxdb2-pushinterval" href="#opt-metrics-influxdb2-pushinterval" title="#opt-metrics-influxdb2-pushinterval">metrics.influxdb2.pushinterval</a> | InfluxDB v2 push interval. | 10 |
|
||||
| <a id="opt-metrics-influxdb2-token" href="#opt-metrics-influxdb2-token" title="#opt-metrics-influxdb2-token">metrics.influxdb2.token</a> | InfluxDB v2 access token. | |
|
||||
| <a id="opt-metrics-influxdb2-token" href="#opt-metrics-influxdb2-token" title="#opt-metrics-influxdb2-token">metrics.influxdb2.token</a> | InfluxDB v2 access token. It accepts either a token value or a file path to the token. | |
|
||||
| <a id="opt-metrics-otlp" href="#opt-metrics-otlp" title="#opt-metrics-otlp">metrics.otlp</a> | OpenTelemetry metrics exporter type. | false |
|
||||
| <a id="opt-metrics-otlp-addentrypointslabels" href="#opt-metrics-otlp-addentrypointslabels" title="#opt-metrics-otlp-addentrypointslabels">metrics.otlp.addentrypointslabels</a> | Enable metrics on entry points. | true |
|
||||
| <a id="opt-metrics-otlp-addrouterslabels" href="#opt-metrics-otlp-addrouterslabels" title="#opt-metrics-otlp-addrouterslabels">metrics.otlp.addrouterslabels</a> | Enable metrics on routers. | false |
|
||||
|
|
@ -399,6 +399,7 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
|||
| <a id="opt-providers-kubernetesingressnginx-endpoint" href="#opt-providers-kubernetesingressnginx-endpoint" title="#opt-providers-kubernetesingressnginx-endpoint">providers.kubernetesingressnginx.endpoint</a> | Kubernetes server endpoint (required for external cluster client). | |
|
||||
| <a id="opt-providers-kubernetesingressnginx-ingressclass" href="#opt-providers-kubernetesingressnginx-ingressclass" title="#opt-providers-kubernetesingressnginx-ingressclass">providers.kubernetesingressnginx.ingressclass</a> | Name of the ingress class this controller satisfies. | nginx |
|
||||
| <a id="opt-providers-kubernetesingressnginx-ingressclassbyname" href="#opt-providers-kubernetesingressnginx-ingressclassbyname" title="#opt-providers-kubernetesingressnginx-ingressclassbyname">providers.kubernetesingressnginx.ingressclassbyname</a> | Define if Ingress Controller should watch for Ingress Class by Name together with Controller Class. | false |
|
||||
| <a id="opt-providers-kubernetesingressnginx-proxyconnecttimeout" href="#opt-providers-kubernetesingressnginx-proxyconnecttimeout" title="#opt-providers-kubernetesingressnginx-proxyconnecttimeout">providers.kubernetesingressnginx.proxyconnecttimeout</a> | Amount of time to wait until a connection to a server can be established. Timeout value is unitless and in seconds. | 60 |
|
||||
| <a id="opt-providers-kubernetesingressnginx-publishservice" href="#opt-providers-kubernetesingressnginx-publishservice" title="#opt-providers-kubernetesingressnginx-publishservice">providers.kubernetesingressnginx.publishservice</a> | Service fronting the Ingress controller. Takes the form 'namespace/name'. | |
|
||||
| <a id="opt-providers-kubernetesingressnginx-publishstatusaddress" href="#opt-providers-kubernetesingressnginx-publishstatusaddress" title="#opt-providers-kubernetesingressnginx-publishstatusaddress">providers.kubernetesingressnginx.publishstatusaddress</a> | Customized address (or addresses, separated by comma) to set as the load-balancer status of Ingress objects this controller satisfies. | |
|
||||
| <a id="opt-providers-kubernetesingressnginx-throttleduration" href="#opt-providers-kubernetesingressnginx-throttleduration" title="#opt-providers-kubernetesingressnginx-throttleduration">providers.kubernetesingressnginx.throttleduration</a> | Ingress refresh throttle duration. | 0 |
|
||||
|
|
|
|||
|
|
@ -1,19 +1,18 @@
|
|||
---
|
||||
title: "Traefik FastProxy Experimental Configuration"
|
||||
description: "This section of the Traefik Proxy documentation explains how to use the new FastProxy option."
|
||||
description: "This section of the Traefik Proxy documentation explains how to use the new FastProxy install configuration option."
|
||||
---
|
||||
|
||||
# Traefik FastProxy Experimental Configuration
|
||||
|
||||
## Overview
|
||||
|
||||
This guide provides instructions on how to configure and use the new experimental `fastProxy` static configuration option in Traefik.
|
||||
The `fastProxy` option introduces a high-performance reverse proxy designed to enhance the performance of routing.
|
||||
This guide provides instructions on how to configure and use the new experimental `fastProxy` install configuration option in Traefik. The `fastProxy` option introduces a high-performance reverse proxy designed to enhance the performance of routing.
|
||||
|
||||
!!! info "Limitations"
|
||||
|
||||
Please note that the new fast proxy implementation does not work with HTTP/2.
|
||||
This means that when a H2C or HTTPS request with [HTTP2 enabled](../routing/services/index.md#disablehttp2) is sent to a backend, the fallback proxy is the regular one.
|
||||
This means that when a H2C or HTTPS request with [HTTP2 enabled](../../routing-configuration/http/load-balancing/service.md#disablehttp2) is sent to a backend, the fallback proxy is the regular one.
|
||||
|
||||
Additionnaly, observability features like tracing and OTEL semconv metrics are not supported for the moment.
|
||||
|
||||
|
|
@ -22,10 +21,10 @@ The `fastProxy` option introduces a high-performance reverse proxy designed to e
|
|||
The `fastProxy` option is currently experimental and subject to change in future releases.
|
||||
Use with caution in production environments.
|
||||
|
||||
### Enabling FastProxy
|
||||
## Enabling FastProxy
|
||||
|
||||
The fastProxy option is a static configuration parameter.
|
||||
To enable it, you need to configure it in your Traefik static configuration
|
||||
The fastProxy option is an install configuration parameter.
|
||||
To enable it, you need to configure it in your Traefik install configuration
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
experimental:
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
title: "Traefik Plugins Experimental Configuration"
|
||||
description: "This section of the Traefik Proxy documentation explains how to use the new Plugins install configuration option."
|
||||
---
|
||||
|
||||
# Traefik Plugins Experimental Configuration
|
||||
|
||||
## Overview
|
||||
|
||||
This guide provides instructions on how to configure and use the new experimental `plugins` install configuration option in Traefik. The `plugins` option introduces a system to extend Traefik capabilities with custom middlewares and providers.
|
||||
|
||||
!!! warning "Experimental"
|
||||
|
||||
The `plugins` option is currently experimental and subject to change in future releases.
|
||||
Use with caution in production environments.
|
||||
|
||||
## Enabling Plugins
|
||||
|
||||
The plugins option is an install configuration parameter.
|
||||
To enable a plugin, you need to define it in your Traefik install configuration
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
experimental:
|
||||
plugins:
|
||||
plugin-name: # The name of the plugin in the routing configuration
|
||||
moduleName: "github.com/github-organization/github-repository" # The plugin module name
|
||||
version: "vX.XX.X" # The version to use
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[experimental.plugins.plugin-name]
|
||||
moduleName = "github.com/github-organization/github-repository" # The plugin module name
|
||||
version = "vX.XX.X" # The version to use
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
# The plugin module name
|
||||
# With plugin-name the name of the plugin in the routing configuration
|
||||
--experimental.plugins.plugin-name.modulename=github.com/github-organization/github-repository
|
||||
--experimental.plugins.plugin-name.version=vX.XX.X # The version to use
|
||||
```
|
||||
|
||||
To learn more about how to add a new plugin to a Traefik instance, please refer to the [developer documentation](https://plugins.traefik.io/install).
|
||||
|
|
@ -128,6 +128,6 @@ See the dedicated section in [routing](../../../../routing/providers/kubernetes-
|
|||
|
||||
## Full Example
|
||||
|
||||
For additional information, refer to the [full example](../../../../user-guides/crd-acme/index.md) with Let's Encrypt.
|
||||
For additional information on exposing services with Kubernetes, refer to the [Kubernetes guide](../../../../expose/kubernetes/basic.md).
|
||||
|
||||
{% include-markdown "includes/traefik-for-business-applications.md" %}
|
||||
|
|
|
|||
|
|
@ -114,23 +114,24 @@ This provider watches for incoming Ingress events and automatically translates N
|
|||
## Configuration Options
|
||||
<!-- markdownlint-disable MD013 -->
|
||||
|
||||
| Field | Description | Default | Required |
|
||||
|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------|:--------|:---------|
|
||||
| <a id="opt-providers-providers-ThrottleDuration" href="#opt-providers-providers-ThrottleDuration" title="#opt-providers-providers-ThrottleDuration">`providers.providers`<br/>`ThrottleDuration`</a> | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.<br />If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.<br />**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-endpoint" href="#opt-providers-kubernetesIngressNGINX-endpoint" title="#opt-providers-kubernetesIngressNGINX-endpoint">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`endpoint`</a> | Server endpoint URL.<br />More information [here](#endpoint). | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-token" href="#opt-providers-kubernetesIngressNGINX-token" title="#opt-providers-kubernetesIngressNGINX-token">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`token`</a> | Bearer token used for the Kubernetes client configuration. | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-certAuthFilePath" href="#opt-providers-kubernetesIngressNGINX-certAuthFilePath" title="#opt-providers-kubernetesIngressNGINX-certAuthFilePath">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`certAuthFilePath`</a> | Path to the certificate authority file.<br />Used for the Kubernetes client configuration. | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-throttleDuration" href="#opt-providers-kubernetesIngressNGINX-throttleDuration" title="#opt-providers-kubernetesIngressNGINX-throttleDuration">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`throttleDuration`</a> | Minimum amount of time to wait between two Kubernetes events before producing a new configuration.<br />This prevents a Kubernetes cluster that updates many times per second from continuously changing your Traefik configuration.<br />If empty, every event is caught. | 0s | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-watchNamespace" href="#opt-providers-kubernetesIngressNGINX-watchNamespace" title="#opt-providers-kubernetesIngressNGINX-watchNamespace">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`watchNamespace`</a> | Namespace the controller watches for updates to Kubernetes objects. All namespaces are watched if this parameter is left empty. | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-watchNamespaceSelector" href="#opt-providers-kubernetesIngressNGINX-watchNamespaceSelector" title="#opt-providers-kubernetesIngressNGINX-watchNamespaceSelector">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`watchNamespaceSelector`</a> | Selector selects namespaces the controller watches for updates to Kubernetes objects. | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-ingressClass" href="#opt-providers-kubernetesIngressNGINX-ingressClass" title="#opt-providers-kubernetesIngressNGINX-ingressClass">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`ingressClass`</a> | Name of the ingress class this controller satisfies. | "nginx" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-controllerClass" href="#opt-providers-kubernetesIngressNGINX-controllerClass" title="#opt-providers-kubernetesIngressNGINX-controllerClass">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`controllerClass`</a> | Ingress Class Controller value this controller satisfies. | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-watchIngressWithoutClass" href="#opt-providers-kubernetesIngressNGINX-watchIngressWithoutClass" title="#opt-providers-kubernetesIngressNGINX-watchIngressWithoutClass">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`watchIngressWithoutClass`</a> | Define if Ingress Controller should also watch for Ingresses without an IngressClass or the annotation specified. | false | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-ingressClassByName" href="#opt-providers-kubernetesIngressNGINX-ingressClassByName" title="#opt-providers-kubernetesIngressNGINX-ingressClassByName">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`ingressClassByName`</a> | Define if Ingress Controller should watch for Ingress Class by Name together with Controller Class. | false | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-publishService" href="#opt-providers-kubernetesIngressNGINX-publishService" title="#opt-providers-kubernetesIngressNGINX-publishService">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`publishService`</a> | Service fronting the Ingress controller. Takes the form `namespace/name`. | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-publishStatusAddress" href="#opt-providers-kubernetesIngressNGINX-publishStatusAddress" title="#opt-providers-kubernetesIngressNGINX-publishStatusAddress">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`publishStatusAddress`</a> | Customized address (or addresses, separated by comma) to set as the load-balancer status of Ingress objects this controller satisfies. | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-defaultBackendService" href="#opt-providers-kubernetesIngressNGINX-defaultBackendService" title="#opt-providers-kubernetesIngressNGINX-defaultBackendService">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`defaultBackendService`</a> | Service used to serve HTTP requests not matching any known server name (catch-all). Takes the form 'namespace/name'. | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-disableSvcExternalName" href="#opt-providers-kubernetesIngressNGINX-disableSvcExternalName" title="#opt-providers-kubernetesIngressNGINX-disableSvcExternalName">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`disableSvcExternalName`</a> | Disable support for Services of type ExternalName. | false | No |
|
||||
| Field | Description | Default | Required |
|
||||
|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------|
|
||||
| <a id="opt-providers-providers-ThrottleDuration" href="#opt-providers-providers-ThrottleDuration" title="#opt-providers-providers-ThrottleDuration">`providers.providers`<br/>`ThrottleDuration`</a> | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.<br />If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.<br />**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-endpoint" href="#opt-providers-kubernetesIngressNGINX-endpoint" title="#opt-providers-kubernetesIngressNGINX-endpoint">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`endpoint`</a> | Server endpoint URL.<br />More information [here](#endpoint). | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-token" href="#opt-providers-kubernetesIngressNGINX-token" title="#opt-providers-kubernetesIngressNGINX-token">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`token`</a> | Bearer token used for the Kubernetes client configuration. | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-certAuthFilePath" href="#opt-providers-kubernetesIngressNGINX-certAuthFilePath" title="#opt-providers-kubernetesIngressNGINX-certAuthFilePath">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`certAuthFilePath`</a> | Path to the certificate authority file.<br />Used for the Kubernetes client configuration. | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-throttleDuration" href="#opt-providers-kubernetesIngressNGINX-throttleDuration" title="#opt-providers-kubernetesIngressNGINX-throttleDuration">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`throttleDuration`</a> | Minimum amount of time to wait between two Kubernetes events before producing a new configuration.<br />This prevents a Kubernetes cluster that updates many times per second from continuously changing your Traefik configuration.<br />If empty, every event is caught. | 0s | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-watchNamespace" href="#opt-providers-kubernetesIngressNGINX-watchNamespace" title="#opt-providers-kubernetesIngressNGINX-watchNamespace">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`watchNamespace`</a> | Namespace the controller watches for updates to Kubernetes objects. All namespaces are watched if this parameter is left empty. | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-watchNamespaceSelector" href="#opt-providers-kubernetesIngressNGINX-watchNamespaceSelector" title="#opt-providers-kubernetesIngressNGINX-watchNamespaceSelector">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`watchNamespaceSelector`</a> | Selector selects namespaces the controller watches for updates to Kubernetes objects. | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-ingressClass" href="#opt-providers-kubernetesIngressNGINX-ingressClass" title="#opt-providers-kubernetesIngressNGINX-ingressClass">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`ingressClass`</a> | Name of the ingress class this controller satisfies. | "nginx" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-controllerClass" href="#opt-providers-kubernetesIngressNGINX-controllerClass" title="#opt-providers-kubernetesIngressNGINX-controllerClass">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`controllerClass`</a> | Ingress Class Controller value this controller satisfies. | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-watchIngressWithoutClass" href="#opt-providers-kubernetesIngressNGINX-watchIngressWithoutClass" title="#opt-providers-kubernetesIngressNGINX-watchIngressWithoutClass">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`watchIngressWithoutClass`</a> | Define if Ingress Controller should also watch for Ingresses without an IngressClass or the annotation specified. | false | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-ingressClassByName" href="#opt-providers-kubernetesIngressNGINX-ingressClassByName" title="#opt-providers-kubernetesIngressNGINX-ingressClassByName">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`ingressClassByName`</a> | Define if Ingress Controller should watch for Ingress Class by Name together with Controller Class. | false | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-publishService" href="#opt-providers-kubernetesIngressNGINX-publishService" title="#opt-providers-kubernetesIngressNGINX-publishService">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`publishService`</a> | Service fronting the Ingress controller. Takes the form `namespace/name`. | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-publishStatusAddress" href="#opt-providers-kubernetesIngressNGINX-publishStatusAddress" title="#opt-providers-kubernetesIngressNGINX-publishStatusAddress">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`publishStatusAddress`</a> | Customized address (or addresses, separated by comma) to set as the load-balancer status of Ingress objects this controller satisfies. | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-defaultBackendService" href="#opt-providers-kubernetesIngressNGINX-defaultBackendService" title="#opt-providers-kubernetesIngressNGINX-defaultBackendService">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`defaultBackendService`</a> | Service used to serve HTTP requests not matching any known server name (catch-all). Takes the form 'namespace/name'. | "" | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-disableSvcExternalName" href="#opt-providers-kubernetesIngressNGINX-disableSvcExternalName" title="#opt-providers-kubernetesIngressNGINX-disableSvcExternalName">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`disableSvcExternalName`</a> | Disable support for Services of type ExternalName. | false | No |
|
||||
| <a id="opt-providers-kubernetesIngressNGINX-proxyConnectTimeout" href="#opt-providers-kubernetesIngressNGINX-proxyConnectTimeout" title="#opt-providers-kubernetesIngressNGINX-proxyConnectTimeout">`providers.`<br/>`kubernetesIngressNGINX.`<br/>`proxyConnectTimeout`</a> | Amount of time to wait until a connection to a server can be established. The value is unitless and in seconds. This is used as the global connection timeout when no ingress-specific timeout is configured. An ingress-specific timeout can be configured using [`nginx.ingress.kubernetes.io/proxy-connect-timeout`](../../../../routing-configuration/kubernetes/ingress-nginx/#opt-nginx-ingress-kubernetes-ioproxy-connect-timeout) annotation. | 60 | No |
|
||||
|
||||
<!-- markdownlint-enable MD013 -->
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ The `grpcWeb` middleware converts gRPC Web requests to HTTP/2 gRPC requests befo
|
|||
!!! tip
|
||||
|
||||
Please note, that Traefik needs to communicate using gRPC with the backends (h2c or HTTP/2 over TLS).
|
||||
Check out the [gRPC](../../../../user-guides/grpc.md) user guide for more details.
|
||||
Check out [Exposing gRPC Services](../../../../expose/overview.md#exposing-grpc-services) for more details.
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
|
|
|
|||
|
|
@ -267,15 +267,17 @@ The following annotations are organized by category for easier navigation.
|
|||
|
||||
### SSL/TLS
|
||||
|
||||
| Annotation | Limitations / Notes |
|
||||
|-------------------------------------------------------|--------------------------------------------------------------------------------------------|
|
||||
| <a id="opt-nginx-ingress-kubernetes-iossl-redirect" href="#opt-nginx-ingress-kubernetes-iossl-redirect" title="#opt-nginx-ingress-kubernetes-iossl-redirect">`nginx.ingress.kubernetes.io/ssl-redirect`</a> | Cannot opt-out per route if enabled globally. |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioforce-ssl-redirect" href="#opt-nginx-ingress-kubernetes-ioforce-ssl-redirect" title="#opt-nginx-ingress-kubernetes-ioforce-ssl-redirect">`nginx.ingress.kubernetes.io/force-ssl-redirect`</a> | Cannot opt-out per route if enabled globally. |
|
||||
| <a id="opt-nginx-ingress-kubernetes-iossl-passthrough" href="#opt-nginx-ingress-kubernetes-iossl-passthrough" title="#opt-nginx-ingress-kubernetes-iossl-passthrough">`nginx.ingress.kubernetes.io/ssl-passthrough`</a> | Some differences in SNI/default backend handling. |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-server-name" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-server-name" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-server-name">`nginx.ingress.kubernetes.io/proxy-ssl-server-name`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-name" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-name" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-name">`nginx.ingress.kubernetes.io/proxy-ssl-name`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-verify" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-verify" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-verify">`nginx.ingress.kubernetes.io/proxy-ssl-verify`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-secret" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-secret" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-secret">`nginx.ingress.kubernetes.io/proxy-ssl-secret`</a> | |
|
||||
| Annotation | Limitations / Notes |
|
||||
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
|
||||
| <a id="opt-nginx-ingress-kubernetes-iossl-redirect" href="#opt-nginx-ingress-kubernetes-iossl-redirect" title="#opt-nginx-ingress-kubernetes-iossl-redirect">`nginx.ingress.kubernetes.io/ssl-redirect`</a> | Cannot opt-out per route if enabled globally. |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioforce-ssl-redirect" href="#opt-nginx-ingress-kubernetes-ioforce-ssl-redirect" title="#opt-nginx-ingress-kubernetes-ioforce-ssl-redirect">`nginx.ingress.kubernetes.io/force-ssl-redirect`</a> | Cannot opt-out per route if enabled globally. |
|
||||
| <a id="opt-nginx-ingress-kubernetes-iossl-passthrough" href="#opt-nginx-ingress-kubernetes-iossl-passthrough" title="#opt-nginx-ingress-kubernetes-iossl-passthrough">`nginx.ingress.kubernetes.io/ssl-passthrough`</a> | Some differences in SNI/default backend handling. |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-server-name" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-server-name" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-server-name">`nginx.ingress.kubernetes.io/proxy-ssl-server-name`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-name" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-name" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-name">`nginx.ingress.kubernetes.io/proxy-ssl-name`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-verify" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-verify" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-verify">`nginx.ingress.kubernetes.io/proxy-ssl-verify`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioproxy-ssl-secret" href="#opt-nginx-ingress-kubernetes-ioproxy-ssl-secret" title="#opt-nginx-ingress-kubernetes-ioproxy-ssl-secret">`nginx.ingress.kubernetes.io/proxy-ssl-secret`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioauth-tls-secret" href="#opt-nginx-ingress-kubernetes-ioauth-tls-secret" title="#opt-nginx-ingress-kubernetes-ioauth-tls-secret">`nginx.ingress.kubernetes.io/auth-tls-secret`</a> | When validation fails, the rejection happens during the TLS handshake rather than returning a 400 Bad Request. |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioauth-tls-verify-client" href="#opt-nginx-ingress-kubernetes-ioauth-tls-verify-client" title="#opt-nginx-ingress-kubernetes-ioauth-tls-verify-client">`nginx.ingress.kubernetes.io/auth-tls-verify-client`</a> | When validation fails, the rejection happens during the TLS handshake rather than returning a 400 Bad Request. |
|
||||
|
||||
### Session Affinity
|
||||
|
||||
|
|
@ -318,6 +320,7 @@ The following annotations are organized by category for easier navigation.
|
|||
| Annotation | Limitations / Notes |
|
||||
|-------------------------------------------------------|--------------------------------------------------------------------------------------------|
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioapp-root" href="#opt-nginx-ingress-kubernetes-ioapp-root" title="#opt-nginx-ingress-kubernetes-ioapp-root">`nginx.ingress.kubernetes.io/app-root`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-iofrom-to-www-redirect" href="#opt-nginx-ingress-kubernetes-iofrom-to-www-redirect" title="#opt-nginx-ingress-kubernetes-iofrom-to-www-redirect">`nginx.ingress.kubernetes.io/from-to-www-redirect`</a> | Doesn't support wildcard hosts. |
|
||||
| <a id="opt-nginx-ingress-kubernetes-iouse-regex" href="#opt-nginx-ingress-kubernetes-iouse-regex" title="#opt-nginx-ingress-kubernetes-iouse-regex">`nginx.ingress.kubernetes.io/use-regex`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-iorewrite-target" href="#opt-nginx-ingress-kubernetes-iorewrite-target" title="#opt-nginx-ingress-kubernetes-iorewrite-target">`nginx.ingress.kubernetes.io/rewrite-target`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-iopermanent-redirect" href="#opt-nginx-ingress-kubernetes-iopermanent-redirect" title="#opt-nginx-ingress-kubernetes-iopermanent-redirect">`nginx.ingress.kubernetes.io/permanent-redirect`</a> | Defaults to a 301 Moved Permanently status code. |
|
||||
|
|
@ -331,6 +334,11 @@ The following annotations are organized by category for easier navigation.
|
|||
|-------------------------------------------------------|--------------------------------------------------------------------------------------------|
|
||||
| <a id="opt-nginx-ingress-kubernetes-iowhitelist-source-range" href="#opt-nginx-ingress-kubernetes-iowhitelist-source-range" title="#opt-nginx-ingress-kubernetes-iowhitelist-source-range">`nginx.ingress.kubernetes.io/whitelist-source-range`</a> | |
|
||||
|
||||
### Timeout
|
||||
|
||||
| Annotation | Limitations / Notes |
|
||||
|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioproxy-connect-timeout" href="#opt-nginx-ingress-kubernetes-ioproxy-connect-timeout" title="#opt-nginx-ingress-kubernetes-ioproxy-connect-timeout">`nginx.ingress.kubernetes.io/proxy-connect-timeout`</a> | Timeout can be defined globally at the provider level using the [`proxyConnectTimeout` option](../../../install-configuration/providers/kubernetes/kubernetes-ingress-nginx/#opt-providers-kubernetesIngressNGINX-proxyConnectTimeout). |
|
||||
|
||||
## Limitations
|
||||
|
||||
|
|
@ -358,10 +366,8 @@ The following annotations are organized by category for easier navigation.
|
|||
|
||||
| Annotation | Notes |
|
||||
|-----------------------------------------------------------------------------|------------------------------------------------------|
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior" href="#opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior" title="#opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior">`nginx.ingress.kubernetes.io/affinity-canary-behavior`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioauth-tls-secret" href="#opt-nginx-ingress-kubernetes-ioauth-tls-secret" title="#opt-nginx-ingress-kubernetes-ioauth-tls-secret">`nginx.ingress.kubernetes.io/auth-tls-secret`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioauth-tls-verify-depth" href="#opt-nginx-ingress-kubernetes-ioauth-tls-verify-depth" title="#opt-nginx-ingress-kubernetes-ioauth-tls-verify-depth">`nginx.ingress.kubernetes.io/auth-tls-verify-depth`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioauth-tls-verify-client" href="#opt-nginx-ingress-kubernetes-ioauth-tls-verify-client" title="#opt-nginx-ingress-kubernetes-ioauth-tls-verify-client">`nginx.ingress.kubernetes.io/auth-tls-verify-client`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior" href="#opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior" title="#opt-nginx-ingress-kubernetes-ioaffinity-canary-behavior">`nginx.ingress.kubernetes.io/affinity-canary-behavior`</a> | | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioauth-tls-verify-depth" href="#opt-nginx-ingress-kubernetes-ioauth-tls-verify-depth" title="#opt-nginx-ingress-kubernetes-ioauth-tls-verify-depth">`nginx.ingress.kubernetes.io/auth-tls-verify-depth`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioauth-tls-error-page" href="#opt-nginx-ingress-kubernetes-ioauth-tls-error-page" title="#opt-nginx-ingress-kubernetes-ioauth-tls-error-page">`nginx.ingress.kubernetes.io/auth-tls-error-page`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioauth-tls-pass-certificate-to-upstream" href="#opt-nginx-ingress-kubernetes-ioauth-tls-pass-certificate-to-upstream" title="#opt-nginx-ingress-kubernetes-ioauth-tls-pass-certificate-to-upstream">`nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioauth-tls-match-cn" href="#opt-nginx-ingress-kubernetes-ioauth-tls-match-cn" title="#opt-nginx-ingress-kubernetes-ioauth-tls-match-cn">`nginx.ingress.kubernetes.io/auth-tls-match-cn`</a> | |
|
||||
|
|
@ -399,8 +405,7 @@ The following annotations are organized by category for easier navigation.
|
|||
| <a id="opt-nginx-ingress-kubernetes-ioglobal-rate-limit-ignored-cidrs" href="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit-ignored-cidrs" title="#opt-nginx-ingress-kubernetes-ioglobal-rate-limit-ignored-cidrs">`nginx.ingress.kubernetes.io/global-rate-limit-ignored-cidrs`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-iopreserve-trailing-slash" href="#opt-nginx-ingress-kubernetes-iopreserve-trailing-slash" title="#opt-nginx-ingress-kubernetes-iopreserve-trailing-slash">`nginx.ingress.kubernetes.io/preserve-trailing-slash`</a> | Traefik preserves trailing slash by default. |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioproxy-cookie-domain" href="#opt-nginx-ingress-kubernetes-ioproxy-cookie-domain" title="#opt-nginx-ingress-kubernetes-ioproxy-cookie-domain">`nginx.ingress.kubernetes.io/proxy-cookie-domain`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioproxy-cookie-path" href="#opt-nginx-ingress-kubernetes-ioproxy-cookie-path" title="#opt-nginx-ingress-kubernetes-ioproxy-cookie-path">`nginx.ingress.kubernetes.io/proxy-cookie-path`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioproxy-connect-timeout" href="#opt-nginx-ingress-kubernetes-ioproxy-connect-timeout" title="#opt-nginx-ingress-kubernetes-ioproxy-connect-timeout">`nginx.ingress.kubernetes.io/proxy-connect-timeout`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioproxy-cookie-path" href="#opt-nginx-ingress-kubernetes-ioproxy-cookie-path" title="#opt-nginx-ingress-kubernetes-ioproxy-cookie-path">`nginx.ingress.kubernetes.io/proxy-cookie-path`</a> | | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioproxy-send-timeout" href="#opt-nginx-ingress-kubernetes-ioproxy-send-timeout" title="#opt-nginx-ingress-kubernetes-ioproxy-send-timeout">`nginx.ingress.kubernetes.io/proxy-send-timeout`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioproxy-read-timeout" href="#opt-nginx-ingress-kubernetes-ioproxy-read-timeout" title="#opt-nginx-ingress-kubernetes-ioproxy-read-timeout">`nginx.ingress.kubernetes.io/proxy-read-timeout`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioproxy-next-upstream" href="#opt-nginx-ingress-kubernetes-ioproxy-next-upstream" title="#opt-nginx-ingress-kubernetes-ioproxy-next-upstream">`nginx.ingress.kubernetes.io/proxy-next-upstream`</a> | |
|
||||
|
|
|
|||
|
|
@ -113,31 +113,33 @@
|
|||
[http.services.Service03.loadBalancer.responseForwarding]
|
||||
flushInterval = "42s"
|
||||
[http.services.Service04]
|
||||
[http.services.Service04.mirroring]
|
||||
middlewares = ["foobar", "foobar"]
|
||||
[http.services.Service05]
|
||||
[http.services.Service05.mirroring]
|
||||
service = "foobar"
|
||||
mirrorBody = true
|
||||
maxBodySize = 42
|
||||
|
||||
[[http.services.Service04.mirroring.mirrors]]
|
||||
[[http.services.Service05.mirroring.mirrors]]
|
||||
name = "foobar"
|
||||
percent = 42
|
||||
|
||||
[[http.services.Service04.mirroring.mirrors]]
|
||||
[[http.services.Service05.mirroring.mirrors]]
|
||||
name = "foobar"
|
||||
percent = 42
|
||||
[http.services.Service04.mirroring.healthCheck]
|
||||
[http.services.Service05]
|
||||
[http.services.Service05.weighted]
|
||||
[http.services.Service05.mirroring.healthCheck]
|
||||
[http.services.Service06]
|
||||
[http.services.Service06.weighted]
|
||||
|
||||
[[http.services.Service05.weighted.services]]
|
||||
[[http.services.Service06.weighted.services]]
|
||||
name = "foobar"
|
||||
weight = 42
|
||||
|
||||
[[http.services.Service05.weighted.services]]
|
||||
[[http.services.Service06.weighted.services]]
|
||||
name = "foobar"
|
||||
weight = 42
|
||||
[http.services.Service05.weighted.sticky]
|
||||
[http.services.Service05.weighted.sticky.cookie]
|
||||
[http.services.Service06.weighted.sticky]
|
||||
[http.services.Service06.weighted.sticky.cookie]
|
||||
name = "foobar"
|
||||
secure = true
|
||||
httpOnly = true
|
||||
|
|
@ -145,7 +147,7 @@
|
|||
maxAge = 42
|
||||
path = "foobar"
|
||||
domain = "foobar"
|
||||
[http.services.Service05.weighted.healthCheck]
|
||||
[http.services.Service06.weighted.healthCheck]
|
||||
[http.middlewares]
|
||||
[http.middlewares.Middleware01]
|
||||
[http.middlewares.Middleware01.addPrefix]
|
||||
|
|
|
|||
|
|
@ -121,6 +121,10 @@ http:
|
|||
flushInterval: 42s
|
||||
serversTransport: foobar
|
||||
Service04:
|
||||
middlewares:
|
||||
- foobar
|
||||
- foobar
|
||||
Service05:
|
||||
mirroring:
|
||||
service: foobar
|
||||
mirrorBody: true
|
||||
|
|
@ -131,7 +135,7 @@ http:
|
|||
- name: foobar
|
||||
percent: 42
|
||||
healthCheck: {}
|
||||
Service05:
|
||||
Service06:
|
||||
weighted:
|
||||
services:
|
||||
- name: foobar
|
||||
|
|
|
|||
|
|
@ -2045,6 +2045,6 @@ If the ServersTransportTCP CRD is defined in another provider the cross-provider
|
|||
|
||||
## Further
|
||||
|
||||
Also see the [full example](../../user-guides/crd-acme/index.md) with Let's Encrypt.
|
||||
For additional information on exposing services with Kubernetes, see the [Kubernetes guide](../../expose/kubernetes/basic.md).
|
||||
|
||||
{% include-markdown "includes/traefik-for-business-applications.md" %}
|
||||
|
|
|
|||
|
|
@ -1,183 +0,0 @@
|
|||
---
|
||||
title: "Integration with cert-manager"
|
||||
description: "Learn how to use cert-manager certificates with Traefik Proxy for your routers. Read the technical documentation."
|
||||
---
|
||||
|
||||
# cert-manager
|
||||
|
||||
Provision TLS Certificate for Traefik Proxy with cert-manager on Kubernetes
|
||||
{: .subtitle }
|
||||
|
||||
## Pre-requisites
|
||||
|
||||
To obtain certificates from cert-manager that can be used in Traefik Proxy, you will need to:
|
||||
|
||||
1. Have cert-manager properly configured
|
||||
2. Have Traefik Proxy configured
|
||||
|
||||
The certificates can then be used in an Ingress / IngressRoute / HTTPRoute.
|
||||
|
||||
## Example with ACME and HTTP challenge
|
||||
|
||||
!!! example "ACME issuer for HTTP challenge"
|
||||
|
||||
```yaml tab="Issuer"
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Issuer
|
||||
metadata:
|
||||
name: acme
|
||||
|
||||
spec:
|
||||
acme:
|
||||
# Production server is on https://acme-v02.api.letsencrypt.org/directory
|
||||
# Use staging by default.
|
||||
server: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
privateKeySecretRef:
|
||||
name: acme
|
||||
solvers:
|
||||
- http01:
|
||||
ingress:
|
||||
ingressClassName: traefik
|
||||
```
|
||||
|
||||
```yaml tab="Certificate"
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: traefik
|
||||
spec:
|
||||
secretName: domain-tls # <=== Name of secret where the generated certificate will be stored.
|
||||
dnsNames:
|
||||
- "domain.example.com"
|
||||
issuerRef:
|
||||
name: acme
|
||||
kind: Issuer
|
||||
```
|
||||
|
||||
Let's see now how to use it with the various Kubernetes providers of Traefik Proxy.
|
||||
The enabled providers can be seen on the [dashboard](../reference/install-configuration/api-dashboard.md) of Traefik Proxy and also in the INFO logs when Traefik Proxy starts.
|
||||
|
||||
### With an Ingress
|
||||
|
||||
To use this certificate with an Ingress, the [Kubernetes Ingress](../providers/kubernetes-ingress.md) provider has to be enabled.
|
||||
|
||||
!!! info Traefik Helm Chart
|
||||
|
||||
This provider is enabled by default in the Traefik Helm Chart.
|
||||
|
||||
!!! example "Route with this Certificate"
|
||||
|
||||
```yaml tab="Ingress"
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: domain
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
|
||||
spec:
|
||||
rules:
|
||||
- host: domain.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Exact
|
||||
backend:
|
||||
service:
|
||||
name: domain-service
|
||||
port:
|
||||
number: 80
|
||||
tls:
|
||||
- secretName: domain-tls # <=== Use the name defined in Certificate resource.
|
||||
```
|
||||
|
||||
### With an IngressRoute
|
||||
|
||||
To use this certificate with an IngressRoute, the [Kubernetes CRD](../providers/kubernetes-crd.md) provider has to be enabled.
|
||||
|
||||
!!! info Traefik Helm Chart
|
||||
|
||||
This provider is enabled by default in the Traefik Helm Chart.
|
||||
|
||||
!!! example "Route with this Certificate"
|
||||
|
||||
```yaml tab="IngressRoute"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: domain
|
||||
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
|
||||
routes:
|
||||
- match: Host(`domain.example.com`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: domain-service
|
||||
port: 80
|
||||
tls:
|
||||
secretName: domain-tls # <=== Use the name defined in Certificate resource.
|
||||
```
|
||||
|
||||
### With an HTTPRoute
|
||||
|
||||
To use this certificate with an HTTPRoute, the [Kubernetes Gateway](../routing/providers/kubernetes-gateway.md) provider has to be enabled.
|
||||
|
||||
!!! info Traefik Helm Chart
|
||||
|
||||
This provider is disabled by default in the Traefik Helm Chart.
|
||||
|
||||
!!! example "Route with this Certificate"
|
||||
|
||||
```yaml tab="HTTPRoute"
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: domain-gateway
|
||||
spec:
|
||||
gatewayClassName: traefik
|
||||
listeners:
|
||||
- name: websecure
|
||||
port: 8443
|
||||
protocol: HTTPS
|
||||
hostname: domain.example.com
|
||||
tls:
|
||||
certificateRefs:
|
||||
- name: domain-tls # <==== Use the name defined in Certificate resource.
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: domain
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: domain-gateway
|
||||
hostnames:
|
||||
- domain.example.com
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: Exact
|
||||
value: /
|
||||
|
||||
backendRefs:
|
||||
- name: domain-service
|
||||
port: 80
|
||||
weight: 1
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
There are multiple event sources available to investigate when using cert-manager:
|
||||
|
||||
1. Kubernetes events in `Certificate` and `CertificateRequest` resources
|
||||
2. cert-manager logs
|
||||
3. Dashboard and/or (debug) logs from Traefik Proxy
|
||||
|
||||
cert-manager documentation provides a [detailed guide](https://cert-manager.io/docs/troubleshooting/) on how to troubleshoot a certificate request.
|
||||
|
||||
{% include-markdown "includes/traefik-for-business-applications.md" %}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: traefik
|
||||
|
||||
spec:
|
||||
ports:
|
||||
- protocol: TCP
|
||||
name: web
|
||||
port: 8000
|
||||
- protocol: TCP
|
||||
name: admin
|
||||
port: 8080
|
||||
- protocol: TCP
|
||||
name: websecure
|
||||
port: 4443
|
||||
selector:
|
||||
app: traefik
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: whoami
|
||||
|
||||
spec:
|
||||
ports:
|
||||
- protocol: TCP
|
||||
name: web
|
||||
port: 80
|
||||
selector:
|
||||
app: whoami
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
namespace: default
|
||||
name: traefik-ingress-controller
|
||||
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
namespace: default
|
||||
name: traefik
|
||||
labels:
|
||||
app: traefik
|
||||
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: traefik
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: traefik
|
||||
spec:
|
||||
serviceAccountName: traefik-ingress-controller
|
||||
containers:
|
||||
- name: traefik
|
||||
image: traefik:v3.6
|
||||
args:
|
||||
- --api.insecure
|
||||
- --accesslog
|
||||
- --entryPoints.web.Address=:8000
|
||||
- --entryPoints.websecure.Address=:4443
|
||||
- --providers.kubernetescrd
|
||||
- --certificatesresolvers.myresolver.acme.tlschallenge
|
||||
- --certificatesresolvers.myresolver.acme.email=foo@you.com
|
||||
- --certificatesresolvers.myresolver.acme.storage=acme.json
|
||||
# Please note that this is the staging Let's Encrypt server.
|
||||
# Once you get things working, you should remove that whole line altogether.
|
||||
- --certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
ports:
|
||||
- name: web
|
||||
containerPort: 8000
|
||||
- name: websecure
|
||||
containerPort: 4443
|
||||
- name: admin
|
||||
containerPort: 8080
|
||||
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
namespace: default
|
||||
name: whoami
|
||||
labels:
|
||||
app: whoami
|
||||
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: whoami
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: whoami
|
||||
spec:
|
||||
containers:
|
||||
- name: whoami
|
||||
image: traefik/whoami
|
||||
ports:
|
||||
- name: web
|
||||
containerPort: 80
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: simpleingressroute
|
||||
namespace: default
|
||||
spec:
|
||||
entryPoints:
|
||||
- web
|
||||
routes:
|
||||
- match: Host(`your.example.com`) && PathPrefix(`/notls`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: ingressroutetls
|
||||
namespace: default
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`your.example.com`) && PathPrefix(`/tls`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
tls:
|
||||
certResolver: myresolver
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
spec:
|
||||
minVersion: VersionTLS12
|
||||
cipherSuites:
|
||||
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 # TLS 1.2
|
||||
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 # TLS 1.2
|
||||
- TLS_AES_256_GCM_SHA384 # TLS 1.3
|
||||
- TLS_CHACHA20_POLY1305_SHA256 # TLS 1.3
|
||||
curvePreferences:
|
||||
- CurveP521
|
||||
- CurveP384
|
||||
sniStrict: true
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
---
|
||||
title: "Traefik CRD TLS Documentation"
|
||||
description: "Learn how to use Traefik Proxy w/ an IngressRoute Custom Resource Definition (CRD) for Kubernetes, and TLS with Let's Encrypt. Read the technical documentation."
|
||||
---
|
||||
|
||||
# Traefik & CRD & Let's Encrypt
|
||||
|
||||
Traefik with an IngressRoute Custom Resource Definition for Kubernetes, and TLS Through Let's Encrypt.
|
||||
{: .subtitle }
|
||||
|
||||
This document is intended to be a fully working example demonstrating how to set up Traefik in [Kubernetes](https://kubernetes.io),
|
||||
with the dynamic configuration coming from the [IngressRoute Custom Resource](../../providers/kubernetes-crd.md),
|
||||
and TLS setup with [Let's Encrypt](https://letsencrypt.org).
|
||||
However, for the sake of simplicity, we're using [k3s](https://github.com/rancher/k3s) docker image for the Kubernetes cluster setup.
|
||||
|
||||
Please note that for this setup, given that we're going to use ACME's TLS-ALPN-01 challenge, the host you'll be running it on must be able to receive connections from the outside on port 443.
|
||||
And of course its internet facing IP address must match the domain name you intend to use.
|
||||
|
||||
In the following, the Kubernetes resources defined in YAML configuration files can be applied to the setup in two different ways:
|
||||
|
||||
- the first, and usual way, is simply with the `kubectl apply` command.
|
||||
- the second, which can be used for this tutorial, is to directly place the files in the directory used by the k3s docker image for such inputs (`/var/lib/rancher/k3s/server/manifests`).
|
||||
|
||||
!!! important "Kubectl Version"
|
||||
|
||||
With the `rancher/k3s` version used in this guide (`0.8.0`), the kubectl version needs to be >= `1.11`.
|
||||
|
||||
## k3s Docker-compose Configuration
|
||||
|
||||
Our starting point is the docker-compose configuration file, to start the k3s cluster.
|
||||
You can start it with:
|
||||
|
||||
```bash
|
||||
docker compose -f k3s.yml up
|
||||
```
|
||||
|
||||
```yaml
|
||||
--8<-- "content/user-guides/crd-acme/k3s.yml"
|
||||
```
|
||||
|
||||
## Cluster Resources
|
||||
|
||||
Let's now have a look (in the order they should be applied, if using `kubectl apply`) at all the required resources for the full setup.
|
||||
|
||||
### IngressRoute Definition
|
||||
|
||||
First, you will need to install Traefik CRDs containing the definition of the `IngressRoute` and the `Middleware` kinds,
|
||||
and the RBAC authorization resources which will be referenced through the `serviceAccountName` of the deployment.
|
||||
|
||||
```bash
|
||||
# Install Traefik Resource Definitions:
|
||||
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml
|
||||
|
||||
# Install RBAC for Traefik:
|
||||
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml
|
||||
```
|
||||
|
||||
### Services
|
||||
|
||||
Then, the services. One for Traefik itself, and one for the app it routes for, i.e. in this case our demo HTTP server: [whoami](https://github.com/traefik/whoami).
|
||||
|
||||
```bash
|
||||
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/user-guides/crd-acme/02-services.yml
|
||||
```
|
||||
|
||||
```yaml
|
||||
--8<-- "content/user-guides/crd-acme/02-services.yml"
|
||||
```
|
||||
|
||||
### Deployments
|
||||
|
||||
Next, the deployments, i.e. the actual pods behind the services.
|
||||
Again, one pod for Traefik, and one for the whoami app.
|
||||
|
||||
```bash
|
||||
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/user-guides/crd-acme/03-deployments.yml
|
||||
```
|
||||
|
||||
```yaml
|
||||
--8<-- "content/user-guides/crd-acme/03-deployments.yml"
|
||||
```
|
||||
|
||||
### Port Forwarding
|
||||
|
||||
Now, as an exception to what we said above, please note that you should not let the ingressRoute resources below be applied automatically to your cluster.
|
||||
The reason is, as soon as the ACME provider of Traefik detects we have TLS routers, it will try to generate the certificates for the corresponding domains.
|
||||
And this will not work, because as it is, our Traefik pod is not reachable from the outside, which will make the ACME TLS challenge fail.
|
||||
Therefore, for the whole thing to work, we must delay applying the ingressRoute resources until we have port-forwarding set up properly, which is the next step.
|
||||
|
||||
```bash
|
||||
kubectl port-forward --address 0.0.0.0 service/traefik 8000:8000 8080:8080 443:4443 -n default
|
||||
```
|
||||
|
||||
Also, and this is out of the scope of this guide, please note that because of the privileged ports limitation on Linux, the above command might fail to listen on port 443.
|
||||
In which case you can use tricks such as elevating caps of `kubectl` with `setcaps`, or using `authbind`, or setting up a NAT between your host and the WAN.
|
||||
Look it up.
|
||||
|
||||
### Traefik Routers
|
||||
|
||||
We can now finally apply the actual ingressRoutes, with:
|
||||
|
||||
```bash
|
||||
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/user-guides/crd-acme/04-ingressroutes.yml
|
||||
```
|
||||
|
||||
```yaml
|
||||
--8<-- "content/user-guides/crd-acme/04-ingressroutes.yml"
|
||||
```
|
||||
|
||||
Give it a few seconds for the ACME TLS challenge to complete, and you should then be able to access your whoami pod (routed through Traefik), from the outside.
|
||||
Both with or (just for fun, do not do that in production) without TLS:
|
||||
|
||||
```bash
|
||||
curl [-k] https://your.example.com/tls
|
||||
```
|
||||
|
||||
```bash
|
||||
curl http://your.example.com:8000/notls
|
||||
```
|
||||
|
||||
Note that you'll have to use `-k` as long as you're using the staging server of Let's Encrypt, since it is not an authorized certificate authority on systems where it hasn't been manually added.
|
||||
|
||||
### Force TLS v1.2+
|
||||
|
||||
Nowadays, TLS v1.0 and v1.1 are deprecated.
|
||||
In order to force TLS v1.2 or later on all your IngressRoute, you can define the `default` TLSOption:
|
||||
|
||||
```bash
|
||||
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/user-guides/crd-acme/05-tlsoption.yml
|
||||
```
|
||||
|
||||
```yaml
|
||||
--8<-- "content/user-guides/crd-acme/05-tlsoption.yml"
|
||||
```
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
server:
|
||||
image: rancher/k3s:v1.34.2-k3s1
|
||||
command: server --disable-agent --no-deploy traefik
|
||||
environment:
|
||||
- K3S_CLUSTER_SECRET=somethingtotallyrandom
|
||||
- K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml
|
||||
- K3S_KUBECONFIG_MODE=666
|
||||
volumes:
|
||||
# k3s will generate a kubeconfig.yaml in this directory. This volume is mounted
|
||||
# on your host, so you can then 'export KUBECONFIG=/somewhere/on/your/host/out/kubeconfig.yaml',
|
||||
# in order for your kubectl commands to work.
|
||||
- /somewhere/on/your/host/out:/output
|
||||
# This directory is where you put all the (yaml) configuration files of
|
||||
# the Kubernetes resources.
|
||||
- /somewhere/on/your/host/in:/var/lib/rancher/k3s/server/manifests
|
||||
ports:
|
||||
- 6443:6443
|
||||
|
||||
node:
|
||||
image: rancher/k3s:v1.34.2-k3s1
|
||||
privileged: true
|
||||
links:
|
||||
- server
|
||||
environment:
|
||||
- K3S_URL=https://server:6443
|
||||
- K3S_CLUSTER_SECRET=somethingtotallyrandom
|
||||
volumes:
|
||||
# this is where you would place a alternative traefik image (saved as a .tar file with
|
||||
# 'docker save'), if you want to use it, instead of the traefik:v3.6 image.
|
||||
- /somewhere/on/your/host/custom-image:/var/lib/rancher/k3s/agent/images
|
||||
|
|
@ -1,283 +0,0 @@
|
|||
---
|
||||
title: "Traefik Proxy gRPC Examples"
|
||||
description: "This section of the Traefik Proxy documentation explains how to use Traefik as reverse proxy for gRPC applications."
|
||||
---
|
||||
|
||||
# gRPC Examples
|
||||
|
||||
## With HTTP (h2c)
|
||||
|
||||
This section explains how to use Traefik as reverse proxy for gRPC application.
|
||||
|
||||
### Traefik Configuration
|
||||
|
||||
Static configuration:
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
entryPoints:
|
||||
web:
|
||||
address: :80
|
||||
|
||||
providers:
|
||||
file:
|
||||
directory: /path/to/dynamic/config
|
||||
|
||||
api: {}
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
address = ":80"
|
||||
|
||||
[api]
|
||||
|
||||
[providers.file]
|
||||
directory = "/path/to/dynamic/config"
|
||||
```
|
||||
|
||||
```yaml tab="CLI"
|
||||
--entryPoints.web.address=:80
|
||||
--providers.file.directory=/path/to/dynamic/config
|
||||
--api.insecure=true
|
||||
```
|
||||
|
||||
`/path/to/dynamic/config/dynamic_conf.{yml,toml}`:
|
||||
|
||||
```yaml tab="YAML"
|
||||
## dynamic configuration ##
|
||||
|
||||
http:
|
||||
routers:
|
||||
routerTest:
|
||||
service: srv-grpc
|
||||
rule: Host(`frontend.local`)
|
||||
|
||||
services:
|
||||
srv-grpc:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: h2c://backend.local:8080
|
||||
```
|
||||
|
||||
```toml tab="TOML"
|
||||
## dynamic configuration ##
|
||||
|
||||
[http]
|
||||
|
||||
[http.routers]
|
||||
[http.routers.routerTest]
|
||||
service = "srv-grpc"
|
||||
rule = "Host(`frontend.local`)"
|
||||
|
||||
[http.services]
|
||||
[http.services.srv-grpc]
|
||||
[http.services.srv-grpc.loadBalancer]
|
||||
[[http.services.srv-grpc.loadBalancer.servers]]
|
||||
url = "h2c://backend.local:8080"
|
||||
```
|
||||
|
||||
!!! warning
|
||||
For providers with labels, you will have to specify the `traefik.http.services.<my-service-name>.loadbalancer.server.scheme=h2c`
|
||||
|
||||
### Conclusion
|
||||
|
||||
We don't need specific configuration to use gRPC in Traefik, we just need to use `h2c` protocol, or use HTTPS communications to have HTTP2 with the backend.
|
||||
|
||||
## With HTTPS
|
||||
|
||||
This section explains how to use Traefik as reverse proxy for gRPC application with self-signed certificates.
|
||||
|
||||

|
||||
|
||||
### gRPC Server Certificate
|
||||
|
||||
In order to secure the gRPC server, we generate a self-signed certificate for service url:
|
||||
|
||||
```bash
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./backend.key -out ./backend.cert
|
||||
```
|
||||
|
||||
That will prompt for information, the important answer is:
|
||||
|
||||
```txt
|
||||
Common Name (e.g. server FQDN or YOUR name) []: backend.local
|
||||
```
|
||||
|
||||
### gRPC Client Certificate
|
||||
|
||||
Generate your self-signed certificate for router url:
|
||||
|
||||
```bash
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./frontend.key -out ./frontend.cert
|
||||
```
|
||||
|
||||
with
|
||||
|
||||
```txt
|
||||
Common Name (e.g. server FQDN or YOUR name) []: frontend.local
|
||||
```
|
||||
|
||||
### Traefik Configuration
|
||||
|
||||
At last, we configure our Traefik instance to use both self-signed certificates.
|
||||
|
||||
Static configuration:
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
entryPoints:
|
||||
websecure:
|
||||
address: :4443
|
||||
|
||||
serversTransport:
|
||||
# For secure connection on backend.local
|
||||
rootCAs:
|
||||
- ./backend.cert
|
||||
|
||||
providers:
|
||||
file:
|
||||
directory: /path/to/dynamic/config
|
||||
|
||||
api: {}
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[entryPoints]
|
||||
[entryPoints.websecure]
|
||||
address = ":4443"
|
||||
|
||||
|
||||
[serversTransport]
|
||||
# For secure connection on backend.local
|
||||
rootCAs = [ "./backend.cert" ]
|
||||
|
||||
[api]
|
||||
|
||||
[provider.file]
|
||||
directory = "/path/to/dynamic/config"
|
||||
```
|
||||
|
||||
```yaml tab="CLI"
|
||||
--entryPoints.websecure.address=:4443
|
||||
# For secure connection on backend.local
|
||||
--serversTransport.rootCAs=./backend.cert
|
||||
--providers.file.directory=/path/to/dynamic/config
|
||||
--api.insecure=true
|
||||
```
|
||||
|
||||
`/path/to/dynamic/config/dynamic_conf.{yml,toml}`:
|
||||
|
||||
```yaml tab="YAML"
|
||||
## dynamic configuration ##
|
||||
|
||||
http:
|
||||
routers:
|
||||
routerTest:
|
||||
service: srv-grpc
|
||||
rule: Host(`frontend.local`)
|
||||
services:
|
||||
srv-grpc:
|
||||
loadBalancer:
|
||||
servers:
|
||||
# Access on backend with HTTPS
|
||||
- url: https://backend.local:8080
|
||||
tls:
|
||||
# For secure connection on frontend.local
|
||||
certificates:
|
||||
- certfile: ./frontend.cert
|
||||
keyfile: ./frontend.key
|
||||
```
|
||||
|
||||
```toml tab="TOML"
|
||||
## dynamic configuration ##
|
||||
|
||||
[http]
|
||||
|
||||
[http.routers]
|
||||
[http.routers.routerTest]
|
||||
service = "srv-grpc"
|
||||
rule = "Host(`frontend.local`)"
|
||||
|
||||
[http.services]
|
||||
[http.services.srv-grpc]
|
||||
[http.services.srv-grpc.loadBalancer]
|
||||
[[http.services.srv-grpc.loadBalancer.servers]]
|
||||
# Access on backend with HTTPS
|
||||
url = "https://backend.local:8080"
|
||||
|
||||
[tls]
|
||||
|
||||
# For secure connection on frontend.local
|
||||
[[tls.certificates]]
|
||||
certFile = "./frontend.cert"
|
||||
keyFile = "./frontend.key"
|
||||
```
|
||||
|
||||
!!! warning
|
||||
With some services, the server URLs use the IP, so you may need to configure `insecureSkipVerify` instead of the `rootCAs` to activate HTTPS without hostname verification.
|
||||
|
||||
### A gRPC example in go (modify for https)
|
||||
|
||||
We use the gRPC greeter example in [grpc-go](https://github.com/grpc/grpc-go/tree/master/examples/helloworld)
|
||||
|
||||
!!! warning
|
||||
In order to use this gRPC example, we need to modify it to use HTTPS
|
||||
|
||||
So we modify the "gRPC server example" to use our own self-signed certificate:
|
||||
|
||||
```go
|
||||
// ...
|
||||
|
||||
// Read cert and key file
|
||||
backendCert, _ := os.ReadFile("./backend.cert")
|
||||
backendKey, _ := os.ReadFile("./backend.key")
|
||||
|
||||
// Generate Certificate struct
|
||||
cert, err := tls.X509KeyPair(backendCert, backendKey)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to parse certificate: %v", err)
|
||||
}
|
||||
|
||||
// Create credentials
|
||||
creds := credentials.NewServerTLSFromCert(&cert)
|
||||
|
||||
// Use Credentials in gRPC server options
|
||||
serverOption := grpc.Creds(creds)
|
||||
var s *grpc.Server = grpc.NewServer(serverOption)
|
||||
defer s.Stop()
|
||||
|
||||
pb.RegisterGreeterServer(s, &server{})
|
||||
err := s.Serve(lis)
|
||||
|
||||
// ...
|
||||
```
|
||||
|
||||
Next we will modify gRPC Client to use our Traefik self-signed certificate:
|
||||
|
||||
```go
|
||||
// ...
|
||||
|
||||
// Read cert file
|
||||
frontendCert, _ := os.ReadFile("./frontend.cert")
|
||||
|
||||
// Create CertPool
|
||||
roots := x509.NewCertPool()
|
||||
roots.AppendCertsFromPEM(frontendCert)
|
||||
|
||||
// Create credentials
|
||||
credsClient := credentials.NewClientTLSFromCert(roots, "")
|
||||
|
||||
// Dial with specific Transport (with credentials)
|
||||
conn, err := grpc.Dial("frontend.local:4443", grpc.WithTransportCredentials(credsClient))
|
||||
if err != nil {
|
||||
log.Fatalf("did not connect: %v", err)
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
client := pb.NewGreeterClient(conn)
|
||||
|
||||
name := "World"
|
||||
r, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: name})
|
||||
|
||||
// ...
|
||||
```
|
||||
|
|
@ -1,355 +0,0 @@
|
|||
---
|
||||
title: "Traefik WebSocket Documentation"
|
||||
description: "How to configure WebSocket and WebSocket Secure (WSS) connections with Traefik Proxy."
|
||||
---
|
||||
|
||||
# WebSocket
|
||||
|
||||
Configuring Traefik to handle WebSocket and WebSocket Secure (WSS) connections.
|
||||
{: .subtitle }
|
||||
|
||||
## Overview
|
||||
|
||||
WebSocket is a communication protocol that provides full-duplex communication channels over a single TCP connection.
|
||||
WebSocket Secure (WSS) is the encrypted version of WebSocket, using TLS/SSL encryption.
|
||||
|
||||
Traefik supports WebSocket and WebSocket Secure (WSS) out of the box. This guide will walk through examples of how to configure Traefik for different WebSocket scenarios.
|
||||
|
||||
## Basic WebSocket Configuration
|
||||
|
||||
A basic WebSocket configuration only requires defining a router and a service that points to your WebSocket server.
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.routers.my-websocket.rule=Host(`ws.example.com`)"
|
||||
- "traefik.http.routers.my-websocket.service=my-websocket-service"
|
||||
- "traefik.http.services.my-websocket-service.loadbalancer.server.port=8000"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: my-websocket-route
|
||||
spec:
|
||||
entryPoints:
|
||||
- web
|
||||
routes:
|
||||
- match: Host(`ws.example.com`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: my-websocket-service
|
||||
port: 8000
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
routers:
|
||||
my-websocket:
|
||||
rule: "Host(`ws.example.com`)"
|
||||
service: my-websocket-service
|
||||
|
||||
services:
|
||||
my-websocket-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://my-websocket-server:8000"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.routers]
|
||||
[http.routers.my-websocket]
|
||||
rule = "Host(`ws.example.com`)"
|
||||
service = "my-websocket-service"
|
||||
|
||||
[http.services]
|
||||
[http.services.my-websocket-service]
|
||||
[http.services.my-websocket-service.loadBalancer]
|
||||
[[http.services.my-websocket-service.loadBalancer.servers]]
|
||||
url = "http://my-websocket-server:8000"
|
||||
```
|
||||
|
||||
## WebSocket Secure (WSS) Configuration
|
||||
|
||||
WebSocket Secure (WSS) requires TLS configuration.
|
||||
The client connects using the `wss://` protocol instead of `ws://`.
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.routers.my-websocket-secure.rule=Host(`wss.example.com`)"
|
||||
- "traefik.http.routers.my-websocket-secure.service=my-websocket-service"
|
||||
- "traefik.http.routers.my-websocket-secure.tls=true"
|
||||
- "traefik.http.services.my-websocket-service.loadbalancer.server.port=8000"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: my-websocket-secure-route
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`wss.example.com`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: my-websocket-service
|
||||
port: 8000
|
||||
tls: {}
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
routers:
|
||||
my-websocket-secure:
|
||||
rule: "Host(`wss.example.com`)"
|
||||
service: my-websocket-service
|
||||
tls: {}
|
||||
|
||||
services:
|
||||
my-websocket-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://my-websocket-server:8000"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.routers]
|
||||
[http.routers.my-websocket-secure]
|
||||
rule = "Host(`wss.example.com`)"
|
||||
service = "my-websocket-service"
|
||||
[http.routers.my-websocket-secure.tls]
|
||||
|
||||
[http.services]
|
||||
[http.services.my-websocket-service]
|
||||
[http.services.my-websocket-service.loadBalancer]
|
||||
[[http.services.my-websocket-service.loadBalancer.servers]]
|
||||
url = "http://my-websocket-server:8000"
|
||||
```
|
||||
|
||||
## SSL Termination for WebSockets
|
||||
|
||||
In this scenario, clients connect to Traefik using WSS (encrypted), but Traefik connects to your backend server using WS (unencrypted).
|
||||
This is called SSL termination.
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.routers.my-wss-termination.rule=Host(`wss.example.com`)"
|
||||
- "traefik.http.routers.my-wss-termination.service=my-ws-service"
|
||||
- "traefik.http.routers.my-wss-termination.tls=true"
|
||||
- "traefik.http.services.my-ws-service.loadbalancer.server.port=8000"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: my-wss-termination-route
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`wss.example.com`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: my-ws-service
|
||||
port: 8000
|
||||
tls: {}
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
routers:
|
||||
my-wss-termination:
|
||||
rule: "Host(`wss.example.com`)"
|
||||
service: my-ws-service
|
||||
tls: {}
|
||||
|
||||
services:
|
||||
my-ws-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://my-ws-server:8000"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.routers]
|
||||
[http.routers.my-wss-termination]
|
||||
rule = "Host(`wss.example.com`)"
|
||||
service = "my-ws-service"
|
||||
[http.routers.my-wss-termination.tls]
|
||||
|
||||
[http.services]
|
||||
[http.services.my-ws-service]
|
||||
[http.services.my-ws-service.loadBalancer]
|
||||
[[http.services.my-ws-service.loadBalancer.servers]]
|
||||
url = "http://my-ws-server:8000"
|
||||
```
|
||||
|
||||
## End-to-End WebSocket Secure (WSS)
|
||||
|
||||
For end-to-end encryption, Traefik can be configured to connect to your backend using HTTPS.
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.routers.my-wss-e2e.rule=Host(`wss.example.com`)"
|
||||
- "traefik.http.routers.my-wss-e2e.service=my-wss-service"
|
||||
- "traefik.http.routers.my-wss-e2e.tls=true"
|
||||
- "traefik.http.services.my-wss-service.loadbalancer.server.port=8443"
|
||||
# If the backend uses a self-signed certificate
|
||||
- "traefik.http.serversTransports.insecureTransport.insecureSkipVerify=true"
|
||||
- "traefik.http.services.my-wss-service.loadBalancer.serversTransport=insecureTransport"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: ServersTransport
|
||||
metadata:
|
||||
name: insecure-transport
|
||||
spec:
|
||||
insecureSkipVerify: true
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: my-wss-e2e-route
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`wss.example.com`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: my-wss-service
|
||||
port: 8443
|
||||
serversTransport: insecure-transport
|
||||
tls: {}
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
serversTransports:
|
||||
insecureTransport:
|
||||
insecureSkipVerify: true
|
||||
|
||||
routers:
|
||||
my-wss-e2e:
|
||||
rule: "Host(`wss.example.com`)"
|
||||
service: my-wss-service
|
||||
tls: {}
|
||||
|
||||
services:
|
||||
my-wss-service:
|
||||
loadBalancer:
|
||||
serversTransport: insecureTransport
|
||||
servers:
|
||||
- url: "https://my-wss-server:8443"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.serversTransports]
|
||||
[http.serversTransports.insecureTransport]
|
||||
insecureSkipVerify = true
|
||||
|
||||
[http.routers]
|
||||
[http.routers.my-wss-e2e]
|
||||
rule = "Host(`wss.example.com`)"
|
||||
service = "my-wss-service"
|
||||
[http.routers.my-wss-e2e.tls]
|
||||
|
||||
[http.services]
|
||||
[http.services.my-wss-service]
|
||||
[http.services.my-wss-service.loadBalancer]
|
||||
serversTransport = "insecureTransport"
|
||||
[[http.services.my-wss-service.loadBalancer.servers]]
|
||||
url = "https://my-wss-server:8443"
|
||||
```
|
||||
|
||||
## EntryPoints Configuration for WebSockets
|
||||
|
||||
In your Traefik static configuration, you'll need to define entryPoints for both WS and WSS:
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
websecure:
|
||||
address: ":443"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
address = ":80"
|
||||
[entryPoints.websecure]
|
||||
address = ":443"
|
||||
```
|
||||
|
||||
## Testing WebSocket Connections
|
||||
|
||||
You can test your WebSocket configuration using various tools:
|
||||
|
||||
1. Browser Developer Tools: Most modern browsers include WebSocket debugging in their developer tools.
|
||||
2. WebSocket client tools like [wscat](https://github.com/websockets/wscat) or online tools like [Piesocket's WebSocket Tester](https://www.piesocket.com/websocket-tester).
|
||||
|
||||
Example wscat commands:
|
||||
|
||||
```bash
|
||||
# Test standard WebSocket
|
||||
wscat -c ws://ws.example.com
|
||||
|
||||
# Test WebSocket Secure
|
||||
wscat -c wss://wss.example.com
|
||||
```
|
||||
|
||||
## Common Issues and Solutions
|
||||
|
||||
### Headers and Origin Checks
|
||||
|
||||
Some WebSocket servers implement origin checking. Traefik passes the original headers to your backend, including the `Origin` header.
|
||||
|
||||
If you need to manipulate headers for WebSocket connections, you can use Traefik's Headers middleware:
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.middlewares.my-headers.headers.customrequestheaders.Origin=https://allowed-origin.com"
|
||||
- "traefik.http.routers.my-websocket.middlewares=my-headers"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: my-headers
|
||||
spec:
|
||||
headers:
|
||||
customRequestHeaders:
|
||||
Origin: "https://allowed-origin.com"
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: my-websocket-route
|
||||
spec:
|
||||
routes:
|
||||
- match: Host(`ws.example.com`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: my-headers
|
||||
services:
|
||||
- name: my-websocket-service
|
||||
port: 8000
|
||||
```
|
||||
|
||||
### Certificate Issues with WSS
|
||||
|
||||
If you're experiencing certificate issues with WSS:
|
||||
|
||||
1. Ensure your certificates are valid and not expired
|
||||
2. For testing with self-signed certificates, configure your clients to accept them
|
||||
3. When using Let's Encrypt, ensure your domain is properly configured
|
||||
|
||||
For backends with self-signed certificates, use the `insecureSkipVerify` option in the ServersTransport configuration as shown in the examples above.
|
||||
|
|
@ -139,13 +139,12 @@ plugins:
|
|||
'user-guides/crd-acme/index.md': 'expose/kubernetes/basic.md'
|
||||
'user-guides/cert-manager.md': 'expose/kubernetes/advanced.md'
|
||||
'user-guides/docker-compose/basic-example/index.md': 'expose/docker/basic.md'
|
||||
'user-guides/docker-compose/acme-tls/index.md': 'expose/docker/advanced.md'
|
||||
'user-guides/docker-compose/acme-http/index.md': 'expose/docker/advanced.md'
|
||||
'user-guides/docker-compose/acme-dns/index.md': 'expose/docker/advanced.md'
|
||||
## Expose pages (redirect old URLs to new structure)
|
||||
'expose/kubernetes.md': 'expose/kubernetes/basic.md'
|
||||
'expose/docker.md': 'expose/docker/basic.md'
|
||||
'expose/swarm.md': 'expose/swarm/basic.md'
|
||||
'user-guides/docker-compose/acme-tls/index.md': 'expose/docker/basic.md'
|
||||
'user-guides/docker-compose/acme-http/index.md': 'expose/docker/basic.md'
|
||||
'user-guides/docker-compose/acme-dns/index.md': 'expose/docker/basic.md'
|
||||
'user-guides/fastproxy.md': 'reference/install-configuration/experimental/fastproxy.md'
|
||||
'user-guides/grpc.md': 'expose/overview.md#exposing-grpc-services'
|
||||
'user-guides/websocket.md': 'expose/overview.md#exposing-websocket-services'
|
||||
# References
|
||||
# Static Configuration
|
||||
'reference/static-configuration/overview.md': 'reference/install-configuration/configuration-options.md'
|
||||
|
|
@ -273,6 +272,9 @@ nav:
|
|||
- 'Tracing': 'reference/install-configuration/observability/tracing.md'
|
||||
- 'Logs & AccessLogs': 'reference/install-configuration/observability/logs-and-accesslogs.md'
|
||||
- 'Health Check (CLI & Ping)': 'reference/install-configuration/observability/healthcheck.md'
|
||||
- 'Experimental':
|
||||
- 'FastProxy': 'reference/install-configuration/experimental/fastproxy.md'
|
||||
- 'Plugins': 'reference/install-configuration/experimental/plugins.md'
|
||||
- 'Options List': 'reference/install-configuration/configuration-options.md'
|
||||
- 'Routing Configuration':
|
||||
- 'Common Configuration' :
|
||||
|
|
@ -379,12 +381,6 @@ nav:
|
|||
- 'Deprecation Notices':
|
||||
- 'Releases': 'deprecation/releases.md'
|
||||
- 'Features': 'deprecation/features.md'
|
||||
- 'User Guides':
|
||||
- 'FastProxy': 'user-guides/fastproxy.md'
|
||||
- 'Kubernetes and Let''s Encrypt': 'user-guides/crd-acme/index.md'
|
||||
- 'Kubernetes and cert-manager': 'user-guides/cert-manager.md'
|
||||
- 'gRPC Examples': 'user-guides/grpc.md'
|
||||
- 'WebSocket Examples': 'user-guides/websocket.md'
|
||||
- 'Contributing':
|
||||
- 'Thank You!': 'contributing/thank-you.md'
|
||||
- 'Submitting Issues': 'contributing/submitting-issues.md'
|
||||
|
|
|
|||
6
go.mod
6
go.mod
|
|
@ -37,6 +37,7 @@ require (
|
|||
github.com/hashicorp/go-version v1.8.0
|
||||
github.com/hashicorp/nomad/api v0.0.0-20231213195942-64e3dca9274b // No tag on the repo.
|
||||
github.com/http-wasm/http-wasm-host-go v0.7.0
|
||||
github.com/huandu/xstrings v1.5.0
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.7.0
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab // No tag on the repo.
|
||||
github.com/klauspost/compress v1.18.0
|
||||
|
|
@ -71,6 +72,7 @@ require (
|
|||
github.com/tidwall/gjson v1.17.0
|
||||
github.com/traefik/grpc-web v0.16.0
|
||||
github.com/traefik/paerser v0.2.2
|
||||
github.com/traefik/traefik/dynamic/ext v0.0.0-00010101000000-000000000000
|
||||
github.com/traefik/yaegi v0.16.1
|
||||
github.com/unrolled/render v1.0.2
|
||||
github.com/unrolled/secure v1.0.9
|
||||
|
|
@ -253,7 +255,6 @@ require (
|
|||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
|
||||
github.com/hashicorp/serf v0.10.1 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.182 // indirect
|
||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
|
|
@ -411,6 +412,9 @@ require (
|
|||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
)
|
||||
|
||||
// Dynamic config extension.
|
||||
replace github.com/traefik/traefik/dynamic/ext => ./pkg/config/dynamic/ext
|
||||
|
||||
// Containous forks
|
||||
replace (
|
||||
github.com/abbot/go-http-auth => github.com/containous/go-http-auth v0.4.1-0.20200324110947-a37a7636d23e
|
||||
|
|
|
|||
|
|
@ -223,6 +223,25 @@ spec:
|
|||
- Service
|
||||
- TraefikService
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references
|
||||
to Middleware resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of
|
||||
the referenced Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
description: |-
|
||||
Name defines the name of the referenced Kubernetes Service or TraefikService.
|
||||
|
|
@ -1233,6 +1252,25 @@ spec:
|
|||
- Service
|
||||
- TraefikService
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references to
|
||||
Middleware resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of the
|
||||
referenced Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
description: |-
|
||||
Name defines the name of the referenced Kubernetes Service or TraefikService.
|
||||
|
|
@ -2975,6 +3013,25 @@ spec:
|
|||
- Service
|
||||
- TraefikService
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references
|
||||
to Middleware resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of the
|
||||
referenced Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
description: |-
|
||||
Name defines the name of the referenced Kubernetes Service or TraefikService.
|
||||
|
|
@ -3212,6 +3269,24 @@ spec:
|
|||
Default value is -1, which means unlimited size.
|
||||
format: int64
|
||||
type: integer
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references to Middleware
|
||||
resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced Middleware
|
||||
resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
mirrorBody:
|
||||
description: |-
|
||||
MirrorBody defines whether the body of the request should be mirrored.
|
||||
|
|
@ -3299,6 +3374,25 @@ spec:
|
|||
- Service
|
||||
- TraefikService
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references
|
||||
to Middleware resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of the
|
||||
referenced Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
description: |-
|
||||
Name defines the name of the referenced Kubernetes Service or TraefikService.
|
||||
|
|
@ -3687,6 +3781,25 @@ spec:
|
|||
- Service
|
||||
- TraefikService
|
||||
type: string
|
||||
middlewares:
|
||||
description: Middlewares defines the list of references
|
||||
to Middleware resources to apply to the service.
|
||||
items:
|
||||
description: MiddlewareRef is a reference to a Middleware
|
||||
resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name defines the name of the referenced
|
||||
Middleware resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the namespace of the
|
||||
referenced Middleware resource.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
description: |-
|
||||
Name defines the name of the referenced Kubernetes Service or TraefikService.
|
||||
|
|
|
|||
35
integration/fixtures/service_middleware.toml
Normal file
35
integration/fixtures/service_middleware.toml
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
[global]
|
||||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[api]
|
||||
insecure = true
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
noColor = true
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
address = ":8000"
|
||||
|
||||
[providers.file]
|
||||
filename = "{{ .SelfFilename }}"
|
||||
|
||||
## dynamic configuration ##
|
||||
|
||||
[http.routers]
|
||||
[http.routers.router1]
|
||||
service = "service1"
|
||||
rule = "Path(`/whoami`)"
|
||||
|
||||
[http.middlewares]
|
||||
[http.middlewares.add-header.headers.customRequestHeaders]
|
||||
X-Custom-Header = "service-middleware-test"
|
||||
|
||||
[http.services]
|
||||
[http.services.service1]
|
||||
middlewares = ["add-header"]
|
||||
[http.services.service1.loadBalancer]
|
||||
[[http.services.service1.loadBalancer.servers]]
|
||||
url = "{{ .Server }}"
|
||||
|
|
@ -8,7 +8,7 @@ implementation:
|
|||
organization: traefik
|
||||
project: traefik
|
||||
url: https://traefik.io/
|
||||
version: v3.6
|
||||
version: v3.7
|
||||
kind: ConformanceReport
|
||||
mode: default
|
||||
profiles:
|
||||
|
|
@ -30,12 +30,13 @@ profiles:
|
|||
result: success
|
||||
statistics:
|
||||
Failed: 0
|
||||
Passed: 13
|
||||
Passed: 15
|
||||
Skipped: 0
|
||||
supportedFeatures:
|
||||
- GatewayPort8080
|
||||
- HTTPRouteBackendProtocolH2C
|
||||
- HTTPRouteBackendProtocolWebSocket
|
||||
- HTTPRouteBackendRequestHeaderModification
|
||||
- HTTPRouteDestinationPortMatching
|
||||
- HTTPRouteHostRewrite
|
||||
- HTTPRouteMethodMatching
|
||||
|
|
@ -50,7 +51,6 @@ profiles:
|
|||
- GatewayHTTPListenerIsolation
|
||||
- GatewayInfrastructurePropagation
|
||||
- GatewayStaticAddresses
|
||||
- HTTPRouteBackendRequestHeaderModification
|
||||
- HTTPRouteBackendTimeout
|
||||
- HTTPRouteCORS
|
||||
- HTTPRouteNamedRouteRule
|
||||
|
|
@ -2364,3 +2364,37 @@ func (s *SimpleSuite) TestEncodedCharactersDifferentEntryPoints() {
|
|||
require.NoError(s.T(), err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestServiceMiddleware() {
|
||||
s.createComposeProject("base")
|
||||
|
||||
s.composeUp()
|
||||
defer s.composeDown()
|
||||
|
||||
whoamiIP := s.getComposeServiceIP("whoami1")
|
||||
|
||||
file := s.adaptFile("fixtures/service_middleware.toml", struct {
|
||||
Server string
|
||||
}{Server: "http://" + whoamiIP})
|
||||
|
||||
s.traefikCmd(withConfigFile(file))
|
||||
|
||||
// Wait for Traefik to be ready
|
||||
err := try.GetRequest("http://127.0.0.1:8080/api/http/services", 2*time.Second, try.BodyContains("service1"))
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
// Make a request and verify the middleware added the custom header
|
||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
response, err := http.DefaultClient.Do(req)
|
||||
require.NoError(s.T(), err)
|
||||
assert.Equal(s.T(), http.StatusOK, response.StatusCode)
|
||||
|
||||
// Read the response body to check if the whoami service received the custom header
|
||||
body, err := io.ReadAll(response.Body)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
// The whoami service should have received the X-Custom-Header that was added by the service middleware
|
||||
assert.Contains(s.T(), string(body), "X-Custom-Header: service-middleware-test")
|
||||
}
|
||||
|
|
|
|||
7
pkg/config/dynamic/ext/ext.go
Normal file
7
pkg/config/dynamic/ext/ext.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package ext
|
||||
|
||||
// HTTP is a dynamic.HTTP extension.
|
||||
type HTTP struct{}
|
||||
|
||||
// Router is a dynamic.Router extension.
|
||||
type Router struct{}
|
||||
3
pkg/config/dynamic/ext/go.mod
Normal file
3
pkg/config/dynamic/ext/go.mod
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
module github.com/traefik/traefik/dynamic/ext
|
||||
|
||||
go 1.24.0
|
||||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"time"
|
||||
|
||||
ptypes "github.com/traefik/paerser/types"
|
||||
"github.com/traefik/traefik/dynamic/ext"
|
||||
otypes "github.com/traefik/traefik/v3/pkg/observability/types"
|
||||
traefiktls "github.com/traefik/traefik/v3/pkg/tls"
|
||||
"github.com/traefik/traefik/v3/pkg/types"
|
||||
|
|
@ -33,6 +34,8 @@ const (
|
|||
|
||||
// HTTPConfiguration contains all the HTTP configuration parameters.
|
||||
type HTTPConfiguration struct {
|
||||
ext.HTTP `yaml:",inline"`
|
||||
|
||||
Routers map[string]*Router `json:"routers,omitempty" toml:"routers,omitempty" yaml:"routers,omitempty" export:"true"`
|
||||
Services map[string]*Service `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty" export:"true"`
|
||||
Middlewares map[string]*Middleware `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
|
||||
|
|
@ -55,6 +58,7 @@ type Model struct {
|
|||
|
||||
// Service holds a service configuration (can only be of one type at the same time).
|
||||
type Service struct {
|
||||
Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
|
||||
LoadBalancer *ServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty" export:"true"`
|
||||
HighestRandomWeight *HighestRandomWeight `json:"highestRandomWeight,omitempty" toml:"highestRandomWeight,omitempty" yaml:"highestRandomWeight,omitempty" label:"-" export:"true"`
|
||||
Weighted *WeightedRoundRobin `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-" export:"true"`
|
||||
|
|
@ -62,10 +66,22 @@ type Service struct {
|
|||
Failover *Failover `json:"failover,omitempty" toml:"failover,omitempty" yaml:"failover,omitempty" label:"-" export:"true"`
|
||||
}
|
||||
|
||||
// Merge merges another Service into this one.
|
||||
// Returns true if the merge succeeds, false if configurations conflict.
|
||||
func (s *Service) Merge(other *Service) bool {
|
||||
if s.LoadBalancer == nil || other.LoadBalancer == nil {
|
||||
return reflect.DeepEqual(s, other)
|
||||
}
|
||||
|
||||
return s.LoadBalancer.Merge(other.LoadBalancer)
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// Router holds the router configuration.
|
||||
type Router struct {
|
||||
ext.Router `yaml:",inline"`
|
||||
|
||||
EntryPoints []string `json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty" export:"true"`
|
||||
Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
|
||||
Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"`
|
||||
|
|
@ -342,8 +358,39 @@ type ServersLoadBalancer struct {
|
|||
ServersTransport string `json:"serversTransport,omitempty" toml:"serversTransport,omitempty" yaml:"serversTransport,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
// Mergeable tells if the given service is mergeable.
|
||||
func (l *ServersLoadBalancer) Mergeable(loadBalancer *ServersLoadBalancer) bool {
|
||||
// Merge merges the other load balancer into this one.
|
||||
// Returns true if merge succeeded, false if configurations conflict.
|
||||
func (l *ServersLoadBalancer) Merge(other *ServersLoadBalancer) bool {
|
||||
if !l.mergeable(other) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Deduplicate and append servers.
|
||||
uniq := make(map[string]struct{}, len(l.Servers))
|
||||
for _, server := range l.Servers {
|
||||
uniq[server.URL] = struct{}{}
|
||||
}
|
||||
for _, server := range other.Servers {
|
||||
if _, ok := uniq[server.URL]; !ok {
|
||||
l.Servers = append(l.Servers, server)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// SetDefaults Default values for a ServersLoadBalancer.
|
||||
func (l *ServersLoadBalancer) SetDefaults() {
|
||||
defaultPassHostHeader := DefaultPassHostHeader
|
||||
l.PassHostHeader = &defaultPassHostHeader
|
||||
|
||||
l.Strategy = BalancerStrategyWRR
|
||||
l.ResponseForwarding = &ResponseForwarding{}
|
||||
l.ResponseForwarding.SetDefaults()
|
||||
}
|
||||
|
||||
// mergeable tells if the given service is mergeable.
|
||||
func (l *ServersLoadBalancer) mergeable(loadBalancer *ServersLoadBalancer) bool {
|
||||
savedServers := l.Servers
|
||||
defer func() {
|
||||
l.Servers = savedServers
|
||||
|
|
@ -359,16 +406,6 @@ func (l *ServersLoadBalancer) Mergeable(loadBalancer *ServersLoadBalancer) bool
|
|||
return reflect.DeepEqual(l, loadBalancer)
|
||||
}
|
||||
|
||||
// SetDefaults Default values for a ServersLoadBalancer.
|
||||
func (l *ServersLoadBalancer) SetDefaults() {
|
||||
defaultPassHostHeader := DefaultPassHostHeader
|
||||
l.PassHostHeader = &defaultPassHostHeader
|
||||
|
||||
l.Strategy = BalancerStrategyWRR
|
||||
l.ResponseForwarding = &ResponseForwarding{}
|
||||
l.ResponseForwarding.SetDefaults()
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// ResponseForwarding holds the response forwarding configuration.
|
||||
|
|
|
|||
|
|
@ -35,6 +35,16 @@ type TCPService struct {
|
|||
Weighted *TCPWeightedRoundRobin `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-" export:"true"`
|
||||
}
|
||||
|
||||
// Merge merges another TCPService into this one.
|
||||
// Returns true if the merge succeeds, false if configurations conflict.
|
||||
func (s *TCPService) Merge(other *TCPService) bool {
|
||||
if s.LoadBalancer == nil || other.LoadBalancer == nil {
|
||||
return reflect.DeepEqual(s, other)
|
||||
}
|
||||
|
||||
return s.LoadBalancer.Merge(other.LoadBalancer)
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// TCPWeightedRoundRobin is a weighted round robin tcp load-balancer of services.
|
||||
|
|
@ -102,8 +112,29 @@ type TCPServersLoadBalancer struct {
|
|||
HealthCheck *TCPServerHealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
|
||||
}
|
||||
|
||||
// Mergeable tells if the given service is mergeable.
|
||||
func (l *TCPServersLoadBalancer) Mergeable(loadBalancer *TCPServersLoadBalancer) bool {
|
||||
// Merge merges the other load balancer into this one.
|
||||
// Returns true if the merge succeeds, false if configurations conflict.
|
||||
func (l *TCPServersLoadBalancer) Merge(other *TCPServersLoadBalancer) bool {
|
||||
if !l.mergeable(other) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Deduplicate and append servers.
|
||||
uniq := make(map[string]struct{}, len(l.Servers))
|
||||
for _, server := range l.Servers {
|
||||
uniq[server.Address] = struct{}{}
|
||||
}
|
||||
for _, server := range other.Servers {
|
||||
if _, ok := uniq[server.Address]; !ok {
|
||||
l.Servers = append(l.Servers, server)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// mergeable tells if the given service is mergeable.
|
||||
func (l *TCPServersLoadBalancer) mergeable(loadBalancer *TCPServersLoadBalancer) bool {
|
||||
savedServers := l.Servers
|
||||
defer func() {
|
||||
l.Servers = savedServers
|
||||
|
|
|
|||
|
|
@ -20,6 +20,16 @@ type UDPService struct {
|
|||
Weighted *UDPWeightedRoundRobin `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-" export:"true"`
|
||||
}
|
||||
|
||||
// Merge merges another UDPService into this one.
|
||||
// Returns true if the merge succeeds, false if configurations conflict.
|
||||
func (s *UDPService) Merge(other *UDPService) bool {
|
||||
if s.LoadBalancer == nil || other.LoadBalancer == nil {
|
||||
return reflect.DeepEqual(s, other)
|
||||
}
|
||||
|
||||
return s.LoadBalancer.Merge(other.LoadBalancer)
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// UDPWeightedRoundRobin is a weighted round robin UDP load-balancer of services.
|
||||
|
|
@ -56,8 +66,29 @@ type UDPServersLoadBalancer struct {
|
|||
Servers []UDPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server" export:"true"`
|
||||
}
|
||||
|
||||
// Mergeable reports whether the given load-balancer can be merged with the receiver.
|
||||
func (l *UDPServersLoadBalancer) Mergeable(loadBalancer *UDPServersLoadBalancer) bool {
|
||||
// Merge merges the other load balancer into this one.
|
||||
// Returns true if merge succeeded, false if configurations conflict.
|
||||
func (l *UDPServersLoadBalancer) Merge(other *UDPServersLoadBalancer) bool {
|
||||
if !l.mergeable(other) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Deduplicate and append servers.
|
||||
uniq := make(map[string]struct{}, len(l.Servers))
|
||||
for _, server := range l.Servers {
|
||||
uniq[server.Address] = struct{}{}
|
||||
}
|
||||
for _, server := range other.Servers {
|
||||
if _, ok := uniq[server.Address]; !ok {
|
||||
l.Servers = append(l.Servers, server)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// mergeable reports whether the given load-balancer can be merged with the receiver.
|
||||
func (l *UDPServersLoadBalancer) mergeable(loadBalancer *UDPServersLoadBalancer) bool {
|
||||
savedServers := l.Servers
|
||||
defer func() {
|
||||
l.Servers = savedServers
|
||||
|
|
|
|||
|
|
@ -489,6 +489,7 @@ func (in *HRWService) DeepCopy() *HRWService {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPConfiguration) DeepCopyInto(out *HTTPConfiguration) {
|
||||
*out = *in
|
||||
out.HTTP = in.HTTP
|
||||
if in.Routers != nil {
|
||||
in, out := &in.Routers, &out.Routers
|
||||
*out = make(map[string]*Router, len(*in))
|
||||
|
|
@ -1390,6 +1391,7 @@ func (in *Retry) DeepCopy() *Retry {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Router) DeepCopyInto(out *Router) {
|
||||
*out = *in
|
||||
out.Router = in.Router
|
||||
if in.EntryPoints != nil {
|
||||
in, out := &in.EntryPoints, &out.EntryPoints
|
||||
*out = make([]string, len(*in))
|
||||
|
|
@ -1672,6 +1674,11 @@ func (in *ServersTransport) DeepCopy() *ServersTransport {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Service) DeepCopyInto(out *Service) {
|
||||
*out = *in
|
||||
if in.Middlewares != nil {
|
||||
in, out := &in.Middlewares, &out.Middlewares
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.LoadBalancer != nil {
|
||||
in, out := &in.LoadBalancer, &out.LoadBalancer
|
||||
*out = new(ServersLoadBalancer)
|
||||
|
|
|
|||
|
|
@ -466,42 +466,52 @@ func TestServiceTCPHealthChecker_Launch(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
lb := &testLoadBalancer{}
|
||||
// Create load balancer with event channel for synchronization.
|
||||
lb := &testLoadBalancer{
|
||||
RWMutex: &sync.RWMutex{},
|
||||
eventCh: make(chan struct{}, len(test.server.StatusSequence)+5),
|
||||
}
|
||||
serviceInfo := &truntime.TCPServiceInfo{}
|
||||
|
||||
service := NewServiceTCPHealthChecker(ctx, test.config, lb, serviceInfo, targets, "serviceName")
|
||||
|
||||
go service.Launch(ctx)
|
||||
|
||||
// How much time to wait for the health check to actually complete.
|
||||
deadline := time.Now().Add(200 * time.Millisecond)
|
||||
// TLS handshake can take much longer.
|
||||
// Timeout for each event - TLS handshake can take longer.
|
||||
eventTimeout := 500 * time.Millisecond
|
||||
if test.server.TLS {
|
||||
deadline = time.Now().Add(1000 * time.Millisecond)
|
||||
eventTimeout = 2 * time.Second
|
||||
}
|
||||
|
||||
// Wait for all health checks to complete deterministically
|
||||
// Wait for health check events using channel synchronization.
|
||||
// Iterate over StatusSequence to release each connection via Next().
|
||||
for i := range test.server.StatusSequence {
|
||||
test.server.Next()
|
||||
|
||||
initialUpserted := lb.numUpsertedServers
|
||||
initialRemoved := lb.numRemovedServers
|
||||
|
||||
for time.Now().Before(deadline) {
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
if lb.numUpsertedServers > initialUpserted || lb.numRemovedServers > initialRemoved {
|
||||
// Stop the health checker immediately after the last expected sequence completes
|
||||
// to prevent extra health checks from firing and modifying the counters.
|
||||
if i == len(test.server.StatusSequence)-1 {
|
||||
cancel()
|
||||
}
|
||||
break
|
||||
select {
|
||||
case <-lb.eventCh:
|
||||
// Event received
|
||||
// On the last iteration, stop the health checker immediately
|
||||
// to prevent extra checks from modifying the counters.
|
||||
if i == len(test.server.StatusSequence)-1 {
|
||||
test.server.Close()
|
||||
cancel()
|
||||
}
|
||||
case <-time.After(eventTimeout):
|
||||
t.Fatalf("timeout waiting for health check event %d/%d", i+1, len(test.server.StatusSequence))
|
||||
}
|
||||
}
|
||||
|
||||
assert.Equal(t, test.expNumRemovedServers, lb.numRemovedServers, "removed servers")
|
||||
assert.Equal(t, test.expNumUpsertedServers, lb.numUpsertedServers, "upserted servers")
|
||||
// Small delay to let goroutines clean up.
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
lb.RLock()
|
||||
removedServers := lb.numRemovedServers
|
||||
upsertedServers := lb.numUpsertedServers
|
||||
lb.RUnlock()
|
||||
|
||||
assert.Equal(t, test.expNumRemovedServers, removedServers, "removed servers")
|
||||
assert.Equal(t, test.expNumUpsertedServers, upsertedServers, "upserted servers")
|
||||
assert.Equal(t, map[string]string{test.server.Addr.String(): test.targetStatus}, serviceInfo.GetAllStatus())
|
||||
})
|
||||
}
|
||||
|
|
@ -597,6 +607,8 @@ type sequencedTCPServer struct {
|
|||
StatusSequence []tcpMockSequence
|
||||
TLS bool
|
||||
release chan struct{}
|
||||
mu sync.Mutex
|
||||
listener net.Listener
|
||||
}
|
||||
|
||||
func newTCPServer(t *testing.T, tlsEnabled bool, statusSequence ...tcpMockSequence) *sequencedTCPServer {
|
||||
|
|
@ -624,17 +636,28 @@ func (s *sequencedTCPServer) Next() {
|
|||
s.release <- struct{}{}
|
||||
}
|
||||
|
||||
func (s *sequencedTCPServer) Close() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.listener != nil {
|
||||
s.listener.Close()
|
||||
s.listener = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sequencedTCPServer) Start(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
go func() {
|
||||
var listener net.Listener
|
||||
|
||||
for _, seq := range s.StatusSequence {
|
||||
<-s.release
|
||||
if listener != nil {
|
||||
listener.Close()
|
||||
|
||||
s.mu.Lock()
|
||||
if s.listener != nil {
|
||||
s.listener.Close()
|
||||
s.listener = nil
|
||||
}
|
||||
s.mu.Unlock()
|
||||
|
||||
if !seq.accept {
|
||||
continue
|
||||
|
|
@ -643,7 +666,7 @@ func (s *sequencedTCPServer) Start(t *testing.T) {
|
|||
lis, err := net.ListenTCP("tcp", s.Addr)
|
||||
require.NoError(t, err)
|
||||
|
||||
listener = lis
|
||||
var listener net.Listener = lis
|
||||
|
||||
if s.TLS {
|
||||
cert, err := tls.X509KeyPair(localhostCert, localhostKey)
|
||||
|
|
@ -670,8 +693,18 @@ func (s *sequencedTCPServer) Start(t *testing.T) {
|
|||
)
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
s.listener = listener
|
||||
s.mu.Unlock()
|
||||
|
||||
conn, err := listener.Accept()
|
||||
require.NoError(t, err)
|
||||
if err != nil {
|
||||
// Listener was closed during shutdown - this is expected behavior.
|
||||
if strings.Contains(err.Error(), "use of closed network connection") {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_ = conn.Close()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -418,7 +418,12 @@ func TestServiceHealthChecker_Launch(t *testing.T) {
|
|||
|
||||
targetURL, timeout := test.server.Start(t, cancel)
|
||||
|
||||
lb := &testLoadBalancer{RWMutex: &sync.RWMutex{}}
|
||||
// Create load balancer with event channel for synchronization.
|
||||
expectedEvents := test.expNumRemovedServers + test.expNumUpsertedServers
|
||||
lb := &testLoadBalancer{
|
||||
RWMutex: &sync.RWMutex{},
|
||||
eventCh: make(chan struct{}, expectedEvents+5),
|
||||
}
|
||||
|
||||
config := &dynamic.ServerHealthCheck{
|
||||
Mode: test.mode,
|
||||
|
|
@ -441,18 +446,30 @@ func TestServiceHealthChecker_Launch(t *testing.T) {
|
|||
wg.Done()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(timeout):
|
||||
t.Fatal("test did not complete in time")
|
||||
case <-ctx.Done():
|
||||
wg.Wait()
|
||||
// Wait for expected health check events using channel synchronization.
|
||||
for i := range expectedEvents {
|
||||
select {
|
||||
case <-lb.eventCh:
|
||||
// Event received.
|
||||
// On the last event, cancel to prevent extra health checks.
|
||||
if i == expectedEvents-1 {
|
||||
cancel()
|
||||
}
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("timeout waiting for health check event %d/%d", i+1, expectedEvents)
|
||||
}
|
||||
}
|
||||
|
||||
lb.Lock()
|
||||
defer lb.Unlock()
|
||||
// Wait for the health checker goroutine to exit before making assertions.
|
||||
wg.Wait()
|
||||
|
||||
assert.Equal(t, test.expNumRemovedServers, lb.numRemovedServers, "removed servers")
|
||||
assert.Equal(t, test.expNumUpsertedServers, lb.numUpsertedServers, "upserted servers")
|
||||
lb.RLock()
|
||||
removedServers := lb.numRemovedServers
|
||||
upsertedServers := lb.numUpsertedServers
|
||||
lb.RUnlock()
|
||||
|
||||
assert.Equal(t, test.expNumRemovedServers, removedServers, "removed servers")
|
||||
assert.Equal(t, test.expNumUpsertedServers, upsertedServers, "upserted servers")
|
||||
assert.InDelta(t, test.expGaugeValue, gauge.GaugeValue, delta, "ServerUp Gauge")
|
||||
assert.Equal(t, []string{"service", "foobar", "url", targetURL.String()}, gauge.LastLabelValues)
|
||||
assert.Equal(t, map[string]string{targetURL.String(): test.targetStatus}, serviceInfo.GetAllStatus())
|
||||
|
|
|
|||
|
|
@ -168,14 +168,29 @@ type testLoadBalancer struct {
|
|||
|
||||
numRemovedServers int
|
||||
numUpsertedServers int
|
||||
|
||||
// eventCh is used to signal when a status change occurs, allowing tests
|
||||
// to synchronize with health check events deterministically.
|
||||
eventCh chan struct{}
|
||||
}
|
||||
|
||||
func (lb *testLoadBalancer) SetStatus(ctx context.Context, childName string, up bool) {
|
||||
lb.Lock()
|
||||
if up {
|
||||
lb.numUpsertedServers++
|
||||
} else {
|
||||
lb.numRemovedServers++
|
||||
}
|
||||
lb.Unlock()
|
||||
|
||||
// Signal the event if a listener is registered.
|
||||
if lb.eventCh != nil {
|
||||
select {
|
||||
case lb.eventCh <- struct{}{}:
|
||||
default:
|
||||
// Don't block if channel is full or no listener.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type MetricsMock struct {
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@ const (
|
|||
typeName = "Chain"
|
||||
)
|
||||
|
||||
type chainBuilder interface {
|
||||
BuildChain(ctx context.Context, middlewares []string) *alice.Chain
|
||||
type middlewareChainBuilder interface {
|
||||
BuildMiddlewareChain(ctx context.Context, middlewares []string) *alice.Chain
|
||||
}
|
||||
|
||||
// New creates a chain middleware.
|
||||
func New(ctx context.Context, next http.Handler, config dynamic.Chain, builder chainBuilder, name string) (http.Handler, error) {
|
||||
func New(ctx context.Context, next http.Handler, config dynamic.Chain, builder middlewareChainBuilder, name string) (http.Handler, error) {
|
||||
middlewares.GetLogger(ctx, name, typeName).Debug().Msg("Creating middleware")
|
||||
|
||||
middlewareChain := builder.BuildChain(ctx, config.Middlewares)
|
||||
middlewareChain := builder.BuildMiddlewareChain(ctx, config.Middlewares)
|
||||
return middlewareChain.Then(next)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package metrics
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/metrics/influx"
|
||||
|
|
@ -138,11 +139,16 @@ func newInfluxDB2Client(config *otypes.InfluxDB2) (influxdb2.Client, error) {
|
|||
return nil, errors.New("token, org or bucket property is missing")
|
||||
}
|
||||
|
||||
token, err := config.Token.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Disable InfluxDB2 logs.
|
||||
// See https://github.com/influxdata/influxdb-client-go/blob/v2.7.0/options.go#L128
|
||||
influxdb2log.Log = nil
|
||||
|
||||
return influxdb2.NewClient(config.Address, config.Token), nil
|
||||
return influxdb2.NewClient(config.Address, strings.TrimSpace(string(token))), nil
|
||||
}
|
||||
|
||||
type influxDB2Writer struct {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/traefik/paerser/types"
|
||||
tTypes "github.com/traefik/traefik/v3/pkg/types"
|
||||
)
|
||||
|
||||
// Metrics provides options to expose and send Traefik metrics to different third party monitoring systems.
|
||||
|
|
@ -87,15 +88,15 @@ func (s *Statsd) SetDefaults() {
|
|||
|
||||
// InfluxDB2 contains address, token and metrics pushing interval configuration.
|
||||
type InfluxDB2 struct {
|
||||
Address string `description:"InfluxDB v2 address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
|
||||
Token string `description:"InfluxDB v2 access token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"`
|
||||
PushInterval types.Duration `description:"InfluxDB v2 push interval." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"`
|
||||
Org string `description:"InfluxDB v2 org ID." json:"org,omitempty" toml:"org,omitempty" yaml:"org,omitempty" export:"true"`
|
||||
Bucket string `description:"InfluxDB v2 bucket ID." json:"bucket,omitempty" toml:"bucket,omitempty" yaml:"bucket,omitempty" export:"true"`
|
||||
AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"`
|
||||
AddRoutersLabels bool `description:"Enable metrics on routers." json:"addRoutersLabels,omitempty" toml:"addRoutersLabels,omitempty" yaml:"addRoutersLabels,omitempty" export:"true"`
|
||||
AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"`
|
||||
AdditionalLabels map[string]string `description:"Additional labels (influxdb tags) on all metrics" json:"additionalLabels,omitempty" toml:"additionalLabels,omitempty" yaml:"additionalLabels,omitempty" export:"true"`
|
||||
Address string `description:"InfluxDB v2 address." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
|
||||
Token tTypes.FileOrContent `description:"InfluxDB v2 access token. It accepts either a token value or a file path to the token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"`
|
||||
PushInterval types.Duration `description:"InfluxDB v2 push interval." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"`
|
||||
Org string `description:"InfluxDB v2 org ID." json:"org,omitempty" toml:"org,omitempty" yaml:"org,omitempty" export:"true"`
|
||||
Bucket string `description:"InfluxDB v2 bucket ID." json:"bucket,omitempty" toml:"bucket,omitempty" yaml:"bucket,omitempty" export:"true"`
|
||||
AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"`
|
||||
AddRoutersLabels bool `description:"Enable metrics on routers." json:"addRoutersLabels,omitempty" toml:"addRoutersLabels,omitempty" yaml:"addRoutersLabels,omitempty" export:"true"`
|
||||
AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"`
|
||||
AdditionalLabels map[string]string `description:"Additional labels (influxdb tags) on all metrics" json:"additionalLabels,omitempty" toml:"additionalLabels,omitempty" yaml:"additionalLabels,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"maps"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
|
@ -14,388 +13,8 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v3/pkg/observability/logs"
|
||||
"github.com/traefik/traefik/v3/pkg/tls"
|
||||
)
|
||||
|
||||
// Merge merges multiple configurations.
|
||||
func Merge(ctx context.Context, configurations map[string]*dynamic.Configuration) *dynamic.Configuration {
|
||||
logger := log.Ctx(ctx)
|
||||
|
||||
configuration := &dynamic.Configuration{
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Routers: make(map[string]*dynamic.Router),
|
||||
Middlewares: make(map[string]*dynamic.Middleware),
|
||||
Services: make(map[string]*dynamic.Service),
|
||||
ServersTransports: make(map[string]*dynamic.ServersTransport),
|
||||
},
|
||||
TCP: &dynamic.TCPConfiguration{
|
||||
Routers: make(map[string]*dynamic.TCPRouter),
|
||||
Services: make(map[string]*dynamic.TCPService),
|
||||
Middlewares: make(map[string]*dynamic.TCPMiddleware),
|
||||
ServersTransports: make(map[string]*dynamic.TCPServersTransport),
|
||||
},
|
||||
UDP: &dynamic.UDPConfiguration{
|
||||
Routers: make(map[string]*dynamic.UDPRouter),
|
||||
Services: make(map[string]*dynamic.UDPService),
|
||||
},
|
||||
TLS: &dynamic.TLSConfiguration{
|
||||
Stores: make(map[string]tls.Store),
|
||||
},
|
||||
}
|
||||
|
||||
servicesToDelete := map[string]struct{}{}
|
||||
services := map[string][]string{}
|
||||
|
||||
routersToDelete := map[string]struct{}{}
|
||||
routers := map[string][]string{}
|
||||
|
||||
servicesTCPToDelete := map[string]struct{}{}
|
||||
servicesTCP := map[string][]string{}
|
||||
|
||||
routersTCPToDelete := map[string]struct{}{}
|
||||
routersTCP := map[string][]string{}
|
||||
|
||||
servicesUDPToDelete := map[string]struct{}{}
|
||||
servicesUDP := map[string][]string{}
|
||||
|
||||
routersUDPToDelete := map[string]struct{}{}
|
||||
routersUDP := map[string][]string{}
|
||||
|
||||
middlewaresToDelete := map[string]struct{}{}
|
||||
middlewares := map[string][]string{}
|
||||
|
||||
middlewaresTCPToDelete := map[string]struct{}{}
|
||||
middlewaresTCP := map[string][]string{}
|
||||
|
||||
transportsToDelete := map[string]struct{}{}
|
||||
transports := map[string][]string{}
|
||||
|
||||
transportsTCPToDelete := map[string]struct{}{}
|
||||
transportsTCP := map[string][]string{}
|
||||
|
||||
storesToDelete := map[string]struct{}{}
|
||||
stores := map[string][]string{}
|
||||
|
||||
var sortedKeys []string
|
||||
for key := range configurations {
|
||||
sortedKeys = append(sortedKeys, key)
|
||||
}
|
||||
slices.Sort(sortedKeys)
|
||||
|
||||
for _, root := range sortedKeys {
|
||||
conf := configurations[root]
|
||||
for serviceName, service := range conf.HTTP.Services {
|
||||
services[serviceName] = append(services[serviceName], root)
|
||||
if !AddService(configuration.HTTP, serviceName, service) {
|
||||
servicesToDelete[serviceName] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for routerName, router := range conf.HTTP.Routers {
|
||||
routers[routerName] = append(routers[routerName], root)
|
||||
if !AddRouter(configuration.HTTP, routerName, router) {
|
||||
routersToDelete[routerName] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for transportName, transport := range conf.HTTP.ServersTransports {
|
||||
transports[transportName] = append(transports[transportName], root)
|
||||
if !AddTransport(configuration.HTTP, transportName, transport) {
|
||||
transportsToDelete[transportName] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for serviceName, service := range conf.TCP.Services {
|
||||
servicesTCP[serviceName] = append(servicesTCP[serviceName], root)
|
||||
if !AddServiceTCP(configuration.TCP, serviceName, service) {
|
||||
servicesTCPToDelete[serviceName] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for routerName, router := range conf.TCP.Routers {
|
||||
routersTCP[routerName] = append(routersTCP[routerName], root)
|
||||
if !AddRouterTCP(configuration.TCP, routerName, router) {
|
||||
routersTCPToDelete[routerName] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for transportName, transport := range conf.TCP.ServersTransports {
|
||||
transportsTCP[transportName] = append(transportsTCP[transportName], root)
|
||||
if !AddTransportTCP(configuration.TCP, transportName, transport) {
|
||||
transportsTCPToDelete[transportName] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for serviceName, service := range conf.UDP.Services {
|
||||
servicesUDP[serviceName] = append(servicesUDP[serviceName], root)
|
||||
if !AddServiceUDP(configuration.UDP, serviceName, service) {
|
||||
servicesUDPToDelete[serviceName] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for routerName, router := range conf.UDP.Routers {
|
||||
routersUDP[routerName] = append(routersUDP[routerName], root)
|
||||
if !AddRouterUDP(configuration.UDP, routerName, router) {
|
||||
routersUDPToDelete[routerName] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for middlewareName, middleware := range conf.HTTP.Middlewares {
|
||||
middlewares[middlewareName] = append(middlewares[middlewareName], root)
|
||||
if !AddMiddleware(configuration.HTTP, middlewareName, middleware) {
|
||||
middlewaresToDelete[middlewareName] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for middlewareName, middleware := range conf.TCP.Middlewares {
|
||||
middlewaresTCP[middlewareName] = append(middlewaresTCP[middlewareName], root)
|
||||
if !AddMiddlewareTCP(configuration.TCP, middlewareName, middleware) {
|
||||
middlewaresTCPToDelete[middlewareName] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for storeName, store := range conf.TLS.Stores {
|
||||
stores[storeName] = append(stores[storeName], root)
|
||||
if !AddStore(configuration.TLS, storeName, store) {
|
||||
storesToDelete[storeName] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for serviceName := range servicesToDelete {
|
||||
logger.Error().Str(logs.ServiceName, serviceName).
|
||||
Interface("configuration", services[serviceName]).
|
||||
Msg("Service defined multiple times with different configurations")
|
||||
delete(configuration.HTTP.Services, serviceName)
|
||||
}
|
||||
|
||||
for routerName := range routersToDelete {
|
||||
logger.Error().Str(logs.RouterName, routerName).
|
||||
Interface("configuration", routers[routerName]).
|
||||
Msg("Router defined multiple times with different configurations")
|
||||
delete(configuration.HTTP.Routers, routerName)
|
||||
}
|
||||
|
||||
for transportName := range transportsToDelete {
|
||||
logger.Error().Str(logs.ServersTransportName, transportName).
|
||||
Interface("configuration", transports[transportName]).
|
||||
Msg("ServersTransport defined multiple times with different configurations")
|
||||
delete(configuration.HTTP.ServersTransports, transportName)
|
||||
}
|
||||
|
||||
for serviceName := range servicesTCPToDelete {
|
||||
logger.Error().Str(logs.ServiceName, serviceName).
|
||||
Interface("configuration", servicesTCP[serviceName]).
|
||||
Msg("Service TCP defined multiple times with different configurations")
|
||||
delete(configuration.TCP.Services, serviceName)
|
||||
}
|
||||
|
||||
for routerName := range routersTCPToDelete {
|
||||
logger.Error().Str(logs.RouterName, routerName).
|
||||
Interface("configuration", routersTCP[routerName]).
|
||||
Msg("Router TCP defined multiple times with different configurations")
|
||||
delete(configuration.TCP.Routers, routerName)
|
||||
}
|
||||
|
||||
for transportName := range transportsTCPToDelete {
|
||||
logger.Error().Str(logs.ServersTransportName, transportName).
|
||||
Interface("configuration", transportsTCP[transportName]).
|
||||
Msg("ServersTransport TCP defined multiple times with different configurations")
|
||||
delete(configuration.TCP.ServersTransports, transportName)
|
||||
}
|
||||
|
||||
for serviceName := range servicesUDPToDelete {
|
||||
logger.Error().Str(logs.ServiceName, serviceName).
|
||||
Interface("configuration", servicesUDP[serviceName]).
|
||||
Msg("UDP service defined multiple times with different configurations")
|
||||
delete(configuration.UDP.Services, serviceName)
|
||||
}
|
||||
|
||||
for routerName := range routersUDPToDelete {
|
||||
logger.Error().Str(logs.RouterName, routerName).
|
||||
Interface("configuration", routersUDP[routerName]).
|
||||
Msg("UDP router defined multiple times with different configurations")
|
||||
delete(configuration.UDP.Routers, routerName)
|
||||
}
|
||||
|
||||
for middlewareName := range middlewaresToDelete {
|
||||
logger.Error().Str(logs.MiddlewareName, middlewareName).
|
||||
Interface("configuration", middlewares[middlewareName]).
|
||||
Msg("Middleware defined multiple times with different configurations")
|
||||
delete(configuration.HTTP.Middlewares, middlewareName)
|
||||
}
|
||||
|
||||
for middlewareName := range middlewaresTCPToDelete {
|
||||
logger.Error().Str(logs.MiddlewareName, middlewareName).
|
||||
Interface("configuration", middlewaresTCP[middlewareName]).
|
||||
Msg("TCP Middleware defined multiple times with different configurations")
|
||||
delete(configuration.TCP.Middlewares, middlewareName)
|
||||
}
|
||||
|
||||
for storeName := range storesToDelete {
|
||||
logger.Error().Str("storeName", storeName).
|
||||
Msgf("TLS store defined multiple times with different configurations in %v", stores[storeName])
|
||||
delete(configuration.TLS.Stores, storeName)
|
||||
}
|
||||
|
||||
return configuration
|
||||
}
|
||||
|
||||
// AddServiceTCP adds a service to a configuration.
|
||||
func AddServiceTCP(configuration *dynamic.TCPConfiguration, serviceName string, service *dynamic.TCPService) bool {
|
||||
if _, ok := configuration.Services[serviceName]; !ok {
|
||||
configuration.Services[serviceName] = service
|
||||
return true
|
||||
}
|
||||
|
||||
if !configuration.Services[serviceName].LoadBalancer.Mergeable(service.LoadBalancer) {
|
||||
return false
|
||||
}
|
||||
|
||||
uniq := map[string]struct{}{}
|
||||
for _, server := range configuration.Services[serviceName].LoadBalancer.Servers {
|
||||
uniq[server.Address] = struct{}{}
|
||||
}
|
||||
|
||||
for _, server := range service.LoadBalancer.Servers {
|
||||
if _, ok := uniq[server.Address]; !ok {
|
||||
configuration.Services[serviceName].LoadBalancer.Servers = append(configuration.Services[serviceName].LoadBalancer.Servers, server)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// AddRouterTCP adds a router to a configuration.
|
||||
func AddRouterTCP(configuration *dynamic.TCPConfiguration, routerName string, router *dynamic.TCPRouter) bool {
|
||||
if _, ok := configuration.Routers[routerName]; !ok {
|
||||
configuration.Routers[routerName] = router
|
||||
return true
|
||||
}
|
||||
|
||||
return reflect.DeepEqual(configuration.Routers[routerName], router)
|
||||
}
|
||||
|
||||
// AddMiddlewareTCP adds a middleware to a configuration.
|
||||
func AddMiddlewareTCP(configuration *dynamic.TCPConfiguration, middlewareName string, middleware *dynamic.TCPMiddleware) bool {
|
||||
if _, ok := configuration.Middlewares[middlewareName]; !ok {
|
||||
configuration.Middlewares[middlewareName] = middleware
|
||||
return true
|
||||
}
|
||||
|
||||
return reflect.DeepEqual(configuration.Middlewares[middlewareName], middleware)
|
||||
}
|
||||
|
||||
// AddTransportTCP adds a servers transport to a configuration.
|
||||
func AddTransportTCP(configuration *dynamic.TCPConfiguration, transportName string, transport *dynamic.TCPServersTransport) bool {
|
||||
if _, ok := configuration.ServersTransports[transportName]; !ok {
|
||||
configuration.ServersTransports[transportName] = transport
|
||||
return true
|
||||
}
|
||||
|
||||
return reflect.DeepEqual(configuration.ServersTransports[transportName], transport)
|
||||
}
|
||||
|
||||
// AddServiceUDP adds a service to a configuration.
|
||||
func AddServiceUDP(configuration *dynamic.UDPConfiguration, serviceName string, service *dynamic.UDPService) bool {
|
||||
if _, ok := configuration.Services[serviceName]; !ok {
|
||||
configuration.Services[serviceName] = service
|
||||
return true
|
||||
}
|
||||
|
||||
if !configuration.Services[serviceName].LoadBalancer.Mergeable(service.LoadBalancer) {
|
||||
return false
|
||||
}
|
||||
|
||||
uniq := map[string]struct{}{}
|
||||
for _, server := range configuration.Services[serviceName].LoadBalancer.Servers {
|
||||
uniq[server.Address] = struct{}{}
|
||||
}
|
||||
|
||||
for _, server := range service.LoadBalancer.Servers {
|
||||
if _, ok := uniq[server.Address]; !ok {
|
||||
configuration.Services[serviceName].LoadBalancer.Servers = append(configuration.Services[serviceName].LoadBalancer.Servers, server)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// AddRouterUDP adds a router to a configuration.
|
||||
func AddRouterUDP(configuration *dynamic.UDPConfiguration, routerName string, router *dynamic.UDPRouter) bool {
|
||||
if _, ok := configuration.Routers[routerName]; !ok {
|
||||
configuration.Routers[routerName] = router
|
||||
return true
|
||||
}
|
||||
|
||||
return reflect.DeepEqual(configuration.Routers[routerName], router)
|
||||
}
|
||||
|
||||
// AddService adds a service to a configuration.
|
||||
func AddService(configuration *dynamic.HTTPConfiguration, serviceName string, service *dynamic.Service) bool {
|
||||
if _, ok := configuration.Services[serviceName]; !ok {
|
||||
configuration.Services[serviceName] = service
|
||||
return true
|
||||
}
|
||||
|
||||
if !configuration.Services[serviceName].LoadBalancer.Mergeable(service.LoadBalancer) {
|
||||
return false
|
||||
}
|
||||
|
||||
uniq := map[string]struct{}{}
|
||||
for _, server := range configuration.Services[serviceName].LoadBalancer.Servers {
|
||||
uniq[server.URL] = struct{}{}
|
||||
}
|
||||
|
||||
for _, server := range service.LoadBalancer.Servers {
|
||||
if _, ok := uniq[server.URL]; !ok {
|
||||
configuration.Services[serviceName].LoadBalancer.Servers = append(configuration.Services[serviceName].LoadBalancer.Servers, server)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// AddRouter adds a router to a configuration.
|
||||
func AddRouter(configuration *dynamic.HTTPConfiguration, routerName string, router *dynamic.Router) bool {
|
||||
if _, ok := configuration.Routers[routerName]; !ok {
|
||||
configuration.Routers[routerName] = router
|
||||
return true
|
||||
}
|
||||
|
||||
return reflect.DeepEqual(configuration.Routers[routerName], router)
|
||||
}
|
||||
|
||||
// AddTransport adds a servers transport to a configuration.
|
||||
func AddTransport(configuration *dynamic.HTTPConfiguration, transportName string, transport *dynamic.ServersTransport) bool {
|
||||
if _, ok := configuration.ServersTransports[transportName]; !ok {
|
||||
configuration.ServersTransports[transportName] = transport
|
||||
return true
|
||||
}
|
||||
|
||||
return reflect.DeepEqual(configuration.ServersTransports[transportName], transport)
|
||||
}
|
||||
|
||||
// AddMiddleware adds a middleware to a configuration.
|
||||
func AddMiddleware(configuration *dynamic.HTTPConfiguration, middlewareName string, middleware *dynamic.Middleware) bool {
|
||||
if _, ok := configuration.Middlewares[middlewareName]; !ok {
|
||||
configuration.Middlewares[middlewareName] = middleware
|
||||
return true
|
||||
}
|
||||
|
||||
return reflect.DeepEqual(configuration.Middlewares[middlewareName], middleware)
|
||||
}
|
||||
|
||||
// AddStore adds a middleware to a configurations.
|
||||
func AddStore(configuration *dynamic.TLSConfiguration, storeName string, store tls.Store) bool {
|
||||
if _, ok := configuration.Stores[storeName]; !ok {
|
||||
configuration.Stores[storeName] = store
|
||||
return true
|
||||
}
|
||||
|
||||
return reflect.DeepEqual(configuration.Stores[storeName], store)
|
||||
}
|
||||
|
||||
// MakeDefaultRuleTemplate creates the default rule template.
|
||||
func MakeDefaultRuleTemplate(defaultRule string, funcMap template.FuncMap) (*template.Template, error) {
|
||||
defaultFuncMap := sprig.TxtFuncMap()
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, items []itemData, cer
|
|||
configurations[svcName] = confFromLabel
|
||||
}
|
||||
|
||||
return provider.Merge(ctx, configurations)
|
||||
return provider.Merge(ctx, provider.NameSortedConfigurations(configurations), provider.ResourceStrategyMerge)
|
||||
}
|
||||
|
||||
func (p *Provider) keepContainer(ctx context.Context, item itemData) bool {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@ import (
|
|||
|
||||
// connectCert holds our certificates as a client of the Consul Connect protocol.
|
||||
type connectCert struct {
|
||||
root []string
|
||||
leaf keyPair
|
||||
trustDomain string
|
||||
root []string
|
||||
leaf keyPair
|
||||
}
|
||||
|
||||
func (c *connectCert) getRoot() []types.FileOrContent {
|
||||
|
|
@ -52,7 +53,8 @@ func (c *connectCert) equals(other *connectCert) bool {
|
|||
}
|
||||
|
||||
func (c *connectCert) serversTransport(item itemData) *dynamic.ServersTransport {
|
||||
spiffeID := fmt.Sprintf("spiffe:///ns/%s/dc/%s/svc/%s",
|
||||
spiffeID := fmt.Sprintf("spiffe://%s/ns/%s/dc/%s/svc/%s",
|
||||
c.trustDomain,
|
||||
item.Namespace,
|
||||
item.Datacenter,
|
||||
item.Name,
|
||||
|
|
@ -72,7 +74,8 @@ func (c *connectCert) serversTransport(item itemData) *dynamic.ServersTransport
|
|||
}
|
||||
|
||||
func (c *connectCert) tcpServersTransport(item itemData) *dynamic.TCPServersTransport {
|
||||
spiffeID := fmt.Sprintf("spiffe:///ns/%s/dc/%s/svc/%s",
|
||||
spiffeID := fmt.Sprintf("spiffe://%s/ns/%s/dc/%s/svc/%s",
|
||||
c.trustDomain,
|
||||
item.Namespace,
|
||||
item.Datacenter,
|
||||
item.Name,
|
||||
|
|
|
|||
|
|
@ -465,7 +465,7 @@ func (p *Provider) watchConnectTLS(ctx context.Context) error {
|
|||
}
|
||||
leafWatcher.HybridHandler = leafWatcherHandler(ctx, leafChan)
|
||||
|
||||
rootsChan := make(chan []string)
|
||||
rootsChan := make(chan caRootList)
|
||||
rootsWatcher, err := watch.Parse(map[string]any{
|
||||
"type": "connect_roots",
|
||||
})
|
||||
|
|
@ -497,9 +497,9 @@ func (p *Provider) watchConnectTLS(ctx context.Context) error {
|
|||
}()
|
||||
|
||||
var (
|
||||
certInfo *connectCert
|
||||
leafCerts keyPair
|
||||
rootCerts []string
|
||||
certInfo *connectCert
|
||||
leafCert keyPair
|
||||
caRoots caRootList
|
||||
)
|
||||
|
||||
for {
|
||||
|
|
@ -510,13 +510,14 @@ func (p *Provider) watchConnectTLS(ctx context.Context) error {
|
|||
case err := <-errChan:
|
||||
return fmt.Errorf("leaf or roots watcher terminated: %w", err)
|
||||
|
||||
case rootCerts = <-rootsChan:
|
||||
case leafCerts = <-leafChan:
|
||||
case caRoots = <-rootsChan:
|
||||
case leafCert = <-leafChan:
|
||||
}
|
||||
|
||||
newCertInfo := &connectCert{
|
||||
root: rootCerts,
|
||||
leaf: leafCerts,
|
||||
trustDomain: caRoots.trustDomain,
|
||||
root: caRoots.roots,
|
||||
leaf: leafCert,
|
||||
}
|
||||
if newCertInfo.isReady() && !newCertInfo.equals(certInfo) {
|
||||
log.Ctx(ctx).Debug().Msgf("Updating connect certs for service %s", p.ServiceName)
|
||||
|
|
@ -546,7 +547,12 @@ func (p *Provider) includesHealthStatus(status string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func rootsWatchHandler(ctx context.Context, dest chan<- []string) func(watch.BlockingParamVal, any) {
|
||||
type caRootList struct {
|
||||
trustDomain string
|
||||
roots []string
|
||||
}
|
||||
|
||||
func rootsWatchHandler(ctx context.Context, dest chan<- caRootList) func(watch.BlockingParamVal, any) {
|
||||
return func(_ watch.BlockingParamVal, raw any) {
|
||||
if raw == nil {
|
||||
log.Ctx(ctx).Error().Msg("Root certificate watcher called with nil")
|
||||
|
|
@ -566,7 +572,7 @@ func rootsWatchHandler(ctx context.Context, dest chan<- []string) func(watch.Blo
|
|||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case dest <- roots:
|
||||
case dest <- caRootList{trustDomain: v.TrustDomain, roots: roots}:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ func (p *DynConfBuilder) build(ctx context.Context, containersInspected []docker
|
|||
configurations[containerName] = confFromLabel
|
||||
}
|
||||
|
||||
return provider.Merge(ctx, configurations)
|
||||
return provider.Merge(ctx, provider.NameSortedConfigurations(configurations), provider.ResourceStrategyMerge)
|
||||
}
|
||||
|
||||
func (p *DynConfBuilder) buildTCPServiceConfiguration(ctx context.Context, container dockerData, configuration *dynamic.TCPConfiguration) error {
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, instances []ecsInstan
|
|||
configurations[instanceName] = confFromLabel
|
||||
}
|
||||
|
||||
return provider.Merge(ctx, configurations)
|
||||
return provider.Merge(ctx, provider.NameSortedConfigurations(configurations), provider.ResourceStrategyMerge)
|
||||
}
|
||||
|
||||
func (p *Provider) buildTCPServiceConfiguration(instance ecsInstance, configuration *dynamic.TCPConfiguration) error {
|
||||
|
|
|
|||
|
|
@ -221,7 +221,12 @@ func (p *Provider) buildConfiguration() (*dynamic.Configuration, error) {
|
|||
ctx := log.With().Str(logs.ProviderName, providerName).Logger().WithContext(context.Background())
|
||||
|
||||
if len(p.Directory) > 0 {
|
||||
return p.loadFileConfigFromDirectory(ctx, p.Directory, nil)
|
||||
configurations, err := p.collectFileConfigs(ctx, p.Directory, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("collecting file configs: %w", err)
|
||||
}
|
||||
|
||||
return provider.Merge(ctx, configurations, provider.ResourceStrategySkipDuplicates), nil
|
||||
}
|
||||
|
||||
if len(p.Filename) > 0 {
|
||||
|
|
@ -376,47 +381,28 @@ func (p *Provider) loadFileConfig(ctx context.Context, filename string, parseTem
|
|||
return configuration, nil
|
||||
}
|
||||
|
||||
func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory string, configuration *dynamic.Configuration) (*dynamic.Configuration, error) {
|
||||
// collectFileConfigs recursively collects configurations from files in the given directory.
|
||||
func (p *Provider) collectFileConfigs(ctx context.Context, directory, prefix string) ([]provider.NamedConfiguration, error) {
|
||||
var configurations []provider.NamedConfiguration
|
||||
|
||||
fileList, err := os.ReadDir(directory)
|
||||
if err != nil {
|
||||
return configuration, fmt.Errorf("unable to read directory %s: %w", directory, err)
|
||||
return nil, fmt.Errorf("reading directory %s: %w", directory, err)
|
||||
}
|
||||
|
||||
if configuration == nil {
|
||||
configuration = &dynamic.Configuration{
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Routers: make(map[string]*dynamic.Router),
|
||||
Middlewares: make(map[string]*dynamic.Middleware),
|
||||
Services: make(map[string]*dynamic.Service),
|
||||
ServersTransports: make(map[string]*dynamic.ServersTransport),
|
||||
},
|
||||
TCP: &dynamic.TCPConfiguration{
|
||||
Routers: make(map[string]*dynamic.TCPRouter),
|
||||
Services: make(map[string]*dynamic.TCPService),
|
||||
Middlewares: make(map[string]*dynamic.TCPMiddleware),
|
||||
ServersTransports: make(map[string]*dynamic.TCPServersTransport),
|
||||
},
|
||||
TLS: &dynamic.TLSConfiguration{
|
||||
Stores: make(map[string]tls.Store),
|
||||
Options: make(map[string]tls.Options),
|
||||
},
|
||||
UDP: &dynamic.UDPConfiguration{
|
||||
Routers: make(map[string]*dynamic.UDPRouter),
|
||||
Services: make(map[string]*dynamic.UDPService),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
configTLSMaps := make(map[*tls.CertAndStores]struct{})
|
||||
|
||||
for _, item := range fileList {
|
||||
logger := log.Ctx(ctx).With().Str("filename", item.Name()).Logger()
|
||||
itemPath := filepath.Join(directory, item.Name())
|
||||
filename := item.Name()
|
||||
if prefix != "" {
|
||||
filename = filepath.Join(prefix, item.Name())
|
||||
}
|
||||
|
||||
if item.IsDir() {
|
||||
configuration, err = p.loadFileConfigFromDirectory(logger.WithContext(ctx), filepath.Join(directory, item.Name()), configuration)
|
||||
sub, err := p.collectFileConfigs(ctx, itemPath, filename)
|
||||
if err != nil {
|
||||
return configuration, fmt.Errorf("unable to load content configuration from subdirectory %s: %w", item, err)
|
||||
return nil, fmt.Errorf("loading content configuration from subdirectory %s: %w", item, err)
|
||||
}
|
||||
configurations = append(configurations, sub...)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -427,132 +413,18 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
|
|||
continue
|
||||
}
|
||||
|
||||
var c *dynamic.Configuration
|
||||
c, err = p.loadFileConfig(logger.WithContext(ctx), filepath.Join(directory, item.Name()), true)
|
||||
c, err := p.loadFileConfig(ctx, itemPath, true)
|
||||
if err != nil {
|
||||
return configuration, fmt.Errorf("%s: %w", filepath.Join(directory, item.Name()), err)
|
||||
return nil, fmt.Errorf("%s: %w", itemPath, err)
|
||||
}
|
||||
|
||||
for name, conf := range c.HTTP.Routers {
|
||||
if _, exists := configuration.HTTP.Routers[name]; exists {
|
||||
logger.Warn().Str(logs.RouterName, name).Msg("HTTP router already configured, skipping")
|
||||
} else {
|
||||
configuration.HTTP.Routers[name] = conf
|
||||
}
|
||||
}
|
||||
|
||||
for name, conf := range c.HTTP.Middlewares {
|
||||
if _, exists := configuration.HTTP.Middlewares[name]; exists {
|
||||
logger.Warn().Str(logs.MiddlewareName, name).Msg("HTTP middleware already configured, skipping")
|
||||
} else {
|
||||
configuration.HTTP.Middlewares[name] = conf
|
||||
}
|
||||
}
|
||||
|
||||
for name, conf := range c.HTTP.Services {
|
||||
if _, exists := configuration.HTTP.Services[name]; exists {
|
||||
logger.Warn().Str(logs.ServiceName, name).Msg("HTTP service already configured, skipping")
|
||||
} else {
|
||||
configuration.HTTP.Services[name] = conf
|
||||
}
|
||||
}
|
||||
|
||||
for name, conf := range c.HTTP.ServersTransports {
|
||||
if _, exists := configuration.HTTP.ServersTransports[name]; exists {
|
||||
logger.Warn().Str(logs.ServersTransportName, name).Msg("HTTP servers transport already configured, skipping")
|
||||
} else {
|
||||
configuration.HTTP.ServersTransports[name] = conf
|
||||
}
|
||||
}
|
||||
|
||||
for name, conf := range c.TCP.Routers {
|
||||
if _, exists := configuration.TCP.Routers[name]; exists {
|
||||
logger.Warn().Str(logs.RouterName, name).Msg("TCP router already configured, skipping")
|
||||
} else {
|
||||
configuration.TCP.Routers[name] = conf
|
||||
}
|
||||
}
|
||||
|
||||
for name, conf := range c.TCP.Middlewares {
|
||||
if _, exists := configuration.TCP.Middlewares[name]; exists {
|
||||
logger.Warn().Str(logs.MiddlewareName, name).Msg("TCP middleware already configured, skipping")
|
||||
} else {
|
||||
configuration.TCP.Middlewares[name] = conf
|
||||
}
|
||||
}
|
||||
|
||||
for name, conf := range c.TCP.Services {
|
||||
if _, exists := configuration.TCP.Services[name]; exists {
|
||||
logger.Warn().Str(logs.ServiceName, name).Msg("TCP service already configured, skipping")
|
||||
} else {
|
||||
configuration.TCP.Services[name] = conf
|
||||
}
|
||||
}
|
||||
|
||||
for name, conf := range c.TCP.ServersTransports {
|
||||
if _, exists := configuration.TCP.ServersTransports[name]; exists {
|
||||
logger.Warn().Str(logs.ServersTransportName, name).Msg("TCP servers transport already configured, skipping")
|
||||
} else {
|
||||
configuration.TCP.ServersTransports[name] = conf
|
||||
}
|
||||
}
|
||||
|
||||
for name, conf := range c.UDP.Routers {
|
||||
if _, exists := configuration.UDP.Routers[name]; exists {
|
||||
logger.Warn().Str(logs.RouterName, name).Msg("UDP router already configured, skipping")
|
||||
} else {
|
||||
configuration.UDP.Routers[name] = conf
|
||||
}
|
||||
}
|
||||
|
||||
for name, conf := range c.UDP.Services {
|
||||
if _, exists := configuration.UDP.Services[name]; exists {
|
||||
logger.Warn().Str(logs.ServiceName, name).Msg("UDP service already configured, skipping")
|
||||
} else {
|
||||
configuration.UDP.Services[name] = conf
|
||||
}
|
||||
}
|
||||
|
||||
for _, conf := range c.TLS.Certificates {
|
||||
if _, exists := configTLSMaps[conf]; exists {
|
||||
logger.Warn().Msgf("TLS configuration %v already configured, skipping", conf)
|
||||
} else {
|
||||
configTLSMaps[conf] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for name, conf := range c.TLS.Options {
|
||||
if _, exists := configuration.TLS.Options[name]; exists {
|
||||
logger.Warn().Msgf("TLS options %v already configured, skipping", name)
|
||||
} else {
|
||||
if configuration.TLS.Options == nil {
|
||||
configuration.TLS.Options = map[string]tls.Options{}
|
||||
}
|
||||
configuration.TLS.Options[name] = conf
|
||||
}
|
||||
}
|
||||
|
||||
for name, conf := range c.TLS.Stores {
|
||||
if _, exists := configuration.TLS.Stores[name]; exists {
|
||||
logger.Warn().Msgf("TLS store %v already configured, skipping", name)
|
||||
} else {
|
||||
if configuration.TLS.Stores == nil {
|
||||
configuration.TLS.Stores = map[string]tls.Store{}
|
||||
}
|
||||
configuration.TLS.Stores[name] = conf
|
||||
}
|
||||
}
|
||||
configurations = append(configurations, provider.NamedConfiguration{
|
||||
Name: filename,
|
||||
Configuration: c,
|
||||
})
|
||||
}
|
||||
|
||||
if len(configTLSMaps) > 0 && configuration.TLS == nil {
|
||||
configuration.TLS = &dynamic.TLSConfiguration{}
|
||||
}
|
||||
|
||||
for conf := range configTLSMaps {
|
||||
configuration.TLS.Certificates = append(configuration.TLS.Certificates, conf)
|
||||
}
|
||||
|
||||
return configuration, nil
|
||||
return configurations, nil
|
||||
}
|
||||
|
||||
func (p *Provider) decodeConfiguration(filePath, content string) (*dynamic.Configuration, error) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: stripprefix
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
stripPrefix:
|
||||
prefixes:
|
||||
- /tobestripped
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: TraefikService
|
||||
metadata:
|
||||
name: test-weighted
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
weighted:
|
||||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
weight: 1
|
||||
middlewares:
|
||||
- name: stripprefix
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: test.route
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
entryPoints:
|
||||
- web
|
||||
|
||||
routes:
|
||||
- match: Host(`foo.com`) && PathPrefix(`/bar`)
|
||||
kind: Rule
|
||||
priority: 12
|
||||
services:
|
||||
- name: test-weighted
|
||||
kind: TraefikService
|
||||
|
|
@ -37,6 +37,7 @@ type LoadBalancerSpecApplyConfiguration struct {
|
|||
Name *string `json:"name,omitempty"`
|
||||
Kind *string `json:"kind,omitempty"`
|
||||
Namespace *string `json:"namespace,omitempty"`
|
||||
Middlewares []MiddlewareRefApplyConfiguration `json:"middlewares,omitempty"`
|
||||
Sticky *dynamic.Sticky `json:"sticky,omitempty"`
|
||||
Port *intstr.IntOrString `json:"port,omitempty"`
|
||||
Scheme *string `json:"scheme,omitempty"`
|
||||
|
|
@ -81,6 +82,19 @@ func (b *LoadBalancerSpecApplyConfiguration) WithNamespace(value string) *LoadBa
|
|||
return b
|
||||
}
|
||||
|
||||
// WithMiddlewares adds the given value to the Middlewares field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, values provided by each call will be appended to the Middlewares field.
|
||||
func (b *LoadBalancerSpecApplyConfiguration) WithMiddlewares(values ...*MiddlewareRefApplyConfiguration) *LoadBalancerSpecApplyConfiguration {
|
||||
for i := range values {
|
||||
if values[i] == nil {
|
||||
panic("nil value passed to WithMiddlewares")
|
||||
}
|
||||
b.Middlewares = append(b.Middlewares, *values[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// WithSticky sets the Sticky field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Sticky field is set to the value of the last call.
|
||||
|
|
|
|||
|
|
@ -70,6 +70,19 @@ func (b *MirroringApplyConfiguration) WithNamespace(value string) *MirroringAppl
|
|||
return b
|
||||
}
|
||||
|
||||
// WithMiddlewares adds the given value to the Middlewares field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, values provided by each call will be appended to the Middlewares field.
|
||||
func (b *MirroringApplyConfiguration) WithMiddlewares(values ...*MiddlewareRefApplyConfiguration) *MirroringApplyConfiguration {
|
||||
for i := range values {
|
||||
if values[i] == nil {
|
||||
panic("nil value passed to WithMiddlewares")
|
||||
}
|
||||
b.LoadBalancerSpecApplyConfiguration.Middlewares = append(b.LoadBalancerSpecApplyConfiguration.Middlewares, *values[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// WithSticky sets the Sticky field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Sticky field is set to the value of the last call.
|
||||
|
|
|
|||
|
|
@ -68,6 +68,19 @@ func (b *MirrorServiceApplyConfiguration) WithNamespace(value string) *MirrorSer
|
|||
return b
|
||||
}
|
||||
|
||||
// WithMiddlewares adds the given value to the Middlewares field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, values provided by each call will be appended to the Middlewares field.
|
||||
func (b *MirrorServiceApplyConfiguration) WithMiddlewares(values ...*MiddlewareRefApplyConfiguration) *MirrorServiceApplyConfiguration {
|
||||
for i := range values {
|
||||
if values[i] == nil {
|
||||
panic("nil value passed to WithMiddlewares")
|
||||
}
|
||||
b.LoadBalancerSpecApplyConfiguration.Middlewares = append(b.LoadBalancerSpecApplyConfiguration.Middlewares, *values[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// WithSticky sets the Sticky field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Sticky field is set to the value of the last call.
|
||||
|
|
|
|||
|
|
@ -67,6 +67,19 @@ func (b *ServiceApplyConfiguration) WithNamespace(value string) *ServiceApplyCon
|
|||
return b
|
||||
}
|
||||
|
||||
// WithMiddlewares adds the given value to the Middlewares field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, values provided by each call will be appended to the Middlewares field.
|
||||
func (b *ServiceApplyConfiguration) WithMiddlewares(values ...*MiddlewareRefApplyConfiguration) *ServiceApplyConfiguration {
|
||||
for i := range values {
|
||||
if values[i] == nil {
|
||||
panic("nil value passed to WithMiddlewares")
|
||||
}
|
||||
b.LoadBalancerSpecApplyConfiguration.Middlewares = append(b.LoadBalancerSpecApplyConfiguration.Middlewares, *values[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// WithSticky sets the Sticky field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Sticky field is set to the value of the last call.
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
|
|||
continue
|
||||
}
|
||||
|
||||
errorPage, errorPageService, err := p.createErrorPageMiddleware(client, middleware.Namespace, middleware.Spec.Errors)
|
||||
errorPage, errorPageService, err := p.createErrorPageMiddleware(ctxMid, client, middleware.Namespace, middleware.Spec.Errors)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Error while reading error page middleware")
|
||||
continue
|
||||
|
|
@ -645,7 +645,7 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
|
|||
return conf
|
||||
}
|
||||
|
||||
func (p *Provider) createErrorPageMiddleware(client Client, namespace string, errorPage *traefikv1alpha1.ErrorPage) (*dynamic.ErrorPage, *dynamic.Service, error) {
|
||||
func (p *Provider) createErrorPageMiddleware(ctx context.Context, client Client, namespace string, errorPage *traefikv1alpha1.ErrorPage) (*dynamic.ErrorPage, *dynamic.Service, error) {
|
||||
if errorPage == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
|
@ -663,7 +663,7 @@ func (p *Provider) createErrorPageMiddleware(client Client, namespace string, er
|
|||
allowEmptyServices: p.AllowEmptyServices,
|
||||
}
|
||||
|
||||
balancerServerHTTP, err := cb.buildServersLB(namespace, errorPage.Service.LoadBalancerSpec)
|
||||
balancerServerHTTP, err := cb.buildServersLB(ctx, namespace, errorPage.Service.LoadBalancerSpec)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
|
|||
|
||||
serviceKey := makeServiceKey(route.Match, ingressName)
|
||||
|
||||
mds, err := p.makeMiddlewareKeys(ctx, ingressRoute.Namespace, route.Middlewares)
|
||||
mds, err := makeMiddlewareKeys(ctx, ingressRoute.Namespace, route.Middlewares, p.AllowCrossNamespace)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Failed to create middleware keys")
|
||||
continue
|
||||
|
|
@ -172,13 +172,13 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
|
|||
return conf
|
||||
}
|
||||
|
||||
func (p *Provider) makeMiddlewareKeys(ctx context.Context, ingRouteNamespace string, middlewares []traefikv1alpha1.MiddlewareRef) ([]string, error) {
|
||||
func makeMiddlewareKeys(ctx context.Context, namespace string, middlewares []traefikv1alpha1.MiddlewareRef, allowCrossNamespace bool) ([]string, error) {
|
||||
var mds []string
|
||||
|
||||
for _, mi := range middlewares {
|
||||
name := mi.Name
|
||||
|
||||
if !p.AllowCrossNamespace && strings.HasSuffix(mi.Name, providerNamespaceSeparator+providerName) {
|
||||
if !allowCrossNamespace && strings.HasSuffix(mi.Name, providerNamespaceSeparator+providerName) {
|
||||
// Since we are not able to know if another namespace is in the name (namespace-name@kubernetescrd),
|
||||
// if the provider namespace kubernetescrd is used,
|
||||
// we don't allow this format to avoid cross namespace references.
|
||||
|
|
@ -196,10 +196,10 @@ func (p *Provider) makeMiddlewareKeys(ctx context.Context, ingRouteNamespace str
|
|||
continue
|
||||
}
|
||||
|
||||
ns := ingRouteNamespace
|
||||
ns := namespace
|
||||
if len(mi.Namespace) > 0 {
|
||||
if !isNamespaceAllowed(p.AllowCrossNamespace, ingRouteNamespace, mi.Namespace) {
|
||||
return nil, fmt.Errorf("middleware %s/%s is not in the IngressRoute namespace %s", mi.Namespace, mi.Name, ingRouteNamespace)
|
||||
if !isNamespaceAllowed(allowCrossNamespace, namespace, mi.Namespace) {
|
||||
return nil, fmt.Errorf("middleware %s/%s is not in the parent namespace %s", mi.Namespace, mi.Name, namespace)
|
||||
}
|
||||
|
||||
ns = mi.Namespace
|
||||
|
|
@ -333,6 +333,7 @@ func (c configBuilder) buildServicesLB(ctx context.Context, namespace string, tS
|
|||
Sticky: sticky,
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -378,7 +379,7 @@ func (c configBuilder) buildMirroring(ctx context.Context, tService *traefikv1al
|
|||
}
|
||||
|
||||
// buildServersLB creates the configuration for the load-balancer of servers defined by svc.
|
||||
func (c configBuilder) buildServersLB(namespace string, svc traefikv1alpha1.LoadBalancerSpec) (*dynamic.Service, error) {
|
||||
func (c configBuilder) buildServersLB(ctx context.Context, namespace string, svc traefikv1alpha1.LoadBalancerSpec) (*dynamic.Service, error) {
|
||||
lb := &dynamic.ServersLoadBalancer{}
|
||||
lb.SetDefaults()
|
||||
|
||||
|
|
@ -501,7 +502,16 @@ func (c configBuilder) buildServersLB(namespace string, svc traefikv1alpha1.Load
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return &dynamic.Service{LoadBalancer: lb}, nil
|
||||
service := &dynamic.Service{LoadBalancer: lb}
|
||||
if len(svc.Middlewares) > 0 {
|
||||
mds, err := makeMiddlewareKeys(ctx, namespace, svc.Middlewares, c.allowCrossNamespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create middleware keys: %w", err)
|
||||
}
|
||||
service.Middlewares = mds
|
||||
}
|
||||
|
||||
return service, nil
|
||||
}
|
||||
|
||||
func (c configBuilder) makeServersTransportKey(parentNamespace string, serversTransportName string) (string, error) {
|
||||
|
|
@ -687,7 +697,7 @@ func (c configBuilder) nameAndService(ctx context.Context, parentNamespace strin
|
|||
|
||||
switch service.Kind {
|
||||
case "", "Service":
|
||||
serversLB, err := c.buildServersLB(namespace, service)
|
||||
serversLB, err := c.buildServersLB(ctx, namespace, service)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3279,6 +3279,71 @@ func TestLoadIngressRoutes(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "TraefikService with service middleware",
|
||||
paths: []string{"services.yml", "with_traefik_service_middleware.yml"},
|
||||
expected: &dynamic.Configuration{
|
||||
UDP: &dynamic.UDPConfiguration{
|
||||
Routers: map[string]*dynamic.UDPRouter{},
|
||||
Services: map[string]*dynamic.UDPService{},
|
||||
},
|
||||
TLS: &dynamic.TLSConfiguration{},
|
||||
TCP: &dynamic.TCPConfiguration{
|
||||
Routers: map[string]*dynamic.TCPRouter{},
|
||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||
Services: map[string]*dynamic.TCPService{},
|
||||
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
||||
},
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Routers: map[string]*dynamic.Router{
|
||||
"default-test-route-6b204d94623b3df4370c": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "default-test-weighted",
|
||||
Rule: "Host(`foo.com`) && PathPrefix(`/bar`)",
|
||||
Priority: 12,
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*dynamic.Middleware{
|
||||
"default-stripprefix": {
|
||||
StripPrefix: &dynamic.StripPrefix{
|
||||
Prefixes: []string{"/tobestripped"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Services: map[string]*dynamic.Service{
|
||||
"default-test-weighted": {
|
||||
Weighted: &dynamic.WeightedRoundRobin{
|
||||
Services: []dynamic.WRRService{
|
||||
{
|
||||
Name: "default-whoami-80",
|
||||
Weight: pointer(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"default-whoami-80": {
|
||||
Middlewares: []string{"default-stripprefix"},
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
Strategy: dynamic.BalancerStrategyWRR,
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://10.10.0.1:80",
|
||||
},
|
||||
{
|
||||
URL: "http://10.10.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: pointer(true),
|
||||
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||
FlushInterval: ptypes.Duration(100 * time.Millisecond),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ServersTransports: map[string]*dynamic.ServersTransport{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "one kube services in a highest random weight",
|
||||
paths: []string{"with_highest_random_weight.yml"},
|
||||
|
|
|
|||
|
|
@ -110,6 +110,8 @@ type LoadBalancerSpec struct {
|
|||
Kind string `json:"kind,omitempty"`
|
||||
// Namespace defines the namespace of the referenced Kubernetes Service or TraefikService.
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
// Middlewares defines the list of references to Middleware resources to apply to the service.
|
||||
Middlewares []MiddlewareRef `json:"middlewares,omitempty"`
|
||||
// Sticky defines the sticky sessions configuration.
|
||||
// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions
|
||||
Sticky *dynamic.Sticky `json:"sticky,omitempty"`
|
||||
|
|
|
|||
|
|
@ -685,6 +685,11 @@ func (in *IngressRouteUDPSpec) DeepCopy() *IngressRouteUDPSpec {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *LoadBalancerSpec) DeepCopyInto(out *LoadBalancerSpec) {
|
||||
*out = *in
|
||||
if in.Middlewares != nil {
|
||||
in, out := &in.Middlewares, &out.Middlewares
|
||||
*out = make([]MiddlewareRef, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Sticky != nil {
|
||||
in, out := &in.Sticky, &out.Sticky
|
||||
*out = new(dynamic.Sticky)
|
||||
|
|
|
|||
|
|
@ -44,5 +44,6 @@ func extendedHTTPRouteFeatures() sets.Set[features.Feature] {
|
|||
features.HTTPRouteBackendProtocolH2CFeature,
|
||||
features.HTTPRouteBackendProtocolWebSocketFeature,
|
||||
features.HTTPRouteDestinationPortMatchingFeature,
|
||||
features.HTTPRouteBackendRequestHeaderModificationFeature,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
---
|
||||
kind: GatewayClass
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
metadata:
|
||||
name: my-gateway-class
|
||||
spec:
|
||||
controllerName: traefik.io/gateway-controller
|
||||
|
||||
---
|
||||
kind: Gateway
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
metadata:
|
||||
name: my-gateway
|
||||
namespace: default
|
||||
spec:
|
||||
gatewayClassName: my-gateway-class
|
||||
listeners:
|
||||
- name: http
|
||||
protocol: HTTP
|
||||
port: 80
|
||||
allowedRoutes:
|
||||
kinds:
|
||||
- kind: HTTPRoute
|
||||
group: gateway.networking.k8s.io
|
||||
namespaces:
|
||||
from: Same
|
||||
|
||||
---
|
||||
kind: HTTPRoute
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
metadata:
|
||||
name: http-app-1
|
||||
namespace: default
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: my-gateway
|
||||
kind: Gateway
|
||||
group: gateway.networking.k8s.io
|
||||
hostnames:
|
||||
- "foo.com"
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /bar
|
||||
backendRefs:
|
||||
- name: whoami
|
||||
port: 80
|
||||
weight: 1
|
||||
kind: Service
|
||||
group: ""
|
||||
filters:
|
||||
- type: RequestHeaderModifier
|
||||
requestHeaderModifier:
|
||||
set:
|
||||
- name: X-Foo
|
||||
value: Bar
|
||||
add:
|
||||
- name: X-Bar
|
||||
value: Foo
|
||||
remove:
|
||||
- X-Baz
|
||||
|
|
@ -140,6 +140,7 @@ func (p *Provider) loadHTTPRoute(ctx context.Context, listener gatewayListener,
|
|||
|
||||
var err error
|
||||
routerName := makeRouterName(rule, routeKey)
|
||||
// TODO loadMiddlewares errors could change the condition.
|
||||
router.Middlewares, err = p.loadMiddlewares(conf, route.Namespace, routerName, routeRule.Filters, match.Path)
|
||||
switch {
|
||||
case err != nil:
|
||||
|
|
@ -164,7 +165,7 @@ func (p *Provider) loadHTTPRoute(ctx context.Context, listener gatewayListener,
|
|||
|
||||
default:
|
||||
var serviceCondition *metav1.Condition
|
||||
router.Service, serviceCondition = p.loadWRRService(ctx, listener, conf, routerName, routeRule, route)
|
||||
router.Service, serviceCondition = p.loadWRRService(ctx, listener, conf, routerName, routeRule, route, match.Path)
|
||||
if serviceCondition != nil {
|
||||
condition = *serviceCondition
|
||||
}
|
||||
|
|
@ -179,7 +180,7 @@ func (p *Provider) loadHTTPRoute(ctx context.Context, listener gatewayListener,
|
|||
return conf, condition
|
||||
}
|
||||
|
||||
func (p *Provider) loadWRRService(ctx context.Context, listener gatewayListener, conf *dynamic.Configuration, routeKey string, routeRule gatev1.HTTPRouteRule, route *gatev1.HTTPRoute) (string, *metav1.Condition) {
|
||||
func (p *Provider) loadWRRService(ctx context.Context, listener gatewayListener, conf *dynamic.Configuration, routeKey string, routeRule gatev1.HTTPRouteRule, route *gatev1.HTTPRoute, pathMatch *gatev1.HTTPPathMatch) (string, *metav1.Condition) {
|
||||
name := routeKey + "-wrr"
|
||||
if _, ok := conf.HTTP.Services[name]; ok {
|
||||
return name, nil
|
||||
|
|
@ -188,7 +189,9 @@ func (p *Provider) loadWRRService(ctx context.Context, listener gatewayListener,
|
|||
var wrr dynamic.WeightedRoundRobin
|
||||
var condition *metav1.Condition
|
||||
for _, backendRef := range routeRule.BackendRefs {
|
||||
svcName, errCondition := p.loadService(ctx, listener, conf, route, backendRef)
|
||||
// TODO in loadService we need to always return a non-nil serviceName even when there is an error which is not the
|
||||
// usual defacto.
|
||||
svcName, errCondition := p.loadService(ctx, listener, conf, route, backendRef, pathMatch)
|
||||
weight := ptr.To(int(ptr.Deref(backendRef.Weight, 1)))
|
||||
if errCondition != nil {
|
||||
log.Ctx(ctx).Error().
|
||||
|
|
@ -215,7 +218,7 @@ func (p *Provider) loadWRRService(ctx context.Context, listener gatewayListener,
|
|||
|
||||
// loadService returns a dynamic.Service config corresponding to the given gatev1.HTTPBackendRef.
|
||||
// Note that the returned dynamic.Service config can be nil (for cross-provider, internal services, and backendFunc).
|
||||
func (p *Provider) loadService(ctx context.Context, listener gatewayListener, conf *dynamic.Configuration, route *gatev1.HTTPRoute, backendRef gatev1.HTTPBackendRef) (string, *metav1.Condition) {
|
||||
func (p *Provider) loadService(ctx context.Context, listener gatewayListener, conf *dynamic.Configuration, route *gatev1.HTTPRoute, backendRef gatev1.HTTPBackendRef, pathMatch *gatev1.HTTPPathMatch) (string, *metav1.Condition) {
|
||||
kind := ptr.Deref(backendRef.Kind, kindService)
|
||||
|
||||
group := groupCore
|
||||
|
|
@ -241,6 +244,19 @@ func (p *Provider) loadService(ctx context.Context, listener gatewayListener, co
|
|||
}
|
||||
}
|
||||
|
||||
middlewares, err := p.loadMiddlewares(conf, namespace, serviceName, backendRef.Filters, pathMatch)
|
||||
if err != nil {
|
||||
return serviceName, &metav1.Condition{
|
||||
Type: string(gatev1.RouteConditionResolvedRefs),
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: route.Generation,
|
||||
LastTransitionTime: metav1.Now(),
|
||||
Reason: string(gatev1.RouteReasonInvalidKind),
|
||||
Message: fmt.Sprintf("Cannot load filters on HTTPBackendRef %s/%s/%s/%s: %s", group, kind, namespace, backendRef.Name, err),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO may be we could incorporate this "ignored" case into the loadHTTPBackendRef.
|
||||
if group != groupCore || kind != kindService {
|
||||
name, service, err := p.loadHTTPBackendRef(namespace, backendRef)
|
||||
if err != nil {
|
||||
|
|
@ -255,6 +271,7 @@ func (p *Provider) loadService(ctx context.Context, listener gatewayListener, co
|
|||
}
|
||||
|
||||
if service != nil {
|
||||
service.Middlewares = middlewares
|
||||
conf.HTTP.Services[name] = service
|
||||
}
|
||||
|
||||
|
|
@ -286,7 +303,7 @@ func (p *Provider) loadService(ctx context.Context, listener gatewayListener, co
|
|||
conf.HTTP.ServersTransports[serviceName] = st
|
||||
}
|
||||
|
||||
conf.HTTP.Services[serviceName] = &dynamic.Service{LoadBalancer: lb}
|
||||
conf.HTTP.Services[serviceName] = &dynamic.Service{LoadBalancer: lb, Middlewares: middlewares}
|
||||
|
||||
return serviceName, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1917,6 +1917,77 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
|||
TLS: &dynamic.TLSConfiguration{},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Simple HTTPRoute, backend filter request header modifier",
|
||||
paths: []string{"services.yml", "httproute/backend_filter_request_header_modifier.yml"},
|
||||
entryPoints: map[string]Entrypoint{"web": {
|
||||
Address: ":80",
|
||||
}},
|
||||
expected: &dynamic.Configuration{
|
||||
UDP: &dynamic.UDPConfiguration{
|
||||
Routers: map[string]*dynamic.UDPRouter{},
|
||||
Services: map[string]*dynamic.UDPService{},
|
||||
},
|
||||
TCP: &dynamic.TCPConfiguration{
|
||||
Routers: map[string]*dynamic.TCPRouter{},
|
||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||
Services: map[string]*dynamic.TCPService{},
|
||||
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
||||
},
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Routers: map[string]*dynamic.Router{
|
||||
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-99f4d29346f69ccb6fc3": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-99f4d29346f69ccb6fc3-wrr",
|
||||
Rule: "Host(`foo.com`) && (Path(`/bar`) || PathPrefix(`/bar/`))",
|
||||
Priority: 10408,
|
||||
RuleSyntax: "default",
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*dynamic.Middleware{
|
||||
"default-whoami-http-requestheadermodifier-0": {
|
||||
RequestHeaderModifier: &dynamic.HeaderModifier{
|
||||
Set: map[string]string{"X-Foo": "Bar"},
|
||||
Add: map[string]string{"X-Bar": "Foo"},
|
||||
Remove: []string{"X-Baz"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Services: map[string]*dynamic.Service{
|
||||
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-99f4d29346f69ccb6fc3-wrr": {
|
||||
Weighted: &dynamic.WeightedRoundRobin{
|
||||
Services: []dynamic.WRRService{
|
||||
{
|
||||
Name: "default-whoami-http-80",
|
||||
Weight: ptr.To(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"default-whoami-http-80": {
|
||||
Middlewares: []string{"default-whoami-http-requestheadermodifier-0"},
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
Strategy: dynamic.BalancerStrategyWRR,
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://10.10.0.1:80",
|
||||
},
|
||||
{
|
||||
URL: "http://10.10.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: ptr.To(true),
|
||||
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||
FlushInterval: ptypes.Duration(100 * time.Millisecond),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ServersTransports: map[string]*dynamic.ServersTransport{},
|
||||
},
|
||||
TLS: &dynamic.TLSConfiguration{},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Simple HTTPRoute, redirect HTTP to HTTPS",
|
||||
paths: []string{"services.yml", "httproute/filter_http_to_https.yml"},
|
||||
|
|
@ -1954,6 +2025,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
|
||||
Services: map[string]*dynamic.Service{
|
||||
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-364ce6ec04c3d49b19c4-wrr": {
|
||||
Weighted: &dynamic.WeightedRoundRobin{},
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ type ingressConfig struct {
|
|||
AuthSignin *string `annotation:"nginx.ingress.kubernetes.io/auth-signin"`
|
||||
AuthResponseHeaders *string `annotation:"nginx.ingress.kubernetes.io/auth-response-headers"`
|
||||
|
||||
AuthTLSSecret *string `annotation:"nginx.ingress.kubernetes.io/auth-tls-secret"`
|
||||
AuthTLSVerifyClient *string `annotation:"nginx.ingress.kubernetes.io/auth-tls-verify-client"`
|
||||
|
||||
ForceSSLRedirect *bool `annotation:"nginx.ingress.kubernetes.io/force-ssl-redirect"`
|
||||
SSLRedirect *bool `annotation:"nginx.ingress.kubernetes.io/ssl-redirect"`
|
||||
|
||||
|
|
@ -33,6 +36,8 @@ type ingressConfig struct {
|
|||
TemporalRedirect *string `annotation:"nginx.ingress.kubernetes.io/temporal-redirect"`
|
||||
TemporalRedirectCode *int `annotation:"nginx.ingress.kubernetes.io/temporal-redirect-code"`
|
||||
|
||||
FromToWwwRedirect *bool `annotation:"nginx.ingress.kubernetes.io/from-to-www-redirect"`
|
||||
|
||||
Affinity *string `annotation:"nginx.ingress.kubernetes.io/affinity"`
|
||||
SessionCookieName *string `annotation:"nginx.ingress.kubernetes.io/session-cookie-name"`
|
||||
SessionCookieSecure *bool `annotation:"nginx.ingress.kubernetes.io/session-cookie-secure"`
|
||||
|
|
@ -46,10 +51,11 @@ type ingressConfig struct {
|
|||
|
||||
BackendProtocol *string `annotation:"nginx.ingress.kubernetes.io/backend-protocol"`
|
||||
|
||||
ProxySSLSecret *string `annotation:"nginx.ingress.kubernetes.io/proxy-ssl-secret"`
|
||||
ProxySSLVerify *string `annotation:"nginx.ingress.kubernetes.io/proxy-ssl-verify"`
|
||||
ProxySSLName *string `annotation:"nginx.ingress.kubernetes.io/proxy-ssl-name"`
|
||||
ProxySSLServerName *string `annotation:"nginx.ingress.kubernetes.io/proxy-ssl-server-name"`
|
||||
ProxySSLSecret *string `annotation:"nginx.ingress.kubernetes.io/proxy-ssl-secret"`
|
||||
ProxySSLVerify *string `annotation:"nginx.ingress.kubernetes.io/proxy-ssl-verify"`
|
||||
ProxySSLName *string `annotation:"nginx.ingress.kubernetes.io/proxy-ssl-name"`
|
||||
ProxySSLServerName *string `annotation:"nginx.ingress.kubernetes.io/proxy-ssl-server-name"`
|
||||
ProxyConnectTimeout *int `annotation:"nginx.ingress.kubernetes.io/proxy-connect-timeout"`
|
||||
|
||||
EnableCORS *bool `annotation:"nginx.ingress.kubernetes.io/enable-cors"`
|
||||
EnableCORSAllowCredentials *bool `annotation:"nginx.ingress.kubernetes.io/cors-allow-credentials"`
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ func Test_parseIngressConfig(t *testing.T) {
|
|||
"nginx.ingress.kubernetes.io/cors-expose-headers": "foo, bar",
|
||||
"nginx.ingress.kubernetes.io/auth-url": "http://auth.example.com/verify",
|
||||
"nginx.ingress.kubernetes.io/auth-signin": "https://auth.example.com/oauth2/start?rd=foo",
|
||||
"nginx.ingress.kubernetes.io/proxy-connect-timeout": "30",
|
||||
},
|
||||
expected: ingressConfig{
|
||||
SSLPassthrough: ptr.To(true),
|
||||
|
|
@ -44,6 +45,7 @@ func Test_parseIngressConfig(t *testing.T) {
|
|||
CORSExposeHeaders: ptr.To([]string{"foo", "bar"}),
|
||||
AuthURL: ptr.To("http://auth.example.com/verify"),
|
||||
AuthSignin: ptr.To("https://auth.example.com/oauth2/start?rd=foo"),
|
||||
ProxyConnectTimeout: ptr.To(30),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -60,6 +62,7 @@ func Test_parseIngressConfig(t *testing.T) {
|
|||
annotations: map[string]string{
|
||||
"nginx.ingress.kubernetes.io/ssl-passthrough": "notabool",
|
||||
"nginx.ingress.kubernetes.io/session-cookie-max-age (in seconds)": "notanint",
|
||||
"nginx.ingress.kubernetes.io/proxy-connect-timeout": "notanint",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: ingress-with-auth-tls-secret
|
||||
namespace: default
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/auth-tls-secret: "default/ca-secret"
|
||||
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
tls:
|
||||
- hosts:
|
||||
- auth-tls-secret.localhost
|
||||
- secretName: whoami-tls
|
||||
rules:
|
||||
- host: auth-tls-secret.localhost
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Exact
|
||||
backend:
|
||||
service:
|
||||
name: whoami
|
||||
port:
|
||||
number: 80
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: ingress-with-auth-tls-verify-client
|
||||
namespace: default
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/auth-tls-secret: "default/ca-secret"
|
||||
nginx.ingress.kubernetes.io/auth-tls-verify-client: "optional"
|
||||
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
tls:
|
||||
- hosts:
|
||||
- auth-tls-verify-client.localhost
|
||||
- secretName: whoami-tls
|
||||
rules:
|
||||
- host: auth-tls-verify-client.localhost
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Exact
|
||||
backend:
|
||||
service:
|
||||
name: whoami
|
||||
port:
|
||||
number: 80
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: ingress-with-host
|
||||
namespace: default
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
|
||||
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: host.localhost
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: whoami
|
||||
port:
|
||||
number: 80
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
kind: Ingress
|
||||
apiVersion: networking.k8s.io/v1
|
||||
metadata:
|
||||
name: ingress-with-proxy-timeout
|
||||
namespace: default
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-connect-timeout: "30"
|
||||
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: whoami.localhost
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Exact
|
||||
backend:
|
||||
service:
|
||||
name: whoami
|
||||
port:
|
||||
number: 80
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue