Skip to content

Commit 3f7a1ab

Browse files
committed
feat: support crls in client traffic policies
Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
1 parent 398cd8a commit 3f7a1ab

19 files changed

+1198
-105
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: 64 additions & 40 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-
898864
if err := validateCertificate(caCertBytes); err != nil {
899-
return irTLSConfig, fmt.Errorf(
900-
"invalid certificate in %s %s: %w", caCertRefKind, caCertRef.Name, err)
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] not found", 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] not found", 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 protocolPort struct {
@@ -461,6 +462,10 @@ func irTLSCACertName(namespace, name string) string {
461462
return fmt.Sprintf("%s/%s/%s", namespace, name, caCertKey)
462463
}
463464

465+
func irTLSCrlName(namespace, name string) string {
466+
return fmt.Sprintf("%s/%s/%s", namespace, name, crlKey)
467+
}
468+
464469
func IsMergeGatewaysEnabled(resources *resource.Resources) bool {
465470
return resources.EnvoyProxyForGatewayClass != nil && resources.EnvoyProxyForGatewayClass.Spec.MergeGateways != nil && *resources.EnvoyProxyForGatewayClass.Spec.MergeGateways
466471
}
@@ -696,38 +701,16 @@ func getPreserveRouteOrder(envoyProxy *egv1a1.EnvoyProxy) bool {
696701
return false
697702
}
698703

699-
func getCaCertFromConfigMap(cm *corev1.ConfigMap) (string, bool) {
700-
var data string
701-
data, exits := cm.Data[caCertKey]
702-
switch {
703-
case exits:
704-
return data, true
705-
case len(cm.Data) == 1: // Fallback to the first key if ca.crt is not found
706-
for _, value := range cm.Data {
707-
data = value
708-
break
709-
}
710-
return data, true
711-
default:
712-
return "", false
713-
}
714-
}
715-
716-
func getCaCertFromSecret(s *corev1.Secret) ([]byte, bool) {
717-
var data []byte
718-
data, exits := s.Data[caCertKey]
719-
switch {
720-
case exits:
721-
return data, true
722-
case len(s.Data) == 1: // Fallback to the first key if ca.crt is not found
723-
for _, value := range s.Data {
724-
data = value
725-
break
704+
// getOrFirstFromData returns the value of the key in the data map, or the first value if the key is not found
705+
func getOrFirstFromData[T any](data map[string]T, key string) (T, bool) {
706+
if val, exists := data[key]; exists {
707+
return val, true
708+
} else if len(data) > 0 {
709+
for _, value := range data {
710+
return value, true
726711
}
727-
return data, true
728-
default:
729-
return nil, false
730712
}
713+
return *new(T), false
731714
}
732715

733716
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.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 has expired since 2025-01-25 23:15:31 +0000 UTC
35+
TLS: invalid certificate in tls-secret-1: certificate 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)