Service Foundry
Young Gyu Kim <credemol@gmail.com>

Securing Sensitive Data in GitOps with Sealed Secrets

github sealed secret

Overview

GitOps empowers teams to manage Kubernetes resources declaratively with Git as the single source of truth. However, managing sensitive data such as passwords, API keys, and tokens poses significant security challenges when stored directly in version control.

Sealed Secrets, developed by Bitnami, addresses this challenge by allowing users to encrypt Kubernetes secrets into a format that can be safely committed to Git. These secrets can only be decrypted by the Sealed Secrets controller running inside the Kubernetes cluster, ensuring data security while maintaining GitOps workflows.

This guide walks you through installing the Sealed Secrets controller and CLI, creating encrypted secrets, and automating their deployment using Argo CD.

Installing Sealed Secrets Controller

Install the controller by applying the release manifest:

$ kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.30.0/controller.yaml

Alternatively, download and apply manually:

$ curl -L https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.30.0/controller.yaml -o sealed-secrets-controller-0.30.0.yaml
$ kubectl apply -f sealed-secrets-controller-0.30.0.yaml

Sample output:

role.rbac.authorization.k8s.io/sealed-secrets-key-admin created
serviceaccount/sealed-secrets-controller created
rolebinding.rbac.authorization.k8s.io/sealed-secrets-service-proxier created
role.rbac.authorization.k8s.io/sealed-secrets-service-proxier created
service/sealed-secrets-controller-metrics created
rolebinding.rbac.authorization.k8s.io/sealed-secrets-controller created
deployment.apps/sealed-secrets-controller created
customresourcedefinition.apiextensions.k8s.io/sealedsecrets.bitnami.com created
service/sealed-secrets-controller created
clusterrolebinding.rbac.authorization.k8s.io/sealed-secrets-controller created
clusterrole.rbac.authorization.k8s.io/secrets-unsealer created

Verify Installation:

$ kubectl -n kube-system get pods -l name=sealed-secrets-controller

Installing the Sealed Secrets CLI

Install the CLI using Homebrew:

$ brew install kubeseal

$ kubeseal --version

Creating a Sealed Secret

Step 1: Fetch the Public Certificate

This public certificate will be used to encrypt secrets.

$ kubeseal --fetch-cert \
    --controller-name=sealed-secrets-controller \
    --controller-namespace=kube-system \
    > pub-cert.pem

Step 2: Define a Kubernetes Secret

Keycloak requires two secrets below:

  • keycloak-credentials

  • keycloak-postgresql-credentials

keycloak-credentials-secret.yaml
apiVersion: v1
data:
  admin-password: eW91ci1wYXNzd29yZCAtbgo=
kind: Secret
metadata:
  name: keycloak-credentials
  namespace: keycloak

Step 3: Generate a Sealed Secret

$ kubeseal --format=yaml --cert=pub-cert.pem \
    < keycloak-credentials-secret.yaml > keycloak-credentials-sealed-secret.yaml

The result:

keycloak-credentials-sealed-secret.yaml
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  creationTimestamp: null
  name: keycloak-credentials
  namespace: keycloak
spec:
  encryptedData:
    admin-password: AgCJ5mZppPrj493ASGPtz/mRWKuEeqqTpz0A5AVdvkCr+40vnbf/dH3VL/91tI1QY2bXqZlGUgPdGEuw6+AijvfWcNMGS52TqxOc+pAU2X5wny4WR2lJl3e5Do1KxWSk2JiprqSNdZodYnoRIwNb1XqZrVbJDLrnIgqrjfmweXyI03kxY3VL5P75+4wep0UZifXl2cMSmFni9Wkm+LDTEu9PicUCWx2iSWqDzK70HAUURJh1iyiuHtKzHemfeOLOEf8LmKanhJet5oMU2Atv1Lb7dqO+RqTRR203CBfaGQIlESqfNu7cdJq5RGSJXsByOb64tEvsUrkZt/pKPqlhgIYNvl3yGC+TsBS/zCXU1anQQ9B/iQ3M/nJqziPR+mY7E6GEMwpYjxL3o+RVBi/Y0bQJQzGWhrV7+/39t8f2XGpOnJ2qrzl627C5ptZbRWcDNDeRHPrHgebees4fWD5GnfmZ8x9iDARaZVklxPfOktuGeErPwGicnGZiXi613otEmHJZ2HTG6PybSHyWsg8dir5iFzRqwHhnL7uY7SeEVwOoEuQWxsRttJ3ImlyYoth8GNLjJ0reHRKoWAEFS+WK5DGvhc04bYAg8/zk50AhAfhnb04eEL2uxgwdPkwFOlyH6qXx2+NQIrmZPCz0+dV5pBAZpVnk/Gtz+syAgylMyOvfbF676H59LLRb++C74A9RYdzhSTywSyFayQ==
  template:
    metadata:
      creationTimestamp: null
      name: keycloak-credentials
      namespace: keycloak

You can also overwrite the original file inline:

$ kubeseal --format=yaml --cert=pub-cert.pem \
    < keycloak-credentials-secret.yaml > tmp.yaml && \
    mv tmp.yaml keycloak-credentials-secret.yaml


$ kubeseal --format=yaml --cert=pub-cert.pem \
    < keycloak-postgresql-credentials-secret.yaml > tmp.yaml && \
    mv tmp.yaml keycloak-postgresql-credentials-secret.yaml

Applying the Sealed Secret via GitOps

With Argo CD in place, simply commit your Sealed Secret to your Git repository:

$ git add *
$ git commit -m “Add sealed secret for Keycloak”
$ git push origin main

The encrypted Sealed Secret is stored in the Git repository, and it is much more secure than a regular Kubernetes Secret.

github sealed secret
Figure 1. Sealed Secrets on GitHub

Argo CD will detect the changes and apply them automatically, thanks to its continuous sync capabilities.

argocd auto sync
Figure 2. Sealed Secrets and Secrets in ArgoCD

Secrets created by Sealed Secrets will be automatically decrypted and applied to the cluster by the Sealed Secrets controller. You can view the decrypted secrets in the ArgoCD UI or by using kubectl commands.

Verifying Decryption and Secret Availability

List the SealedSecrets in the namespace:

kubectl get sealedsecrets -n keycloak
NAME                              AGE
keycloak-credentials              17m
keycloak-postgresql-credentials   28m

List the unsealed, usable Kubernetes Secrets:

kubectl get secrets -n keycloak
NAME                              TYPE     DATA   AGE
keycloak-credentials              Opaque   1      17m
keycloak-postgresql-credentials   Opaque   2      28m

Conclusion

Sealed Secrets enables secure secret management for GitOps-based workflows by encrypting sensitive information before it is committed to Git. Combined with Argo CD, you can automate and securely manage the lifecycle of Kubernetes secrets while adhering to GitOps best practices. This approach ensures both auditability and compliance without compromising security.

📘 View the web version: