- Inspect the existing Flux Kustomization and the plain Secret file in Git.
- Demonstrate Flux reconciliation re-creating a deleted Secret.
- Suspend reconciliation, delete the in-cluster plain Secret, and show the workload failing.
- Create a SealedSecret using the
kubesealCLI and the Sealed Secrets controller public certificate. - Replace the plain Secret in Git with the SealedSecret and resume reconciliation.
- Verify the Secret is decrypted in-cluster and the workload recovers.
Kustomization in the infrastructure repository (applies manifests in the
database namespace)
How Flux reconciliation behaves (short demonstration) Flux constantly reconciles Git to the cluster. If you delete a resource in-cluster, Flux will likely re-create it when it next reconciles. Example session (cleaned for readability):
interval: 10s, Flux checks and applies changes frequently.
Suspend reconciliation so we can perform the SealedSecret transition safely Temporarily suspend the Kustomization so Flux does not immediately re-create the plain Secret while you prepare the SealedSecret:
Create a SealedSecret using the kubeseal CLI We will convert the plain Secret YAML into a SealedSecret using the Sealed Secrets controller public certificate and the
kubeseal CLI. The SealedSecret preserves key names (like password) but encrypts values so the file is safe to store in Git.
-
Ensure the plain Secret manifest (the file that previously lived in Git) is present locally as
secret-mysql.yaml. - Run kubeseal with the controller public certificate:
-o yaml— output a SealedSecret resource as YAML.--scope cluster-wide— annotate the SealedSecret for cluster-wide usage (be explicit about scope).--cert ../../sealed-secret.pub— use the Sealed Secrets controller public certificate. Adjust the path to your cert file.< secret-mysql.yaml— read the original Secret from stdin.> sealed-secret-mysql.yaml— write the SealedSecret YAML to a new file.
password remains visible so the manifest stays clear.
Before committing the SealedSecret to Git, remove or rename the plain Secret manifest so Flux will no longer reconcile the plain file:
sealed-secret-mysql.yaml to your repository in the same location where the plain Secret previously lived.
Do not keep plain-text secret files in Git. Use SealedSecrets (or another secret management solution) to store secrets safely in your repository.
Resume Flux reconciliation and let the SealedSecrets controller decrypt and apply After pushing the SealedSecret to Git, resume the Kustomization so Flux fetches the latest revision and applies the SealedSecret:
encryptedData and create a standard Kubernetes Secret in the database namespace.
Verify the Secret exists and decode the password to confirm the value:
jqfor JSON querying: https://stedolan.github.io/jq/base64for decoding: https://man7.org/linux/man-pages/man1/base64.1.html
How it works (high level)
kubesealencrypts secret values using the Sealed Secrets controller public key and produces a SealedSecret manifest safe to store in Git.- When Flux applies the SealedSecret, the Sealed Secrets controller (running in-cluster) intercepts it and decrypts the data using its private key, creating a standard Kubernetes Secret in the desired namespace.
- This workflow lets you keep encrypted secrets in Git while enabling the cluster to receive decrypted Secrets at apply-time, improving security in GitOps workflows.
Links and References
- Bitnami Sealed Secrets: https://github.com/bitnami-labs/sealed-secrets
- kubeseal CLI: https://github.com/bitnami-labs/sealed-secrets#kubeseal-cli
- Sealed Secrets controller: https://github.com/bitnami-labs/sealed-secrets#controller
- FluxCD: https://fluxcd.io/
- Flux Kustomization docs: https://toolkit.fluxcd.io/components/kustomize/kustomization/
- jq: https://stedolan.github.io/jq/
- base64 man page: https://man7.org/linux/man-pages/man1/base64.1.html