Makes ALPN protocols configurable

This commit is contained in:
Romain 2021-08-20 18:20:06 +02:00 committed by GitHub
parent fa53f7ec85
commit 2644c1f598
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 216 additions and 15 deletions

View file

@ -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.

View file

@ -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"

View file

@ -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:

View file

@ -194,6 +194,9 @@ spec:
clientAuthType: RequireAndVerifyClientCert
sniStrict: true
preferServerCipherSuites: true
alpnProtocols:
- foobar
- foobar
---
apiVersion: traefik.containo.us/v1alpha1

View file

@ -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` |

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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,
}
}

View file

@ -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",
},
},
},
},

View file

@ -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

View file

@ -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
}

View file

@ -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{

View file

@ -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",
},

View file

@ -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{},
},

View file

@ -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

View file

@ -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()

View file

@ -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
}