github.com/cilium/cilium@v1.16.2/pkg/envoy/secretsync.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package envoy 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 11 envoy_config_core_v3 "github.com/cilium/proxy/go/envoy/config/core/v3" 12 envoy_entensions_tls_v3 "github.com/cilium/proxy/go/envoy/extensions/transport_sockets/tls/v3" 13 "github.com/sirupsen/logrus" 14 15 "github.com/cilium/cilium/pkg/k8s/resource" 16 slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1" 17 "github.com/cilium/cilium/pkg/logging/logfields" 18 ) 19 20 const ( 21 tlsCrtAttribute = "tls.crt" 22 tlsKeyAttribute = "tls.key" 23 24 // Key for CA certificate is fixed as 'ca.crt' even though is not as "standard" 25 // as 'tls.crt' and 'tls.key' are via k8s tls secret type. 26 caCrtAttribute = "ca.crt" 27 ) 28 29 // secretSyncer is responsible to sync K8s TLS Secrets in pre-defined namespaces 30 // via xDS SDS to Envoy. 31 type secretSyncer struct { 32 logger logrus.FieldLogger 33 envoyXdsServer XDSServer 34 } 35 36 func newSecretSyncer(logger logrus.FieldLogger, envoyXdsServer XDSServer) *secretSyncer { 37 return &secretSyncer{ 38 logger: logger, 39 envoyXdsServer: envoyXdsServer, 40 } 41 } 42 43 func (r *secretSyncer) handleSecretEvent(ctx context.Context, event resource.Event[*slim_corev1.Secret]) error { 44 scopedLogger := r.logger. 45 WithField(logfields.K8sNamespace, event.Key.Namespace). 46 WithField("name", event.Key.Name) 47 48 var err error 49 50 switch event.Kind { 51 case resource.Upsert: 52 scopedLogger.Debug("Received Secret upsert event") 53 err = r.upsertK8sSecretV1(ctx, event.Object) 54 if err != nil { 55 scopedLogger.WithError(err).Error("failed to handle Secret upsert") 56 err = fmt.Errorf("failed to handle CEC upsert: %w", err) 57 } 58 case resource.Delete: 59 scopedLogger.Debug("Received Secret delete event") 60 err = r.deleteK8sSecretV1(ctx, event.Key) 61 if err != nil { 62 scopedLogger.WithError(err).Error("failed to handle Secret delete") 63 err = fmt.Errorf("failed to handle Secret delete: %w", err) 64 } 65 } 66 67 event.Done(err) 68 69 return err 70 } 71 72 // updateK8sSecretV1 performs Envoy upsert operation for added or updated secret. 73 func (r *secretSyncer) upsertK8sSecretV1(ctx context.Context, secret *slim_corev1.Secret) error { 74 if secret == nil { 75 return errors.New("secret is nil") 76 } 77 78 resource := Resources{ 79 Secrets: []*envoy_entensions_tls_v3.Secret{k8sToEnvoySecret(secret)}, 80 } 81 return r.envoyXdsServer.UpsertEnvoyResources(ctx, resource) 82 } 83 84 // deleteK8sSecretV1 makes sure the related secret values in Envoy SDS is removed. 85 func (r *secretSyncer) deleteK8sSecretV1(ctx context.Context, key resource.Key) error { 86 if len(key.Namespace) == 0 || len(key.Name) == 0 { 87 return errors.New("key has empty namespace and/or name") 88 } 89 90 resource := Resources{ 91 Secrets: []*envoy_entensions_tls_v3.Secret{ 92 { 93 // For deletion, only the name is required. 94 Name: getEnvoySecretName(key.Namespace, key.Name), 95 }, 96 }, 97 } 98 return r.envoyXdsServer.DeleteEnvoyResources(ctx, resource) 99 } 100 101 // k8sToEnvoySecret converts k8s secret object to envoy TLS secret object 102 func k8sToEnvoySecret(secret *slim_corev1.Secret) *envoy_entensions_tls_v3.Secret { 103 if secret == nil { 104 return nil 105 } 106 envoySecret := &envoy_entensions_tls_v3.Secret{ 107 Name: getEnvoySecretName(secret.GetNamespace(), secret.GetName()), 108 } 109 110 if len(secret.Data[tlsCrtAttribute]) > 0 || len(secret.Data[tlsKeyAttribute]) > 0 { 111 envoySecret.Type = &envoy_entensions_tls_v3.Secret_TlsCertificate{ 112 TlsCertificate: &envoy_entensions_tls_v3.TlsCertificate{ 113 CertificateChain: &envoy_config_core_v3.DataSource{ 114 Specifier: &envoy_config_core_v3.DataSource_InlineBytes{ 115 InlineBytes: secret.Data[tlsCrtAttribute], 116 }, 117 }, 118 PrivateKey: &envoy_config_core_v3.DataSource{ 119 Specifier: &envoy_config_core_v3.DataSource_InlineBytes{ 120 InlineBytes: secret.Data[tlsKeyAttribute], 121 }, 122 }, 123 }, 124 } 125 } else if len(secret.Data[caCrtAttribute]) > 0 { 126 envoySecret.Type = &envoy_entensions_tls_v3.Secret_ValidationContext{ 127 ValidationContext: &envoy_entensions_tls_v3.CertificateValidationContext{ 128 TrustedCa: &envoy_config_core_v3.DataSource{ 129 Specifier: &envoy_config_core_v3.DataSource_InlineBytes{ 130 InlineBytes: secret.Data[caCrtAttribute], 131 }, 132 }, 133 // TODO: Consider support for other ValidationContext config. 134 }, 135 } 136 } 137 138 return envoySecret 139 } 140 141 func getEnvoySecretName(namespace, name string) string { 142 return fmt.Sprintf("%s/%s", namespace, name) 143 }