diff --git a/CHANGELOG.md b/CHANGELOG.md index 1eda61b6a2..b03c6d97d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -126,6 +126,8 @@ Adding a new version? You'll need three changes: [#3125](https://github.com/Kong/kubernetes-ingress-controller/pull/3125) - Warning events are recorded when annotations in services backing a single route do not match. [#3130](https://github.com/Kong/kubernetes-ingress-controller/pull/3130) +- Warning events are recorded when a service's referred client-cert does not exist. + [#3137](https://github.com/Kong/kubernetes-ingress-controller/pull/3137) - CRDs' validations improvements: `UDPIngressRule.Port`, `IngressRule.Port` and `IngressBackend.ServiceName` instead of being validated in the Parser, are validated by the Kubernetes API now. [#3136](https://github.com/Kong/kubernetes-ingress-controller/pull/3136) diff --git a/internal/dataplane/parser/ingressrules.go b/internal/dataplane/parser/ingressrules.go index 8b46f2a4cb..bcc1e677c9 100644 --- a/internal/dataplane/parser/ingressrules.go +++ b/internal/dataplane/parser/ingressrules.go @@ -73,21 +73,21 @@ func (ir *ingressRules) populateServices(log logrus.FieldLogger, s store.Storer, // extract client certificates intended for use by the service secretName := annotations.ExtractClientCertificate(k8sService.Annotations) if secretName != "" { - secret, err := s.GetSecret(k8sService.Namespace, secretName) secretKey := k8sService.Namespace + "/" + secretName + secret, err := s.GetSecret(k8sService.Namespace, secretName) + if err != nil { + failuresCollector.PushTranslationFailure( + fmt.Sprintf("failed to fetch secret '%s': %v", secretKey, err), k8sService, + ) + continue + } + // ensure that the cert is loaded into Kong if _, ok := ir.SecretNameToSNIs[secretKey]; !ok { ir.SecretNameToSNIs[secretKey] = []string{} } - if err == nil { - service.ClientCertificate = &kong.Certificate{ - ID: kong.String(string(secret.UID)), - } - } else { - log.WithFields(logrus.Fields{ - "secret_name": secretName, - "secret_namespace": k8sService.Namespace, - }).Errorf("failed to fetch secret: %v", err) + service.ClientCertificate = &kong.Certificate{ + ID: kong.String(string(secret.UID)), } } } diff --git a/internal/dataplane/parser/parser_test.go b/internal/dataplane/parser/parser_test.go index 8c46108d4e..d8f7daf6b6 100644 --- a/internal/dataplane/parser/parser_test.go +++ b/internal/dataplane/parser/parser_test.go @@ -1142,7 +1142,7 @@ func TestServiceClientCertificate(t *testing.T) { assert.Nil(err) p := mustNewParser(t, store) state, translationFailures := p.Build() - require.Empty(t, translationFailures) + require.Len(t, translationFailures, 1) assert.NotNil(state) assert.Equal(0, len(state.Certificates), "expected no certificates to be rendered") @@ -2856,7 +2856,7 @@ func TestDefaultBackend(t *testing.T) { assert.Nil(err) p := mustNewParser(t, store) state, translationFailures := p.Build() - require.Empty(t, translationFailures) + require.Len(t, translationFailures, 1) assert.NotNil(state) assert.Equal(0, len(state.Certificates), "expected no certificates to be rendered") diff --git a/test/integration/translation_failures_test.go b/test/integration/translation_failures_test.go index a7fd223512..15f9f9e58c 100644 --- a/test/integration/translation_failures_test.go +++ b/test/integration/translation_failures_test.go @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" + netv1 "k8s.io/api/networking/v1" v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" @@ -115,6 +116,37 @@ func TestTranslationFailures(t *testing.T) { return []client.Object{service2} }, }, + { + name: "missing client-cert for service", + translationFailureTrigger: func(t *testing.T, cleaner *clusters.Cleaner, ns string) []client.Object { + service := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: testutils.RandomName(testTranslationFailuresObjectsPrefix), + Annotations: map[string]string{ + "konghq.com/client-cert": "not-existing-secret", + }, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Port: 80, + }, + }, + }, + } + service, err := env.Cluster().Client().CoreV1().Services(ns).Create(ctx, service, metav1.CreateOptions{}) + require.NoError(t, err) + + _, err = env.Cluster().Client().NetworkingV1().Ingresses(ns).Create( + ctx, + ingressWithPathBackedByService(service), + metav1.CreateOptions{}, + ) + require.NoError(t, err) + + return []client.Object{service} + }, + }, } for _, tt := range testCases { @@ -265,3 +297,39 @@ func httpRouteWithBackends(gatewayName string, services ...*corev1.Service) *gat }, } } + +func ingressWithPathBackedByService(service *corev1.Service) *netv1.Ingress { + pathType := netv1.PathTypePrefix + return &netv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: testutils.RandomName(testTranslationFailuresObjectsPrefix), + Annotations: map[string]string{ + annotations.IngressClassKey: ingressClass, + }, + }, + Spec: netv1.IngressSpec{ + Rules: []netv1.IngressRule{ + { + IngressRuleValue: netv1.IngressRuleValue{ + HTTP: &netv1.HTTPIngressRuleValue{ + Paths: []netv1.HTTPIngressPath{ + { + Path: "/", + PathType: &pathType, + Backend: netv1.IngressBackend{ + Service: &netv1.IngressServiceBackend{ + Name: service.Name, + Port: netv1.ServiceBackendPort{ + Number: 80, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +}