1
0
Fork 0

Auth support in frontends for k8s and file

This commit is contained in:
Mikael Rapp 2018-07-02 11:52:04 +02:00 committed by Traefiker Bot
parent e8e36bd9d5
commit bb14ec70bd
14 changed files with 867 additions and 181 deletions

View file

@ -5,33 +5,39 @@ import (
)
const (
annotationKubernetesIngressClass = "kubernetes.io/ingress.class"
annotationKubernetesAuthRealm = "ingress.kubernetes.io/auth-realm"
annotationKubernetesAuthType = "ingress.kubernetes.io/auth-type"
annotationKubernetesAuthSecret = "ingress.kubernetes.io/auth-secret"
annotationKubernetesRewriteTarget = "ingress.kubernetes.io/rewrite-target"
annotationKubernetesWhiteListSourceRange = "ingress.kubernetes.io/whitelist-source-range"
annotationKubernetesWhiteListUseXForwardedFor = "ingress.kubernetes.io/whitelist-x-forwarded-for"
annotationKubernetesPreserveHost = "ingress.kubernetes.io/preserve-host"
annotationKubernetesPassTLSCert = "ingress.kubernetes.io/pass-tls-cert"
annotationKubernetesFrontendEntryPoints = "ingress.kubernetes.io/frontend-entry-points"
annotationKubernetesPriority = "ingress.kubernetes.io/priority"
annotationKubernetesCircuitBreakerExpression = "ingress.kubernetes.io/circuit-breaker-expression"
annotationKubernetesLoadBalancerMethod = "ingress.kubernetes.io/load-balancer-method"
annotationKubernetesAffinity = "ingress.kubernetes.io/affinity"
annotationKubernetesSessionCookieName = "ingress.kubernetes.io/session-cookie-name"
annotationKubernetesRuleType = "ingress.kubernetes.io/rule-type"
annotationKubernetesRedirectEntryPoint = "ingress.kubernetes.io/redirect-entry-point"
annotationKubernetesRedirectPermanent = "ingress.kubernetes.io/redirect-permanent"
annotationKubernetesRedirectRegex = "ingress.kubernetes.io/redirect-regex"
annotationKubernetesRedirectReplacement = "ingress.kubernetes.io/redirect-replacement"
annotationKubernetesMaxConnAmount = "ingress.kubernetes.io/max-conn-amount"
annotationKubernetesMaxConnExtractorFunc = "ingress.kubernetes.io/max-conn-extractor-func"
annotationKubernetesRateLimit = "ingress.kubernetes.io/rate-limit"
annotationKubernetesErrorPages = "ingress.kubernetes.io/error-pages"
annotationKubernetesBuffering = "ingress.kubernetes.io/buffering"
annotationKubernetesAppRoot = "ingress.kubernetes.io/app-root"
annotationKubernetesServiceWeights = "ingress.kubernetes.io/service-weights"
annotationKubernetesIngressClass = "kubernetes.io/ingress.class"
annotationKubernetesAuthRealm = "ingress.kubernetes.io/auth-realm"
annotationKubernetesAuthType = "ingress.kubernetes.io/auth-type"
annotationKubernetesAuthSecret = "ingress.kubernetes.io/auth-secret"
annotationKubernetesAuthHeaderField = "ingress.kubernetes.io/auth-header-field"
annotationKubernetesAuthForwardResponseHeaders = "ingress.kubernetes.io/auth-response-headers"
annotationKubernetesAuthForwardURL = "ingress.kubernetes.io/auth-url"
annotationKubernetesAuthForwardTrustHeaders = "ingress.kubernetes.io/auth-trust-headers"
annotationKubernetesAuthForwardTLSSecret = "ingress.kubernetes.io/auth-tls-secret"
annotationKubernetesAuthForwardTLSInsecure = "ingress.kubernetes.io/auth-tls-insecure"
annotationKubernetesRewriteTarget = "ingress.kubernetes.io/rewrite-target"
annotationKubernetesWhiteListSourceRange = "ingress.kubernetes.io/whitelist-source-range"
annotationKubernetesWhiteListUseXForwardedFor = "ingress.kubernetes.io/whitelist-x-forwarded-for"
annotationKubernetesPreserveHost = "ingress.kubernetes.io/preserve-host"
annotationKubernetesPassTLSCert = "ingress.kubernetes.io/pass-tls-cert"
annotationKubernetesFrontendEntryPoints = "ingress.kubernetes.io/frontend-entry-points"
annotationKubernetesPriority = "ingress.kubernetes.io/priority"
annotationKubernetesCircuitBreakerExpression = "ingress.kubernetes.io/circuit-breaker-expression"
annotationKubernetesLoadBalancerMethod = "ingress.kubernetes.io/load-balancer-method"
annotationKubernetesAffinity = "ingress.kubernetes.io/affinity"
annotationKubernetesSessionCookieName = "ingress.kubernetes.io/session-cookie-name"
annotationKubernetesRuleType = "ingress.kubernetes.io/rule-type"
annotationKubernetesRedirectEntryPoint = "ingress.kubernetes.io/redirect-entry-point"
annotationKubernetesRedirectPermanent = "ingress.kubernetes.io/redirect-permanent"
annotationKubernetesRedirectRegex = "ingress.kubernetes.io/redirect-regex"
annotationKubernetesRedirectReplacement = "ingress.kubernetes.io/redirect-replacement"
annotationKubernetesMaxConnAmount = "ingress.kubernetes.io/max-conn-amount"
annotationKubernetesMaxConnExtractorFunc = "ingress.kubernetes.io/max-conn-extractor-func"
annotationKubernetesRateLimit = "ingress.kubernetes.io/rate-limit"
annotationKubernetesErrorPages = "ingress.kubernetes.io/error-pages"
annotationKubernetesBuffering = "ingress.kubernetes.io/buffering"
annotationKubernetesAppRoot = "ingress.kubernetes.io/app-root"
annotationKubernetesServiceWeights = "ingress.kubernetes.io/service-weights"
annotationKubernetesSSLForceHost = "ingress.kubernetes.io/ssl-force-host"
annotationKubernetesSSLRedirect = "ingress.kubernetes.io/ssl-redirect"

View file

@ -208,9 +208,52 @@ func entryPoints(eps ...string) func(*types.Frontend) {
}
}
func basicAuth(auth ...string) func(*types.Frontend) {
// Deprecated
func basicAuthDeprecated(auth ...string) func(*types.Frontend) {
return func(f *types.Frontend) {
f.BasicAuth = auth
f.Auth = &types.Auth{Basic: &types.Basic{Users: auth}}
}
}
func auth(opt func(*types.Auth)) func(*types.Frontend) {
return func(f *types.Frontend) {
auth := &types.Auth{}
opt(auth)
f.Auth = auth
}
}
func basicAuth(users ...string) func(*types.Auth) {
return func(a *types.Auth) {
a.Basic = &types.Basic{Users: users}
}
}
func forwardAuth(forwardURL string, opts ...func(*types.Forward)) func(*types.Auth) {
return func(a *types.Auth) {
fwd := &types.Forward{Address: forwardURL}
for _, opt := range opts {
opt(fwd)
}
a.Forward = fwd
}
}
func fwdAuthResponseHeaders(headers ...string) func(*types.Forward) {
return func(f *types.Forward) {
f.AuthResponseHeaders = headers
}
}
func fwdTrustForwardHeader() func(*types.Forward) {
return func(f *types.Forward) {
f.TrustForwardHeader = true
}
}
func fwdAuthTLS(cert, key string, insecure bool) func(*types.Forward) {
return func(f *types.Forward) {
f.TLS = &types.ClientTLS{Cert: cert, Key: key, InsecureSkipVerify: insecure}
}
}

View file

@ -221,9 +221,9 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
}
if _, exists := templateObjects.Frontends[baseName]; !exists {
basicAuthCreds, err := handleBasicAuthConfig(i, k8sClient)
auth, err := getAuthConfig(i, k8sClient)
if err != nil {
log.Errorf("Failed to retrieve basic auth configuration for ingress %s/%s: %s", i.Namespace, i.Name, err)
log.Errorf("Failed to retrieve auth configuration for ingress %s/%s: %s", i.Namespace, i.Name, err)
continue
}
@ -238,13 +238,13 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
PassTLSCert: passTLSCert,
Routes: make(map[string]types.Route),
Priority: priority,
BasicAuth: basicAuthCreds,
WhiteList: getWhiteList(i),
Redirect: getFrontendRedirect(i),
EntryPoints: entryPoints,
Headers: getHeader(i),
Errors: getErrorPages(i),
RateLimit: getRateLimit(i),
Auth: auth,
}
}
@ -438,67 +438,11 @@ func getRuleForHost(host string) string {
return "Host:" + host
}
func handleBasicAuthConfig(i *extensionsv1beta1.Ingress, k8sClient Client) ([]string, error) {
annotationAuthType := getAnnotationName(i.Annotations, annotationKubernetesAuthType)
authType, exists := i.Annotations[annotationAuthType]
if !exists {
return nil, nil
}
if strings.ToLower(authType) != "basic" {
return nil, fmt.Errorf("unsupported auth-type on annotation ingress.kubernetes.io/auth-type: %q", authType)
}
authSecret := getStringValue(i.Annotations, annotationKubernetesAuthSecret, "")
if authSecret == "" {
return nil, errors.New("auth-secret annotation ingress.kubernetes.io/auth-secret must be set")
}
basicAuthCreds, err := loadAuthCredentials(i.Namespace, authSecret, k8sClient)
if err != nil {
return nil, fmt.Errorf("failed to load auth credentials: %s", err)
}
return basicAuthCreds, nil
}
func loadAuthCredentials(namespace, secretName string, k8sClient Client) ([]string, error) {
secret, ok, err := k8sClient.GetSecret(namespace, secretName)
switch { // keep order of case conditions
case err != nil:
return nil, fmt.Errorf("failed to fetch secret %q/%q: %s", namespace, secretName, err)
case !ok:
return nil, fmt.Errorf("secret %q/%q not found", namespace, secretName)
case secret == nil:
return nil, fmt.Errorf("data for secret %q/%q must not be nil", namespace, secretName)
case len(secret.Data) != 1:
return nil, fmt.Errorf("found %d elements for secret %q/%q, must be single element exactly", len(secret.Data), namespace, secretName)
default:
}
var firstSecret []byte
for _, v := range secret.Data {
firstSecret = v
break
}
creds := make([]string, 0)
scanner := bufio.NewScanner(bytes.NewReader(firstSecret))
for scanner.Scan() {
if cred := scanner.Text(); cred != "" {
creds = append(creds, cred)
}
}
if len(creds) == 0 {
return nil, fmt.Errorf("secret %q/%q does not contain any credentials", namespace, secretName)
}
return creds, nil
}
func getTLS(ingress *extensionsv1beta1.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)
secret, 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)
}
@ -506,19 +450,9 @@ func getTLS(ingress *extensionsv1beta1.Ingress, k8sClient Client) ([]*tls.Config
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, ", "))
cert, key, err := getCertificateBlocks(secret, ingress.Namespace, t.SecretName)
if err != nil {
return nil, err
}
entryPoints := getSliceStringValue(ingress.Annotations, annotationKubernetesFrontendEntryPoints)
@ -526,8 +460,8 @@ func getTLS(ingress *extensionsv1beta1.Ingress, k8sClient Client) ([]*tls.Config
tlsConfig := &tls.Configuration{
EntryPoints: entryPoints,
Certificate: &tls.Certificate{
CertFile: tls.FileOrContent(tlsCrtData),
KeyFile: tls.FileOrContent(tlsKeyData),
CertFile: tls.FileOrContent(cert),
KeyFile: tls.FileOrContent(key),
},
}
@ -537,6 +471,42 @@ func getTLS(ingress *extensionsv1beta1.Ingress, k8sClient Client) ([]*tls.Config
return tlsConfigs, nil
}
func getCertificateBlocks(secret *corev1.Secret, namespace, secretName string) (string, string, error) {
var missingEntries []string
tlsCrtData, tlsCrtExists := secret.Data["tls.crt"]
if !tlsCrtExists {
missingEntries = append(missingEntries, "tls.crt")
}
tlsKeyData, tlsKeyExists := secret.Data["tls.key"]
if !tlsKeyExists {
missingEntries = append(missingEntries, "tls.key")
}
if len(missingEntries) > 0 {
return "", "", fmt.Errorf("secret %s/%s is missing the following TLS data entries: %s",
namespace, secretName, strings.Join(missingEntries, ", "))
}
cert := string(tlsCrtData[:])
if cert == "" {
missingEntries = append(missingEntries, "tls.crt")
}
key := string(tlsKeyData[:])
if key == "" {
missingEntries = append(missingEntries, "tls.key")
}
if len(missingEntries) > 0 {
return "", "", fmt.Errorf("secret %s/%s contains the following empty TLS data entries: %s",
namespace, secretName, strings.Join(missingEntries, ", "))
}
return cert, key, nil
}
// endpointPortNumber returns the port to be used for this endpoint. It is zero
// if the endpoint does not match the given service port.
func endpointPortNumber(servicePort corev1.ServicePort, endpointPorts []corev1.EndpointPort) int32 {
@ -573,6 +543,160 @@ func (p *Provider) shouldProcessIngress(annotationIngressClass string) bool {
return annotationIngressClass == p.IngressClass
}
func getAuthConfig(i *extensionsv1beta1.Ingress, k8sClient Client) (*types.Auth, error) {
authType := getStringValue(i.Annotations, annotationKubernetesAuthType, "")
if len(authType) == 0 {
return nil, nil
}
auth := &types.Auth{
HeaderField: getStringValue(i.Annotations, annotationKubernetesAuthHeaderField, ""),
}
switch strings.ToLower(authType) {
case "basic":
basic, err := getBasicAuthConfig(i, k8sClient)
if err != nil {
return nil, err
}
auth.Basic = basic
case "digest":
digest, err := getDigestAuthConfig(i, k8sClient)
if err != nil {
return nil, err
}
auth.Digest = digest
case "forward":
forward, err := getForwardAuthConfig(i, k8sClient)
if err != nil {
return nil, err
}
auth.Forward = forward
default:
return nil, fmt.Errorf("unsupported auth-type on annotation %s: %s", annotationKubernetesAuthType, authType)
}
return auth, nil
}
func getBasicAuthConfig(i *extensionsv1beta1.Ingress, k8sClient Client) (*types.Basic, error) {
credentials, err := getAuthCredentials(i, k8sClient)
if err != nil {
return nil, err
}
return &types.Basic{Users: credentials}, nil
}
func getDigestAuthConfig(i *extensionsv1beta1.Ingress, k8sClient Client) (*types.Digest, error) {
credentials, err := getAuthCredentials(i, k8sClient)
if err != nil {
return nil, err
}
return &types.Digest{Users: credentials}, nil
}
func getAuthCredentials(i *extensionsv1beta1.Ingress, k8sClient Client) ([]string, error) {
authSecret := getStringValue(i.Annotations, annotationKubernetesAuthSecret, "")
if authSecret == "" {
return nil, fmt.Errorf("auth-secret annotation %s must be set", annotationKubernetesAuthSecret)
}
auth, err := loadAuthCredentials(i.Namespace, authSecret, k8sClient)
if err != nil {
return nil, fmt.Errorf("failed to load auth credentials: %s", err)
}
return auth, nil
}
func loadAuthCredentials(namespace, secretName string, k8sClient Client) ([]string, error) {
secret, ok, err := k8sClient.GetSecret(namespace, secretName)
if err != nil {
return nil, fmt.Errorf("failed to fetch secret %q/%q: %s", namespace, secretName, err)
}
if !ok {
return nil, fmt.Errorf("secret %q/%q not found", namespace, secretName)
}
if secret == nil {
return nil, fmt.Errorf("data for secret %q/%q must not be nil", namespace, secretName)
}
if len(secret.Data) != 1 {
return nil, fmt.Errorf("found %d elements for secret %q/%q, must be single element exactly", len(secret.Data), namespace, secretName)
}
var firstSecret []byte
for _, v := range secret.Data {
firstSecret = v
break
}
var credentials []string
scanner := bufio.NewScanner(bytes.NewReader(firstSecret))
for scanner.Scan() {
if cred := scanner.Text(); len(cred) > 0 {
credentials = append(credentials, cred)
}
}
if len(credentials) == 0 {
return nil, fmt.Errorf("secret %q/%q does not contain any credentials", namespace, secretName)
}
return credentials, nil
}
func getForwardAuthConfig(i *extensionsv1beta1.Ingress, k8sClient Client) (*types.Forward, error) {
authURL := getStringValue(i.Annotations, annotationKubernetesAuthForwardURL, "")
if len(authURL) == 0 {
return nil, fmt.Errorf("forward authentication requires a url")
}
forwardAuth := &types.Forward{
Address: authURL,
TrustForwardHeader: getBoolValue(i.Annotations, annotationKubernetesAuthForwardTrustHeaders, false),
AuthResponseHeaders: getSliceStringValue(i.Annotations, annotationKubernetesAuthForwardResponseHeaders),
}
authSecretName := getStringValue(i.Annotations, annotationKubernetesAuthForwardTLSSecret, "")
if len(authSecretName) > 0 {
authSecretCert, authSecretKey, err := loadAuthTLSSecret(i.Namespace, authSecretName, k8sClient)
if err != nil {
return nil, fmt.Errorf("failed to load auth secret: %s", err)
}
forwardAuth.TLS = &types.ClientTLS{
Cert: authSecretCert,
Key: authSecretKey,
InsecureSkipVerify: getBoolValue(i.Annotations, annotationKubernetesAuthForwardTLSInsecure, false),
}
}
return forwardAuth, nil
}
func loadAuthTLSSecret(namespace, secretName string, k8sClient Client) (string, string, error) {
secret, exists, err := k8sClient.GetSecret(namespace, secretName)
if err != nil {
return "", "", fmt.Errorf("failed to fetch secret %q/%q: %s", namespace, secretName, err)
}
if !exists {
return "", "", fmt.Errorf("secret %q/%q does not exist", namespace, secretName)
}
if secret == nil {
return "", "", fmt.Errorf("data for secret %q/%q must not be nil", namespace, secretName)
}
if len(secret.Data) != 2 {
return "", "", fmt.Errorf("found %d elements for secret %q/%q, must be two elements exactly", len(secret.Data), namespace, secretName)
}
return getCertificateBlocks(secret, namespace, secretName)
}
func getFrontendRedirect(i *extensionsv1beta1.Ingress) *types.Redirect {
permanent := getBoolValue(i.Annotations, annotationKubernetesRedirectPermanent, false)

View file

@ -4,7 +4,6 @@ import (
"errors"
"fmt"
"os"
"reflect"
"testing"
"time"
@ -1032,7 +1031,7 @@ rateset:
),
frontend("basic/auth",
passHostHeader(),
basicAuth("myUser:myEncodedPW"),
basicAuthDeprecated("myUser:myEncodedPW"),
routes(
route("/auth", "PathPrefix:/auth"),
route("basic", "Host:basic")),
@ -1680,7 +1679,7 @@ func TestMissingResources(t *testing.T) {
assert.Equal(t, expected, actual)
}
func TestBasicAuthInTemplate(t *testing.T) {
func TestLoadIngressesBasicAuth(t *testing.T) {
ingresses := []*extensionsv1beta1.Ingress{
buildIngress(
iNamespace("testing"),
@ -1734,9 +1733,372 @@ func TestBasicAuthInTemplate(t *testing.T) {
actual = provider.loadConfig(*actual)
require.NotNil(t, actual)
got := actual.Frontends["basic/auth"].BasicAuth
if !reflect.DeepEqual(got, []string{"myUser:myEncodedPW"}) {
t.Fatalf("unexpected credentials: %+v", got)
got := actual.Frontends["basic/auth"].Auth.Basic.Users
assert.Equal(t, types.Users{"myUser:myEncodedPW"}, got)
}
func TestLoadIngressesForwardAuth(t *testing.T) {
ingresses := []*extensionsv1beta1.Ingress{
buildIngress(
iNamespace("testing"),
iAnnotation(annotationKubernetesAuthType, "forward"),
iAnnotation(annotationKubernetesAuthForwardURL, "https://auth.host"),
iAnnotation(annotationKubernetesAuthForwardTrustHeaders, "true"),
iAnnotation(annotationKubernetesAuthForwardResponseHeaders, "X-Auth,X-Test,X-Secret"),
iRules(
iRule(iHost("foo"),
iPaths(
onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))))),
),
),
}
services := []*corev1.Service{
buildService(
sName("service1"),
sNamespace("testing"),
sUID("1"),
sSpec(
clusterIP("10.0.0.1"),
sPorts(sPort(80, ""))),
),
}
endpoints := []*corev1.Endpoints{
buildEndpoint(
eNamespace("testing"),
eName("service1"),
eUID("1"),
subset(
eAddresses(eAddress("10.10.0.1")),
ePorts(ePort(8080, ""))),
),
}
watchChan := make(chan interface{})
client := clientMock{
ingresses: ingresses,
services: services,
endpoints: endpoints,
watchChan: watchChan,
}
provider := Provider{}
actual, err := provider.loadIngresses(client)
require.NoError(t, err, "error loading ingresses")
expected := buildConfiguration(
backends(
backend("foo/bar",
lbMethod("wrr"),
servers(
server("http://10.10.0.1:8080", weight(1))),
),
),
frontends(
frontend("foo/bar",
passHostHeader(),
auth(forwardAuth("https://auth.host",
fwdTrustForwardHeader(),
fwdAuthResponseHeaders("X-Auth", "X-Test", "X-Secret"))),
routes(
route("/bar", "PathPrefix:/bar"),
route("foo", "Host:foo")),
),
),
)
assert.Equal(t, expected, actual)
}
func TestLoadIngressesForwardAuthMissingURL(t *testing.T) {
ingresses := []*extensionsv1beta1.Ingress{
buildIngress(
iNamespace("testing"),
iAnnotation(annotationKubernetesAuthType, "forward"),
iRules(
iRule(iHost("foo"),
iPaths(
onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))))),
),
),
}
services := []*corev1.Service{
buildService(
sName("service1"),
sNamespace("testing"),
sUID("1"),
sSpec(
clusterIP("10.0.0.1"),
sPorts(sPort(80, ""))),
),
}
endpoints := []*corev1.Endpoints{
buildEndpoint(
eNamespace("testing"),
eName("service1"),
eUID("1"),
subset(
eAddresses(eAddress("10.10.0.1")),
ePorts(ePort(8080, ""))),
),
}
watchChan := make(chan interface{})
client := clientMock{
ingresses: ingresses,
services: services,
endpoints: endpoints,
watchChan: watchChan,
}
provider := Provider{}
actual, err := provider.loadIngresses(client)
require.NoError(t, err, "error loading ingresses")
expected := buildConfiguration(
backends(
backend("foo/bar",
lbMethod("wrr"),
servers(),
),
),
frontends(),
)
assert.Equal(t, expected, actual)
}
func TestLoadIngressesForwardAuthWithTLSSecret(t *testing.T) {
ingresses := []*extensionsv1beta1.Ingress{
buildIngress(
iNamespace("testing"),
iAnnotation(annotationKubernetesAuthType, "forward"),
iAnnotation(annotationKubernetesAuthForwardURL, "https://auth.host"),
iAnnotation(annotationKubernetesAuthForwardTLSSecret, "secret"),
iAnnotation(annotationKubernetesAuthForwardTLSInsecure, "true"),
iRules(
iRule(iHost("foo"),
iPaths(
onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))))),
),
),
}
secrets := []*corev1.Secret{{
ObjectMeta: metav1.ObjectMeta{
Name: "secret",
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-----"),
},
}}
services := []*corev1.Service{
buildService(
sName("service1"),
sNamespace("testing"),
sUID("1"),
sSpec(
clusterIP("10.0.0.1"),
sPorts(sPort(80, ""))),
),
}
endpoints := []*corev1.Endpoints{
buildEndpoint(
eNamespace("testing"),
eName("service1"),
eUID("1"),
subset(
eAddresses(eAddress("10.10.0.1")),
ePorts(ePort(8080, ""))),
),
}
watchChan := make(chan interface{})
client := clientMock{
ingresses: ingresses,
services: services,
endpoints: endpoints,
secrets: secrets,
watchChan: watchChan,
}
provider := Provider{}
actual, err := provider.loadIngresses(client)
require.NoError(t, err, "error loading ingresses")
expected := buildConfiguration(
backends(
backend("foo/bar",
lbMethod("wrr"),
servers(
server("http://10.10.0.1:8080", weight(1))),
),
),
frontends(
frontend("foo/bar",
passHostHeader(),
auth(
forwardAuth("https://auth.host",
fwdAuthTLS(
"-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----",
"-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----",
true))),
routes(
route("/bar", "PathPrefix:/bar"),
route("foo", "Host:foo")),
),
),
)
assert.Equal(t, expected, actual)
}
func TestLoadIngressesForwardAuthWithTLSSecretFailures(t *testing.T) {
tests := []struct {
desc string
secretName string
certName string
certData string
keyName string
keyData string
}{
{
desc: "empty certificate and key",
secretName: "secret",
certName: "",
certData: "",
keyName: "",
keyData: "",
},
{
desc: "wrong secret name, empty certificate and key",
secretName: "wrongSecret",
certName: "",
certData: "",
keyName: "",
keyData: "",
},
{
desc: "empty certificate data",
secretName: "secret",
certName: "tls.crt",
certData: "",
keyName: "tls.key",
keyData: "-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----",
},
{
desc: "empty key data",
secretName: "secret",
certName: "tls.crt",
certData: "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----",
keyName: "tls.key",
keyData: "",
},
{
desc: "wrong cert name",
secretName: "secret",
certName: "cert.crt",
certData: "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE----",
keyName: "tls.key",
keyData: "-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----",
},
{
desc: "wrong key name",
secretName: "secret",
certName: "tls.crt",
certData: "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----",
keyName: "cert.key",
keyData: "-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----",
},
}
ingresses := []*extensionsv1beta1.Ingress{
buildIngress(
iNamespace("testing"),
iAnnotation(annotationKubernetesAuthType, "forward"),
iAnnotation(annotationKubernetesAuthForwardURL, "https://auth.host"),
iAnnotation(annotationKubernetesAuthForwardTLSSecret, "secret"),
iRules(
iRule(iHost("foo"),
iPaths(
onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))))),
),
),
}
services := []*corev1.Service{
buildService(
sName("service1"),
sNamespace("testing"),
sUID("1"),
sSpec(
clusterIP("10.0.0.1"),
sPorts(sPort(80, ""))),
),
}
endpoints := []*corev1.Endpoints{
buildEndpoint(
eNamespace("testing"),
eName("service1"),
eUID("1"),
subset(
eAddresses(eAddress("10.10.0.1")),
ePorts(ePort(8080, ""))),
),
}
for _, test := range tests {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
secrets := []*corev1.Secret{{
ObjectMeta: metav1.ObjectMeta{
Name: test.secretName,
UID: "1",
Namespace: "testing",
},
Data: map[string][]byte{
test.certName: []byte(test.certData),
test.keyName: []byte(test.keyData),
},
}}
watchChan := make(chan interface{})
client := clientMock{
ingresses: ingresses,
services: services,
endpoints: endpoints,
secrets: secrets,
watchChan: watchChan,
}
provider := Provider{}
actual, err := provider.loadIngresses(client)
require.NoError(t, err, "error loading ingresses")
expected := buildConfiguration(
backends(
backend("foo/bar",
lbMethod("wrr"),
servers(),
),
),
frontends(),
)
assert.Equal(t, expected, actual)
})
}
}