FluxCD is a GitOps operator for Kubernetes. It synchronizes the status of the cluster from manifests allocated in different repositories (Git or Helm). This approach fits perfectly with External Secrets on clusters which are dynamically created, to get credentials with no manual intervention from the beginning.
This approach has several advantages as follows:
FluxCD is composed by several controllers dedicated to manage different custom resources. The most important ones are Kustomization (to clarify, Flux one, not Kubernetes' one) and HelmRelease to deploy using the approaches of the same names.
External Secrets can be deployed using Helm as explained here. The deployment includes the
CRDs if enabled on the values.yaml, but after this, you need to deploy some SecretStore to start
getting credentials from your secrets manager with External Secrets.
The idea of this guide is to deploy the whole stack, using flux, needed by developers not to worry about the credentials, but only about the application and its code.
This can sound easy, but External Secrets is deployed using Helm, which is managed by the HelmController,
and your custom resources, for example a ClusterSecretStore and the related Secret, are often deployed using a
kustomization.yaml, which is deployed by the KustomizeController.
Both controllers manage the resources independently, at different moments, with no possibility to wait each other.
This means that we have a wonderful race condition where sometimes the CRs (SecretStore,ClusterSecretStore...) tries
to be deployed before than the CRDs needed to recognize them.
Let's see the conditions to start working on a solution:
CustomResourceDefinition and the CRs needed laterKustomizationKustomization too, allowing dependency between CRDs and CRskustomizationTo have a better view of things needed later, the first manifest to be created is the kustomization.yaml
{% include 'gitops/kustomization.yaml' %}
To access your secret manager, External Secrets needs some credentials. They are stored inside a Secret, which is intended
to be deployed by automation as a good practise. This time, a placeholder called secret-token.yaml is show as an example:
# The namespace.yaml first
{% include 'gitops/namespace.yaml' %}
{% include 'gitops/secret-token.yaml' %}
Create a manifest called repositories.yaml to store the references to external repositories for Flux
{% include 'gitops/repositories.yaml' %}
As mentioned, CRDs can be deployed using the official Helm package, but to solve the race condition, they will be deployed
from our git repository using a Kustomization manifest called deployment-crds.yaml as follows:
{% include 'gitops/deployment-crds.yaml' %}
The operator is deployed using a HelmRelease manifest to deploy the Helm package, but due to the special race condition,
the deployment must be disabled in the values of the manifest called deployment.yaml, as follows:
{% include 'gitops/deployment.yaml' %}
Now, be ready for the arcane magic. Create a Kustomization manifest called deployment-crs.yaml with the following content:
{% include 'gitops/deployment-crs.yaml' %}
There are several interesting details to see here, that finally solves the race condition:
dependsOn, which points to a previous Kustomization called external-secrets-crds. This
dependency forces this deployment to wait for the other to be ready, before start being deployed.yaml
path: ./infrastructure/external-secrets/crs
sourceRef:
kind: GitRepository
name: flux-system
Custom Resources will be searched in the relative path ./infrastructure/external-secrets/crs of the GitRepository
called flux-system, which is a reference to the same repository that FluxCD watches to synchronize the cluster.
With fewer words, a reference to itself, but going to another directory called crsOf course, allocate inside the mentioned path ./infrastructure/external-secrets/crs, all the desired CRs to be deployed,
for example, a manifest clusterSecretStore.yaml to reach your Hashicorp Vault as follows:
{% include 'gitops/crs/clusterSecretStore.yaml' %}
At the end, the required files tree is shown in the following picture: