This commit is contained in:
Shi Yue 2026-01-30 13:42:34 +00:00 committed by GitHub
commit 7e5f59bbbf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 862 additions and 68 deletions

View file

@ -587,4 +587,55 @@ spec:
disableSessionTickets: true
```
### Encrypted Client Hello Keys
_Optional_
The `ECH Keys` option enables the server-side ECH feature. This option does not impact clients that do not support ECH.
The configuration file should be in PEM format and requires both a private key and an ECH configuration block.
[Reference](https://www.ietf.org/archive/id/draft-farrell-tls-pemesni-09.html)
Below is an example of the configuration file:
```text
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VuBCIEICjd4yGRdsoP9gU7YT7My8DHx1Tjme8GYDXrOMCi8v1V
-----END PRIVATE KEY-----
-----BEGIN ECHCONFIG-----
AD7+DQA65wAgACA8wVN2BtscOl3vQheUzHeIkVmKIiydUhDCliA4iyQRCwAEAAEA
AQALZXhhbXBsZS5jb20AAA==
-----END ECHCONFIG-----
```
```yaml tab="File (YAML)"
# Dynamic configuration
tls:
options:
default:
echKeys:
- example.pem
```
```toml tab="File (TOML)"
# Dynamic configuration
[tls.options]
[tls.options.default]
echKeys = ["example.pem"]
```
```yaml tab="Kubernetes"
apiVersion: traefik.io/v1alpha1
kind: TLSOption
metadata:
name: default
namespace: default
spec:
echKeys:
- example.pem
```
{% include-markdown "includes/traefik-for-business-applications.md" %}

View file

@ -598,6 +598,7 @@
sniStrict = true
alpnProtocols = ["foobar", "foobar"]
disableSessionTickets = true
echKeys = ["foobar", "foobar"]
preferServerCipherSuites = true
[tls.options.Options0.clientAuth]
caFiles = ["foobar", "foobar"]
@ -610,6 +611,7 @@
sniStrict = true
alpnProtocols = ["foobar", "foobar"]
disableSessionTickets = true
echKeys = ["foobar", "foobar"]
preferServerCipherSuites = true
[tls.options.Options1.clientAuth]
caFiles = ["foobar", "foobar"]

View file

@ -673,6 +673,9 @@ tls:
- foobar
- foobar
disableSessionTickets: true
echKeys:
- foobar
- foobar
preferServerCipherSuites: true
Options1:
minVersion: foobar
@ -693,6 +696,9 @@ tls:
- foobar
- foobar
disableSessionTickets: true
echKeys:
- foobar
- foobar
preferServerCipherSuites: true
stores:
Store0:

View file

@ -199,7 +199,6 @@ THIS FILE MUST NOT BE EDITED BY HAND
| <a id="opt-traefikhttproutersRouter0middlewares1" href="#opt-traefikhttproutersRouter0middlewares1" title="#opt-traefikhttproutersRouter0middlewares1">`traefik/http/routers/Router0/middlewares/1`</a> | `foobar` |
| <a id="opt-traefikhttproutersRouter0observabilityaccessLogs" href="#opt-traefikhttproutersRouter0observabilityaccessLogs" title="#opt-traefikhttproutersRouter0observabilityaccessLogs">`traefik/http/routers/Router0/observability/accessLogs`</a> | `true` |
| <a id="opt-traefikhttproutersRouter0observabilitymetrics" href="#opt-traefikhttproutersRouter0observabilitymetrics" title="#opt-traefikhttproutersRouter0observabilitymetrics">`traefik/http/routers/Router0/observability/metrics`</a> | `true` |
| <a id="opt-traefikhttproutersRouter0observabilitytraceVerbosity" href="#opt-traefikhttproutersRouter0observabilitytraceVerbosity" title="#opt-traefikhttproutersRouter0observabilitytraceVerbosity">`traefik/http/routers/Router0/observability/traceVerbosity`</a> | `foobar` |
| <a id="opt-traefikhttproutersRouter0observabilitytracing" href="#opt-traefikhttproutersRouter0observabilitytracing" title="#opt-traefikhttproutersRouter0observabilitytracing">`traefik/http/routers/Router0/observability/tracing`</a> | `true` |
| <a id="opt-traefikhttproutersRouter0priority" href="#opt-traefikhttproutersRouter0priority" title="#opt-traefikhttproutersRouter0priority">`traefik/http/routers/Router0/priority`</a> | `42` |
| <a id="opt-traefikhttproutersRouter0rule" href="#opt-traefikhttproutersRouter0rule" title="#opt-traefikhttproutersRouter0rule">`traefik/http/routers/Router0/rule`</a> | `foobar` |
@ -219,7 +218,6 @@ THIS FILE MUST NOT BE EDITED BY HAND
| <a id="opt-traefikhttproutersRouter1middlewares1" href="#opt-traefikhttproutersRouter1middlewares1" title="#opt-traefikhttproutersRouter1middlewares1">`traefik/http/routers/Router1/middlewares/1`</a> | `foobar` |
| <a id="opt-traefikhttproutersRouter1observabilityaccessLogs" href="#opt-traefikhttproutersRouter1observabilityaccessLogs" title="#opt-traefikhttproutersRouter1observabilityaccessLogs">`traefik/http/routers/Router1/observability/accessLogs`</a> | `true` |
| <a id="opt-traefikhttproutersRouter1observabilitymetrics" href="#opt-traefikhttproutersRouter1observabilitymetrics" title="#opt-traefikhttproutersRouter1observabilitymetrics">`traefik/http/routers/Router1/observability/metrics`</a> | `true` |
| <a id="opt-traefikhttproutersRouter1observabilitytraceVerbosity" href="#opt-traefikhttproutersRouter1observabilitytraceVerbosity" title="#opt-traefikhttproutersRouter1observabilitytraceVerbosity">`traefik/http/routers/Router1/observability/traceVerbosity`</a> | `foobar` |
| <a id="opt-traefikhttproutersRouter1observabilitytracing" href="#opt-traefikhttproutersRouter1observabilitytracing" title="#opt-traefikhttproutersRouter1observabilitytracing">`traefik/http/routers/Router1/observability/tracing`</a> | `true` |
| <a id="opt-traefikhttproutersRouter1priority" href="#opt-traefikhttproutersRouter1priority" title="#opt-traefikhttproutersRouter1priority">`traefik/http/routers/Router1/priority`</a> | `42` |
| <a id="opt-traefikhttproutersRouter1rule" href="#opt-traefikhttproutersRouter1rule" title="#opt-traefikhttproutersRouter1rule">`traefik/http/routers/Router1/rule`</a> | `foobar` |
@ -282,63 +280,56 @@ THIS FILE MUST NOT BE EDITED BY HAND
| <a id="opt-traefikhttpservicesService01failoverfallback" href="#opt-traefikhttpservicesService01failoverfallback" title="#opt-traefikhttpservicesService01failoverfallback">`traefik/http/services/Service01/failover/fallback`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService01failoverhealthCheck" href="#opt-traefikhttpservicesService01failoverhealthCheck" title="#opt-traefikhttpservicesService01failoverhealthCheck">`traefik/http/services/Service01/failover/healthCheck`</a> | `` |
| <a id="opt-traefikhttpservicesService01failoverservice" href="#opt-traefikhttpservicesService01failoverservice" title="#opt-traefikhttpservicesService01failoverservice">`traefik/http/services/Service01/failover/service`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService02highestRandomWeighthealthCheck" href="#opt-traefikhttpservicesService02highestRandomWeighthealthCheck" title="#opt-traefikhttpservicesService02highestRandomWeighthealthCheck">`traefik/http/services/Service02/highestRandomWeight/healthCheck`</a> | `` |
| <a id="opt-traefikhttpservicesService02highestRandomWeightservices0name" href="#opt-traefikhttpservicesService02highestRandomWeightservices0name" title="#opt-traefikhttpservicesService02highestRandomWeightservices0name">`traefik/http/services/Service02/highestRandomWeight/services/0/name`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService02highestRandomWeightservices0weight" href="#opt-traefikhttpservicesService02highestRandomWeightservices0weight" title="#opt-traefikhttpservicesService02highestRandomWeightservices0weight">`traefik/http/services/Service02/highestRandomWeight/services/0/weight`</a> | `42` |
| <a id="opt-traefikhttpservicesService02highestRandomWeightservices1name" href="#opt-traefikhttpservicesService02highestRandomWeightservices1name" title="#opt-traefikhttpservicesService02highestRandomWeightservices1name">`traefik/http/services/Service02/highestRandomWeight/services/1/name`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService02highestRandomWeightservices1weight" href="#opt-traefikhttpservicesService02highestRandomWeightservices1weight" title="#opt-traefikhttpservicesService02highestRandomWeightservices1weight">`traefik/http/services/Service02/highestRandomWeight/services/1/weight`</a> | `42` |
| <a id="opt-traefikhttpservicesService03loadBalancerhealthCheckfollowRedirects" href="#opt-traefikhttpservicesService03loadBalancerhealthCheckfollowRedirects" title="#opt-traefikhttpservicesService03loadBalancerhealthCheckfollowRedirects">`traefik/http/services/Service03/loadBalancer/healthCheck/followRedirects`</a> | `true` |
| <a id="opt-traefikhttpservicesService03loadBalancerhealthCheckheadersname0" href="#opt-traefikhttpservicesService03loadBalancerhealthCheckheadersname0" title="#opt-traefikhttpservicesService03loadBalancerhealthCheckheadersname0">`traefik/http/services/Service03/loadBalancer/healthCheck/headers/name0`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService03loadBalancerhealthCheckheadersname1" href="#opt-traefikhttpservicesService03loadBalancerhealthCheckheadersname1" title="#opt-traefikhttpservicesService03loadBalancerhealthCheckheadersname1">`traefik/http/services/Service03/loadBalancer/healthCheck/headers/name1`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService03loadBalancerhealthCheckhostname" href="#opt-traefikhttpservicesService03loadBalancerhealthCheckhostname" title="#opt-traefikhttpservicesService03loadBalancerhealthCheckhostname">`traefik/http/services/Service03/loadBalancer/healthCheck/hostname`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService03loadBalancerhealthCheckinterval" href="#opt-traefikhttpservicesService03loadBalancerhealthCheckinterval" title="#opt-traefikhttpservicesService03loadBalancerhealthCheckinterval">`traefik/http/services/Service03/loadBalancer/healthCheck/interval`</a> | `42s` |
| <a id="opt-traefikhttpservicesService03loadBalancerhealthCheckmethod" href="#opt-traefikhttpservicesService03loadBalancerhealthCheckmethod" title="#opt-traefikhttpservicesService03loadBalancerhealthCheckmethod">`traefik/http/services/Service03/loadBalancer/healthCheck/method`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService03loadBalancerhealthCheckmode" href="#opt-traefikhttpservicesService03loadBalancerhealthCheckmode" title="#opt-traefikhttpservicesService03loadBalancerhealthCheckmode">`traefik/http/services/Service03/loadBalancer/healthCheck/mode`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService03loadBalancerhealthCheckpath" href="#opt-traefikhttpservicesService03loadBalancerhealthCheckpath" title="#opt-traefikhttpservicesService03loadBalancerhealthCheckpath">`traefik/http/services/Service03/loadBalancer/healthCheck/path`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService03loadBalancerhealthCheckport" href="#opt-traefikhttpservicesService03loadBalancerhealthCheckport" title="#opt-traefikhttpservicesService03loadBalancerhealthCheckport">`traefik/http/services/Service03/loadBalancer/healthCheck/port`</a> | `42` |
| <a id="opt-traefikhttpservicesService03loadBalancerhealthCheckscheme" href="#opt-traefikhttpservicesService03loadBalancerhealthCheckscheme" title="#opt-traefikhttpservicesService03loadBalancerhealthCheckscheme">`traefik/http/services/Service03/loadBalancer/healthCheck/scheme`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService03loadBalancerhealthCheckstatus" href="#opt-traefikhttpservicesService03loadBalancerhealthCheckstatus" title="#opt-traefikhttpservicesService03loadBalancerhealthCheckstatus">`traefik/http/services/Service03/loadBalancer/healthCheck/status`</a> | `42` |
| <a id="opt-traefikhttpservicesService03loadBalancerhealthChecktimeout" href="#opt-traefikhttpservicesService03loadBalancerhealthChecktimeout" title="#opt-traefikhttpservicesService03loadBalancerhealthChecktimeout">`traefik/http/services/Service03/loadBalancer/healthCheck/timeout`</a> | `42s` |
| <a id="opt-traefikhttpservicesService03loadBalancerhealthCheckunhealthyInterval" href="#opt-traefikhttpservicesService03loadBalancerhealthCheckunhealthyInterval" title="#opt-traefikhttpservicesService03loadBalancerhealthCheckunhealthyInterval">`traefik/http/services/Service03/loadBalancer/healthCheck/unhealthyInterval`</a> | `42s` |
| <a id="opt-traefikhttpservicesService03loadBalancerpassHostHeader" href="#opt-traefikhttpservicesService03loadBalancerpassHostHeader" title="#opt-traefikhttpservicesService03loadBalancerpassHostHeader">`traefik/http/services/Service03/loadBalancer/passHostHeader`</a> | `true` |
| <a id="opt-traefikhttpservicesService03loadBalancerpassiveHealthCheckfailureWindow" href="#opt-traefikhttpservicesService03loadBalancerpassiveHealthCheckfailureWindow" title="#opt-traefikhttpservicesService03loadBalancerpassiveHealthCheckfailureWindow">`traefik/http/services/Service03/loadBalancer/passiveHealthCheck/failureWindow`</a> | `42s` |
| <a id="opt-traefikhttpservicesService03loadBalancerpassiveHealthCheckmaxFailedAttempts" href="#opt-traefikhttpservicesService03loadBalancerpassiveHealthCheckmaxFailedAttempts" title="#opt-traefikhttpservicesService03loadBalancerpassiveHealthCheckmaxFailedAttempts">`traefik/http/services/Service03/loadBalancer/passiveHealthCheck/maxFailedAttempts`</a> | `42` |
| <a id="opt-traefikhttpservicesService03loadBalancerresponseForwardingflushInterval" href="#opt-traefikhttpservicesService03loadBalancerresponseForwardingflushInterval" title="#opt-traefikhttpservicesService03loadBalancerresponseForwardingflushInterval">`traefik/http/services/Service03/loadBalancer/responseForwarding/flushInterval`</a> | `42s` |
| <a id="opt-traefikhttpservicesService03loadBalancerservers0preservePath" href="#opt-traefikhttpservicesService03loadBalancerservers0preservePath" title="#opt-traefikhttpservicesService03loadBalancerservers0preservePath">`traefik/http/services/Service03/loadBalancer/servers/0/preservePath`</a> | `true` |
| <a id="opt-traefikhttpservicesService03loadBalancerservers0url" href="#opt-traefikhttpservicesService03loadBalancerservers0url" title="#opt-traefikhttpservicesService03loadBalancerservers0url">`traefik/http/services/Service03/loadBalancer/servers/0/url`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService03loadBalancerservers0weight" href="#opt-traefikhttpservicesService03loadBalancerservers0weight" title="#opt-traefikhttpservicesService03loadBalancerservers0weight">`traefik/http/services/Service03/loadBalancer/servers/0/weight`</a> | `42` |
| <a id="opt-traefikhttpservicesService03loadBalancerservers1preservePath" href="#opt-traefikhttpservicesService03loadBalancerservers1preservePath" title="#opt-traefikhttpservicesService03loadBalancerservers1preservePath">`traefik/http/services/Service03/loadBalancer/servers/1/preservePath`</a> | `true` |
| <a id="opt-traefikhttpservicesService03loadBalancerservers1url" href="#opt-traefikhttpservicesService03loadBalancerservers1url" title="#opt-traefikhttpservicesService03loadBalancerservers1url">`traefik/http/services/Service03/loadBalancer/servers/1/url`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService03loadBalancerservers1weight" href="#opt-traefikhttpservicesService03loadBalancerservers1weight" title="#opt-traefikhttpservicesService03loadBalancerservers1weight">`traefik/http/services/Service03/loadBalancer/servers/1/weight`</a> | `42` |
| <a id="opt-traefikhttpservicesService03loadBalancerserversTransport" href="#opt-traefikhttpservicesService03loadBalancerserversTransport" title="#opt-traefikhttpservicesService03loadBalancerserversTransport">`traefik/http/services/Service03/loadBalancer/serversTransport`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService03loadBalancerstickycookiedomain" href="#opt-traefikhttpservicesService03loadBalancerstickycookiedomain" title="#opt-traefikhttpservicesService03loadBalancerstickycookiedomain">`traefik/http/services/Service03/loadBalancer/sticky/cookie/domain`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService03loadBalancerstickycookiehttpOnly" href="#opt-traefikhttpservicesService03loadBalancerstickycookiehttpOnly" title="#opt-traefikhttpservicesService03loadBalancerstickycookiehttpOnly">`traefik/http/services/Service03/loadBalancer/sticky/cookie/httpOnly`</a> | `true` |
| <a id="opt-traefikhttpservicesService03loadBalancerstickycookiemaxAge" href="#opt-traefikhttpservicesService03loadBalancerstickycookiemaxAge" title="#opt-traefikhttpservicesService03loadBalancerstickycookiemaxAge">`traefik/http/services/Service03/loadBalancer/sticky/cookie/maxAge`</a> | `42` |
| <a id="opt-traefikhttpservicesService03loadBalancerstickycookiename" href="#opt-traefikhttpservicesService03loadBalancerstickycookiename" title="#opt-traefikhttpservicesService03loadBalancerstickycookiename">`traefik/http/services/Service03/loadBalancer/sticky/cookie/name`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService03loadBalancerstickycookiepath" href="#opt-traefikhttpservicesService03loadBalancerstickycookiepath" title="#opt-traefikhttpservicesService03loadBalancerstickycookiepath">`traefik/http/services/Service03/loadBalancer/sticky/cookie/path`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService03loadBalancerstickycookiesameSite" href="#opt-traefikhttpservicesService03loadBalancerstickycookiesameSite" title="#opt-traefikhttpservicesService03loadBalancerstickycookiesameSite">`traefik/http/services/Service03/loadBalancer/sticky/cookie/sameSite`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService03loadBalancerstickycookiesecure" href="#opt-traefikhttpservicesService03loadBalancerstickycookiesecure" title="#opt-traefikhttpservicesService03loadBalancerstickycookiesecure">`traefik/http/services/Service03/loadBalancer/sticky/cookie/secure`</a> | `true` |
| <a id="opt-traefikhttpservicesService03loadBalancerstrategy" href="#opt-traefikhttpservicesService03loadBalancerstrategy" title="#opt-traefikhttpservicesService03loadBalancerstrategy">`traefik/http/services/Service03/loadBalancer/strategy`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService04mirroringhealthCheck" href="#opt-traefikhttpservicesService04mirroringhealthCheck" title="#opt-traefikhttpservicesService04mirroringhealthCheck">`traefik/http/services/Service04/mirroring/healthCheck`</a> | `` |
| <a id="opt-traefikhttpservicesService04mirroringmaxBodySize" href="#opt-traefikhttpservicesService04mirroringmaxBodySize" title="#opt-traefikhttpservicesService04mirroringmaxBodySize">`traefik/http/services/Service04/mirroring/maxBodySize`</a> | `42` |
| <a id="opt-traefikhttpservicesService04mirroringmirrorBody" href="#opt-traefikhttpservicesService04mirroringmirrorBody" title="#opt-traefikhttpservicesService04mirroringmirrorBody">`traefik/http/services/Service04/mirroring/mirrorBody`</a> | `true` |
| <a id="opt-traefikhttpservicesService04mirroringmirrors0name" href="#opt-traefikhttpservicesService04mirroringmirrors0name" title="#opt-traefikhttpservicesService04mirroringmirrors0name">`traefik/http/services/Service04/mirroring/mirrors/0/name`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService04mirroringmirrors0percent" href="#opt-traefikhttpservicesService04mirroringmirrors0percent" title="#opt-traefikhttpservicesService04mirroringmirrors0percent">`traefik/http/services/Service04/mirroring/mirrors/0/percent`</a> | `42` |
| <a id="opt-traefikhttpservicesService04mirroringmirrors1name" href="#opt-traefikhttpservicesService04mirroringmirrors1name" title="#opt-traefikhttpservicesService04mirroringmirrors1name">`traefik/http/services/Service04/mirroring/mirrors/1/name`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService04mirroringmirrors1percent" href="#opt-traefikhttpservicesService04mirroringmirrors1percent" title="#opt-traefikhttpservicesService04mirroringmirrors1percent">`traefik/http/services/Service04/mirroring/mirrors/1/percent`</a> | `42` |
| <a id="opt-traefikhttpservicesService04mirroringservice" href="#opt-traefikhttpservicesService04mirroringservice" title="#opt-traefikhttpservicesService04mirroringservice">`traefik/http/services/Service04/mirroring/service`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService05weightedhealthCheck" href="#opt-traefikhttpservicesService05weightedhealthCheck" title="#opt-traefikhttpservicesService05weightedhealthCheck">`traefik/http/services/Service05/weighted/healthCheck`</a> | `` |
| <a id="opt-traefikhttpservicesService05weightedservices0name" href="#opt-traefikhttpservicesService05weightedservices0name" title="#opt-traefikhttpservicesService05weightedservices0name">`traefik/http/services/Service05/weighted/services/0/name`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService05weightedservices0weight" href="#opt-traefikhttpservicesService05weightedservices0weight" title="#opt-traefikhttpservicesService05weightedservices0weight">`traefik/http/services/Service05/weighted/services/0/weight`</a> | `42` |
| <a id="opt-traefikhttpservicesService05weightedservices1name" href="#opt-traefikhttpservicesService05weightedservices1name" title="#opt-traefikhttpservicesService05weightedservices1name">`traefik/http/services/Service05/weighted/services/1/name`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService05weightedservices1weight" href="#opt-traefikhttpservicesService05weightedservices1weight" title="#opt-traefikhttpservicesService05weightedservices1weight">`traefik/http/services/Service05/weighted/services/1/weight`</a> | `42` |
| <a id="opt-traefikhttpservicesService05weightedstickycookiedomain" href="#opt-traefikhttpservicesService05weightedstickycookiedomain" title="#opt-traefikhttpservicesService05weightedstickycookiedomain">`traefik/http/services/Service05/weighted/sticky/cookie/domain`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService05weightedstickycookiehttpOnly" href="#opt-traefikhttpservicesService05weightedstickycookiehttpOnly" title="#opt-traefikhttpservicesService05weightedstickycookiehttpOnly">`traefik/http/services/Service05/weighted/sticky/cookie/httpOnly`</a> | `true` |
| <a id="opt-traefikhttpservicesService05weightedstickycookiemaxAge" href="#opt-traefikhttpservicesService05weightedstickycookiemaxAge" title="#opt-traefikhttpservicesService05weightedstickycookiemaxAge">`traefik/http/services/Service05/weighted/sticky/cookie/maxAge`</a> | `42` |
| <a id="opt-traefikhttpservicesService05weightedstickycookiename" href="#opt-traefikhttpservicesService05weightedstickycookiename" title="#opt-traefikhttpservicesService05weightedstickycookiename">`traefik/http/services/Service05/weighted/sticky/cookie/name`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService05weightedstickycookiepath" href="#opt-traefikhttpservicesService05weightedstickycookiepath" title="#opt-traefikhttpservicesService05weightedstickycookiepath">`traefik/http/services/Service05/weighted/sticky/cookie/path`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService05weightedstickycookiesameSite" href="#opt-traefikhttpservicesService05weightedstickycookiesameSite" title="#opt-traefikhttpservicesService05weightedstickycookiesameSite">`traefik/http/services/Service05/weighted/sticky/cookie/sameSite`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService05weightedstickycookiesecure" href="#opt-traefikhttpservicesService05weightedstickycookiesecure" title="#opt-traefikhttpservicesService05weightedstickycookiesecure">`traefik/http/services/Service05/weighted/sticky/cookie/secure`</a> | `true` |
| <a id="opt-traefikhttpservicesService02loadBalancerhealthCheckfollowRedirects" href="#opt-traefikhttpservicesService02loadBalancerhealthCheckfollowRedirects" title="#opt-traefikhttpservicesService02loadBalancerhealthCheckfollowRedirects">`traefik/http/services/Service02/loadBalancer/healthCheck/followRedirects`</a> | `true` |
| <a id="opt-traefikhttpservicesService02loadBalancerhealthCheckheadersname0" href="#opt-traefikhttpservicesService02loadBalancerhealthCheckheadersname0" title="#opt-traefikhttpservicesService02loadBalancerhealthCheckheadersname0">`traefik/http/services/Service02/loadBalancer/healthCheck/headers/name0`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService02loadBalancerhealthCheckheadersname1" href="#opt-traefikhttpservicesService02loadBalancerhealthCheckheadersname1" title="#opt-traefikhttpservicesService02loadBalancerhealthCheckheadersname1">`traefik/http/services/Service02/loadBalancer/healthCheck/headers/name1`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService02loadBalancerhealthCheckhostname" href="#opt-traefikhttpservicesService02loadBalancerhealthCheckhostname" title="#opt-traefikhttpservicesService02loadBalancerhealthCheckhostname">`traefik/http/services/Service02/loadBalancer/healthCheck/hostname`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService02loadBalancerhealthCheckinterval" href="#opt-traefikhttpservicesService02loadBalancerhealthCheckinterval" title="#opt-traefikhttpservicesService02loadBalancerhealthCheckinterval">`traefik/http/services/Service02/loadBalancer/healthCheck/interval`</a> | `42s` |
| <a id="opt-traefikhttpservicesService02loadBalancerhealthCheckmethod" href="#opt-traefikhttpservicesService02loadBalancerhealthCheckmethod" title="#opt-traefikhttpservicesService02loadBalancerhealthCheckmethod">`traefik/http/services/Service02/loadBalancer/healthCheck/method`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService02loadBalancerhealthCheckmode" href="#opt-traefikhttpservicesService02loadBalancerhealthCheckmode" title="#opt-traefikhttpservicesService02loadBalancerhealthCheckmode">`traefik/http/services/Service02/loadBalancer/healthCheck/mode`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService02loadBalancerhealthCheckpath" href="#opt-traefikhttpservicesService02loadBalancerhealthCheckpath" title="#opt-traefikhttpservicesService02loadBalancerhealthCheckpath">`traefik/http/services/Service02/loadBalancer/healthCheck/path`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService02loadBalancerhealthCheckport" href="#opt-traefikhttpservicesService02loadBalancerhealthCheckport" title="#opt-traefikhttpservicesService02loadBalancerhealthCheckport">`traefik/http/services/Service02/loadBalancer/healthCheck/port`</a> | `42` |
| <a id="opt-traefikhttpservicesService02loadBalancerhealthCheckscheme" href="#opt-traefikhttpservicesService02loadBalancerhealthCheckscheme" title="#opt-traefikhttpservicesService02loadBalancerhealthCheckscheme">`traefik/http/services/Service02/loadBalancer/healthCheck/scheme`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService02loadBalancerhealthCheckstatus" href="#opt-traefikhttpservicesService02loadBalancerhealthCheckstatus" title="#opt-traefikhttpservicesService02loadBalancerhealthCheckstatus">`traefik/http/services/Service02/loadBalancer/healthCheck/status`</a> | `42` |
| <a id="opt-traefikhttpservicesService02loadBalancerhealthChecktimeout" href="#opt-traefikhttpservicesService02loadBalancerhealthChecktimeout" title="#opt-traefikhttpservicesService02loadBalancerhealthChecktimeout">`traefik/http/services/Service02/loadBalancer/healthCheck/timeout`</a> | `42s` |
| <a id="opt-traefikhttpservicesService02loadBalancerhealthCheckunhealthyInterval" href="#opt-traefikhttpservicesService02loadBalancerhealthCheckunhealthyInterval" title="#opt-traefikhttpservicesService02loadBalancerhealthCheckunhealthyInterval">`traefik/http/services/Service02/loadBalancer/healthCheck/unhealthyInterval`</a> | `42s` |
| <a id="opt-traefikhttpservicesService02loadBalancerpassHostHeader" href="#opt-traefikhttpservicesService02loadBalancerpassHostHeader" title="#opt-traefikhttpservicesService02loadBalancerpassHostHeader">`traefik/http/services/Service02/loadBalancer/passHostHeader`</a> | `true` |
| <a id="opt-traefikhttpservicesService02loadBalancerresponseForwardingflushInterval" href="#opt-traefikhttpservicesService02loadBalancerresponseForwardingflushInterval" title="#opt-traefikhttpservicesService02loadBalancerresponseForwardingflushInterval">`traefik/http/services/Service02/loadBalancer/responseForwarding/flushInterval`</a> | `42s` |
| <a id="opt-traefikhttpservicesService02loadBalancerservers0preservePath" href="#opt-traefikhttpservicesService02loadBalancerservers0preservePath" title="#opt-traefikhttpservicesService02loadBalancerservers0preservePath">`traefik/http/services/Service02/loadBalancer/servers/0/preservePath`</a> | `true` |
| <a id="opt-traefikhttpservicesService02loadBalancerservers0url" href="#opt-traefikhttpservicesService02loadBalancerservers0url" title="#opt-traefikhttpservicesService02loadBalancerservers0url">`traefik/http/services/Service02/loadBalancer/servers/0/url`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService02loadBalancerservers0weight" href="#opt-traefikhttpservicesService02loadBalancerservers0weight" title="#opt-traefikhttpservicesService02loadBalancerservers0weight">`traefik/http/services/Service02/loadBalancer/servers/0/weight`</a> | `42` |
| <a id="opt-traefikhttpservicesService02loadBalancerservers1preservePath" href="#opt-traefikhttpservicesService02loadBalancerservers1preservePath" title="#opt-traefikhttpservicesService02loadBalancerservers1preservePath">`traefik/http/services/Service02/loadBalancer/servers/1/preservePath`</a> | `true` |
| <a id="opt-traefikhttpservicesService02loadBalancerservers1url" href="#opt-traefikhttpservicesService02loadBalancerservers1url" title="#opt-traefikhttpservicesService02loadBalancerservers1url">`traefik/http/services/Service02/loadBalancer/servers/1/url`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService02loadBalancerservers1weight" href="#opt-traefikhttpservicesService02loadBalancerservers1weight" title="#opt-traefikhttpservicesService02loadBalancerservers1weight">`traefik/http/services/Service02/loadBalancer/servers/1/weight`</a> | `42` |
| <a id="opt-traefikhttpservicesService02loadBalancerserversTransport" href="#opt-traefikhttpservicesService02loadBalancerserversTransport" title="#opt-traefikhttpservicesService02loadBalancerserversTransport">`traefik/http/services/Service02/loadBalancer/serversTransport`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService02loadBalancerstickycookiedomain" href="#opt-traefikhttpservicesService02loadBalancerstickycookiedomain" title="#opt-traefikhttpservicesService02loadBalancerstickycookiedomain">`traefik/http/services/Service02/loadBalancer/sticky/cookie/domain`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService02loadBalancerstickycookiehttpOnly" href="#opt-traefikhttpservicesService02loadBalancerstickycookiehttpOnly" title="#opt-traefikhttpservicesService02loadBalancerstickycookiehttpOnly">`traefik/http/services/Service02/loadBalancer/sticky/cookie/httpOnly`</a> | `true` |
| <a id="opt-traefikhttpservicesService02loadBalancerstickycookiemaxAge" href="#opt-traefikhttpservicesService02loadBalancerstickycookiemaxAge" title="#opt-traefikhttpservicesService02loadBalancerstickycookiemaxAge">`traefik/http/services/Service02/loadBalancer/sticky/cookie/maxAge`</a> | `42` |
| <a id="opt-traefikhttpservicesService02loadBalancerstickycookiename" href="#opt-traefikhttpservicesService02loadBalancerstickycookiename" title="#opt-traefikhttpservicesService02loadBalancerstickycookiename">`traefik/http/services/Service02/loadBalancer/sticky/cookie/name`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService02loadBalancerstickycookiepath" href="#opt-traefikhttpservicesService02loadBalancerstickycookiepath" title="#opt-traefikhttpservicesService02loadBalancerstickycookiepath">`traefik/http/services/Service02/loadBalancer/sticky/cookie/path`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService02loadBalancerstickycookiesameSite" href="#opt-traefikhttpservicesService02loadBalancerstickycookiesameSite" title="#opt-traefikhttpservicesService02loadBalancerstickycookiesameSite">`traefik/http/services/Service02/loadBalancer/sticky/cookie/sameSite`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService02loadBalancerstickycookiesecure" href="#opt-traefikhttpservicesService02loadBalancerstickycookiesecure" title="#opt-traefikhttpservicesService02loadBalancerstickycookiesecure">`traefik/http/services/Service02/loadBalancer/sticky/cookie/secure`</a> | `true` |
| <a id="opt-traefikhttpservicesService02loadBalancerstrategy" href="#opt-traefikhttpservicesService02loadBalancerstrategy" title="#opt-traefikhttpservicesService02loadBalancerstrategy">`traefik/http/services/Service02/loadBalancer/strategy`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService03mirroringhealthCheck" href="#opt-traefikhttpservicesService03mirroringhealthCheck" title="#opt-traefikhttpservicesService03mirroringhealthCheck">`traefik/http/services/Service03/mirroring/healthCheck`</a> | `` |
| <a id="opt-traefikhttpservicesService03mirroringmaxBodySize" href="#opt-traefikhttpservicesService03mirroringmaxBodySize" title="#opt-traefikhttpservicesService03mirroringmaxBodySize">`traefik/http/services/Service03/mirroring/maxBodySize`</a> | `42` |
| <a id="opt-traefikhttpservicesService03mirroringmirrorBody" href="#opt-traefikhttpservicesService03mirroringmirrorBody" title="#opt-traefikhttpservicesService03mirroringmirrorBody">`traefik/http/services/Service03/mirroring/mirrorBody`</a> | `true` |
| <a id="opt-traefikhttpservicesService03mirroringmirrors0name" href="#opt-traefikhttpservicesService03mirroringmirrors0name" title="#opt-traefikhttpservicesService03mirroringmirrors0name">`traefik/http/services/Service03/mirroring/mirrors/0/name`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService03mirroringmirrors0percent" href="#opt-traefikhttpservicesService03mirroringmirrors0percent" title="#opt-traefikhttpservicesService03mirroringmirrors0percent">`traefik/http/services/Service03/mirroring/mirrors/0/percent`</a> | `42` |
| <a id="opt-traefikhttpservicesService03mirroringmirrors1name" href="#opt-traefikhttpservicesService03mirroringmirrors1name" title="#opt-traefikhttpservicesService03mirroringmirrors1name">`traefik/http/services/Service03/mirroring/mirrors/1/name`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService03mirroringmirrors1percent" href="#opt-traefikhttpservicesService03mirroringmirrors1percent" title="#opt-traefikhttpservicesService03mirroringmirrors1percent">`traefik/http/services/Service03/mirroring/mirrors/1/percent`</a> | `42` |
| <a id="opt-traefikhttpservicesService03mirroringservice" href="#opt-traefikhttpservicesService03mirroringservice" title="#opt-traefikhttpservicesService03mirroringservice">`traefik/http/services/Service03/mirroring/service`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService04weightedhealthCheck" href="#opt-traefikhttpservicesService04weightedhealthCheck" title="#opt-traefikhttpservicesService04weightedhealthCheck">`traefik/http/services/Service04/weighted/healthCheck`</a> | `` |
| <a id="opt-traefikhttpservicesService04weightedservices0name" href="#opt-traefikhttpservicesService04weightedservices0name" title="#opt-traefikhttpservicesService04weightedservices0name">`traefik/http/services/Service04/weighted/services/0/name`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService04weightedservices0weight" href="#opt-traefikhttpservicesService04weightedservices0weight" title="#opt-traefikhttpservicesService04weightedservices0weight">`traefik/http/services/Service04/weighted/services/0/weight`</a> | `42` |
| <a id="opt-traefikhttpservicesService04weightedservices1name" href="#opt-traefikhttpservicesService04weightedservices1name" title="#opt-traefikhttpservicesService04weightedservices1name">`traefik/http/services/Service04/weighted/services/1/name`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService04weightedservices1weight" href="#opt-traefikhttpservicesService04weightedservices1weight" title="#opt-traefikhttpservicesService04weightedservices1weight">`traefik/http/services/Service04/weighted/services/1/weight`</a> | `42` |
| <a id="opt-traefikhttpservicesService04weightedstickycookiedomain" href="#opt-traefikhttpservicesService04weightedstickycookiedomain" title="#opt-traefikhttpservicesService04weightedstickycookiedomain">`traefik/http/services/Service04/weighted/sticky/cookie/domain`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService04weightedstickycookiehttpOnly" href="#opt-traefikhttpservicesService04weightedstickycookiehttpOnly" title="#opt-traefikhttpservicesService04weightedstickycookiehttpOnly">`traefik/http/services/Service04/weighted/sticky/cookie/httpOnly`</a> | `true` |
| <a id="opt-traefikhttpservicesService04weightedstickycookiemaxAge" href="#opt-traefikhttpservicesService04weightedstickycookiemaxAge" title="#opt-traefikhttpservicesService04weightedstickycookiemaxAge">`traefik/http/services/Service04/weighted/sticky/cookie/maxAge`</a> | `42` |
| <a id="opt-traefikhttpservicesService04weightedstickycookiename" href="#opt-traefikhttpservicesService04weightedstickycookiename" title="#opt-traefikhttpservicesService04weightedstickycookiename">`traefik/http/services/Service04/weighted/sticky/cookie/name`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService04weightedstickycookiepath" href="#opt-traefikhttpservicesService04weightedstickycookiepath" title="#opt-traefikhttpservicesService04weightedstickycookiepath">`traefik/http/services/Service04/weighted/sticky/cookie/path`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService04weightedstickycookiesameSite" href="#opt-traefikhttpservicesService04weightedstickycookiesameSite" title="#opt-traefikhttpservicesService04weightedstickycookiesameSite">`traefik/http/services/Service04/weighted/sticky/cookie/sameSite`</a> | `foobar` |
| <a id="opt-traefikhttpservicesService04weightedstickycookiesecure" href="#opt-traefikhttpservicesService04weightedstickycookiesecure" title="#opt-traefikhttpservicesService04weightedstickycookiesecure">`traefik/http/services/Service04/weighted/sticky/cookie/secure`</a> | `true` |
| <a id="opt-traefiktcpmiddlewaresTCPMiddleware01ipAllowListsourceRange0" href="#opt-traefiktcpmiddlewaresTCPMiddleware01ipAllowListsourceRange0" title="#opt-traefiktcpmiddlewaresTCPMiddleware01ipAllowListsourceRange0">`traefik/tcp/middlewares/TCPMiddleware01/ipAllowList/sourceRange/0`</a> | `foobar` |
| <a id="opt-traefiktcpmiddlewaresTCPMiddleware01ipAllowListsourceRange1" href="#opt-traefiktcpmiddlewaresTCPMiddleware01ipAllowListsourceRange1" title="#opt-traefiktcpmiddlewaresTCPMiddleware01ipAllowListsourceRange1">`traefik/tcp/middlewares/TCPMiddleware01/ipAllowList/sourceRange/1`</a> | `foobar` |
| <a id="opt-traefiktcpmiddlewaresTCPMiddleware02ipWhiteListsourceRange0" href="#opt-traefiktcpmiddlewaresTCPMiddleware02ipWhiteListsourceRange0" title="#opt-traefiktcpmiddlewaresTCPMiddleware02ipWhiteListsourceRange0">`traefik/tcp/middlewares/TCPMiddleware02/ipWhiteList/sourceRange/0`</a> | `foobar` |
@ -437,6 +428,8 @@ THIS FILE MUST NOT BE EDITED BY HAND
| <a id="opt-traefiktlsoptionsOptions0curvePreferences0" href="#opt-traefiktlsoptionsOptions0curvePreferences0" title="#opt-traefiktlsoptionsOptions0curvePreferences0">`traefik/tls/options/Options0/curvePreferences/0`</a> | `foobar` |
| <a id="opt-traefiktlsoptionsOptions0curvePreferences1" href="#opt-traefiktlsoptionsOptions0curvePreferences1" title="#opt-traefiktlsoptionsOptions0curvePreferences1">`traefik/tls/options/Options0/curvePreferences/1`</a> | `foobar` |
| <a id="opt-traefiktlsoptionsOptions0disableSessionTickets" href="#opt-traefiktlsoptionsOptions0disableSessionTickets" title="#opt-traefiktlsoptionsOptions0disableSessionTickets">`traefik/tls/options/Options0/disableSessionTickets`</a> | `true` |
| <a id="opt-traefiktlsoptionsOptions0echKeys0" href="#opt-traefiktlsoptionsOptions0echKeys0" title="#opt-traefiktlsoptionsOptions0echKeys0">`traefik/tls/options/Options0/echKeys/0`</a> | `foobar` |
| <a id="opt-traefiktlsoptionsOptions0echKeys1" href="#opt-traefiktlsoptionsOptions0echKeys1" title="#opt-traefiktlsoptionsOptions0echKeys1">`traefik/tls/options/Options0/echKeys/1`</a> | `foobar` |
| <a id="opt-traefiktlsoptionsOptions0maxVersion" href="#opt-traefiktlsoptionsOptions0maxVersion" title="#opt-traefiktlsoptionsOptions0maxVersion">`traefik/tls/options/Options0/maxVersion`</a> | `foobar` |
| <a id="opt-traefiktlsoptionsOptions0minVersion" href="#opt-traefiktlsoptionsOptions0minVersion" title="#opt-traefiktlsoptionsOptions0minVersion">`traefik/tls/options/Options0/minVersion`</a> | `foobar` |
| <a id="opt-traefiktlsoptionsOptions0preferServerCipherSuites" href="#opt-traefiktlsoptionsOptions0preferServerCipherSuites" title="#opt-traefiktlsoptionsOptions0preferServerCipherSuites">`traefik/tls/options/Options0/preferServerCipherSuites`</a> | `true` |
@ -451,6 +444,8 @@ THIS FILE MUST NOT BE EDITED BY HAND
| <a id="opt-traefiktlsoptionsOptions1curvePreferences0" href="#opt-traefiktlsoptionsOptions1curvePreferences0" title="#opt-traefiktlsoptionsOptions1curvePreferences0">`traefik/tls/options/Options1/curvePreferences/0`</a> | `foobar` |
| <a id="opt-traefiktlsoptionsOptions1curvePreferences1" href="#opt-traefiktlsoptionsOptions1curvePreferences1" title="#opt-traefiktlsoptionsOptions1curvePreferences1">`traefik/tls/options/Options1/curvePreferences/1`</a> | `foobar` |
| <a id="opt-traefiktlsoptionsOptions1disableSessionTickets" href="#opt-traefiktlsoptionsOptions1disableSessionTickets" title="#opt-traefiktlsoptionsOptions1disableSessionTickets">`traefik/tls/options/Options1/disableSessionTickets`</a> | `true` |
| <a id="opt-traefiktlsoptionsOptions1echKeys0" href="#opt-traefiktlsoptionsOptions1echKeys0" title="#opt-traefiktlsoptionsOptions1echKeys0">`traefik/tls/options/Options1/echKeys/0`</a> | `foobar` |
| <a id="opt-traefiktlsoptionsOptions1echKeys1" href="#opt-traefiktlsoptionsOptions1echKeys1" title="#opt-traefiktlsoptionsOptions1echKeys1">`traefik/tls/options/Options1/echKeys/1`</a> | `foobar` |
| <a id="opt-traefiktlsoptionsOptions1maxVersion" href="#opt-traefiktlsoptionsOptions1maxVersion" title="#opt-traefiktlsoptionsOptions1maxVersion">`traefik/tls/options/Options1/maxVersion`</a> | `foobar` |
| <a id="opt-traefiktlsoptionsOptions1minVersion" href="#opt-traefiktlsoptionsOptions1minVersion" title="#opt-traefiktlsoptionsOptions1minVersion">`traefik/tls/options/Options1/minVersion`</a> | `foobar` |
| <a id="opt-traefiktlsoptionsOptions1preferServerCipherSuites" href="#opt-traefiktlsoptionsOptions1preferServerCipherSuites" title="#opt-traefiktlsoptionsOptions1preferServerCipherSuites">`traefik/tls/options/Options1/preferServerCipherSuites`</a> | `true` |

View file

@ -620,6 +620,7 @@
sniStrict = true
alpnProtocols = ["foobar", "foobar"]
disableSessionTickets = true
echKeys = ["foobar", "foobar"]
preferServerCipherSuites = true
[tls.options.Options0.clientAuth]
caFiles = ["foobar", "foobar"]
@ -632,6 +633,7 @@
sniStrict = true
alpnProtocols = ["foobar", "foobar"]
disableSessionTickets = true
echKeys = ["foobar", "foobar"]
preferServerCipherSuites = true
[tls.options.Options1.clientAuth]
caFiles = ["foobar", "foobar"]

View file

@ -701,6 +701,9 @@ tls:
- foobar
- foobar
disableSessionTickets: true
echKeys:
- foobar
- foobar
preferServerCipherSuites: true
Options1:
minVersion: foobar
@ -721,6 +724,9 @@ tls:
- foobar
- foobar
disableSessionTickets: true
echKeys:
- foobar
- foobar
preferServerCipherSuites: true
stores:
Store0:

3
go.mod
View file

@ -16,6 +16,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/ssm v1.56.13
github.com/aws/smithy-go v1.24.0
github.com/cenkalti/backoff/v4 v4.3.0
github.com/cloudflare/circl v1.6.2
github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd // No tag on the repo.
github.com/coreos/go-systemd/v22 v22.5.0
github.com/docker/cli v28.3.3+incompatible
@ -53,6 +54,7 @@ require (
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // No tag on the repo.
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pires/go-proxyproto v0.8.1
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // No tag on the repo.
github.com/prometheus/client_golang v1.23.0
github.com/prometheus/client_model v0.6.2
@ -323,7 +325,6 @@ require (
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/peterhellberg/link v1.2.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/pquerna/otp v1.5.0 // indirect
github.com/prometheus/common v0.65.0 // indirect

2
go.sum
View file

@ -294,6 +294,8 @@ github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5P
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.6.2 h1:hL7VBpHHKzrV5WTfHCaBsgx/HGbBYlgrwvNXEVDYYsQ=
github.com/cloudflare/circl v1.6.2/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=

46
internal/ech/cmd/main.go Normal file
View file

@ -0,0 +1,46 @@
// Command ech provides utilities for generating ECH (Encrypted Client Hello) keys.
//
// Usage:
//
// go run ./internal/ech/cmd generate example.com,example.org
package main
import (
"fmt"
"os"
"strings"
"github.com/traefik/traefik/v3/internal/ech"
)
func main() {
if len(os.Args) < 3 {
printUsage()
os.Exit(1)
}
command := os.Args[1]
switch command {
case "generate":
names := strings.Split(os.Args[2], ",")
if err := ech.GenerateMultiple(os.Stdout, names); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
default:
fmt.Fprintf(os.Stderr, "Unknown command: %s\n", command)
printUsage()
os.Exit(1)
}
}
func printUsage() {
fmt.Fprintln(os.Stderr, "Usage: go run ./internal/ech/cmd <command> [arguments]")
fmt.Fprintln(os.Stderr, "")
fmt.Fprintln(os.Stderr, "Commands:")
fmt.Fprintln(os.Stderr, " generate <sni,sni,...> Generate ECH keys for the given SNI names (comma-separated)")
fmt.Fprintln(os.Stderr, "")
fmt.Fprintln(os.Stderr, "Examples:")
fmt.Fprintln(os.Stderr, " go run ./internal/ech/cmd generate example.com")
fmt.Fprintln(os.Stderr, " go run ./internal/ech/cmd generate example.com,example.org")
}

41
internal/ech/ech.go Normal file
View file

@ -0,0 +1,41 @@
// Package ech provides utilities for generating and working with
// Encrypted Client Hello (ECH) keys.
package ech
import (
"fmt"
"io"
"github.com/traefik/traefik/v3/pkg/tls"
)
// Generate creates a new ECH key for the given public name (SNI) and writes
// the PEM-encoded result to the provided writer.
func Generate(w io.Writer, publicName string) error {
key, err := tls.NewECHKey(publicName)
if err != nil {
return fmt.Errorf("failed to generate ECH key for %s: %w", publicName, err)
}
data, err := tls.MarshalECHKey(key)
if err != nil {
return fmt.Errorf("failed to marshal ECH key for %s: %w", publicName, err)
}
if _, err = w.Write(data); err != nil {
return fmt.Errorf("failed to write ECH key for %s: %w", publicName, err)
}
return nil
}
// GenerateMultiple creates ECH keys for multiple public names and writes
// all PEM-encoded results to the provided writer.
func GenerateMultiple(w io.Writer, publicNames []string) error {
for _, name := range publicNames {
if err := Generate(w, name); err != nil {
return err
}
}
return nil
}

View file

@ -267,6 +267,18 @@ func (p *Provider) loadFileConfig(ctx context.Context, filename string, parseTem
}
options.ClientAuth.CAFiles = caCerts
var echKeyContents []types.FileOrContent
for _, echKey := range options.ECHKeys {
content, err := echKey.Read()
if err != nil {
log.Ctx(ctx).Error().Err(err).Send()
continue
}
echKeyContents = append(echKeyContents, types.FileOrContent(content))
}
options.ECHKeys = echKeyContents
configuration.TLS.Options[name] = options
}
}

165
pkg/tls/ech.go Normal file
View file

@ -0,0 +1,165 @@
package tls
import (
"crypto/tls"
"encoding/binary"
"encoding/pem"
"errors"
"fmt"
"math/rand/v2"
"github.com/cloudflare/circl/hpke"
"golang.org/x/crypto/cryptobyte"
)
// sha256PrivateKeyLength is the required private key length for SHA-256 based ECDH.
const sha256PrivateKeyLength = 32
func UnmarshalECHKey(data []byte) (*tls.EncryptedClientHelloKey, error) {
var k tls.EncryptedClientHelloKey
for {
block, rest := pem.Decode(data)
if block == nil {
break
}
switch block.Type {
case "PRIVATE KEY":
k.PrivateKey = block.Bytes
case "ECHCONFIG":
k.Config = block.Bytes[2:] // Skip the first two bytes (length prefix)
default:
return nil, fmt.Errorf("unknown PEM block %s", block.Type)
}
data = rest
}
if len(k.Config) == 0 || len(k.PrivateKey) == 0 {
return nil, errors.New("missing ECH configuration or private key in PEM file")
}
// go ecdh now only supports SHA-256 (32-byte private key)
if len(k.PrivateKey) < sha256PrivateKeyLength {
return nil, fmt.Errorf("invalid private key length: expected at least %d bytes, got %d bytes", sha256PrivateKeyLength, len(k.PrivateKey))
} else if len(k.PrivateKey) > sha256PrivateKeyLength {
k.PrivateKey = k.PrivateKey[len(k.PrivateKey)-sha256PrivateKeyLength:]
}
k.SendAsRetry = true
return &k, nil
}
func MarshalECHKey(k *tls.EncryptedClientHelloKey) ([]byte, error) {
if len(k.Config) == 0 || len(k.PrivateKey) == 0 {
return nil, errors.New("missing ECH configuration or private key")
}
configBytes := make([]byte, 2+len(k.Config))
binary.BigEndian.PutUint16(configBytes, uint16(len(k.Config)))
copy(configBytes[2:], k.Config)
var pemData []byte
pemData = append(pemData, pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: k.PrivateKey})...)
pemData = append(pemData, pem.EncodeToMemory(&pem.Block{Type: "ECHCONFIG", Bytes: configBytes})...)
return pemData, nil
}
type echCipher struct {
KDFID uint16
AEADID uint16
}
type echExtension struct {
Type uint16
Data []byte
}
type echConfig struct {
Version uint16
Length uint16
ConfigID uint8
KemID uint16
PublicKey []byte
SymmetricCipherSuite []echCipher
MaxNameLength uint8
PublicName []byte
Extensions []echExtension
}
func NewECHKey(publicName string) (*tls.EncryptedClientHelloKey, error) {
publicKey, privateKey, err := hpke.KEM_X25519_HKDF_SHA256.Scheme().GenerateKeyPair()
if err != nil {
return nil, err
}
publicKeyBytes, err := publicKey.MarshalBinary()
if err != nil {
return nil, err
}
privateKeyBytes, err := privateKey.MarshalBinary()
if err != nil {
return nil, err
}
config := echConfig{
Version: 0xfe0d, // ECH version 0xfe0d
Length: 0x0000,
ConfigID: uint8(rand.Uint()),
KemID: uint16(hpke.KEM_X25519_HKDF_SHA256),
PublicKey: publicKeyBytes,
SymmetricCipherSuite: []echCipher{
{KDFID: uint16(hpke.KDF_HKDF_SHA256), AEADID: uint16(hpke.AEAD_AES256GCM)},
},
MaxNameLength: 32,
PublicName: []byte(publicName),
Extensions: nil,
}
if len(config.PublicName) > int(config.MaxNameLength) {
return nil, fmt.Errorf("public name exceeds maximum length of %d bytes", config.MaxNameLength)
}
var b cryptobyte.Builder
b.AddUint16(config.Version) // Version
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddUint8(config.ConfigID)
b.AddUint16(config.KemID)
b.AddUint16(uint16(len(config.PublicKey)))
b.AddBytes(config.PublicKey)
b.AddUint16LengthPrefixed(func(c *cryptobyte.Builder) {
for _, cipher := range config.SymmetricCipherSuite {
c.AddUint16(cipher.KDFID)
c.AddUint16(cipher.AEADID)
}
})
b.AddUint8(config.MaxNameLength)
b.AddUint8(uint8(len(config.PublicName)))
b.AddBytes(config.PublicName)
b.AddUint16LengthPrefixed(func(c *cryptobyte.Builder) {
for _, ext := range config.Extensions {
c.AddUint16(ext.Type)
c.AddUint16(uint16(len(ext.Data)))
c.AddBytes(ext.Data)
}
})
})
configBytes, err := b.Bytes()
if err != nil {
return nil, err
}
return &tls.EncryptedClientHelloKey{
Config: configBytes,
PrivateKey: privateKeyBytes,
SendAsRetry: true,
}, nil
}
func ECHConfigToConfigList(echConfig []byte) ([]byte, error) {
var b cryptobyte.Builder
b.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) {
child.AddBytes(echConfig)
})
return b.Bytes()
}

441
pkg/tls/ech_test.go Normal file
View file

@ -0,0 +1,441 @@
package tls
import (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/pem"
"fmt"
"io"
"math/big"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// ECHRequestConfig is a configuration struct for making ECH-enabled requests.
// This is only used for testing purposes.
type ECHRequestConfig[T []byte | string] struct {
URL string
Host string
ECH T
Insecure bool
}
// RequestWithECH sends a GET request to a server using the provided ECH configuration.
// This is only used for testing purposes.
func RequestWithECH[T []byte | string](c ECHRequestConfig[T]) (body []byte, err error) {
// Decode the ECH configuration from base64 if it's a string, otherwise use it directly.
var ech []byte
if s, ok := any(c.ECH).(string); ok {
ech, err = base64.StdEncoding.DecodeString(s)
if err != nil {
return nil, err
}
} else {
ech = []byte(c.ECH)
}
requestURL, err := url.Parse(c.URL)
if err != nil {
return nil, fmt.Errorf("failed to parse URL: %w", err)
}
if c.Host == "" {
c.Host = requestURL.Hostname()
}
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
ServerName: c.Host,
EncryptedClientHelloConfigList: ech,
MinVersion: tls.VersionTLS13,
InsecureSkipVerify: c.Insecure,
},
},
}
req := &http.Request{
Method: http.MethodGet,
URL: requestURL,
Host: c.Host,
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err = io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}
return body, nil
}
func TestNewECHKey(t *testing.T) {
testCases := []struct {
desc string
publicName string
expectError bool
}{
{
desc: "valid short public name",
publicName: "server.local",
},
{
desc: "valid public name at max length",
publicName: "abcdefghijklmnopqrstuvwxyz012345", // 32 chars
},
{
desc: "public name exceeds max length",
publicName: "abcdefghijklmnopqrstuvwxyz0123456", // 33 chars
expectError: true,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
echKey, err := NewECHKey(test.publicName)
if test.expectError {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.NotEmpty(t, echKey.Config)
assert.NotEmpty(t, echKey.PrivateKey)
assert.True(t, echKey.SendAsRetry)
})
}
}
func TestMarshalUnmarshalECHKey(t *testing.T) {
testCases := []struct {
desc string
publicName string
}{
{
desc: "standard domain",
publicName: "server.local",
},
{
desc: "subdomain",
publicName: "api.example.com",
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
echKey, err := NewECHKey(test.publicName)
require.NoError(t, err)
echKeyBytes, err := MarshalECHKey(echKey)
require.NoError(t, err)
assert.NotEmpty(t, echKeyBytes)
newKey, err := UnmarshalECHKey(echKeyBytes)
require.NoError(t, err)
assert.Equal(t, echKey.Config, newKey.Config)
assert.Equal(t, echKey.PrivateKey, newKey.PrivateKey)
assert.True(t, newKey.SendAsRetry)
})
}
}
func TestMarshalECHKey_Errors(t *testing.T) {
testCases := []struct {
desc string
key *tls.EncryptedClientHelloKey
}{
{
desc: "missing config",
key: &tls.EncryptedClientHelloKey{
PrivateKey: []byte("some-key"),
},
},
{
desc: "missing private key",
key: &tls.EncryptedClientHelloKey{
Config: []byte("some-config"),
},
},
{
desc: "both missing",
key: &tls.EncryptedClientHelloKey{},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
_, err := MarshalECHKey(test.key)
require.Error(t, err)
})
}
}
func TestUnmarshalECHKey_Errors(t *testing.T) {
testCases := []struct {
desc string
data []byte
}{
{
desc: "empty data",
data: []byte{},
},
{
desc: "invalid PEM",
data: []byte("not a valid PEM"),
},
{
desc: "unknown PEM block type",
data: pem.EncodeToMemory(&pem.Block{Type: "UNKNOWN", Bytes: []byte("data")}),
},
{
desc: "missing private key",
data: func() []byte {
// Create ECHCONFIG block with length prefix
configBytes := append([]byte{0, 4}, []byte("test")...)
return pem.EncodeToMemory(&pem.Block{Type: "ECHCONFIG", Bytes: configBytes})
}(),
},
{
desc: "missing config",
data: pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: make([]byte, 32)}),
},
{
desc: "private key too short",
data: func() []byte {
var pemData []byte
pemData = append(pemData, pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: make([]byte, 16)})...)
configBytes := append([]byte{0, 4}, []byte("test")...)
pemData = append(pemData, pem.EncodeToMemory(&pem.Block{Type: "ECHCONFIG", Bytes: configBytes})...)
return pemData
}(),
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
_, err := UnmarshalECHKey(test.data)
require.Error(t, err)
})
}
}
func TestECHConfigToConfigList(t *testing.T) {
testCases := []struct {
desc string
config []byte
}{
{
desc: "empty config",
config: []byte{},
},
{
desc: "simple config",
config: []byte{0x01, 0x02, 0x03, 0x04},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
configList, err := ECHConfigToConfigList(test.config)
require.NoError(t, err)
// Config list should have 2-byte length prefix followed by the config
expectedLen := 2 + len(test.config)
assert.Len(t, configList, expectedLen)
})
}
}
func TestRequestWithECH(t *testing.T) {
const commonName = "server.local"
echKey, err := NewECHKey(commonName)
require.NoError(t, err)
testCert, err := generateTestCert(commonName)
require.NoError(t, err)
echConfigList, err := ECHConfigToConfigList(echKey.Config)
require.NoError(t, err)
server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello, ECH-enabled TLS server!")
}))
server.TLS = &tls.Config{
Certificates: []tls.Certificate{testCert},
MinVersion: tls.VersionTLS13,
EncryptedClientHelloKeys: []tls.EncryptedClientHelloKey{*echKey},
}
server.StartTLS()
t.Cleanup(server.Close)
testCases := []struct {
desc string
config ECHRequestConfig[[]byte]
expectedBody string
expectError bool
}{
{
desc: "successful ECH request with bytes",
config: ECHRequestConfig[[]byte]{
URL: server.URL + "/",
Host: commonName,
ECH: echConfigList,
Insecure: true,
},
expectedBody: "Hello, ECH-enabled TLS server!",
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
response, err := RequestWithECH(test.config)
if test.expectError {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, test.expectedBody, string(response))
})
}
}
func TestRequestWithECH_StringConfig(t *testing.T) {
const commonName = "server.local"
echKey, err := NewECHKey(commonName)
require.NoError(t, err)
testCert, err := generateTestCert(commonName)
require.NoError(t, err)
echConfigList, err := ECHConfigToConfigList(echKey.Config)
require.NoError(t, err)
server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello from string config!")
}))
server.TLS = &tls.Config{
Certificates: []tls.Certificate{testCert},
MinVersion: tls.VersionTLS13,
EncryptedClientHelloKeys: []tls.EncryptedClientHelloKey{*echKey},
}
server.StartTLS()
t.Cleanup(server.Close)
// Test with base64-encoded string config
echConfigBase64 := base64.StdEncoding.EncodeToString(echConfigList)
response, err := RequestWithECH(ECHRequestConfig[string]{
URL: server.URL + "/",
Host: commonName,
ECH: echConfigBase64,
Insecure: true,
})
require.NoError(t, err)
assert.Equal(t, "Hello from string config!", string(response))
}
func TestRequestWithECH_Errors(t *testing.T) {
testCases := []struct {
desc string
config ECHRequestConfig[string]
}{
{
desc: "invalid base64 ECH config",
config: ECHRequestConfig[string]{
URL: "https://localhost:12345/",
ECH: "not-valid-base64!!!",
},
},
{
desc: "invalid URL",
config: ECHRequestConfig[string]{
URL: "://invalid-url",
ECH: "dGVzdA==",
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
_, err := RequestWithECH(test.config)
require.Error(t, err)
})
}
}
func generateTestCert(commonName string) (tls.Certificate, error) {
rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return tls.Certificate{}, fmt.Errorf("failed to generate RSA key: %w", err)
}
keyBytes := x509.MarshalPKCS1PrivateKey(rsaKey)
keyPEM := pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: keyBytes,
})
notBefore := time.Now()
notAfter := notBefore.Add(24 * time.Hour)
template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: commonName},
DNSNames: []string{commonName, "localhost"},
SignatureAlgorithm: x509.SHA256WithRSA,
NotBefore: notBefore,
NotAfter: notAfter,
BasicConstraintsValid: true,
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &rsaKey.PublicKey, rsaKey)
if err != nil {
return tls.Certificate{}, fmt.Errorf("failed to create certificate: %w", err)
}
certPEM := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: derBytes,
})
return tls.X509KeyPair(certPEM, keyPEM)
}

View file

@ -42,14 +42,15 @@ type ClientAuth struct {
// Options configures TLS for an entry point.
type Options struct {
MinVersion string `json:"minVersion,omitempty" toml:"minVersion,omitempty" yaml:"minVersion,omitempty" export:"true"`
MaxVersion string `json:"maxVersion,omitempty" toml:"maxVersion,omitempty" yaml:"maxVersion,omitempty" export:"true"`
CipherSuites []string `json:"cipherSuites,omitempty" toml:"cipherSuites,omitempty" yaml:"cipherSuites,omitempty" export:"true"`
CurvePreferences []string `json:"curvePreferences,omitempty" toml:"curvePreferences,omitempty" yaml:"curvePreferences,omitempty" export:"true"`
ClientAuth ClientAuth `json:"clientAuth,omitempty" toml:"clientAuth,omitempty" yaml:"clientAuth,omitempty"`
SniStrict bool `json:"sniStrict,omitempty" toml:"sniStrict,omitempty" yaml:"sniStrict,omitempty" export:"true"`
ALPNProtocols []string `json:"alpnProtocols,omitempty" toml:"alpnProtocols,omitempty" yaml:"alpnProtocols,omitempty" export:"true"`
DisableSessionTickets bool `json:"disableSessionTickets,omitempty" toml:"disableSessionTickets,omitempty" yaml:"disableSessionTickets,omitempty" export:"true"`
MinVersion string `json:"minVersion,omitempty" toml:"minVersion,omitempty" yaml:"minVersion,omitempty" export:"true"`
MaxVersion string `json:"maxVersion,omitempty" toml:"maxVersion,omitempty" yaml:"maxVersion,omitempty" export:"true"`
CipherSuites []string `json:"cipherSuites,omitempty" toml:"cipherSuites,omitempty" yaml:"cipherSuites,omitempty" export:"true"`
CurvePreferences []string `json:"curvePreferences,omitempty" toml:"curvePreferences,omitempty" yaml:"curvePreferences,omitempty" export:"true"`
ClientAuth ClientAuth `json:"clientAuth,omitempty" toml:"clientAuth,omitempty" yaml:"clientAuth,omitempty"`
SniStrict bool `json:"sniStrict,omitempty" toml:"sniStrict,omitempty" yaml:"sniStrict,omitempty" export:"true"`
ALPNProtocols []string `json:"alpnProtocols,omitempty" toml:"alpnProtocols,omitempty" yaml:"alpnProtocols,omitempty" export:"true"`
DisableSessionTickets bool `json:"disableSessionTickets,omitempty" toml:"disableSessionTickets,omitempty" yaml:"disableSessionTickets,omitempty" export:"true"`
ECHKeys []types.FileOrContent `json:"echKeys,omitempty" toml:"echKeys,omitempty" yaml:"echKeys,omitempty" export:"true"`
// Deprecated: https://github.com/golang/go/issues/45430
PreferServerCipherSuites *bool `json:"preferServerCipherSuites,omitempty" toml:"preferServerCipherSuites,omitempty" yaml:"preferServerCipherSuites,omitempty" export:"true"`

View file

@ -513,6 +513,24 @@ func buildTLSConfig(tlsOption Options) (*tls.Config, error) {
}
}
// Set the EncryptedClientHelloKeys if set in the config
if tlsOption.ECHKeys != nil {
conf.EncryptedClientHelloKeys = make([]tls.EncryptedClientHelloKey, 0, len(tlsOption.ECHKeys))
for _, content := range tlsOption.ECHKeys {
data, err := content.Read()
if err != nil {
return nil, fmt.Errorf("reading ECH key file failed: %w", err)
}
echKey, err := UnmarshalECHKey(data)
if err != nil {
return nil, fmt.Errorf("unmarshalling ECH key failed: %w", err)
}
conf.EncryptedClientHelloKeys = append(conf.EncryptedClientHelloKeys, *echKey)
}
}
return conf, nil
}

View file

@ -116,6 +116,11 @@ func (in *Options) DeepCopyInto(out *Options) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.ECHKeys != nil {
in, out := &in.ECHKeys, &out.ECHKeys
*out = make([]types.FileOrContent, len(*in))
copy(*out, *in)
}
if in.PreferServerCipherSuites != nil {
in, out := &in.PreferServerCipherSuites, &out.PreferServerCipherSuites
*out = new(bool)