From 03b4d14b56f2a21940c4ca20e895419bf9211fd5 Mon Sep 17 00:00:00 2001 From: Mattia Lavacca Date: Mon, 2 Dec 2024 18:04:29 +0100 Subject: [PATCH] feat: convert BackendTLSPolicies into service anns Signed-off-by: Mattia Lavacca --- internal/annotations/annotations.go | 20 ++++++++ internal/dataplane/translator/ingressrules.go | 47 +++++++++++++++++++ internal/store/store.go | 27 +++++++++-- 3 files changed, 91 insertions(+), 3 deletions(-) diff --git a/internal/annotations/annotations.go b/internal/annotations/annotations.go index dfa42f4436..1a0b360d26 100644 --- a/internal/annotations/annotations.go +++ b/internal/annotations/annotations.go @@ -440,3 +440,23 @@ func extractCommaDelimitedStrings(s string, sanitizeFns ...func(string) string) return out } + +// SetTLSVerify sets the tls-verify annotation value. +func SetTLSVerify(anns map[string]string, value bool) { + anns[AnnotationPrefix+TLSVerifyKey] = strconv.FormatBool(value) +} + +// SetCACertificates merge the ca-certificates secret names into the already existing CA certificates set via annotation. +func SetCACertificates(anns map[string]string, certificates []string) { + existingCACerts := anns[AnnotationPrefix+CACertificatesKey] + if existingCACerts == "" { + anns[AnnotationPrefix+CACertificatesKey] = strings.Join(certificates, ",") + } else { + anns[AnnotationPrefix+CACertificatesKey] = existingCACerts + "," + strings.Join(certificates, ",") + } +} + +// SetHostHeader sets the host-header annotation value. +func SetHostHeader(anns map[string]string, value string) { + anns[AnnotationPrefix+HostHeaderKey] = value +} diff --git a/internal/dataplane/translator/ingressrules.go b/internal/dataplane/translator/ingressrules.go index 482770ae95..32bfa9d500 100644 --- a/internal/dataplane/translator/ingressrules.go +++ b/internal/dataplane/translator/ingressrules.go @@ -86,6 +86,9 @@ func (ir *ingressRules) populateServices( } for _, k8sService := range k8sServices { + // TODO: comment + ir.handleBackendTLSPolices(logger, s, k8sService, failuresCollector, translatedObjectsCollector) + // at this point we know the Kubernetes service itself is valid and can be // used for traffic, so cache it amongst the kong Services k8s services. service.K8sServices[fmt.Sprintf("%s/%s", k8sService.Namespace, k8sService.Name)] = k8sService @@ -105,6 +108,50 @@ func (ir *ingressRules) populateServices( return serviceNamesToSkip } +func (ir *ingressRules) handleBackendTLSPolices( + logger logr.Logger, + s store.Storer, + k8sService *corev1.Service, + failuresCollector *failures.ResourceFailuresCollector, + translatedObjectsCollector *ObjectsCollector, +) { + policies, err := s.ListBackendTLSPoliciesByTargetService(client.ObjectKeyFromObject(k8sService)) + if err != nil { + failuresCollector.PushResourceFailure( + fmt.Sprintf("Failed to list backendTLSPolicies: %v", err), k8sService, + ) + return + } + if len(policies) == 0 { + return + } + if len(policies) > 1 { + failuresCollector.PushResourceFailure( + "Multiple BackendTLSPolicies attached to service", k8sService, + ) + return + } + + if k8sService.Annotations == nil { + k8sService.Annotations = make(map[string]string) + } + + annotations.SetTLSVerify(k8sService.Annotations, true) + caCerts := []string{} + + policy := policies[0] + + // TODO: change as this needs to be a configmap according to the spec + for _, cert := range policy.Spec.Validation.CACertificateRefs { + if cert.Group != "core" && cert.Group != "" || cert.Kind != "Secret" { + continue + } + caCerts = append(caCerts, string(cert.Name)) + } + annotations.SetCACertificates(k8sService.Annotations, caCerts) + annotations.SetHostHeader(k8sService.Annotations, string(policy.Spec.Validation.Hostname)) +} + func (ir *ingressRules) handleServiceClientCertificates( s store.Storer, k8sService *corev1.Service, diff --git a/internal/store/store.go b/internal/store/store.go index 73576a0a15..357728e6ad 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -22,6 +22,7 @@ import ( "strings" "github.com/go-logr/logr" + "github.com/samber/lo" corev1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" netv1 "k8s.io/api/networking/v1" @@ -30,6 +31,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/selection" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/cache" "sigs.k8s.io/controller-runtime/pkg/client" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" @@ -102,7 +104,7 @@ type Storer interface { ListGRPCRoutes() ([]*gatewayapi.GRPCRoute, error) ListReferenceGrants() ([]*gatewayapi.ReferenceGrant, error) ListGateways() ([]*gatewayapi.Gateway, error) - ListBackendTLSPolicies() ([]*gatewayapi.BackendTLSPolicy, error) + ListBackendTLSPoliciesByTargetService(service types.NamespacedName) ([]*gatewayapi.BackendTLSPolicy, error) } // Store implements Storer and can be used to list Ingress, Services @@ -337,8 +339,27 @@ func (s Store) ListGateways() ([]*gatewayapi.Gateway, error) { return List[*gatewayapi.Gateway](s.stores) } -func (s Store) ListBackendTLSPolicies() ([]*gatewayapi.BackendTLSPolicy, error) { - return List[*gatewayapi.BackendTLSPolicy](s.stores) +// ListBackendTLSPoliciesByTargetService returns the list of BackendTLSPolicies in the BackendTLSPolicy cache store. +// The policies are filtered by the target service. +// TODO: implement indexers to speed up the lookup. +func (s Store) ListBackendTLSPoliciesByTargetService(service types.NamespacedName) ([]*gatewayapi.BackendTLSPolicy, error) { + policiesToReturn := []*gatewayapi.BackendTLSPolicy{} + policies, err := List[*gatewayapi.BackendTLSPolicy](s.stores) + if err != nil { + return nil, err + } + + for _, policy := range policies { + if lo.ContainsBy(policy.Spec.TargetRefs, func(ref gatewayapi.LocalPolicyTargetReferenceWithSectionName) bool { + return (ref.Group == "" || ref.Group == "core") && + ref.Kind == "Service" && + (policy.Namespace == service.Namespace && string(ref.Name) == service.Name) + }) { + policiesToReturn = append(policiesToReturn, policy) + } + } + + return policiesToReturn, nil } // ListTCPIngresses returns the list of TCP Ingresses from