Skip to content

Commit ffd9251

Browse files
committed
feat: support crls in client traffic policies
Signed-off-by: Rudrakh Panigrahi <[email protected]>
1 parent d4e44fd commit ffd9251

20 files changed

+1242
-108
lines changed

api/v1alpha1/tls_types.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@ type ClientValidationContext struct {
167167

168168
// Crl specifies the crl configuration that can be used to validate the client initiating the TLS connection
169169
// +optional
170-
// +notImplementedHide
171170
Crl *CrlContext `json:"crl,omitempty"`
172171
}
173172

internal/gatewayapi/backendtlspolicy.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ func getCaCertsFromCARefs(namespace string, caCertificates []gwapiv1.LocalObject
468468
case resource.KindConfigMap:
469469
cm := resources.GetConfigMap(namespace, string(caRef.Name))
470470
if cm != nil {
471-
if crt, dataOk := getCaCertFromConfigMap(cm); dataOk {
471+
if crt, dataOk := getOrFirstFromData(cm.Data, caCertKey); dataOk {
472472
if ca != "" {
473473
ca += "\n"
474474
}
@@ -482,7 +482,7 @@ func getCaCertsFromCARefs(namespace string, caCertificates []gwapiv1.LocalObject
482482
case resource.KindSecret:
483483
secret := resources.GetSecret(namespace, string(caRef.Name))
484484
if secret != nil {
485-
if crt, dataOk := getCaCertFromSecret(secret); dataOk {
485+
if crt, dataOk := getOrFirstFromData(secret.Data, caCertKey); dataOk {
486486
if ca != "" {
487487
ca += "\n"
488488
}

internal/gatewayapi/clienttrafficpolicy.go

Lines changed: 65 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -857,56 +857,43 @@ func (t *Translator) buildListenerTLSParameters(policy *egv1a1.ClientTrafficPoli
857857
}
858858

859859
for _, caCertRef := range tlsParams.ClientValidation.CACertificateRefs {
860-
caCertRefKind := string(ptr.Deref(caCertRef.Kind, resource.KindSecret))
861-
var caCertBytes []byte
862-
switch caCertRefKind {
863-
case resource.KindSecret:
864-
secret, err := t.validateSecretRef(false, from, caCertRef, resources)
865-
if err != nil {
866-
return irTLSConfig, err
867-
}
868-
869-
secretCertBytes, ok := getCaCertFromSecret(secret)
870-
if !ok || len(secretCertBytes) == 0 {
871-
return irTLSConfig, fmt.Errorf(
872-
"caCertificateRef secret [%s] not found", caCertRef.Name)
873-
}
874-
caCertBytes = secretCertBytes
875-
case resource.KindConfigMap:
876-
configMap, err := t.validateConfigMapRef(false, from, caCertRef, resources)
877-
if err != nil {
878-
return irTLSConfig, err
879-
}
880-
881-
configMapData, ok := getCaCertFromConfigMap(configMap)
882-
if !ok || len(configMapData) == 0 {
883-
return irTLSConfig, fmt.Errorf(
884-
"caCertificateRef configmap [%s] not found", caCertRef.Name)
885-
}
886-
caCertBytes = []byte(configMapData)
887-
case resource.KindClusterTrustBundle:
888-
trustBundle := resources.GetClusterTrustBundle(string(caCertRef.Name))
889-
if trustBundle == nil {
890-
return irTLSConfig, fmt.Errorf(
891-
"caCertificateRef ClusterTrustBundle [%s] not found", caCertRef.Name)
892-
}
893-
caCertBytes = []byte(trustBundle.Spec.TrustBundle)
894-
default:
895-
return irTLSConfig, fmt.Errorf("unsupported caCertificateRef kind:%s", caCertRefKind)
860+
caCertBytes, err := t.validateAndGetDataAtKeyInRef(caCertRef, caCertKey, resources, from)
861+
if err != nil {
862+
return irTLSConfig, fmt.Errorf("failed to get certificate from ref: %w", err)
896863
}
897-
898-
if err := validateCertificate(caCertBytes); err != nil {
899-
return irTLSConfig, fmt.Errorf(
900-
"invalid certificate in %s %s: %w", caCertRefKind, caCertRef.Name, err)
864+
if err := validateCertificates(caCertBytes); err != nil {
865+
return irTLSConfig, fmt.Errorf("invalid certificate in %s: %w", caCertRef.Name, err)
901866
}
902867
irCACert.Certificate = append(irCACert.Certificate, caCertBytes...)
903868
}
904-
905869
if len(irCACert.Certificate) > 0 {
906870
irTLSConfig.CACertificate = irCACert
907871
irTLSConfig.RequireClientCertificate = !tlsParams.ClientValidation.Optional
908872
setTLSClientValidationContext(tlsParams.ClientValidation, irTLSConfig)
909873
}
874+
875+
irCrl := &ir.TLSCrl{
876+
Name: irTLSCrlName(policy.Namespace, policy.Name),
877+
}
878+
879+
if tlsParams.ClientValidation.Crl != nil {
880+
for _, crlRef := range tlsParams.ClientValidation.Crl.Refs {
881+
crlBytes, err := t.validateAndGetDataAtKeyInRef(crlRef, crlKey, resources, from)
882+
if err != nil {
883+
return irTLSConfig, fmt.Errorf("failed to get crl from ref: %w", err)
884+
}
885+
if err := validateCrl(crlBytes); err != nil {
886+
return irTLSConfig, fmt.Errorf("invalid crl in %s: %w", crlRef.Name, err)
887+
}
888+
irCrl.Data = append(irCrl.Data, crlBytes...)
889+
}
890+
if len(irCrl.Data) > 0 {
891+
irTLSConfig.Crl = irCrl
892+
if tlsParams.ClientValidation.Crl.OnlyVerifyLeafCertificate != nil {
893+
irCrl.OnlyVerifyLeafCertificate = *tlsParams.ClientValidation.Crl.OnlyVerifyLeafCertificate
894+
}
895+
}
896+
}
910897
}
911898

912899
if tlsParams.Session != nil && tlsParams.Session.Resumption != nil {
@@ -921,6 +908,43 @@ func (t *Translator) buildListenerTLSParameters(policy *egv1a1.ClientTrafficPoli
921908
return irTLSConfig, nil
922909
}
923910

911+
// validateAndGetDataAtKeyInRef validates the secret object reference and gets the data at the key in the secret or configmap
912+
func (t *Translator) validateAndGetDataAtKeyInRef(ref gwapiv1.SecretObjectReference, key string, resources *resource.Resources, from crossNamespaceFrom) ([]byte, error) {
913+
refKind := string(ptr.Deref(ref.Kind, resource.KindSecret))
914+
switch refKind {
915+
case resource.KindSecret:
916+
secret, err := t.validateSecretRef(false, from, ref, resources)
917+
if err != nil {
918+
return nil, err
919+
}
920+
921+
secretCertBytes, ok := getOrFirstFromData(secret.Data, key)
922+
if !ok || len(secretCertBytes) == 0 {
923+
return nil, fmt.Errorf("ref secret [%s] has empty data", ref.Name)
924+
}
925+
return secretCertBytes, nil
926+
case resource.KindConfigMap:
927+
configMap, err := t.validateConfigMapRef(false, from, ref, resources)
928+
if err != nil {
929+
return nil, err
930+
}
931+
932+
configMapData, ok := getOrFirstFromData(configMap.Data, key)
933+
if !ok || len(configMapData) == 0 {
934+
return nil, fmt.Errorf("ref configmap [%s] has empty data", ref.Name)
935+
}
936+
return []byte(configMapData), nil
937+
case resource.KindClusterTrustBundle:
938+
trustBundle := resources.GetClusterTrustBundle(string(ref.Name))
939+
if trustBundle == nil {
940+
return nil, fmt.Errorf("ref ClusterTrustBundle [%s] not found", ref.Name)
941+
}
942+
return []byte(trustBundle.Spec.TrustBundle), nil
943+
default:
944+
return nil, fmt.Errorf("unsupported ref kind:%s", refKind)
945+
}
946+
}
947+
924948
func setTLSClientValidationContext(tlsClientValidation *egv1a1.ClientValidationContext, irTLSConfig *ir.TLSConfig) {
925949
if len(tlsClientValidation.SPKIHashes) > 0 {
926950
irTLSConfig.VerifyCertificateSpki = append(irTLSConfig.VerifyCertificateSpki, tlsClientValidation.SPKIHashes...)

internal/gatewayapi/helpers.go

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const (
3838
L7Protocol = "L7"
3939

4040
caCertKey = "ca.crt"
41+
crlKey = "ca.crl"
4142
)
4243

4344
type NamespacedNameWithSection struct {
@@ -466,6 +467,10 @@ func irTLSCACertName(namespace, name string) string {
466467
return fmt.Sprintf("%s/%s/%s", namespace, name, caCertKey)
467468
}
468469

470+
func irTLSCrlName(namespace, name string) string {
471+
return fmt.Sprintf("%s/%s/%s", namespace, name, crlKey)
472+
}
473+
469474
func IsMergeGatewaysEnabled(resources *resource.Resources) bool {
470475
return resources.EnvoyProxyForGatewayClass != nil && resources.EnvoyProxyForGatewayClass.Spec.MergeGateways != nil && *resources.EnvoyProxyForGatewayClass.Spec.MergeGateways
471476
}
@@ -712,38 +717,16 @@ func getPreserveRouteOrder(envoyProxy *egv1a1.EnvoyProxy) bool {
712717
return false
713718
}
714719

715-
func getCaCertFromConfigMap(cm *corev1.ConfigMap) (string, bool) {
716-
var data string
717-
data, exits := cm.Data[caCertKey]
718-
switch {
719-
case exits:
720-
return data, true
721-
case len(cm.Data) == 1: // Fallback to the first key if ca.crt is not found
722-
for _, value := range cm.Data {
723-
data = value
724-
break
725-
}
726-
return data, true
727-
default:
728-
return "", false
729-
}
730-
}
731-
732-
func getCaCertFromSecret(s *corev1.Secret) ([]byte, bool) {
733-
var data []byte
734-
data, exits := s.Data[caCertKey]
735-
switch {
736-
case exits:
737-
return data, true
738-
case len(s.Data) == 1: // Fallback to the first key if ca.crt is not found
739-
for _, value := range s.Data {
740-
data = value
741-
break
720+
// getOrFirstFromData returns the value of the key in the data map, or the first value if the key is not found
721+
func getOrFirstFromData[T any](data map[string]T, key string) (T, bool) {
722+
if val, exists := data[key]; exists {
723+
return val, true
724+
} else if len(data) > 0 {
725+
for _, value := range data {
726+
return value, true
742727
}
743-
return data, true
744-
default:
745-
return nil, false
746728
}
729+
return *new(T), false
747730
}
748731

749732
func irStringMatch(name string, match egv1a1.StringMatch) *ir.StringMatch {

internal/gatewayapi/helpers_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,7 @@ func TestGetCaCertFromConfigMap(t *testing.T) {
757757

758758
for _, tc := range cases {
759759
t.Run(tc.name, func(t *testing.T) {
760-
got, found := getCaCertFromConfigMap(tc.cm)
760+
got, found := getOrFirstFromData(tc.cm.Data, caCertKey)
761761
require.Equal(t, tc.expectedFound, found)
762762
require.Equal(t, tc.expected, got)
763763
})
@@ -803,7 +803,7 @@ func TestGetCaCertFromSecret(t *testing.T) {
803803

804804
for _, tc := range cases {
805805
t.Run(tc.name, func(t *testing.T) {
806-
got, found := getCaCertFromSecret(tc.s)
806+
got, found := getOrFirstFromData(tc.s.Data, caCertKey)
807807
require.Equal(t, tc.expectedFound, found)
808808
require.Equal(t, tc.expected, string(got))
809809
})

internal/gatewayapi/testdata/clienttrafficpolicy-invalid-settings.in.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,19 @@ tcpRoutes:
263263
- backendRefs:
264264
- name: service-1
265265
port: 8080
266+
services:
267+
- apiVersion: v1
268+
kind: Service
269+
metadata:
270+
namespace: envoy-gateway
271+
name: service-1
272+
spec:
273+
selector:
274+
app: service-1
275+
ports:
276+
- name: http
277+
protocol: TCP
278+
port: 8080
266279
secrets:
267280
- apiVersion: v1
268281
kind: Secret

internal/gatewayapi/testdata/clienttrafficpolicy-invalid-settings.out.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ clientTrafficPolicies:
3131
conditions:
3232
- lastTransitionTime: null
3333
message: |-
34-
TLS: caCertificateRef secret [tls-secret-1] not found
35-
TLS: caCertificateRef secret [tls-secret-1] not found.
34+
TLS: invalid certificate in tls-secret-1: certificate example.com has expired since 2025-01-25 23:15:31 +0000 UTC
35+
TLS: invalid certificate in tls-secret-1: certificate example.com has expired since 2025-01-25 23:15:31 +0000 UTC.
3636
reason: Invalid
3737
status: "False"
3838
type: Accepted
@@ -69,8 +69,8 @@ clientTrafficPolicies:
6969
conditions:
7070
- lastTransitionTime: null
7171
message: |-
72-
TLS: configmap default/tls-cm-1 does not exist
73-
TLS: configmap default/tls-cm-1 does not exist.
72+
TLS: failed to get certificate from ref: configmap default/tls-cm-1 does not exist
73+
TLS: failed to get certificate from ref: configmap default/tls-cm-1 does not exist.
7474
reason: Invalid
7575
status: "False"
7676
type: Accepted
@@ -107,8 +107,8 @@ clientTrafficPolicies:
107107
conditions:
108108
- lastTransitionTime: null
109109
message: |-
110-
TLS: caCertificateRef ClusterTrustBundle [tls-ctb-1] not found
111-
TLS: caCertificateRef ClusterTrustBundle [tls-ctb-1] not found.
110+
TLS: failed to get certificate from ref: ref ClusterTrustBundle [tls-ctb-1] not found
111+
TLS: failed to get certificate from ref: ref ClusterTrustBundle [tls-ctb-1] not found.
112112
reason: Invalid
113113
status: "False"
114114
type: Accepted

0 commit comments

Comments
 (0)