Skip to content

Commit

Permalink
ca from cm implemented
Browse files Browse the repository at this point in the history
Signed-off-by: Mattia Lavacca <[email protected]>
  • Loading branch information
mlavacca committed Dec 5, 2024
1 parent 343d922 commit 7800ee4
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 82 deletions.
80 changes: 45 additions & 35 deletions internal/annotations/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,35 +41,36 @@ const (

AnnotationPrefix = "konghq.com"

ConfigurationKey = "/override"
PluginsKey = "/plugins"
ProtocolKey = "/protocol"
ProtocolsKey = "/protocols"
ClientCertKey = "/client-cert"
StripPathKey = "/strip-path"
PathKey = "/path"
HTTPSRedirectCodeKey = "/https-redirect-status-code"
PreserveHostKey = "/preserve-host"
RegexPriorityKey = "/regex-priority"
HostHeaderKey = "/host-header"
MethodsKey = "/methods"
SNIsKey = "/snis"
RequestBuffering = "/request-buffering"
ResponseBuffering = "/response-buffering"
HostAliasesKey = "/host-aliases"
RegexPrefixKey = "/regex-prefix"
ConnectTimeoutKey = "/connect-timeout"
WriteTimeoutKey = "/write-timeout"
ReadTimeoutKey = "/read-timeout"
RetriesKey = "/retries"
HeadersKey = "/headers"
HeadersSeparatorKey = "/headers-separator"
PathHandlingKey = "/path-handling"
UserTagKey = "/tags"
RewriteURIKey = "/rewrite"
TLSVerifyKey = "/tls-verify"
TLSVerifyDepthKey = "/tls-verify-depth"
CACertificatesKey = "/ca-certificates"
ConfigurationKey = "/override"
PluginsKey = "/plugins"
ProtocolKey = "/protocol"
ProtocolsKey = "/protocols"
ClientCertKey = "/client-cert"
StripPathKey = "/strip-path"
PathKey = "/path"
HTTPSRedirectCodeKey = "/https-redirect-status-code"
PreserveHostKey = "/preserve-host"
RegexPriorityKey = "/regex-priority"
HostHeaderKey = "/host-header"
MethodsKey = "/methods"
SNIsKey = "/snis"
RequestBuffering = "/request-buffering"
ResponseBuffering = "/response-buffering"
HostAliasesKey = "/host-aliases"
RegexPrefixKey = "/regex-prefix"
ConnectTimeoutKey = "/connect-timeout"
WriteTimeoutKey = "/write-timeout"
ReadTimeoutKey = "/read-timeout"
RetriesKey = "/retries"
HeadersKey = "/headers"
HeadersSeparatorKey = "/headers-separator"
PathHandlingKey = "/path-handling"
UserTagKey = "/tags"
RewriteURIKey = "/rewrite"
TLSVerifyKey = "/tls-verify"
TLSVerifyDepthKey = "/tls-verify-depth"
CACertificatesFromSecretKey = "/ca-certificates-from-secret"
CACertificatesFromConfigMapKey = "/ca-certificates-from-configmap"

// GatewayClassUnmanagedKey is an annotation used on a Gateway resource to
// indicate that the GatewayClass should be reconciled according to unmanaged
Expand Down Expand Up @@ -395,10 +396,18 @@ func ExtractTLSVerifyDepth(anns map[string]string) (int, bool) {
return depth, true
}

// ExtractCACertificates extracts the ca-certificates secret names from the annotation.
// ExtractCACertificatesFromSecrets extracts the ca-certificates secret names from the annotation.
// It expects a comma-separated list of certificate names.
func ExtractCACertificates(anns map[string]string) []string {
s, ok := anns[AnnotationPrefix+CACertificatesKey]
func ExtractCACertificatesFromSecrets(anns map[string]string) []string {
s, ok := anns[AnnotationPrefix+CACertificatesFromSecretKey]
if !ok {
return nil
}
return extractCommaDelimitedStrings(s)
}

func ExtractCACertificatesFromConfigMap(anns map[string]string) []string {
s, ok := anns[AnnotationPrefix+CACertificatesFromConfigMapKey]
if !ok {
return nil
}
Expand Down Expand Up @@ -448,11 +457,11 @@ func SetTLSVerify(anns map[string]string, value bool) {

// 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]
existingCACerts := anns[AnnotationPrefix+CACertificatesFromConfigMapKey]
if existingCACerts == "" {
anns[AnnotationPrefix+CACertificatesKey] = strings.Join(certificates, ",")
anns[AnnotationPrefix+CACertificatesFromConfigMapKey] = strings.Join(certificates, ",")
} else {
anns[AnnotationPrefix+CACertificatesKey] = existingCACerts + "," + strings.Join(certificates, ",")
anns[AnnotationPrefix+CACertificatesFromConfigMapKey] = existingCACerts + "," + strings.Join(certificates, ",")
}
}

Expand All @@ -461,6 +470,7 @@ func SetHostHeader(anns map[string]string, value string) {
anns[AnnotationPrefix+HostHeaderKey] = value
}

// SetProtocol sets the protocol annotation value.
func SetProtocol(anns map[string]string, value string) {
anns[AnnotationPrefix+ProtocolKey] = value
}
10 changes: 5 additions & 5 deletions internal/annotations/annotations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1106,19 +1106,19 @@ func TestExtractTLSVerifyDepth(t *testing.T) {
}

func TestExtractCACertificates(t *testing.T) {
v := ExtractCACertificates(nil)
v := ExtractCACertificatesFromSecrets(nil)
assert.Empty(t, v)

v = ExtractCACertificates(map[string]string{})
v = ExtractCACertificatesFromSecrets(map[string]string{})
assert.Empty(t, v)

v = ExtractCACertificates(map[string]string{AnnotationPrefix + CACertificatesKey: "foo,bar"})
v = ExtractCACertificatesFromSecrets(map[string]string{AnnotationPrefix + CACertificatesFromSecretKey: "foo,bar"})
assert.Equal(t, []string{"foo", "bar"}, v, "expected to split by comma")

v = ExtractCACertificates(map[string]string{AnnotationPrefix + CACertificatesKey: " foo, bar ,baz "})
v = ExtractCACertificatesFromSecrets(map[string]string{AnnotationPrefix + CACertificatesFromSecretKey: " foo, bar ,baz "})
assert.Equal(t, []string{"foo", "bar", "baz"}, v, "expected to trim spaces")

v = ExtractCACertificates(map[string]string{AnnotationPrefix + CACertificatesKey: "foo, bar, "})
v = ExtractCACertificatesFromSecrets(map[string]string{AnnotationPrefix + CACertificatesFromSecretKey: "foo, bar, "})
assert.Equal(t, []string{"foo", "bar"}, v, "expected to ignore empty values")
}

Expand Down
57 changes: 39 additions & 18 deletions internal/dataplane/translator/ingressrules.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/kong/kubernetes-ingress-controller/v3/internal/annotations"
"github.com/kong/kubernetes-ingress-controller/v3/internal/dataplane/failures"
"github.com/kong/kubernetes-ingress-controller/v3/internal/dataplane/kongstate"
"github.com/kong/kubernetes-ingress-controller/v3/internal/gatewayapi"
"github.com/kong/kubernetes-ingress-controller/v3/internal/logging"
"github.com/kong/kubernetes-ingress-controller/v3/internal/store"
"github.com/kong/kubernetes-ingress-controller/v3/internal/util"
Expand Down Expand Up @@ -86,17 +87,17 @@ 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

// extract client certificates intended for use by the service
// convert the backendTLSPolicy targeting the service to the proper set of annotations.
ir.handleBackendTLSPolices(logger, s, k8sService, failuresCollector, translatedObjectsCollector)

// extract client certificates intended for use by the service.
ir.handleServiceClientCertificates(s, k8sService, &service, failuresCollector)

// extract CA certificates intended for use by the service
// extract CA certificates intended for use by the service.
ir.handleServiceCACertificates(s, k8sService, &service, failuresCollector)
}
service.Tags = ir.generateKongServiceTags(k8sServices, service, logger)
Expand Down Expand Up @@ -138,15 +139,13 @@ func (ir *ingressRules) handleBackendTLSPolices(
}

annotations.SetTLSVerify(k8sService.Annotations, true)

caCerts := []string{}
for _, cert := range policy.Spec.Validation.CACertificateRefs {
caCerts = append(caCerts, string(cert.Name))
}

annotations.SetCACertificates(k8sService.Annotations, caCerts)
annotations.SetHostHeader(k8sService.Annotations, string(policy.Spec.Validation.Hostname))
annotations.SetProtocol(k8sService.Annotations, "https")
annotations.SetCACertificates(k8sService.Annotations,
lo.Map(policy.Spec.Validation.CACertificateRefs, func(ref gatewayapi.LocalObjectReference, _ int) string {
return string(ref.Name)
}),
)
}

func (ir *ingressRules) handleServiceClientCertificates(
Expand Down Expand Up @@ -189,8 +188,9 @@ func (ir *ingressRules) handleServiceCACertificates(
k *kongstate.Service,
collector *failures.ResourceFailuresCollector,
) {
certificates := annotations.ExtractCACertificates(service.Annotations)
if len(certificates) == 0 {
Secretcertificates := annotations.ExtractCACertificatesFromSecrets(service.Annotations)
configMapCertificates := annotations.ExtractCACertificatesFromConfigMap(service.Annotations)
if len(Secretcertificates)+len(configMapCertificates) == 0 {
// No CA certificates to process.
return
}
Expand All @@ -214,8 +214,8 @@ func (ir *ingressRules) handleServiceCACertificates(
return
}

// Process each CA certificate and add it to the Kong Service.
for _, certificate := range certificates {
// Process each CA certificate from secret and add it to the Kong Service.
for _, certificate := range Secretcertificates {
secretKey := service.Namespace + "/" + certificate
secret, err := s.GetSecret(service.Namespace, certificate)
if err != nil {
Expand All @@ -225,10 +225,31 @@ func (ir *ingressRules) handleServiceCACertificates(
continue
}

certID, ok := secret.Data["id"]
certID, ok := secret.Data["ca.crt"]
if !ok {
collector.PushResourceFailure(
fmt.Sprintf("Invalid CA certificate '%s': missing 'ca.crt' field in data", secretKey), secret, service,
)
continue
}
k.CACertificates = append(k.CACertificates, lo.ToPtr(string(certID)))
}

// Process each CA certificate from ConfigMap and add it to the Kong Service.
for _, certificate := range configMapCertificates {
configmapKey := service.Namespace + "/" + certificate
configMap, err := s.GetConfigMap(service.Namespace, certificate)
if err != nil {
collector.PushResourceFailure(
fmt.Sprintf("Failed to fetch configmap for CA Certificate '%s': %v", configmapKey, err), service,
)
continue
}

certID, ok := configMap.Data["ca.crt"]
if !ok {
collector.PushResourceFailure(
fmt.Sprintf("Invalid CA certificate '%s': missing 'id' field in data", secretKey), secret, service,
fmt.Sprintf("Invalid CA certificate '%s': missing 'ca.crt' field in data", configmapKey), configMap, service,
)
continue
}
Expand Down
32 changes: 16 additions & 16 deletions internal/dataplane/translator/ingressrules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -654,9 +654,9 @@ func TestPopulateServices(t *testing.T) {
Name: "s-1",
Namespace: "test-namespace",
Annotations: map[string]string{
annotations.AnnotationPrefix + annotations.ProtocolKey: "https",
annotations.AnnotationPrefix + annotations.TLSVerifyKey: "true",
annotations.AnnotationPrefix + annotations.CACertificatesKey: "ca-1,ca-2",
annotations.AnnotationPrefix + annotations.ProtocolKey: "https",
annotations.AnnotationPrefix + annotations.TLSVerifyKey: "true",
annotations.AnnotationPrefix + annotations.CACertificatesFromSecretKey: "ca-1,ca-2",
},
},
},
Expand Down Expand Up @@ -717,8 +717,8 @@ func TestPopulateServices(t *testing.T) {
Name: "s-1",
Namespace: "test-namespace",
Annotations: map[string]string{
annotations.AnnotationPrefix + annotations.ProtocolKey: "https",
annotations.AnnotationPrefix + annotations.CACertificatesKey: "ca-1,ca-2",
annotations.AnnotationPrefix + annotations.ProtocolKey: "https",
annotations.AnnotationPrefix + annotations.CACertificatesFromSecretKey: "ca-1,ca-2",
},
},
},
Expand Down Expand Up @@ -773,9 +773,9 @@ func TestPopulateServices(t *testing.T) {
Name: "s-1",
Namespace: "test-namespace",
Annotations: map[string]string{
annotations.AnnotationPrefix + annotations.ProtocolKey: "https",
annotations.AnnotationPrefix + annotations.TLSVerifyKey: "true",
annotations.AnnotationPrefix + annotations.CACertificatesKey: "ca-not-existing",
annotations.AnnotationPrefix + annotations.ProtocolKey: "https",
annotations.AnnotationPrefix + annotations.TLSVerifyKey: "true",
annotations.AnnotationPrefix + annotations.CACertificatesFromSecretKey: "ca-not-existing",
},
},
},
Expand Down Expand Up @@ -807,9 +807,9 @@ func TestPopulateServices(t *testing.T) {
Name: "s-1",
Namespace: "test-namespace",
Annotations: map[string]string{
annotations.AnnotationPrefix + annotations.ProtocolKey: "https",
annotations.AnnotationPrefix + annotations.TLSVerifyKey: "true",
annotations.AnnotationPrefix + annotations.CACertificatesKey: "ca-1",
annotations.AnnotationPrefix + annotations.ProtocolKey: "https",
annotations.AnnotationPrefix + annotations.TLSVerifyKey: "true",
annotations.AnnotationPrefix + annotations.CACertificatesFromSecretKey: "ca-1",
},
},
},
Expand Down Expand Up @@ -853,9 +853,9 @@ func TestPopulateServices(t *testing.T) {
Name: "s-1",
Namespace: "test-namespace",
Annotations: map[string]string{
annotations.AnnotationPrefix + annotations.TLSVerifyKey: "true",
annotations.AnnotationPrefix + annotations.CACertificatesKey: "ca-1",
annotations.AnnotationPrefix + annotations.ProtocolKey: "grpc",
annotations.AnnotationPrefix + annotations.TLSVerifyKey: "true",
annotations.AnnotationPrefix + annotations.CACertificatesFromSecretKey: "ca-1",
annotations.AnnotationPrefix + annotations.ProtocolKey: "grpc",
},
},
},
Expand Down Expand Up @@ -900,8 +900,8 @@ func TestPopulateServices(t *testing.T) {
Name: "s-1",
Namespace: "test-namespace",
Annotations: map[string]string{
annotations.AnnotationPrefix + annotations.TLSVerifyKey: "true",
annotations.AnnotationPrefix + annotations.CACertificatesKey: "ca-1",
annotations.AnnotationPrefix + annotations.TLSVerifyKey: "true",
annotations.AnnotationPrefix + annotations.CACertificatesFromSecretKey: "ca-1",
},
},
},
Expand Down
8 changes: 4 additions & 4 deletions internal/dataplane/translator/translator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5295,10 +5295,10 @@ func TestTranslator_IngressUpstreamTLSVerification(t *testing.T) {
Name: "svc",
Namespace: "ns",
Annotations: map[string]string{
annotations.AnnotationPrefix + annotations.TLSVerifyKey: "true",
annotations.AnnotationPrefix + annotations.TLSVerifyDepthKey: "2",
annotations.AnnotationPrefix + annotations.CACertificatesKey: "ca",
annotations.AnnotationPrefix + annotations.ProtocolKey: "https",
annotations.AnnotationPrefix + annotations.TLSVerifyKey: "true",
annotations.AnnotationPrefix + annotations.TLSVerifyDepthKey: "2",
annotations.AnnotationPrefix + annotations.CACertificatesFromSecretKey: "ca",
annotations.AnnotationPrefix + annotations.ProtocolKey: "https",
},
},
Spec: corev1.ServiceSpec{
Expand Down
14 changes: 14 additions & 0 deletions internal/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type Storer interface {

// Core Kubernetes resources.
GetSecret(namespace, name string) (*corev1.Secret, error)
GetConfigMap(namespace, name string) (*corev1.ConfigMap, error)
GetService(namespace, name string) (*corev1.Service, error)
GetEndpointSlicesForService(namespace, name string) ([]*discoveryv1.EndpointSlice, error)
ListCACerts() ([]*corev1.Secret, error)
Expand Down Expand Up @@ -156,6 +157,19 @@ func (s Store) GetSecret(namespace, name string) (*corev1.Secret, error) {
return secret.(*corev1.Secret), nil
}

// GetConfigMap returns a ConfigMap using the namespace and name as key.
func (s Store) GetConfigMap(namespace, name string) (*corev1.ConfigMap, error) {
key := fmt.Sprintf("%v/%v", namespace, name)
configMap, exists, err := s.stores.ConfigMap.GetByKey(key)
if err != nil {
return nil, err
}
if !exists {
return nil, NotFoundError{fmt.Sprintf("ConfigMap %v not found", key)}
}
return configMap.(*corev1.ConfigMap), nil
}

// GetService returns a Service using the namespace and name as key.
func (s Store) GetService(namespace, name string) (*corev1.Service, error) {
key := fmt.Sprintf("%v/%v", namespace, name)
Expand Down
2 changes: 2 additions & 0 deletions internal/store/zz_generated.cache_stores.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions test/integration/isolated/ingress_verify_upstream_tls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,10 @@ func TestIngressVerifyUpstreamTLS(t *testing.T) {

t.Logf("Setting up service annotations for TLS verification")
service.Annotations = map[string]string{
annotations.AnnotationPrefix + annotations.ProtocolKey: "https", // Only https or tls are supported.
annotations.AnnotationPrefix + annotations.TLSVerifyKey: "true",
annotations.AnnotationPrefix + annotations.CACertificatesKey: strings.Join([]string{caSecretName, anotherCASecretName}, ","),
annotations.AnnotationPrefix + annotations.TLSVerifyDepthKey: "0", // First, we'll set it to 0 to make sure it fails.
annotations.AnnotationPrefix + annotations.ProtocolKey: "https", // Only https or tls are supported.
annotations.AnnotationPrefix + annotations.TLSVerifyKey: "true",
annotations.AnnotationPrefix + annotations.CACertificatesFromSecretKey: strings.Join([]string{caSecretName, anotherCASecretName}, ","),
annotations.AnnotationPrefix + annotations.TLSVerifyDepthKey: "0", // First, we'll set it to 0 to make sure it fails.
}
service, err = cluster.Client().CoreV1().Services(ns).Create(ctx, service, metav1.CreateOptions{})
assert.NoError(t, err)
Expand Down

0 comments on commit 7800ee4

Please sign in to comment.