externalsecret_controller.go 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996
  1. /*
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package externalsecret
  13. import (
  14. "context"
  15. "encoding/json"
  16. "errors"
  17. "fmt"
  18. "maps"
  19. "slices"
  20. "strings"
  21. "time"
  22. "github.com/go-logr/logr"
  23. "github.com/prometheus/client_golang/prometheus"
  24. v1 "k8s.io/api/core/v1"
  25. "k8s.io/apimachinery/pkg/api/equality"
  26. apierrors "k8s.io/apimachinery/pkg/api/errors"
  27. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  28. "k8s.io/apimachinery/pkg/fields"
  29. "k8s.io/apimachinery/pkg/labels"
  30. "k8s.io/apimachinery/pkg/runtime"
  31. "k8s.io/apimachinery/pkg/runtime/schema"
  32. "k8s.io/apimachinery/pkg/types"
  33. "k8s.io/client-go/rest"
  34. "k8s.io/client-go/tools/record"
  35. "k8s.io/utils/ptr"
  36. ctrl "sigs.k8s.io/controller-runtime"
  37. "sigs.k8s.io/controller-runtime/pkg/builder"
  38. "sigs.k8s.io/controller-runtime/pkg/client"
  39. "sigs.k8s.io/controller-runtime/pkg/controller"
  40. "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
  41. "sigs.k8s.io/controller-runtime/pkg/handler"
  42. "sigs.k8s.io/controller-runtime/pkg/predicate"
  43. "sigs.k8s.io/controller-runtime/pkg/reconcile"
  44. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  45. // Metrics.
  46. "github.com/external-secrets/external-secrets/pkg/controllers/externalsecret/esmetrics"
  47. ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
  48. "github.com/external-secrets/external-secrets/pkg/controllers/util"
  49. "github.com/external-secrets/external-secrets/pkg/utils"
  50. "github.com/external-secrets/external-secrets/pkg/utils/resolvers"
  51. // Loading registered generators.
  52. _ "github.com/external-secrets/external-secrets/pkg/generator/register"
  53. // Loading registered providers.
  54. _ "github.com/external-secrets/external-secrets/pkg/provider/register"
  55. )
  56. const (
  57. fieldOwnerTemplate = "externalsecrets.external-secrets.io/%v"
  58. fieldOwnerTemplateSha = "externalsecrets.external-secrets.io/sha3/%x"
  59. // condition messages for "SecretSynced" reason.
  60. msgSynced = "secret synced"
  61. msgSyncedRetain = "secret retained due to DeletionPolicy=Retain"
  62. // condition messages for "SecretDeleted" reason.
  63. msgDeleted = "secret deleted due to DeletionPolicy=Delete"
  64. // condition messages for "SecretMissing" reason.
  65. msgMissing = "secret will not be created due to CreationPolicy=Merge"
  66. // condition messages for "SecretSyncedError" reason.
  67. msgErrorGetSecretData = "could not get secret data from provider"
  68. msgErrorDeleteSecret = "could not delete secret"
  69. msgErrorDeleteOrphaned = "could not delete orphaned secrets"
  70. msgErrorUpdateSecret = "could not update secret"
  71. msgErrorUpdateImmutable = "could not update secret, target is immutable"
  72. msgErrorBecomeOwner = "failed to take ownership of target secret"
  73. msgErrorIsOwned = "target is owned by another ExternalSecret"
  74. // log messages.
  75. logErrorGetES = "unable to get ExternalSecret"
  76. logErrorUpdateESStatus = "unable to update ExternalSecret status"
  77. logErrorGetSecret = "unable to get Secret"
  78. logErrorPatchSecret = "unable to patch Secret"
  79. logErrorSecretCacheNotSynced = "controller caches for Secret are not in sync"
  80. logErrorUnmanagedStore = "unable to determine if store is managed"
  81. // error formats.
  82. errConvert = "error applying conversion strategy %s to keys: %w"
  83. errRewrite = "error applying rewrite to keys: %w"
  84. errDecode = "error applying decoding strategy %s to data: %w"
  85. errGenerate = "error using generator: %w"
  86. errInvalidKeys = "invalid secret keys (TIP: use rewrite or conversionStrategy to change keys): %w"
  87. errFetchTplFrom = "error fetching templateFrom data: %w"
  88. errApplyTemplate = "could not apply template: %w"
  89. errExecTpl = "could not execute template: %w"
  90. errMutate = "unable to mutate secret %s: %w"
  91. errUpdate = "unable to update secret %s: %w"
  92. errUpdateNotFound = "unable to update secret %s: not found"
  93. errDeleteCreatePolicy = "unable to delete secret %s: creationPolicy=%s is not Owner"
  94. errSecretCachesNotSynced = "controller caches for secret %s are not in sync"
  95. // event messages.
  96. eventCreated = "secret created"
  97. eventUpdated = "secret updated"
  98. eventDeleted = "secret deleted due to DeletionPolicy=Delete"
  99. eventDeletedOrphaned = "secret deleted because it was orphaned"
  100. eventMissingProviderSecret = "secret does not exist at provider using spec.dataFrom[%d]"
  101. eventMissingProviderSecretKey = "secret does not exist at provider using spec.dataFrom[%d] (key=%s)"
  102. )
  103. // these errors are explicitly defined so we can detect them with `errors.Is()`.
  104. var (
  105. ErrSecretImmutable = fmt.Errorf("secret is immutable")
  106. ErrSecretIsOwned = fmt.Errorf("secret is owned by another ExternalSecret")
  107. ErrSecretSetCtrlRef = fmt.Errorf("could not set controller reference on secret")
  108. ErrSecretRemoveCtrlRef = fmt.Errorf("could not remove controller reference on secret")
  109. )
  110. const indexESTargetSecretNameField = ".metadata.targetSecretName"
  111. // Reconciler reconciles a ExternalSecret object.
  112. type Reconciler struct {
  113. client.Client
  114. SecretClient client.Client
  115. Log logr.Logger
  116. Scheme *runtime.Scheme
  117. RestConfig *rest.Config
  118. ControllerClass string
  119. RequeueInterval time.Duration
  120. ClusterSecretStoreEnabled bool
  121. EnableFloodGate bool
  122. EnableGeneratorState bool
  123. recorder record.EventRecorder
  124. }
  125. // Reconcile implements the main reconciliation loop
  126. // for watched objects (ExternalSecret, ClusterSecretStore and SecretStore),
  127. // and updates/creates a Kubernetes secret based on them.
  128. func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) {
  129. log := r.Log.WithValues("ExternalSecret", req.NamespacedName)
  130. resourceLabels := ctrlmetrics.RefineNonConditionMetricLabels(map[string]string{"name": req.Name, "namespace": req.Namespace})
  131. start := time.Now()
  132. syncCallsError := esmetrics.GetCounterVec(esmetrics.SyncCallsErrorKey)
  133. // use closures to dynamically update resourceLabels
  134. defer func() {
  135. esmetrics.GetGaugeVec(esmetrics.ExternalSecretReconcileDurationKey).With(resourceLabels).Set(float64(time.Since(start)))
  136. esmetrics.GetCounterVec(esmetrics.SyncCallsKey).With(resourceLabels).Inc()
  137. }()
  138. externalSecret := &esv1.ExternalSecret{}
  139. err = r.Get(ctx, req.NamespacedName, externalSecret)
  140. if err != nil {
  141. if apierrors.IsNotFound(err) {
  142. // NOTE: this does not actually set the condition on the ExternalSecret, because it does not exist
  143. // this is a hack to disable metrics for deleted ExternalSecrets, see:
  144. // https://github.com/external-secrets/external-secrets/pull/612
  145. conditionSynced := NewExternalSecretCondition(esv1.ExternalSecretDeleted, v1.ConditionFalse, esv1.ConditionReasonSecretDeleted, "Secret was deleted")
  146. SetExternalSecretCondition(&esv1.ExternalSecret{
  147. ObjectMeta: metav1.ObjectMeta{
  148. Name: req.Name,
  149. Namespace: req.Namespace,
  150. },
  151. }, *conditionSynced)
  152. return ctrl.Result{}, nil
  153. }
  154. log.Error(err, logErrorGetES)
  155. syncCallsError.With(resourceLabels).Inc()
  156. return ctrl.Result{}, err
  157. }
  158. // skip reconciliation if deletion timestamp is set on external secret
  159. if !externalSecret.GetDeletionTimestamp().IsZero() {
  160. log.V(1).Info("skipping ExternalSecret, it is marked for deletion")
  161. return ctrl.Result{}, nil
  162. }
  163. // if extended metrics is enabled, refine the time series vector
  164. resourceLabels = ctrlmetrics.RefineLabels(resourceLabels, externalSecret.Labels)
  165. // skip this ExternalSecret if it uses a ClusterSecretStore and the feature is disabled
  166. if shouldSkipClusterSecretStore(r, externalSecret) {
  167. log.V(1).Info("skipping ExternalSecret, ClusterSecretStore feature is disabled")
  168. return ctrl.Result{}, nil
  169. }
  170. // skip this ExternalSecret if it uses any SecretStore not managed by this controller
  171. skip, err := shouldSkipUnmanagedStore(ctx, req.Namespace, r, externalSecret)
  172. if err != nil {
  173. log.Error(err, logErrorUnmanagedStore)
  174. syncCallsError.With(resourceLabels).Inc()
  175. return ctrl.Result{}, err
  176. }
  177. if skip {
  178. log.V(1).Info("skipping ExternalSecret, uses unmanaged SecretStore")
  179. return ctrl.Result{}, nil
  180. }
  181. // the target secret name defaults to the ExternalSecret name, if not explicitly set
  182. secretName := externalSecret.Spec.Target.Name
  183. if secretName == "" {
  184. secretName = externalSecret.Name
  185. }
  186. // fetch the existing secret (from the partial cache)
  187. // - please note that the ~partial cache~ is different from the ~full cache~
  188. // so there can be race conditions between the two caches
  189. // - the WatchesMetadata(v1.Secret{}) in SetupWithManager() is using the partial cache
  190. // so we might receive a reconcile request before the full cache is updated
  191. // - furthermore, when `--enable-managed-secrets-caching` is true, the full cache
  192. // will ONLY include secrets with the "managed" label, so we cant use the full cache
  193. // to reliably determine if a secret exists or not
  194. secretPartial := &metav1.PartialObjectMetadata{}
  195. secretPartial.SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("Secret"))
  196. err = r.Get(ctx, client.ObjectKey{Name: secretName, Namespace: externalSecret.Namespace}, secretPartial)
  197. if err != nil && !apierrors.IsNotFound(err) {
  198. log.Error(err, logErrorGetSecret, "secretName", secretName, "secretNamespace", externalSecret.Namespace)
  199. syncCallsError.With(resourceLabels).Inc()
  200. return ctrl.Result{}, err
  201. }
  202. // if the secret exists but does not have the "managed" label, add the label
  203. // using a PATCH so it is visible in the cache, then requeue immediately
  204. if secretPartial.UID != "" && secretPartial.Labels[esv1.LabelManaged] != esv1.LabelManagedValue {
  205. fqdn := fqdnFor(externalSecret.Name)
  206. patch := client.MergeFrom(secretPartial.DeepCopy())
  207. if secretPartial.Labels == nil {
  208. secretPartial.Labels = make(map[string]string)
  209. }
  210. secretPartial.Labels[esv1.LabelManaged] = esv1.LabelManagedValue
  211. err = r.Patch(ctx, secretPartial, patch, client.FieldOwner(fqdn))
  212. if err != nil {
  213. log.Error(err, logErrorPatchSecret, "secretName", secretName, "secretNamespace", externalSecret.Namespace)
  214. syncCallsError.With(resourceLabels).Inc()
  215. return ctrl.Result{}, err
  216. }
  217. return ctrl.Result{Requeue: true}, nil
  218. }
  219. // fetch existing secret (from the full cache)
  220. // NOTE: we are using the `r.SecretClient` which we only use for managed secrets.
  221. // when `enableManagedSecretsCache` is true, this is a cached client that only sees our managed secrets,
  222. // otherwise it will be the normal controller-runtime client which may be cached or make direct API calls,
  223. // depending on if `enabledSecretCache` is true or false.
  224. existingSecret := &v1.Secret{}
  225. err = r.SecretClient.Get(ctx, client.ObjectKey{Name: secretName, Namespace: externalSecret.Namespace}, existingSecret)
  226. if err != nil && !apierrors.IsNotFound(err) {
  227. log.Error(err, logErrorGetSecret, "secretName", secretName, "secretNamespace", externalSecret.Namespace)
  228. syncCallsError.With(resourceLabels).Inc()
  229. return ctrl.Result{}, err
  230. }
  231. // ensure the full cache is up-to-date
  232. // NOTE: this prevents race conditions between the partial and full cache.
  233. // we return an error so we get an exponential backoff if we end up looping,
  234. // for example, during high cluster load and frequent updates to the target secret by other controllers.
  235. if secretPartial.UID != existingSecret.UID || secretPartial.ResourceVersion != existingSecret.ResourceVersion {
  236. err = fmt.Errorf(errSecretCachesNotSynced, secretName)
  237. log.Error(err, logErrorSecretCacheNotSynced, "secretName", secretName, "secretNamespace", externalSecret.Namespace)
  238. syncCallsError.With(resourceLabels).Inc()
  239. return ctrl.Result{}, err
  240. }
  241. // refresh will be skipped if ALL the following conditions are met:
  242. // 1. refresh interval is not 0
  243. // 2. resource generation of the ExternalSecret has not changed
  244. // 3. the last refresh time of the ExternalSecret is within the refresh interval
  245. // 4. the target secret is valid:
  246. // - it exists
  247. // - it has the correct "managed" label
  248. // - it has the correct "data-hash" annotation
  249. if !shouldRefresh(externalSecret) && isSecretValid(existingSecret, externalSecret) {
  250. log.V(1).Info("skipping refresh")
  251. return r.getRequeueResult(externalSecret), nil
  252. }
  253. // update status of the ExternalSecret when this function returns, if needed.
  254. // NOTE: we use the ability of deferred functions to update named return values `result` and `err`
  255. // NOTE: we dereference the DeepCopy of the status field because status fields are NOT pointers,
  256. // so otherwise the `equality.Semantic.DeepEqual` will always return false.
  257. currentStatus := *externalSecret.Status.DeepCopy()
  258. defer func() {
  259. // if the status has not changed, we don't need to update it
  260. if equality.Semantic.DeepEqual(currentStatus, externalSecret.Status) {
  261. return
  262. }
  263. // update the status of the ExternalSecret, storing any error in a new variable
  264. // if there was no new error, we don't need to change the `result` or `err` values
  265. updateErr := r.Status().Update(ctx, externalSecret)
  266. if updateErr == nil {
  267. return
  268. }
  269. // if we got an update conflict, we should requeue immediately
  270. if apierrors.IsConflict(updateErr) {
  271. log.V(1).Info("conflict while updating status, will requeue")
  272. // we only explicitly request a requeue if the main function did not return an `err`.
  273. // otherwise, we get an annoying log saying that results are ignored when there is an error,
  274. // as errors are always retried.
  275. if err == nil {
  276. result = ctrl.Result{Requeue: true}
  277. }
  278. return
  279. }
  280. // for other errors, log and update the `err` variable if there is no error already
  281. // so the reconciler will requeue the request
  282. log.Error(updateErr, logErrorUpdateESStatus)
  283. if err == nil {
  284. err = updateErr
  285. }
  286. }()
  287. // retrieve the provider secret data.
  288. dataMap, err := r.GetProviderSecretData(ctx, externalSecret)
  289. if err != nil {
  290. r.markAsFailed(msgErrorGetSecretData, err, externalSecret, syncCallsError.With(resourceLabels))
  291. return ctrl.Result{}, err
  292. }
  293. // if no data was found we can delete the secret if needed.
  294. if len(dataMap) == 0 {
  295. switch externalSecret.Spec.Target.DeletionPolicy {
  296. // delete secret and return early.
  297. case esv1.DeletionPolicyDelete:
  298. // safeguard that we only can delete secrets we own.
  299. // this is also implemented in the es validation webhook.
  300. // NOTE: this error cant be fixed by retrying so we don't return an error (which would requeue immediately)
  301. creationPolicy := externalSecret.Spec.Target.CreationPolicy
  302. if creationPolicy != esv1.CreatePolicyOwner {
  303. err = fmt.Errorf(errDeleteCreatePolicy, secretName, creationPolicy)
  304. r.markAsFailed(msgErrorDeleteSecret, err, externalSecret, syncCallsError.With(resourceLabels))
  305. return ctrl.Result{}, nil
  306. }
  307. // delete the secret, if it exists
  308. if existingSecret.UID != "" {
  309. err = r.Delete(ctx, existingSecret)
  310. if err != nil && !apierrors.IsNotFound(err) {
  311. r.markAsFailed(msgErrorDeleteSecret, err, externalSecret, syncCallsError.With(resourceLabels))
  312. return ctrl.Result{}, err
  313. }
  314. r.recorder.Event(externalSecret, v1.EventTypeNormal, esv1.ReasonDeleted, eventDeleted)
  315. }
  316. r.markAsDone(externalSecret, start, log, esv1.ConditionReasonSecretDeleted, msgDeleted)
  317. return r.getRequeueResult(externalSecret), nil
  318. // In case provider secrets don't exist the kubernetes secret will be kept as-is.
  319. case esv1.DeletionPolicyRetain:
  320. r.markAsDone(externalSecret, start, log, esv1.ConditionReasonSecretSynced, msgSyncedRetain)
  321. return r.getRequeueResult(externalSecret), nil
  322. // noop, handled below
  323. case esv1.DeletionPolicyMerge:
  324. }
  325. }
  326. // mutationFunc is a function which can be applied to a secret to make it match the desired state.
  327. mutationFunc := func(secret *v1.Secret) error {
  328. // get information about the current owner of the secret
  329. // - we ignore the API version as it can change over time
  330. // - we ignore the UID for consistency with the SetControllerReference function
  331. currentOwner := metav1.GetControllerOf(secret)
  332. ownerIsESKind := false
  333. ownerIsCurrentES := false
  334. if currentOwner != nil {
  335. currentOwnerGK := schema.FromAPIVersionAndKind(currentOwner.APIVersion, currentOwner.Kind).GroupKind()
  336. ownerIsESKind = currentOwnerGK.String() == esv1.ExtSecretGroupKind
  337. ownerIsCurrentES = ownerIsESKind && currentOwner.Name == externalSecret.Name
  338. }
  339. // if another ExternalSecret is the owner, we should return an error
  340. // otherwise the controller will fight with itself to update the secret.
  341. // note, this does not prevent other controllers from owning the secret.
  342. if ownerIsESKind && !ownerIsCurrentES {
  343. return fmt.Errorf("%w: %s", ErrSecretIsOwned, currentOwner.Name)
  344. }
  345. // if the CreationPolicy is Owner, we should set ourselves as the owner of the secret
  346. if externalSecret.Spec.Target.CreationPolicy == esv1.CreatePolicyOwner {
  347. err = controllerutil.SetControllerReference(externalSecret, secret, r.Scheme)
  348. if err != nil {
  349. return fmt.Errorf("%w: %w", ErrSecretSetCtrlRef, err)
  350. }
  351. }
  352. // if the creation policy is not Owner, we should remove ourselves as the owner
  353. // this could happen if the creation policy was changed after the secret was created
  354. if externalSecret.Spec.Target.CreationPolicy != esv1.CreatePolicyOwner && ownerIsCurrentES {
  355. err = controllerutil.RemoveControllerReference(externalSecret, secret, r.Scheme)
  356. if err != nil {
  357. return fmt.Errorf("%w: %w", ErrSecretRemoveCtrlRef, err)
  358. }
  359. }
  360. // initialize maps within the secret so it's safe to set values
  361. if secret.Annotations == nil {
  362. secret.Annotations = make(map[string]string)
  363. }
  364. if secret.Labels == nil {
  365. secret.Labels = make(map[string]string)
  366. }
  367. if secret.Data == nil {
  368. secret.Data = make(map[string][]byte)
  369. }
  370. // set the immutable flag on the secret if requested by the ExternalSecret
  371. if externalSecret.Spec.Target.Immutable {
  372. secret.Immutable = ptr.To(true)
  373. }
  374. // only apply the template if the secret is mutable or if the secret is new (has no UID)
  375. // otherwise we would mutate an object that is immutable and already exists
  376. objectDoesNotExistOrCanBeMutated := secret.GetUID() == "" || !externalSecret.Spec.Target.Immutable
  377. if objectDoesNotExistOrCanBeMutated {
  378. // get the list of keys that are managed by this ExternalSecret
  379. keys, err := getManagedDataKeys(secret, externalSecret.Name)
  380. if err != nil {
  381. return err
  382. }
  383. // remove any data keys that are managed by this ExternalSecret, so we can re-add them
  384. // this ensures keys added by templates are not left behind when they are removed from the template
  385. for _, key := range keys {
  386. delete(secret.Data, key)
  387. }
  388. // WARNING: this will remove any labels or annotations managed by this ExternalSecret
  389. // so any updates to labels and annotations should be done AFTER this point
  390. err = r.ApplyTemplate(ctx, externalSecret, secret, dataMap)
  391. if err != nil {
  392. return fmt.Errorf(errApplyTemplate, err)
  393. }
  394. }
  395. // we also use a label to keep track of the owner of the secret
  396. // this lets us remove secrets that are no longer needed if the target secret name changes
  397. if externalSecret.Spec.Target.CreationPolicy == esv1.CreatePolicyOwner {
  398. lblValue := utils.ObjectHash(fmt.Sprintf("%v/%v", externalSecret.Namespace, externalSecret.Name))
  399. secret.Labels[esv1.LabelOwner] = lblValue
  400. } else {
  401. // the label should not be set if the creation policy is not Owner
  402. delete(secret.Labels, esv1.LabelOwner)
  403. }
  404. secret.Labels[esv1.LabelManaged] = esv1.LabelManagedValue
  405. secret.Annotations[esv1.AnnotationDataHash] = utils.ObjectHash(secret.Data)
  406. return nil
  407. }
  408. switch externalSecret.Spec.Target.CreationPolicy {
  409. case esv1.CreatePolicyNone:
  410. log.V(1).Info("secret creation skipped due to CreationPolicy=None")
  411. err = nil
  412. case esv1.CreatePolicyMerge:
  413. // update the secret, if it exists
  414. if existingSecret.UID != "" {
  415. err = r.updateSecret(ctx, existingSecret, mutationFunc, externalSecret, secretName)
  416. } else {
  417. // if the secret does not exist, we wait until the next refresh interval
  418. // rather than returning an error which would requeue immediately
  419. r.markAsDone(externalSecret, start, log, esv1.ConditionReasonSecretMissing, msgMissing)
  420. return r.getRequeueResult(externalSecret), nil
  421. }
  422. case esv1.CreatePolicyOrphan:
  423. // create the secret, if it does not exist
  424. if existingSecret.UID == "" {
  425. err = r.createSecret(ctx, mutationFunc, externalSecret, secretName)
  426. } else {
  427. // if the secret exists, we should update it
  428. err = r.updateSecret(ctx, existingSecret, mutationFunc, externalSecret, secretName)
  429. }
  430. case esv1.CreatePolicyOwner:
  431. // we may have orphaned secrets to clean up,
  432. // for example, if the target secret name was changed
  433. err = r.deleteOrphanedSecrets(ctx, externalSecret, secretName)
  434. if err != nil {
  435. r.markAsFailed(msgErrorDeleteOrphaned, err, externalSecret, syncCallsError.With(resourceLabels))
  436. return ctrl.Result{}, err
  437. }
  438. // create the secret, if it does not exist
  439. if existingSecret.UID == "" {
  440. err = r.createSecret(ctx, mutationFunc, externalSecret, secretName)
  441. } else {
  442. // if the secret exists, we should update it
  443. err = r.updateSecret(ctx, existingSecret, mutationFunc, externalSecret, secretName)
  444. }
  445. }
  446. if err != nil {
  447. // if we got an update conflict, we should requeue immediately
  448. if apierrors.IsConflict(err) {
  449. log.V(1).Info("conflict while updating secret, will requeue")
  450. return ctrl.Result{Requeue: true}, nil
  451. }
  452. // detect errors indicating that we failed to set ourselves as the owner of the secret
  453. // NOTE: this error cant be fixed by retrying so we don't return an error (which would requeue immediately)
  454. if errors.Is(err, ErrSecretSetCtrlRef) {
  455. r.markAsFailed(msgErrorBecomeOwner, err, externalSecret, syncCallsError.With(resourceLabels))
  456. return ctrl.Result{}, nil
  457. }
  458. // detect errors indicating that the secret has another ExternalSecret as owner
  459. // NOTE: this error cant be fixed by retrying so we don't return an error (which would requeue immediately)
  460. if errors.Is(err, ErrSecretIsOwned) {
  461. r.markAsFailed(msgErrorIsOwned, err, externalSecret, syncCallsError.With(resourceLabels))
  462. return ctrl.Result{}, nil
  463. }
  464. // detect errors indicating that the secret is immutable
  465. // NOTE: this error cant be fixed by retrying so we don't return an error (which would requeue immediately)
  466. if errors.Is(err, ErrSecretImmutable) {
  467. r.markAsFailed(msgErrorUpdateImmutable, err, externalSecret, syncCallsError.With(resourceLabels))
  468. return ctrl.Result{}, nil
  469. }
  470. r.markAsFailed(msgErrorUpdateSecret, err, externalSecret, syncCallsError.With(resourceLabels))
  471. return ctrl.Result{}, err
  472. }
  473. r.markAsDone(externalSecret, start, log, esv1.ConditionReasonSecretSynced, msgSynced)
  474. return r.getRequeueResult(externalSecret), nil
  475. }
  476. // getRequeueResult create a result with requeueAfter based on the ExternalSecret refresh interval.
  477. func (r *Reconciler) getRequeueResult(externalSecret *esv1.ExternalSecret) ctrl.Result {
  478. // default to the global requeue interval
  479. // note, this will never be used because the CRD has a default value of 1 hour
  480. refreshInterval := r.RequeueInterval
  481. if externalSecret.Spec.RefreshInterval != nil {
  482. refreshInterval = externalSecret.Spec.RefreshInterval.Duration
  483. }
  484. // if the refresh interval is <= 0, we should not requeue
  485. if refreshInterval <= 0 {
  486. return ctrl.Result{}
  487. }
  488. // if the last refresh time is not set, requeue after the refresh interval
  489. // note, this should not happen, as we only call this function on ExternalSecrets
  490. // that have been reconciled at least once
  491. if externalSecret.Status.RefreshTime.IsZero() {
  492. return ctrl.Result{RequeueAfter: refreshInterval}
  493. }
  494. timeSinceLastRefresh := time.Since(externalSecret.Status.RefreshTime.Time)
  495. // if the last refresh time is in the future, we should requeue immediately
  496. // note, this should not happen, as we always refresh an ExternalSecret
  497. // that has a last refresh time in the future
  498. if timeSinceLastRefresh < 0 {
  499. return ctrl.Result{Requeue: true}
  500. }
  501. // if there is time remaining, requeue after the remaining time
  502. if timeSinceLastRefresh < refreshInterval {
  503. return ctrl.Result{RequeueAfter: refreshInterval - timeSinceLastRefresh}
  504. }
  505. // otherwise, requeue immediately
  506. return ctrl.Result{Requeue: true}
  507. }
  508. func (r *Reconciler) markAsDone(externalSecret *esv1.ExternalSecret, start time.Time, log logr.Logger, reason, msg string) {
  509. oldReadyCondition := GetExternalSecretCondition(externalSecret.Status, esv1.ExternalSecretReady)
  510. newReadyCondition := NewExternalSecretCondition(esv1.ExternalSecretReady, v1.ConditionTrue, reason, msg)
  511. SetExternalSecretCondition(externalSecret, *newReadyCondition)
  512. externalSecret.Status.RefreshTime = metav1.NewTime(start)
  513. externalSecret.Status.SyncedResourceVersion = util.GetResourceVersion(externalSecret.ObjectMeta)
  514. // if the status or reason has changed, log at the appropriate verbosity level
  515. if oldReadyCondition == nil || oldReadyCondition.Status != newReadyCondition.Status || oldReadyCondition.Reason != newReadyCondition.Reason {
  516. if newReadyCondition.Reason == esv1.ConditionReasonSecretDeleted {
  517. log.Info("deleted secret")
  518. } else {
  519. log.Info("reconciled secret")
  520. }
  521. } else {
  522. log.V(1).Info("reconciled secret")
  523. }
  524. }
  525. func (r *Reconciler) markAsFailed(msg string, err error, externalSecret *esv1.ExternalSecret, counter prometheus.Counter) {
  526. r.recorder.Event(externalSecret, v1.EventTypeWarning, esv1.ReasonUpdateFailed, err.Error())
  527. conditionSynced := NewExternalSecretCondition(esv1.ExternalSecretReady, v1.ConditionFalse, esv1.ConditionReasonSecretSyncedError, msg)
  528. SetExternalSecretCondition(externalSecret, *conditionSynced)
  529. counter.Inc()
  530. }
  531. func (r *Reconciler) deleteOrphanedSecrets(ctx context.Context, externalSecret *esv1.ExternalSecret, secretName string) error {
  532. ownerLabel := utils.ObjectHash(fmt.Sprintf("%v/%v", externalSecret.Namespace, externalSecret.Name))
  533. // we use a PartialObjectMetadataList to avoid loading the full secret objects
  534. // and because the Secrets partials are always cached due to WatchesMetadata() in SetupWithManager()
  535. secretListPartial := &metav1.PartialObjectMetadataList{}
  536. secretListPartial.SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("SecretList"))
  537. listOpts := &client.ListOptions{
  538. LabelSelector: labels.SelectorFromSet(map[string]string{
  539. esv1.LabelOwner: ownerLabel,
  540. }),
  541. Namespace: externalSecret.Namespace,
  542. }
  543. if err := r.List(ctx, secretListPartial, listOpts); err != nil {
  544. return err
  545. }
  546. // delete all secrets that are not the target secret
  547. for _, secretPartial := range secretListPartial.Items {
  548. if secretPartial.GetName() != secretName {
  549. err := r.Delete(ctx, &secretPartial)
  550. if err != nil && !apierrors.IsNotFound(err) {
  551. return err
  552. }
  553. r.recorder.Event(externalSecret, v1.EventTypeNormal, esv1.ReasonDeleted, eventDeletedOrphaned)
  554. }
  555. }
  556. return nil
  557. }
  558. // createSecret creates a new secret with the given mutation function.
  559. func (r *Reconciler) createSecret(ctx context.Context, mutationFunc func(secret *v1.Secret) error, es *esv1.ExternalSecret, secretName string) error {
  560. fqdn := fqdnFor(es.Name)
  561. // define and mutate the new secret
  562. newSecret := &v1.Secret{
  563. ObjectMeta: metav1.ObjectMeta{
  564. Name: secretName,
  565. Namespace: es.Namespace,
  566. },
  567. Data: make(map[string][]byte),
  568. }
  569. if err := mutationFunc(newSecret); err != nil {
  570. return err
  571. }
  572. // note, we set field owner even for Create
  573. if err := r.Create(ctx, newSecret, client.FieldOwner(fqdn)); err != nil {
  574. return err
  575. }
  576. // set the binding reference to the secret
  577. // https://github.com/external-secrets/external-secrets/pull/2263
  578. es.Status.Binding = v1.LocalObjectReference{Name: newSecret.Name}
  579. r.recorder.Event(es, v1.EventTypeNormal, esv1.ReasonCreated, eventCreated)
  580. return nil
  581. }
  582. func (r *Reconciler) updateSecret(ctx context.Context, existingSecret *v1.Secret, mutationFunc func(secret *v1.Secret) error, es *esv1.ExternalSecret, secretName string) error {
  583. fqdn := fqdnFor(es.Name)
  584. // fail if the secret does not exist
  585. // this should never happen because we check this before calling this function
  586. if existingSecret.UID == "" {
  587. return fmt.Errorf(errUpdateNotFound, secretName)
  588. }
  589. // set the binding reference to the secret
  590. // https://github.com/external-secrets/external-secrets/pull/2263
  591. es.Status.Binding = v1.LocalObjectReference{Name: secretName}
  592. // mutate a copy of the existing secret with the mutation function
  593. updatedSecret := existingSecret.DeepCopy()
  594. if err := mutationFunc(updatedSecret); err != nil {
  595. return fmt.Errorf(errMutate, updatedSecret.Name, err)
  596. }
  597. // if the secret does not need to be updated, return early
  598. if equality.Semantic.DeepEqual(existingSecret, updatedSecret) {
  599. return nil
  600. }
  601. // if the existing secret is immutable, we can only update the object metadata
  602. if ptr.Deref(existingSecret.Immutable, false) {
  603. // check if the metadata was changed
  604. metadataChanged := !equality.Semantic.DeepEqual(existingSecret.ObjectMeta, updatedSecret.ObjectMeta)
  605. // check if the immutable data/type was changed
  606. var dataChanged bool
  607. if metadataChanged {
  608. // update the `existingSecret` object with the metadata from `updatedSecret`
  609. // this lets us compare the objects to see if the immutable data/type was changed
  610. existingSecret.ObjectMeta = *updatedSecret.ObjectMeta.DeepCopy()
  611. dataChanged = !equality.Semantic.DeepEqual(existingSecret, updatedSecret)
  612. // because we use labels and annotations to keep track of the secret,
  613. // we need to update the metadata, regardless of if the immutable data was changed
  614. // NOTE: we are using the `existingSecret` object here, as we ONLY want to update the metadata,
  615. // and we previously copied the metadata from the `updatedSecret` object
  616. if err := r.Update(ctx, existingSecret, client.FieldOwner(fqdn)); err != nil {
  617. // if we get a conflict, we should return early to requeue immediately
  618. // note, we don't wrap this error so we can handle it in the caller
  619. if apierrors.IsConflict(err) {
  620. return err
  621. }
  622. return fmt.Errorf(errUpdate, existingSecret.Name, err)
  623. }
  624. } else {
  625. // we know there was some change in the secret (or we would have returned early)
  626. // we know the metadata was NOT changed (metadataChanged == false)
  627. // so, the only thing that could have changed is the immutable data/type fields
  628. dataChanged = true
  629. }
  630. // if the immutable data was changed, we should return an error
  631. if dataChanged {
  632. return fmt.Errorf(errUpdate, existingSecret.Name, ErrSecretImmutable)
  633. }
  634. }
  635. // update the secret
  636. if err := r.Update(ctx, updatedSecret, client.FieldOwner(fqdn)); err != nil {
  637. // if we get a conflict, we should return early to requeue immediately
  638. // note, we don't wrap this error so we can handle it in the caller
  639. if apierrors.IsConflict(err) {
  640. return err
  641. }
  642. return fmt.Errorf(errUpdate, updatedSecret.Name, err)
  643. }
  644. r.recorder.Event(es, v1.EventTypeNormal, esv1.ReasonUpdated, eventUpdated)
  645. return nil
  646. }
  647. // getManagedDataKeys returns the list of data keys in a secret which are managed by a specified owner.
  648. func getManagedDataKeys(secret *v1.Secret, fieldOwner string) ([]string, error) {
  649. return getManagedFieldKeys(secret, fieldOwner, func(fields map[string]any) []string {
  650. dataFields := fields["f:data"]
  651. if dataFields == nil {
  652. return nil
  653. }
  654. df, ok := dataFields.(map[string]any)
  655. if !ok {
  656. return nil
  657. }
  658. return slices.Collect(maps.Keys(df))
  659. })
  660. }
  661. func getManagedFieldKeys(
  662. secret *v1.Secret,
  663. fieldOwner string,
  664. process func(fields map[string]any) []string,
  665. ) ([]string, error) {
  666. fqdn := fqdnFor(fieldOwner)
  667. var keys []string
  668. for _, v := range secret.ObjectMeta.ManagedFields {
  669. if v.Manager != fqdn {
  670. continue
  671. }
  672. fields := make(map[string]any)
  673. err := json.Unmarshal(v.FieldsV1.Raw, &fields)
  674. if err != nil {
  675. return nil, fmt.Errorf("error unmarshaling managed fields: %w", err)
  676. }
  677. for _, key := range process(fields) {
  678. if key == "." {
  679. continue
  680. }
  681. keys = append(keys, strings.TrimPrefix(key, "f:"))
  682. }
  683. }
  684. return keys, nil
  685. }
  686. func shouldSkipClusterSecretStore(r *Reconciler, es *esv1.ExternalSecret) bool {
  687. return !r.ClusterSecretStoreEnabled && es.Spec.SecretStoreRef.Kind == esv1.ClusterSecretStoreKind
  688. }
  689. // shouldSkipUnmanagedStore iterates over all secretStore references in the externalSecret spec,
  690. // fetches the store and evaluates the controllerClass property.
  691. // Returns true if any storeRef points to store with a non-matching controllerClass.
  692. func shouldSkipUnmanagedStore(ctx context.Context, namespace string, r *Reconciler, es *esv1.ExternalSecret) (bool, error) {
  693. var storeList []esv1.SecretStoreRef
  694. if es.Spec.SecretStoreRef.Name != "" {
  695. storeList = append(storeList, es.Spec.SecretStoreRef)
  696. }
  697. for _, ref := range es.Spec.Data {
  698. if ref.SourceRef != nil {
  699. storeList = append(storeList, ref.SourceRef.SecretStoreRef)
  700. }
  701. }
  702. for _, ref := range es.Spec.DataFrom {
  703. if ref.SourceRef != nil && ref.SourceRef.SecretStoreRef != nil {
  704. storeList = append(storeList, *ref.SourceRef.SecretStoreRef)
  705. }
  706. // verify that generator's controllerClass matches
  707. if ref.SourceRef != nil && ref.SourceRef.GeneratorRef != nil {
  708. _, obj, err := resolvers.GeneratorRef(ctx, r.Client, r.Scheme, namespace, ref.SourceRef.GeneratorRef)
  709. if err != nil {
  710. if apierrors.IsNotFound(err) {
  711. // skip non-existent generators
  712. continue
  713. }
  714. if errors.Is(err, resolvers.ErrUnableToGetGenerator) {
  715. // skip generators that we can't get (e.g. due to being invalid)
  716. continue
  717. }
  718. return false, err
  719. }
  720. skipGenerator, err := shouldSkipGenerator(r, obj)
  721. if err != nil {
  722. return false, err
  723. }
  724. if skipGenerator {
  725. return true, nil
  726. }
  727. }
  728. }
  729. for _, ref := range storeList {
  730. var store esv1.GenericStore
  731. switch ref.Kind {
  732. case esv1.SecretStoreKind, "":
  733. store = &esv1.SecretStore{}
  734. case esv1.ClusterSecretStoreKind:
  735. store = &esv1.ClusterSecretStore{}
  736. namespace = ""
  737. default:
  738. return false, fmt.Errorf("unsupported secret store kind: %s", ref.Kind)
  739. }
  740. err := r.Get(ctx, types.NamespacedName{
  741. Name: ref.Name,
  742. Namespace: namespace,
  743. }, store)
  744. if err != nil {
  745. if apierrors.IsNotFound(err) {
  746. // skip non-existent stores
  747. continue
  748. }
  749. return false, err
  750. }
  751. class := store.GetSpec().Controller
  752. if class != "" && class != r.ControllerClass {
  753. return true, nil
  754. }
  755. }
  756. return false, nil
  757. }
  758. func shouldRefresh(es *esv1.ExternalSecret) bool {
  759. switch es.Spec.RefreshPolicy {
  760. case esv1.RefreshPolicyCreatedOnce:
  761. if es.Status.SyncedResourceVersion == "" || es.Status.RefreshTime.IsZero() {
  762. return true
  763. }
  764. return false
  765. case esv1.RefreshPolicyOnChange:
  766. if es.Status.SyncedResourceVersion == "" || es.Status.RefreshTime.IsZero() {
  767. return true
  768. }
  769. return es.Status.SyncedResourceVersion != util.GetResourceVersion(es.ObjectMeta)
  770. case esv1.RefreshPolicyPeriodic:
  771. return shouldRefreshPeriodic(es)
  772. default:
  773. return shouldRefreshPeriodic(es)
  774. }
  775. }
  776. func shouldRefreshPeriodic(es *esv1.ExternalSecret) bool {
  777. // if the refresh interval is 0, and we have synced previously, we should not refresh
  778. if es.Spec.RefreshInterval.Duration <= 0 && es.Status.SyncedResourceVersion != "" {
  779. return false
  780. }
  781. // if the ExternalSecret has been updated, we should refresh
  782. if es.Status.SyncedResourceVersion != util.GetResourceVersion(es.ObjectMeta) {
  783. return true
  784. }
  785. // if the last refresh time is zero, we should refresh
  786. if es.Status.RefreshTime.IsZero() {
  787. return true
  788. }
  789. // if the last refresh time is in the future, we should refresh
  790. if es.Status.RefreshTime.Time.After(time.Now()) {
  791. return true
  792. }
  793. // if the last refresh time + refresh interval is before now, we should refresh
  794. return es.Status.RefreshTime.Add(es.Spec.RefreshInterval.Duration).Before(time.Now())
  795. }
  796. // isSecretValid checks if the secret exists, and it's data is consistent with the calculated hash.
  797. func isSecretValid(existingSecret *v1.Secret, es *esv1.ExternalSecret) bool {
  798. // Secret is always valid with `CreationPolicy=Orphan`
  799. if es.Spec.Target.CreationPolicy == esv1.CreatePolicyOrphan {
  800. return true
  801. }
  802. if existingSecret.UID == "" {
  803. return false
  804. }
  805. // if the managed label is missing or incorrect, then it's invalid
  806. if existingSecret.Labels[esv1.LabelManaged] != esv1.LabelManagedValue {
  807. return false
  808. }
  809. // if the data-hash annotation is missing or incorrect, then it's invalid
  810. // this is how we know if the data has chanced since we last updated the secret
  811. if existingSecret.Annotations[esv1.AnnotationDataHash] != utils.ObjectHash(existingSecret.Data) {
  812. return false
  813. }
  814. return true
  815. }
  816. // SetupWithManager returns a new controller builder that will be started by the provided Manager.
  817. func (r *Reconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options) error {
  818. r.recorder = mgr.GetEventRecorderFor("external-secrets")
  819. // index ExternalSecrets based on the target secret name,
  820. // this lets us quickly find all ExternalSecrets which target a specific Secret
  821. if err := mgr.GetFieldIndexer().IndexField(context.Background(), &esv1.ExternalSecret{}, indexESTargetSecretNameField, func(obj client.Object) []string {
  822. es := obj.(*esv1.ExternalSecret)
  823. // if the target name is set, use that as the index
  824. if es.Spec.Target.Name != "" {
  825. return []string{es.Spec.Target.Name}
  826. }
  827. // otherwise, use the ExternalSecret name
  828. return []string{es.Name}
  829. }); err != nil {
  830. return err
  831. }
  832. // predicate function to ignore secret events unless they have the "managed" label
  833. secretHasESLabel := predicate.NewPredicateFuncs(func(object client.Object) bool {
  834. value, hasLabel := object.GetLabels()[esv1.LabelManaged]
  835. return hasLabel && value == esv1.LabelManagedValue
  836. })
  837. return ctrl.NewControllerManagedBy(mgr).
  838. WithOptions(opts).
  839. For(&esv1.ExternalSecret{}).
  840. // we cant use Owns(), as we don't set ownerReferences when the creationPolicy is not Owner.
  841. // we use WatchesMetadata() to reduce memory usage, as otherwise we have to process full secret objects.
  842. WatchesMetadata(
  843. &v1.Secret{},
  844. handler.EnqueueRequestsFromMapFunc(r.findObjectsForSecret),
  845. builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}, secretHasESLabel),
  846. ).
  847. Complete(r)
  848. }
  849. func (r *Reconciler) findObjectsForSecret(ctx context.Context, secret client.Object) []reconcile.Request {
  850. externalSecretsList := &esv1.ExternalSecretList{}
  851. listOps := &client.ListOptions{
  852. FieldSelector: fields.OneTermEqualSelector(indexESTargetSecretNameField, secret.GetName()),
  853. Namespace: secret.GetNamespace(),
  854. }
  855. err := r.List(ctx, externalSecretsList, listOps)
  856. if err != nil {
  857. return []reconcile.Request{}
  858. }
  859. requests := make([]reconcile.Request, len(externalSecretsList.Items))
  860. for i := range externalSecretsList.Items {
  861. requests[i] = reconcile.Request{
  862. NamespacedName: types.NamespacedName{
  863. Name: externalSecretsList.Items[i].GetName(),
  864. Namespace: externalSecretsList.Items[i].GetNamespace(),
  865. },
  866. }
  867. }
  868. return requests
  869. }