Using Sealed Secrets with ArgoCD and Helm Charts


When managing Kubernetes applications with ArgoCD and Helm, securing sensitive data such as passwords, API keys, and other secrets is crucial. Bitnami Sealed Secrets provides a powerful way to encrypt secrets that can be safely stored in Git and used within your ArgoCD and Helm workflows.

This guide will cover how to integrate Sealed Secrets with ArgoCD and Helm to securely manage secrets in your values.yaml files for Helm charts.

Overview

ArgoCD allows you to deploy and manage applications in Kubernetes using GitOps principles, where the desired state of your applications is stored in Git repositories. Helm, on the other hand, is a package manager for Kubernetes that simplifies application deployment through reusable templates (Helm charts).

Bitnami Sealed Secrets provides a way to encrypt your Kubernetes secrets using a public key, which can only be decrypted by the Sealed Secrets controller running in your Kubernetes cluster. This allows you to safely store and version-control encrypted secrets.

1. Prerequisites

Before you begin, ensure you have the following set up:

  1. Kubernetes Cluster: A running Kubernetes cluster.
  2. ArgoCD: Installed and configured in your Kubernetes cluster.
  3. Helm: Installed on your local machine.
  4. Sealed Secrets: The Sealed Secrets controller installed in your Kubernetes cluster.
  5. kubeseal: The Sealed Secrets CLI tool installed on your local machine.

2. Setting Up Sealed Secrets

If you haven’t already installed the Sealed Secrets controller, follow these steps:

Install the Sealed Secrets Controller

Using Helm:

helm repo add bitnami https://charts.bitnami.com/bitnami
helm install sealed-secrets-controller bitnami/sealed-secrets

Or using kubectl:

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

3. Encrypting Helm Values Using Sealed Secrets

In this section, we’ll demonstrate how to encrypt sensitive values in a Helm values.yaml file using Sealed Secrets, ensuring they are securely managed and version-controlled.

Step 1: Identify Sensitive Data in values.yaml

Suppose you have a Helm chart with a values.yaml file that contains sensitive information:

# values.yaml
database:
  username: admin
  password: my-secret-password  # Sensitive data
  host: db.example.com

Step 2: Create a Kubernetes Secret Manifest

First, create a Kubernetes Secret manifest for the sensitive data:

# my-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: my-database-secret
  namespace: default
type: Opaque
data:
  password: bXktc2VjcmV0LXBhc3N3b3Jk  # base64 encoded 'my-secret-password'

Step 3: Encrypt the Secret Using kubeseal

Use the kubeseal CLI to encrypt the secret using the public key from the Sealed Secrets controller:

kubeseal --format yaml < my-secret.yaml > my-sealedsecret.yaml

This command generates a SealedSecret resource that is safe to store in your Git repository:

# my-sealedsecret.yaml
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: my-database-secret
  namespace: default
spec:
  encryptedData:
    password: AgA7SyR4l5URRXg...  # Encrypted data

Step 4: Modify the Helm Chart to Use the SealedSecret

In your Helm chart, modify the values.yaml file to reference the Kubernetes Secret instead of directly embedding sensitive values:

# values.yaml
database:
  username: admin
  secretName: my-database-secret
  host: db.example.com

In the deployment.yaml template of your Helm chart, reference the secret:

# templates/deployment.yaml
env:
  - name: DB_USERNAME
    value: {{ .Values.database.username }}
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: {{ .Values.database.secretName }}
        key: password

This approach keeps the sensitive data out of the values.yaml file, instead storing it securely in a SealedSecret.

Step 5: Apply the SealedSecret to Your Kubernetes Cluster

Apply the SealedSecret to your cluster:

kubectl apply -f my-sealedsecret.yaml

The Sealed Secrets controller will decrypt the SealedSecret and create the corresponding Kubernetes Secret.

4. Deploying the Helm Chart with ArgoCD

Step 1: Create an ArgoCD Application

You can create an ArgoCD application either via the ArgoCD UI or using the argocd CLI. Here’s how to do it with the CLI:

argocd app create my-app \
  --repo https://github.com/your-org/your-repo.git \
  --path helm/my-app \
  --dest-server https://kubernetes.default.svc \
  --dest-namespace default

In this command:

  • --repo: The URL of the Git repository where your Helm chart is stored.
  • --path: The path to the Helm chart within the repository.
  • --dest-server: The Kubernetes API server.
  • --dest-namespace: The namespace where the application will be deployed.

Step 2: Sync the Application

Once the ArgoCD application is created, ArgoCD will monitor the Git repository for changes and automatically synchronize the Kubernetes cluster with the desired state.

  • Auto-Sync: If auto-sync is enabled, ArgoCD will automatically deploy the application whenever changes are detected in the Git repository.
  • Manual Sync: You can manually trigger a sync using the ArgoCD UI or CLI:
  argocd app sync my-app

