1 package providerctl
2
3 import (
4 "context"
5 "strings"
6 "time"
7
8 goext "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
9 "github.com/fluxcd/pkg/ssa"
10 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
12 logger "sigs.k8s.io/controller-runtime/pkg/log"
13
14 "sigs.k8s.io/controller-runtime/pkg/client"
15
16 unstructuredutil "edge-infra.dev/pkg/k8s/unstructured"
17
18 "k8s.io/apimachinery/pkg/api/errors"
19 "k8s.io/apimachinery/pkg/types"
20
21 "edge-infra.dev/pkg/edge/constants"
22
23 api "edge-infra.dev/pkg/edge/iam/api/v1alpha1"
24
25 "edge-infra.dev/pkg/edge/iam/config"
26 )
27
28 func (r *ProviderReconciler) reconcileEncryptionKeyRotation(ctx context.Context, provider api.Provider) (api.Provider, error) {
29 log := logger.FromContext(ctx)
30
31
32 if provider.Spec.Encryption.Version == "" {
33
34 var externalSecrets = &goext.ExternalSecretList{}
35 err := r.Client.List(ctx, externalSecrets, &client.ListOptions{Namespace: "edge-iam"})
36 if err != nil {
37 return provider, err
38 }
39
40 if len(externalSecrets.Items) > 0 {
41
42 for _, s := range externalSecrets.Items {
43 if strings.Contains(s.Name, EncryptionKeySecretPrefix) {
44 log.Info("Found encryption external secrets that should be deleted as encryption is no longer enabled")
45
46
47 extSec, err := r.checkExternalSecrets(ctx, s.Name)
48 if err != nil {
49 log.Error(err, "Unable to check if external secret exists")
50 return provider, err
51 }
52 _, err = r.ResourceManager.Delete(ctx, extSec, ssa.DefaultDeleteOptions())
53 if err != nil {
54 log.Error(err, "Unable to delete old external secret")
55 return provider, err
56 }
57 log.Info("Deleted old external secret", "secret", s.Name)
58 }
59 }
60
61 provider = RemoveEncryptionStatus(provider)
62 }
63 log.Info("Encryption is not enabled")
64 return provider, nil
65 } else {
66 log.Info("Encryption is enabled", "version", provider.Spec.Encryption.Version)
67 }
68
69
70 secretName := EncryptionKeySecretPrefix + provider.Spec.Encryption.Version
71
72
73 extSec, err := r.checkExternalSecrets(ctx, secretName)
74 if err != nil {
75 log.Error(err, "Unable to check if external secret exists")
76 return provider, err
77 }
78
79
80 if extSec == nil {
81
82 keyName := "id-encryption-key-" + config.ClusterID()
83
84
85 extSec, err := CreateEncryptionExternalSecret(provider.Spec.Encryption.Version, keyName, secretName)
86 if err != nil {
87 return provider, err
88 }
89
90
91 _, err = r.ResourceManager.Apply(ctx, extSec, ssa.ApplyOptions{Force: true})
92 if err != nil {
93 return provider, err
94 }
95 log.Info("Created external secret for encryption", "secret", secretName)
96 }
97
98
99
100 _, matches := DoesStatusMatchSpecVersion(provider)
101 if matches {
102 err = r.removeOldExternalSecret(ctx, secretName)
103 if err != nil {
104 log.Error(err, "Error trying to remove an old external secret")
105 return provider, err
106 }
107 }
108 return provider, nil
109 }
110
111
112 func (r *ProviderReconciler) checkExternalSecrets(ctx context.Context, secretName string) (*unstructured.Unstructured, error) {
113 var externalSecret = &goext.ExternalSecret{}
114 err := r.Get(ctx, types.NamespacedName{Namespace: "edge-iam", Name: secretName}, externalSecret)
115 if err != nil {
116 if errors.IsNotFound(err) {
117 return nil, nil
118 }
119 return nil, err
120 }
121
122
123 uobj, err := unstructuredutil.ToUnstructured(externalSecret)
124 if err != nil {
125 return nil, err
126 }
127
128 return uobj, nil
129 }
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152 func CreateEncryptionExternalSecret(version string, keyName string, secretName string) (*unstructured.Unstructured, error) {
153 extSec := &goext.ExternalSecret{
154 TypeMeta: metav1.TypeMeta{
155 APIVersion: goext.ExtSecretGroupVersionKind.GroupVersion().String(),
156 Kind: goext.ExtSecretGroupVersionKind.Kind,
157 },
158 ObjectMeta: metav1.ObjectMeta{
159 Name: secretName,
160 Namespace: "edge-iam",
161 Labels: map[string]string{
162 constants.PlatformComponent: "edge-iam",
163 },
164 },
165 Spec: goext.ExternalSecretSpec{
166 DataFrom: []goext.ExternalSecretDataFromRemoteRef{
167 {
168 Extract: &goext.ExternalSecretDataRemoteRef{
169 Key: keyName,
170 Version: version,
171 },
172 },
173 },
174 RefreshInterval: &metav1.Duration{
175 Duration: time.Minute,
176 },
177 SecretStoreRef: goext.SecretStoreRef{
178 Name: "gcp-provider",
179 Kind: "ClusterSecretStore",
180 },
181 Target: goext.ExternalSecretTarget{
182 Name: secretName,
183 CreationPolicy: goext.CreatePolicyOwner,
184 },
185 },
186 }
187
188 uobj, err := unstructuredutil.ToUnstructured(extSec)
189 if err != nil {
190 return uobj, err
191 }
192
193 return uobj, nil
194 }
195
196
197 func DoesStatusMatchSpecVersion(provider api.Provider) (string, bool) {
198 dbVersion := ""
199 prefix := "successfully updated databases to version: "
200
201 for i := range provider.Status.Conditions {
202 if provider.Status.Conditions[i].Reason == "EncryptionRotationSucceeded" {
203
204 dbVersion = strings.TrimSpace(provider.Status.Conditions[i].Message[len(prefix):])
205 }
206 }
207
208
209 return dbVersion, dbVersion == provider.Spec.Encryption.Version
210 }
211
212
213 func (r *ProviderReconciler) removeOldExternalSecret(ctx context.Context, newSecretName string) error {
214 log := logger.FromContext(ctx)
215
216
217 secretList := &goext.ExternalSecretList{}
218 if err := r.Client.List(ctx, secretList, &client.ListOptions{Namespace: "edge-iam"}); err != nil {
219 log.Error(err, "Couldn't list external secrets in the edge-iam namespace")
220 return err
221 }
222
223
224 for _, secret := range secretList.Items {
225 if strings.HasPrefix(secret.Name, "id-encryption-key") && secret.Name != newSecretName {
226
227 extSec, err := r.checkExternalSecrets(ctx, secret.Name)
228 if err != nil {
229 log.Error(err, "Unable to check if external secret exists")
230 return err
231 }
232 _, err = r.ResourceManager.Delete(ctx, extSec, ssa.DefaultDeleteOptions())
233 if err != nil {
234 log.Error(err, "Unable to delete old external secret")
235 return err
236 }
237 log.Info("Deleted old external secret", "secret", secret.Name)
238 }
239 }
240 return nil
241 }
242
243 func RemoveEncryptionStatus(provider api.Provider) api.Provider {
244 providerStatus := provider.Status.Conditions
245
246 for i, condition := range providerStatus {
247 if condition.Reason == "EncryptionRotationSucceeded" {
248
249 provider.Status.Conditions = append(providerStatus[:i], providerStatus[i+1:]...)
250 }
251 }
252
253 return provider
254 }
255
View as plain text