GitOps with FluxCD

Secret Management Sign Verification

DEMO EncryptDecrypt Secret using Bitnami Sealed Secrets

In this guide, we’ll cover how to secure Kubernetes Secrets by encrypting them with Bitnami Sealed Secrets and manage them declaratively using FluxCD and Kustomize.

Table of Contents

StepDescriptionReference Command
1. BackgroundPlaintext Secret in Git
2. Automatic ReconciliationFluxCD constantly applies Git manifestskubectl -n database get po,secret
3. Suspend ReconciliationPause FluxCD Kustomizationflux suspend kustomization …
4. Trigger Pod FailureRestart Pod to observe missing Secretkubectl rollout restart …
5. Encrypt with kubesealGenerate a SealedSecretkubeseal --cert …
6. Replace PlaintextCommit encrypted manifest to Gitgit add sealed-secret-mysql.yaml
7. Resume ReconciliationApply updated Git source and resume FluxCDflux resume kustomization …
8. Verify DecryptionConfirm the decrypted Secret in clusterkubectl get secret …

1. Background: Plaintext Secret in Git

We have a FluxCD Kustomization that applies manifests from a Git repository:

apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
  name: infra-database-kustomize-git-mysql
  namespace: flux-system
spec:
  interval: 10s
  path: ./database
  prune: true
  sourceRef:
    kind: GitRepository
    name: infra-source-git
  targetNamespace: database

Under ./database/secret-mysql.yaml, the MySQL password is stored in plaintext:

apiVersion: v1
kind: Secret
metadata:
  name: secret-mysql
  namespace: database
stringData:
  password: mysql-password-0123456789

Warning

Storing passwords or tokens in plaintext within Git exposes them to unauthorized access. Always encrypt sensitive data before committing.

FluxCD’s Kustomize controller reconciles this Secret every 10 seconds, ensuring it’s present in the cluster.


2. Demonstrate Automatic Reconciliation

Verify the Secret and Pod exist:

kubectl -n database get pods,secret

Delete the Secret to see automatic re-creation:

kubectl -n database delete secret secret-mysql
# Wait a few seconds…
kubectl -n database get secret secret-mysql

FluxCD detects the drift and re-applies the manifest, recreating the Secret.


3. Suspend Reconciliation

Pause the Kustomization so FluxCD stops reconciling this directory:

flux suspend kustomization infra-database-kustomize-git-mysql
flux get kustomization infra-database-kustomize-git-mysql

Now, deleting the Secret will not trigger re-creation:

kubectl -n database delete secret secret-mysql
kubectl -n database get secrets

4. Trigger Pod Failure

Force a deployment restart to spawn a new Pod, which will fail due to the missing Secret:

kubectl -n database rollout restart deployment mysql
kubectl -n database get pods -w

Observe a CreateContainerConfigError:

kubectl -n database describe pod mysql-*
# ...
# Warning  Failed  Error: cannot find secret "secret-mysql"

5. Encrypt the Secret with kubeseal

Ensure you have:

  • The kubeseal CLI installed.
  • The Sealed Secrets public key (sealed-secrets.pub).

Encrypt the existing Secret manifest:

kubeseal --cert ../../sealed-secrets.pub \
  --scope cluster-wide \
  -o yaml < secret-mysql.yaml > sealed-secret-mysql.yaml

This creates a SealedSecret resource:

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: secret-mysql
  namespace: database
  annotations:
    sealedsecrets.bitnami.com/cluster-wide: "true"
spec:
  encryptedData:
    password: AgBv9SokhhVk4WNdTmmPxd9D0J2ETJY...
  template:
    metadata:
      name: secret-mysql
      namespace: database

Note

Only the Secret’s values are encrypted. The keys (password) stay in cleartext for mapping.


6. Replace the Plaintext Secret

Backup the original manifest and commit the sealed version:

mv secret-mysql.yaml secret-mysql.yaml.bak
git add sealed-secret-mysql.yaml
git commit -m "Add SealedSecret for MySQL password"
git push

7. Resume Reconciliation

Sync your Git source and resume the Kustomization:

flux reconcile source git flux-system
flux resume kustomization infra-database-kustomize-git-mysql

FluxCD applies the SealedSecret, and the Bitnami controller decrypts it into a normal Kubernetes Secret in database.


8. Verify the Decrypted Secret

Check that the Secret has been created:

kubectl -n database get secret secret-mysql

Decode and inspect the password:

kubectl -n database get secret secret-mysql -o jsonpath='{.data.password}' | base64 -d
# => mysql-password-0123456789

Confirm the Pod is now running:

kubectl -n database get pods

9. Conclusion

You have successfully:

  1. Suspended FluxCD reconciliation.
  2. Deleted a plaintext Secret and saw a Pod failure.
  3. Used kubeseal to create an encrypted SealedSecret.
  4. Committed the SealedSecret to your Git repo.
  5. Resumed FluxCD reconciliation and verified automatic decryption.

By integrating Bitnami Sealed Secrets with FluxCD and Kustomize, you can store encrypted secrets in Git, maintain GitOps workflows, and ensure secrets only decrypt inside your Kubernetes cluster.


Watch Video

Watch video content

Practice Lab

Practice lab

Previous
DEMO Setup Bitnami Sealed