Makes ALPN protocols configurable
This commit is contained in:
parent
fa53f7ec85
commit
2644c1f598
18 changed files with 216 additions and 15 deletions
|
@ -399,6 +399,47 @@ spec:
|
|||
preferServerCipherSuites: true
|
||||
```
|
||||
|
||||
### ALPN Protocols
|
||||
|
||||
_Optional, Default="h2, http/1.1, acme-tls/1"_
|
||||
|
||||
This option allows to specify the list of supported application level protocols for the TLS handshake,
|
||||
in order of preference.
|
||||
If the client supports ALPN, the selected protocol will be one from this list,
|
||||
and the connection will fail if there is no mutually supported protocol.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
alpnProtocols:
|
||||
- http/1.1
|
||||
- h2
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.options]
|
||||
[tls.options.default]
|
||||
alpnProtocols = ["http/1.1", "h2"]
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
alpnProtocols:
|
||||
- http/1.1
|
||||
- h2
|
||||
```
|
||||
|
||||
### Client Authentication (mTLS)
|
||||
|
||||
Traefik supports mutual authentication, through the `clientAuth` section.
|
||||
|
|
|
@ -421,6 +421,7 @@
|
|||
curvePreferences = ["foobar", "foobar"]
|
||||
sniStrict = true
|
||||
preferServerCipherSuites = true
|
||||
alpnProtocols = ["foobar", "foobar"]
|
||||
[tls.options.Options0.clientAuth]
|
||||
caFiles = ["foobar", "foobar"]
|
||||
clientAuthType = "foobar"
|
||||
|
@ -431,6 +432,7 @@
|
|||
curvePreferences = ["foobar", "foobar"]
|
||||
sniStrict = true
|
||||
preferServerCipherSuites = true
|
||||
alpnProtocols = ["foobar", "foobar"]
|
||||
[tls.options.Options1.clientAuth]
|
||||
caFiles = ["foobar", "foobar"]
|
||||
clientAuthType = "foobar"
|
||||
|
|
|
@ -470,6 +470,9 @@ tls:
|
|||
clientAuthType: foobar
|
||||
sniStrict: true
|
||||
preferServerCipherSuites: true
|
||||
alpnProtocols:
|
||||
- foobar
|
||||
- foobar
|
||||
Options1:
|
||||
minVersion: foobar
|
||||
maxVersion: foobar
|
||||
|
@ -486,6 +489,9 @@ tls:
|
|||
clientAuthType: foobar
|
||||
sniStrict: true
|
||||
preferServerCipherSuites: true
|
||||
alpnProtocols:
|
||||
- foobar
|
||||
- foobar
|
||||
stores:
|
||||
Store0:
|
||||
defaultCertificate:
|
||||
|
|
|
@ -194,6 +194,9 @@ spec:
|
|||
clientAuthType: RequireAndVerifyClientCert
|
||||
sniStrict: true
|
||||
preferServerCipherSuites: true
|
||||
alpnProtocols:
|
||||
- foobar
|
||||
- foobar
|
||||
|
||||
---
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
|
|
|
@ -274,6 +274,8 @@
|
|||
| `traefik/tls/certificates/1/keyFile` | `foobar` |
|
||||
| `traefik/tls/certificates/1/stores/0` | `foobar` |
|
||||
| `traefik/tls/certificates/1/stores/1` | `foobar` |
|
||||
| `traefik/tls/options/Options0/alpnProtocols/0` | `foobar` |
|
||||
| `traefik/tls/options/Options0/alpnProtocols/1` | `foobar` |
|
||||
| `traefik/tls/options/Options0/cipherSuites/0` | `foobar` |
|
||||
| `traefik/tls/options/Options0/cipherSuites/1` | `foobar` |
|
||||
| `traefik/tls/options/Options0/clientAuth/caFiles/0` | `foobar` |
|
||||
|
@ -285,6 +287,8 @@
|
|||
| `traefik/tls/options/Options0/minVersion` | `foobar` |
|
||||
| `traefik/tls/options/Options0/preferServerCipherSuites` | `true` |
|
||||
| `traefik/tls/options/Options0/sniStrict` | `true` |
|
||||
| `traefik/tls/options/Options1/alpnProtocols/0` | `foobar` |
|
||||
| `traefik/tls/options/Options1/alpnProtocols/1` | `foobar` |
|
||||
| `traefik/tls/options/Options1/cipherSuites/0` | `foobar` |
|
||||
| `traefik/tls/options/Options1/cipherSuites/1` | `foobar` |
|
||||
| `traefik/tls/options/Options1/clientAuth/caFiles/0` | `foobar` |
|
||||
|
|
|
@ -36,6 +36,10 @@ spec:
|
|||
spec:
|
||||
description: TLSOptionSpec configures TLS for an entry point.
|
||||
properties:
|
||||
alpnProtocols:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
cipherSuites:
|
||||
items:
|
||||
type: string
|
||||
|
|
|
@ -1506,6 +1506,8 @@ or referencing TLS options in the [`IngressRoute`](#kind-ingressroute) / [`Ingre
|
|||
- secret-ca2
|
||||
clientAuthType: VerifyClientCertIfGiven # [7]
|
||||
sniStrict: true # [8]
|
||||
alpnProtocols: # [9]
|
||||
- foobar
|
||||
```
|
||||
|
||||
| Ref | Attribute | Purpose |
|
||||
|
@ -1518,6 +1520,7 @@ or referencing TLS options in the [`IngressRoute`](#kind-ingressroute) / [`Ingre
|
|||
| [6] | `clientAuth.secretNames` | list of names of the referenced Kubernetes [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/) (in TLSOption namespace). The secret must contain a certificate under either a `tls.ca` or a `ca.crt` key. |
|
||||
| [7] | `clientAuth.clientAuthType` | defines the client authentication type to apply. The available values are: `NoClientCert`, `RequestClientCert`, `VerifyClientCertIfGiven` and `RequireAndVerifyClientCert` |
|
||||
| [8] | `sniStrict` | if `true`, Traefik won't allow connections from clients connections that do not specify a server_name extension |
|
||||
| [9] | `alpnProtocols` | List of supported [application level protocols](../../https/tls.md#alpn-protocols) for the TLS handshake, in order of preference. |
|
||||
|
||||
!!! info "CA Secret"
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1
|
|||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.4.1
|
||||
controller-gen.kubebuilder.io/version: v0.5.0
|
||||
creationTimestamp: null
|
||||
name: ingressroutes.traefik.containo.us
|
||||
spec:
|
||||
|
@ -202,7 +202,7 @@ apiVersion: apiextensions.k8s.io/v1
|
|||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.4.1
|
||||
controller-gen.kubebuilder.io/version: v0.5.0
|
||||
creationTimestamp: null
|
||||
name: ingressroutetcps.traefik.containo.us
|
||||
spec:
|
||||
|
@ -362,7 +362,7 @@ apiVersion: apiextensions.k8s.io/v1
|
|||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.4.1
|
||||
controller-gen.kubebuilder.io/version: v0.5.0
|
||||
creationTimestamp: null
|
||||
name: ingressrouteudps.traefik.containo.us
|
||||
spec:
|
||||
|
@ -1208,6 +1208,10 @@ spec:
|
|||
spec:
|
||||
description: TLSOptionSpec configures TLS for an entry point.
|
||||
properties:
|
||||
alpnProtocols:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
cipherSuites:
|
||||
items:
|
||||
type: string
|
||||
|
|
|
@ -703,6 +703,12 @@ func buildTLSOptions(ctx context.Context, client Client) map[string]tls.Options
|
|||
id = tlsOption.Name
|
||||
nsDefault = append(nsDefault, tlsOption.Namespace)
|
||||
}
|
||||
|
||||
alpnProtocols := tls.DefaultTLSOptions.ALPNProtocols
|
||||
if len(tlsOption.Spec.ALPNProtocols) > 0 {
|
||||
alpnProtocols = tlsOption.Spec.ALPNProtocols
|
||||
}
|
||||
|
||||
tlsOptions[id] = tls.Options{
|
||||
MinVersion: tlsOption.Spec.MinVersion,
|
||||
MaxVersion: tlsOption.Spec.MaxVersion,
|
||||
|
@ -714,6 +720,7 @@ func buildTLSOptions(ctx context.Context, client Client) map[string]tls.Options
|
|||
},
|
||||
SniStrict: tlsOption.Spec.SniStrict,
|
||||
PreferServerCipherSuites: tlsOption.Spec.PreferServerCipherSuites,
|
||||
ALPNProtocols: alpnProtocols,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -616,6 +616,11 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
|||
},
|
||||
SniStrict: true,
|
||||
PreferServerCipherSuites: true,
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -678,6 +683,11 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
|||
ClientAuthType: "VerifyClientCertIfGiven",
|
||||
},
|
||||
SniStrict: true,
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -739,6 +749,11 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
|||
ClientAuthType: "VerifyClientCertIfGiven",
|
||||
},
|
||||
SniStrict: true,
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -789,6 +804,11 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
|||
Options: map[string]tls.Options{
|
||||
"default-foo": {
|
||||
MinVersion: "VersionTLS12",
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -839,6 +859,11 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
|
|||
Options: map[string]tls.Options{
|
||||
"default-foo": {
|
||||
MinVersion: "VersionTLS12",
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -2539,6 +2564,11 @@ func TestLoadIngressRoutes(t *testing.T) {
|
|||
},
|
||||
SniStrict: true,
|
||||
PreferServerCipherSuites: true,
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -2648,6 +2678,11 @@ func TestLoadIngressRoutes(t *testing.T) {
|
|||
},
|
||||
SniStrict: true,
|
||||
PreferServerCipherSuites: true,
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -2716,6 +2751,11 @@ func TestLoadIngressRoutes(t *testing.T) {
|
|||
ClientAuthType: "VerifyClientCertIfGiven",
|
||||
},
|
||||
SniStrict: true,
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -2779,6 +2819,11 @@ func TestLoadIngressRoutes(t *testing.T) {
|
|||
ClientAuthType: "VerifyClientCertIfGiven",
|
||||
},
|
||||
SniStrict: true,
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -2831,6 +2876,11 @@ func TestLoadIngressRoutes(t *testing.T) {
|
|||
Options: map[string]tls.Options{
|
||||
"default-foo": {
|
||||
MinVersion: "VersionTLS12",
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -2883,6 +2933,11 @@ func TestLoadIngressRoutes(t *testing.T) {
|
|||
Options: map[string]tls.Options{
|
||||
"default-foo": {
|
||||
MinVersion: "VersionTLS12",
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -27,6 +27,7 @@ type TLSOptionSpec struct {
|
|||
ClientAuth ClientAuth `json:"clientAuth,omitempty"`
|
||||
SniStrict bool `json:"sniStrict,omitempty"`
|
||||
PreferServerCipherSuites bool `json:"preferServerCipherSuites,omitempty"`
|
||||
ALPNProtocols []string `json:"alpnProtocols,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
|
|
@ -1327,6 +1327,11 @@ func (in *TLSOptionSpec) DeepCopyInto(out *TLSOptionSpec) {
|
|||
copy(*out, *in)
|
||||
}
|
||||
in.ClientAuth.DeepCopyInto(&out.ClientAuth)
|
||||
if in.ALPNProtocols != nil {
|
||||
in, out := &in.ALPNProtocols, &out.ALPNProtocols
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -846,6 +846,11 @@ func Test_buildConfiguration(t *testing.T) {
|
|||
ClientAuthType: "foobar",
|
||||
},
|
||||
SniStrict: true,
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
"Options1": {
|
||||
MinVersion: "foobar",
|
||||
|
@ -866,6 +871,11 @@ func Test_buildConfiguration(t *testing.T) {
|
|||
ClientAuthType: "foobar",
|
||||
},
|
||||
SniStrict: true,
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
},
|
||||
Stores: map[string]tls.Store{
|
||||
|
|
|
@ -182,7 +182,13 @@ func Test_mergeConfiguration_tlsOptions(t *testing.T) {
|
|||
desc: "Nil returns an empty configuration",
|
||||
given: nil,
|
||||
expected: map[string]tls.Options{
|
||||
"default": {},
|
||||
"default": {
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -199,7 +205,13 @@ func Test_mergeConfiguration_tlsOptions(t *testing.T) {
|
|||
},
|
||||
},
|
||||
expected: map[string]tls.Options{
|
||||
"default": {},
|
||||
"default": {
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
"foo@provider-1": {
|
||||
MinVersion: "VersionTLS12",
|
||||
},
|
||||
|
@ -228,7 +240,13 @@ func Test_mergeConfiguration_tlsOptions(t *testing.T) {
|
|||
},
|
||||
},
|
||||
expected: map[string]tls.Options{
|
||||
"default": {},
|
||||
"default": {
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
"foo@provider-1": {
|
||||
MinVersion: "VersionTLS13",
|
||||
},
|
||||
|
@ -334,7 +352,13 @@ func Test_mergeConfiguration_tlsOptions(t *testing.T) {
|
|||
},
|
||||
},
|
||||
expected: map[string]tls.Options{
|
||||
"default": {},
|
||||
"default": {
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
"foo@provider-1": {
|
||||
MinVersion: "VersionTLS12",
|
||||
},
|
||||
|
|
|
@ -76,7 +76,13 @@ func TestNewConfigurationWatcher(t *testing.T) {
|
|||
},
|
||||
TLS: &dynamic.TLSConfiguration{
|
||||
Options: map[string]tls.Options{
|
||||
"default": {},
|
||||
"default": {
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
},
|
||||
Stores: map[string]tls.Store{},
|
||||
},
|
||||
|
@ -236,7 +242,13 @@ func TestListenProvidersDoesNotSkipFlappingConfiguration(t *testing.T) {
|
|||
},
|
||||
TLS: &dynamic.TLSConfiguration{
|
||||
Options: map[string]tls.Options{
|
||||
"default": {},
|
||||
"default": {
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
},
|
||||
Stores: map[string]tls.Store{},
|
||||
},
|
||||
|
@ -292,7 +304,13 @@ func TestListenProvidersPublishesConfigForEachProvider(t *testing.T) {
|
|||
},
|
||||
TLS: &dynamic.TLSConfiguration{
|
||||
Options: map[string]tls.Options{
|
||||
"default": {},
|
||||
"default": {
|
||||
ALPNProtocols: []string{
|
||||
"h2",
|
||||
"http/1.1",
|
||||
"acme-tls/1",
|
||||
},
|
||||
},
|
||||
},
|
||||
Stores: map[string]tls.Store{},
|
||||
},
|
||||
|
|
|
@ -23,6 +23,13 @@ type Options struct {
|
|||
ClientAuth ClientAuth `json:"clientAuth,omitempty" toml:"clientAuth,omitempty" yaml:"clientAuth,omitempty"`
|
||||
SniStrict bool `json:"sniStrict,omitempty" toml:"sniStrict,omitempty" yaml:"sniStrict,omitempty" export:"true"`
|
||||
PreferServerCipherSuites bool `json:"preferServerCipherSuites,omitempty" toml:"preferServerCipherSuites,omitempty" yaml:"preferServerCipherSuites,omitempty" export:"true"`
|
||||
ALPNProtocols []string `json:"alpnProtocols,omitempty" toml:"alpnProtocols,omitempty" yaml:"alpnProtocols,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values for an Options struct.
|
||||
func (o *Options) SetDefaults() {
|
||||
// ensure http2 enabled
|
||||
o.ALPNProtocols = DefaultTLSOptions.ALPNProtocols
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
|
|
@ -24,7 +24,10 @@ const (
|
|||
)
|
||||
|
||||
// DefaultTLSOptions the default TLS options.
|
||||
var DefaultTLSOptions = Options{}
|
||||
var DefaultTLSOptions = Options{
|
||||
// ensure http2 enabled
|
||||
ALPNProtocols: []string{"h2", "http/1.1", tlsalpn01.ACMETLS1Protocol},
|
||||
}
|
||||
|
||||
// Manager is the TLS option/store/configuration factory.
|
||||
type Manager struct {
|
||||
|
@ -230,10 +233,9 @@ func buildCertificateStore(ctx context.Context, tlsStore Store, storename string
|
|||
|
||||
// creates a TLS config that allows terminating HTTPS for multiple domains using SNI.
|
||||
func buildTLSConfig(tlsOption Options) (*tls.Config, error) {
|
||||
conf := &tls.Config{}
|
||||
|
||||
// ensure http2 enabled
|
||||
conf.NextProtos = []string{"h2", "http/1.1", tlsalpn01.ACMETLS1Protocol}
|
||||
conf := &tls.Config{
|
||||
NextProtos: tlsOption.ALPNProtocols,
|
||||
}
|
||||
|
||||
if len(tlsOption.ClientAuth.CAFiles) > 0 {
|
||||
pool := x509.NewCertPool()
|
||||
|
|
|
@ -85,6 +85,11 @@ func (in *Options) DeepCopyInto(out *Options) {
|
|||
copy(*out, *in)
|
||||
}
|
||||
in.ClientAuth.DeepCopyInto(&out.ClientAuth)
|
||||
if in.ALPNProtocols != nil {
|
||||
in, out := &in.ALPNProtocols, &out.ALPNProtocols
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue