Add support for fetching k8s Ingress TLS data from secrets
This commit is contained in:
parent
9b3750320b
commit
8327dd0c0b
7 changed files with 495 additions and 3 deletions
|
@ -837,7 +837,18 @@ var _templatesKubernetesTmpl = []byte(`[backends]{{range $backendName, $backend
|
||||||
[frontends."{{$frontendName}}".routes."{{$routeName}}"]
|
[frontends."{{$frontendName}}".routes."{{$routeName}}"]
|
||||||
rule = "{{$route.Rule}}"
|
rule = "{{$route.Rule}}"
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}`)
|
{{end}}
|
||||||
|
|
||||||
|
{{range $tlsConfiguration := .TLSConfiguration}}
|
||||||
|
[[tlsConfiguration]]
|
||||||
|
entryPoints = [{{range $tlsConfiguration.EntryPoints}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
[tlsConfiguration.certificate]
|
||||||
|
certFile = """{{$tlsConfiguration.Certificate.CertFile}}"""
|
||||||
|
keyFile = """{{$tlsConfiguration.Certificate.KeyFile}}"""
|
||||||
|
{{end}}
|
||||||
|
`)
|
||||||
|
|
||||||
func templatesKubernetesTmplBytes() ([]byte, error) {
|
func templatesKubernetesTmplBytes() ([]byte, error) {
|
||||||
return _templatesKubernetesTmpl, nil
|
return _templatesKubernetesTmpl, nil
|
||||||
|
|
|
@ -333,6 +333,51 @@ echo "$(minikube ip) traefik-ui.minikube" | sudo tee -a /etc/hosts
|
||||||
|
|
||||||
We should now be able to visit [traefik-ui.minikube](http://traefik-ui.minikube) in the browser and view the Træfik Web UI.
|
We should now be able to visit [traefik-ui.minikube](http://traefik-ui.minikube) in the browser and view the Træfik Web UI.
|
||||||
|
|
||||||
|
### Add a TLS Certificate to the Ingress
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
For this example to work you need a TLS entrypoint. You don't have to provide a TLS certificate at this point. For more details see [here](/configuration/entrypoints/).
|
||||||
|
|
||||||
|
To setup an HTTPS-protected ingress, you can leverage the TLS feature of the ingress resource.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: traefik-web-ui
|
||||||
|
namespace: kube-system
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/ingress.class: traefik
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: traefik-ui.minikube
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
serviceName: traefik-web-ui
|
||||||
|
servicePort: 80
|
||||||
|
tls:
|
||||||
|
secretName: traefik-ui-tls-cert
|
||||||
|
```
|
||||||
|
|
||||||
|
In addition to the modified ingress you need to provide the TLS certificate via a kubernetes secret in the same namespace as the ingress. The following two commands will generate a new certificate and create a secret containing the key and cert files.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=traefik-ui.minikube"
|
||||||
|
kubectl -n kube-system create secret tls traefik-ui-tls-cert --key=tls.key --cert=tls.crt
|
||||||
|
```
|
||||||
|
|
||||||
|
If there are any errors while loading the TLS section of an ingress, the whole ingress will be skipped.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
The secret must have two entries named `tls.key`and `tls.crt`. See the [kubernetes documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) for more details.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
The TLS certificates will be added to all entrypoints defined by the ingress annotation `traefik.frontend.entryPoints`. If no such annotation is provided, the TLS certificates will be added to all TLS-enabled `defaultEntryPoints`.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
The field `hosts` in the TLS configuration is ignored. Instead, the domains provided by the certificate are used for this purpose. It is recommended to not use wildcard certificates as they will match globally.
|
||||||
|
|
||||||
## Basic Authentication
|
## Basic Authentication
|
||||||
|
|
||||||
It's possible to add additional authentication annotations in the Ingress rule.
|
It's possible to add additional authentication annotations in the Ingress rule.
|
||||||
|
|
|
@ -3,6 +3,7 @@ package kubernetes
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/tls"
|
||||||
"github.com/containous/traefik/types"
|
"github.com/containous/traefik/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
@ -201,6 +202,39 @@ func route(name string, rule string) func(*types.Route) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tlsConfigurations(opts ...func(*tls.Configuration)) func(*types.Configuration) {
|
||||||
|
return func(c *types.Configuration) {
|
||||||
|
for _, opt := range opts {
|
||||||
|
tlsConf := &tls.Configuration{}
|
||||||
|
opt(tlsConf)
|
||||||
|
c.TLSConfiguration = append(c.TLSConfiguration, tlsConf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tlsConfiguration(opts ...func(*tls.Configuration)) func(*tls.Configuration) {
|
||||||
|
return func(c *tls.Configuration) {
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tlsEntryPoints(entryPoints ...string) func(*tls.Configuration) {
|
||||||
|
return func(c *tls.Configuration) {
|
||||||
|
c.EntryPoints = entryPoints
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func certificate(cert string, key string) func(*tls.Configuration) {
|
||||||
|
return func(c *tls.Configuration) {
|
||||||
|
c.Certificate = &tls.Certificate{
|
||||||
|
CertFile: tls.FileOrContent(cert),
|
||||||
|
KeyFile: tls.FileOrContent(key),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
|
|
||||||
func TestBuildConfiguration(t *testing.T) {
|
func TestBuildConfiguration(t *testing.T) {
|
||||||
|
@ -247,6 +281,12 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
tlsConfigurations(
|
||||||
|
tlsConfiguration(
|
||||||
|
tlsEntryPoints("https"),
|
||||||
|
certificate("certificate", "key"),
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.EqualValues(t, sampleConfiguration(), actual)
|
assert.EqualValues(t, sampleConfiguration(), actual)
|
||||||
|
@ -335,5 +375,14 @@ func sampleConfiguration() *types.Configuration {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
TLSConfiguration: []*tls.Configuration{
|
||||||
|
{
|
||||||
|
EntryPoints: []string{"https"},
|
||||||
|
Certificate: &tls.Certificate{
|
||||||
|
CertFile: tls.FileOrContent("certificate"),
|
||||||
|
KeyFile: tls.FileOrContent("key"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,23 @@ func iBackend(name string, port intstr.IntOrString) func(*v1beta1.HTTPIngressPat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func iTLSes(opts ...func(*v1beta1.IngressTLS)) func(*v1beta1.Ingress) {
|
||||||
|
return func(i *v1beta1.Ingress) {
|
||||||
|
for _, opt := range opts {
|
||||||
|
iTLS := v1beta1.IngressTLS{}
|
||||||
|
opt(&iTLS)
|
||||||
|
i.Spec.TLS = append(i.Spec.TLS, iTLS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func iTLS(secret string, hosts ...string) func(*v1beta1.IngressTLS) {
|
||||||
|
return func(i *v1beta1.IngressTLS) {
|
||||||
|
i.SecretName = secret
|
||||||
|
i.Hosts = hosts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
|
|
||||||
func TestBuildIngress(t *testing.T) {
|
func TestBuildIngress(t *testing.T) {
|
||||||
|
@ -107,7 +124,11 @@ func TestBuildIngress(t *testing.T) {
|
||||||
onePath(iBackend("service2", intstr.FromInt(802))),
|
onePath(iBackend("service2", intstr.FromInt(802))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
))
|
),
|
||||||
|
iTLSes(
|
||||||
|
iTLS("tls-secret", "foo"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
assert.EqualValues(t, sampleIngress(), i)
|
assert.EqualValues(t, sampleIngress(), i)
|
||||||
}
|
}
|
||||||
|
@ -164,6 +185,12 @@ func sampleIngress() *v1beta1.Ingress {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
TLS: []v1beta1.IngressTLS{
|
||||||
|
{
|
||||||
|
Hosts: []string{"foo"},
|
||||||
|
SecretName: "tls-secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/containous/traefik/provider"
|
"github.com/containous/traefik/provider"
|
||||||
"github.com/containous/traefik/provider/label"
|
"github.com/containous/traefik/provider/label"
|
||||||
"github.com/containous/traefik/safe"
|
"github.com/containous/traefik/safe"
|
||||||
|
"github.com/containous/traefik/tls"
|
||||||
"github.com/containous/traefik/types"
|
"github.com/containous/traefik/types"
|
||||||
"k8s.io/client-go/pkg/api/v1"
|
"k8s.io/client-go/pkg/api/v1"
|
||||||
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
||||||
|
@ -174,6 +175,13 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tlsConfigs, err := getTLSConfigurations(i, k8sClient)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error configuring TLS for ingress %s/%s: %v", i.Namespace, i.Name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
templateObjects.TLSConfiguration = append(templateObjects.TLSConfiguration, tlsConfigs...)
|
||||||
|
|
||||||
for _, r := range i.Spec.Rules {
|
for _, r := range i.Spec.Rules {
|
||||||
if r.HTTP == nil {
|
if r.HTTP == nil {
|
||||||
log.Warn("Error in ingress: HTTP is nil")
|
log.Warn("Error in ingress: HTTP is nil")
|
||||||
|
@ -441,6 +449,48 @@ func loadAuthCredentials(namespace, secretName string, k8sClient Client) ([]stri
|
||||||
return creds, nil
|
return creds, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getTLSConfigurations(ingress *v1beta1.Ingress, k8sClient Client) ([]*tls.Configuration, error) {
|
||||||
|
var tlsConfigs []*tls.Configuration
|
||||||
|
|
||||||
|
for _, t := range ingress.Spec.TLS {
|
||||||
|
tlsSecret, exists, err := k8sClient.GetSecret(ingress.Namespace, t.SecretName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to fetch secret %s/%s: %v", ingress.Namespace, t.SecretName, err)
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return nil, fmt.Errorf("secret %s/%s does not exist", ingress.Namespace, t.SecretName)
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsCrtData, tlsCrtExists := tlsSecret.Data["tls.crt"]
|
||||||
|
tlsKeyData, tlsKeyExists := tlsSecret.Data["tls.key"]
|
||||||
|
|
||||||
|
var missingEntries []string
|
||||||
|
if !tlsCrtExists {
|
||||||
|
missingEntries = append(missingEntries, "tls.crt")
|
||||||
|
}
|
||||||
|
if !tlsKeyExists {
|
||||||
|
missingEntries = append(missingEntries, "tls.key")
|
||||||
|
}
|
||||||
|
if len(missingEntries) > 0 {
|
||||||
|
return nil, fmt.Errorf("secret %s/%s is missing the following TLS data entries: %s", ingress.Namespace, t.SecretName, strings.Join(missingEntries, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
entryPoints := label.GetSliceStringValue(ingress.Annotations, label.TraefikFrontendEntryPoints)
|
||||||
|
|
||||||
|
tlsConfig := &tls.Configuration{
|
||||||
|
EntryPoints: entryPoints,
|
||||||
|
Certificate: &tls.Certificate{
|
||||||
|
CertFile: tls.FileOrContent(tlsCrtData),
|
||||||
|
KeyFile: tls.FileOrContent(tlsKeyData),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConfigs = append(tlsConfigs, tlsConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tlsConfigs, nil
|
||||||
|
}
|
||||||
|
|
||||||
func endpointPortNumber(servicePort v1.ServicePort, endpointPorts []v1.EndpointPort) int {
|
func endpointPortNumber(servicePort v1.ServicePort, endpointPorts []v1.EndpointPort) int {
|
||||||
if len(endpointPorts) > 0 {
|
if len(endpointPorts) > 0 {
|
||||||
//name is optional if there is only one port
|
//name is optional if there is only one port
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containous/traefik/provider/label"
|
"github.com/containous/traefik/provider/label"
|
||||||
|
"github.com/containous/traefik/tls"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"k8s.io/client-go/pkg/api/v1"
|
"k8s.io/client-go/pkg/api/v1"
|
||||||
|
@ -1214,3 +1215,302 @@ func TestBasicAuthInTemplate(t *testing.T) {
|
||||||
t.Fatalf("unexpected credentials: %+v", got)
|
t.Fatalf("unexpected credentials: %+v", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTLSSecretLoad(t *testing.T) {
|
||||||
|
ingresses := []*v1beta1.Ingress{
|
||||||
|
buildIngress(
|
||||||
|
iNamespace("testing"),
|
||||||
|
iAnnotation(label.TraefikFrontendEntryPoints, "ep1,ep2"),
|
||||||
|
iRules(
|
||||||
|
iRule(iHost("example.com"), iPaths(
|
||||||
|
onePath(iBackend("example-com", intstr.FromInt(80))),
|
||||||
|
)),
|
||||||
|
iRule(iHost("example.org"), iPaths(
|
||||||
|
onePath(iBackend("example-org", intstr.FromInt(80))),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
iTLSes(
|
||||||
|
iTLS("myTlsSecret"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
buildIngress(
|
||||||
|
iNamespace("testing"),
|
||||||
|
iAnnotation(label.TraefikFrontendEntryPoints, "ep3"),
|
||||||
|
iRules(
|
||||||
|
iRule(iHost("example.fail"), iPaths(
|
||||||
|
onePath(iBackend("example-fail", intstr.FromInt(80))),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
iTLSes(
|
||||||
|
iTLS("myUndefinedSecret"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
services := []*v1.Service{
|
||||||
|
buildService(
|
||||||
|
sName("example-com"),
|
||||||
|
sNamespace("testing"),
|
||||||
|
sUID("1"),
|
||||||
|
sSpec(
|
||||||
|
clusterIP("10.0.0.1"),
|
||||||
|
sType("ClusterIP"),
|
||||||
|
sPorts(sPort(80, "http"))),
|
||||||
|
),
|
||||||
|
buildService(
|
||||||
|
sName("example-org"),
|
||||||
|
sNamespace("testing"),
|
||||||
|
sUID("2"),
|
||||||
|
sSpec(
|
||||||
|
clusterIP("10.0.0.2"),
|
||||||
|
sType("ClusterIP"),
|
||||||
|
sPorts(sPort(80, "http"))),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
secrets := []*v1.Secret{
|
||||||
|
{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: "myTlsSecret",
|
||||||
|
UID: "1",
|
||||||
|
Namespace: "testing",
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"tls.crt": []byte("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"),
|
||||||
|
"tls.key": []byte("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
endpoints := []*v1.Endpoints{}
|
||||||
|
watchChan := make(chan interface{})
|
||||||
|
client := clientMock{
|
||||||
|
ingresses: ingresses,
|
||||||
|
services: services,
|
||||||
|
secrets: secrets,
|
||||||
|
endpoints: endpoints,
|
||||||
|
watchChan: watchChan,
|
||||||
|
}
|
||||||
|
provider := Provider{}
|
||||||
|
actual, err := provider.loadIngresses(client)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := buildConfiguration(
|
||||||
|
backends(
|
||||||
|
backend("example.com",
|
||||||
|
servers(),
|
||||||
|
lbMethod("wrr"),
|
||||||
|
),
|
||||||
|
backend("example.org",
|
||||||
|
servers(),
|
||||||
|
lbMethod("wrr"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
frontends(
|
||||||
|
frontend("example.com",
|
||||||
|
headers(),
|
||||||
|
entryPoints("ep1", "ep2"),
|
||||||
|
passHostHeader(),
|
||||||
|
routes(
|
||||||
|
route("example.com", "Host:example.com"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
frontend("example.org",
|
||||||
|
headers(),
|
||||||
|
entryPoints("ep1", "ep2"),
|
||||||
|
passHostHeader(),
|
||||||
|
routes(
|
||||||
|
route("example.org", "Host:example.org"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
tlsConfigurations(
|
||||||
|
tlsConfiguration(
|
||||||
|
tlsEntryPoints("ep1", "ep2"),
|
||||||
|
certificate(
|
||||||
|
"-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----",
|
||||||
|
"-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetTLSConfigurations(t *testing.T) {
|
||||||
|
testIngressWithoutHostname := buildIngress(
|
||||||
|
iNamespace("testing"),
|
||||||
|
iRules(
|
||||||
|
iRule(iHost("ep1.example.com")),
|
||||||
|
iRule(iHost("ep2.example.com")),
|
||||||
|
),
|
||||||
|
iTLSes(
|
||||||
|
iTLS("test-secret"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
ingress *v1beta1.Ingress
|
||||||
|
client Client
|
||||||
|
result []*tls.Configuration
|
||||||
|
errResult string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "api client returns error",
|
||||||
|
ingress: testIngressWithoutHostname,
|
||||||
|
client: clientMock{
|
||||||
|
apiSecretError: errors.New("api secret error"),
|
||||||
|
},
|
||||||
|
errResult: "failed to fetch secret testing/test-secret: api secret error",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "api client doesn't find secret",
|
||||||
|
ingress: testIngressWithoutHostname,
|
||||||
|
client: clientMock{},
|
||||||
|
errResult: "secret testing/test-secret does not exist",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "entry 'tls.crt' in secret missing",
|
||||||
|
ingress: testIngressWithoutHostname,
|
||||||
|
client: clientMock{
|
||||||
|
secrets: []*v1.Secret{
|
||||||
|
{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: "test-secret",
|
||||||
|
Namespace: "testing",
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"tls.key": []byte("tls-key"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errResult: "secret testing/test-secret is missing the following TLS data entries: tls.crt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "entry 'tls.key' in secret missing",
|
||||||
|
ingress: testIngressWithoutHostname,
|
||||||
|
client: clientMock{
|
||||||
|
secrets: []*v1.Secret{
|
||||||
|
{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: "test-secret",
|
||||||
|
Namespace: "testing",
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"tls.crt": []byte("tls-crt"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errResult: "secret testing/test-secret is missing the following TLS data entries: tls.key",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "secret doesn't provide any of the required fields",
|
||||||
|
ingress: testIngressWithoutHostname,
|
||||||
|
client: clientMock{
|
||||||
|
secrets: []*v1.Secret{
|
||||||
|
{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: "test-secret",
|
||||||
|
Namespace: "testing",
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errResult: "secret testing/test-secret is missing the following TLS data entries: tls.crt, tls.key",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "add certificates to the configuration",
|
||||||
|
ingress: buildIngress(
|
||||||
|
iNamespace("testing"),
|
||||||
|
iRules(
|
||||||
|
iRule(iHost("ep1.example.com")),
|
||||||
|
iRule(iHost("ep2.example.com")),
|
||||||
|
iRule(iHost("ep3.example.com")),
|
||||||
|
),
|
||||||
|
iTLSes(
|
||||||
|
iTLS("test-secret"),
|
||||||
|
iTLS("test-secret"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
client: clientMock{
|
||||||
|
secrets: []*v1.Secret{
|
||||||
|
{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: "test-secret",
|
||||||
|
Namespace: "testing",
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"tls.crt": []byte("tls-crt"),
|
||||||
|
"tls.key": []byte("tls-key"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: []*tls.Configuration{
|
||||||
|
{
|
||||||
|
Certificate: &tls.Certificate{
|
||||||
|
CertFile: tls.FileOrContent("tls-crt"),
|
||||||
|
KeyFile: tls.FileOrContent("tls-key"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Certificate: &tls.Certificate{
|
||||||
|
CertFile: tls.FileOrContent("tls-crt"),
|
||||||
|
KeyFile: tls.FileOrContent("tls-key"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "pass the endpoints defined in the annotation to the certificate",
|
||||||
|
ingress: buildIngress(
|
||||||
|
iNamespace("testing"),
|
||||||
|
iAnnotation(label.TraefikFrontendEntryPoints, "https,api-secure"),
|
||||||
|
iRules(iRule(iHost("example.com"))),
|
||||||
|
iTLSes(iTLS("test-secret")),
|
||||||
|
),
|
||||||
|
client: clientMock{
|
||||||
|
secrets: []*v1.Secret{
|
||||||
|
{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: "test-secret",
|
||||||
|
Namespace: "testing",
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"tls.crt": []byte("tls-crt"),
|
||||||
|
"tls.key": []byte("tls-key"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: []*tls.Configuration{
|
||||||
|
{
|
||||||
|
EntryPoints: []string{"https", "api-secure"},
|
||||||
|
Certificate: &tls.Certificate{
|
||||||
|
CertFile: tls.FileOrContent("tls-crt"),
|
||||||
|
KeyFile: tls.FileOrContent("tls-key"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tlsConfigs, err := getTLSConfigurations(test.ingress, test.client)
|
||||||
|
|
||||||
|
if test.errResult != "" {
|
||||||
|
assert.EqualError(t, err, test.errResult)
|
||||||
|
} else {
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, test.result, tlsConfigs)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -93,3 +93,13 @@
|
||||||
rule = "{{$route.Rule}}"
|
rule = "{{$route.Rule}}"
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{range $tlsConfiguration := .TLSConfiguration}}
|
||||||
|
[[tlsConfiguration]]
|
||||||
|
entryPoints = [{{range $tlsConfiguration.EntryPoints}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
[tlsConfiguration.certificate]
|
||||||
|
certFile = """{{$tlsConfiguration.Certificate.CertFile}}"""
|
||||||
|
keyFile = """{{$tlsConfiguration.Certificate.KeyFile}}"""
|
||||||
|
{{end}}
|
||||||
|
|
Loading…
Reference in a new issue