5. Example: Encrypting and Using Multiple Secrets

In more complex scenarios, you might have multiple sensitive values to encrypt. Here’s how you can manage multiple secrets:

Step 1: Create Multiple Kubernetes Secrets

# db-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
  namespace: default
type: Opaque
data:
  username: YWRtaW4= # base64 encoded 'admin'
  password: c2VjcmV0cGFzcw== # base64 encoded 'secretpass'

# api-key-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: api-key-secret
  namespace: default
type: Opaque
data:
  apiKey: c2VjcmV0YXBpa2V5 # base64 encoded 'secretapikey'

Step 2: Encrypt the Secrets Using kubeseal

Encrypt each secret using kubeseal:

kubeseal --format yaml < db-secret.yaml > db-sealedsecret.yaml
kubeseal --format yaml < api-key-secret.yaml > api-key-sealedsecret.yaml

Step 3: Apply the SealedSecrets

Apply the SealedSecrets to your Kubernetes cluster:

kubectl apply -f db-sealedsecret.yaml
kubectl apply -f api-key-sealedsecret.yaml

Step 4: Reference Secrets in Helm Values

Modify your Helm values.yaml file to reference these secrets:

# values.yaml
database:
  secretName: db-secret
api:
  secretName: api-key-secret

In your Helm chart templates, use the secrets:

# templates/deployment.yaml
env:
  - name: DB_USERNAME
    valueFrom:
      secretKeyRef:
        name: {{ .Values.database.secretName }}
        key: username
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: {{ .Values.database.secretName }}
        key: password
  - name: API_KEY
    valueFrom:
      secretKeyRef:
        name: {{ .Values.api.secretName }}
        key: apiKey

6. Best Practices

  • Environment-Specific Secrets: Use different SealedSecrets for different environments (e.g., staging, production). Encrypt and store these separately.
  • Backup and Rotation: Regularly back up the SealedSecrets and rotate the keys used by the Sealed Secrets controller.
  • Audit and Monitor: Enable logging and monitoring in your Kubernetes cluster to track the use of SealedSecrets.

When creating a Kubernetes Secret, the data must be base64 encoded before you can encrypt it with Sealed Secrets. This is because Kubernetes Secrets expect the values to be base64 encoded, and Sealed Secrets operates on the same principle since it wraps around Kubernetes Secrets.

Why Base64 Encoding?

Kubernetes Secrets require data to be stored as base64-encoded strings. This encoding is necessary because it allows binary data (like certificates, keys, or complex strings) to be stored as plain text in YAML files.

Steps for Using Sealed Secrets with Base64 Encoding

Here’s how you typically work with base64 encoding in the context of Sealed Secrets:

1. Base64 Encode Your Secret Data

Before creating a Kubernetes Secret, you need to base64 encode your sensitive data. For example, if your secret is a password like my-password, you would encode it:

echo -n 'my-password' | base64

This command outputs the base64-encoded version of my-password:

bXktcGFzc3dvcmQ=

2. Create the Kubernetes Secret Manifest

Create a Kubernetes Secret YAML file with the base64-encoded value:

apiVersion: v1
kind: Secret
metadata:
  name: my-secret
  namespace: default
type: Opaque
data:
  password: bXktcGFzc3dvcmQ=  # base64 encoded 'my-password'

3. Encrypt the Secret Using kubeseal

Once the Kubernetes Secret manifest is ready, encrypt it using the kubeseal command:

kubeseal --format yaml < my-secret.yaml > my-sealedsecret.yaml

This command creates a SealedSecret, which can safely be committed to version control.

4. Apply the SealedSecret

Finally, apply the SealedSecret to your Kubernetes cluster:

kubectl apply -f my-sealedsecret.yaml

The Sealed Secrets controller in your cluster will decrypt the SealedSecret and create the corresponding Kubernetes Secret with the base64-encoded data.

Summary

  • Base64 Encoding: You must base64 encode your secret data before creating a Kubernetes Secret manifest because Kubernetes expects the data to be in this format.
  • Encrypting with Sealed Secrets: After creating the Kubernetes Secret manifest with base64-encoded data, use Sealed Secrets to encrypt the entire manifest.
  • Applying SealedSecrets: The Sealed Secrets controller will decrypt the SealedSecret and create the Kubernetes Secret with the correctly encoded data.

Conclusion

By combining ArgoCD, Helm, and Sealed Secrets, you can securely manage and deploy Kubernetes applications in a GitOps workflow. Sealed Secrets ensure that sensitive data remains encrypted and safe, even when stored in a version control system, while Helm provides the flexibility to manage complex applications. Following the steps outlined in this guide, you can confidently manage secrets in your Kubernetes deployments, ensuring both security and efficiency.