Skip to main content
This page covers the technical implementation of Sealed Secrets in Skyhook for platform operators and infrastructure teams.
For developer-facing documentation on using environment variables and secrets, see Environment Variables & Secrets.

Overview

Skyhook uses Bitnami Sealed Secrets to encrypt sensitive configuration data before storing it in Git repositories. This ensures secrets remain secure even when version-controlled alongside application code.

How Sealed Secrets Work

Sealed Secrets uses asymmetric cryptography to enable GitOps-compatible secret management:

Architecture

Plain-text secret → Skyhook UI
                  ↓ (encrypt with public key)
    Encrypted SealedSecret → Git repository
                  ↓ (deploy via GitOps/ArgoCD)
        SealedSecret resource → Kubernetes cluster
                  ↓ (decrypt with private key)
        Sealed Secrets Controller → Standard Kubernetes Secret
                  ↓ (mount to pod)
            Application pods → Access secret values

Key Components

Public Key (Cluster-Specific):
  • Each cluster’s Sealed Secrets controller generates a unique public/private key pair
  • Skyhook fetches the public key to encrypt secrets
  • Public keys are safe to store or share
Private Key (Cluster-Specific):
  • Stored securely within the Sealed Secrets controller in the cluster
  • Never leaves the cluster
  • Used to decrypt SealedSecret resources into standard Kubernetes Secrets
Sealed Secrets Controller:
  • Runs in the target Kubernetes cluster (typically in the kube-system namespace)
  • Watches for SealedSecret custom resources
  • Decrypts them into standard Secrets that pods can consume
  • Manages key rotation and renewal

Security Model

The security model ensures zero-knowledge architecture:
  1. Encryption happens client-side: Skyhook encrypts secrets before they leave the UI
  2. Git stores encrypted data: Only encrypted SealedSecret resources are committed to repositories
  3. Decryption happens in-cluster: Only the target cluster’s controller can decrypt secrets
  4. Skyhook has no access: The platform cannot decrypt secrets; only your clusters can
Benefits:
  • Secrets are safe to commit to version control
  • Git provides audit trail (who changed what, when)
  • No central secret store vulnerability
  • Compatible with GitOps workflows
  • Cluster isolation via cluster-specific keys

Installing Sealed Secrets

The Sealed Secrets controller must be installed on all clusters where you want to use secrets.

Installation via Skyhook GitOps

Recommended: Install via Skyhook’s GitOps settings for automated management. Prerequisites:
  1. ArgoCD installed on your management cluster (see GitOps Setup)
  2. Clusters connected to Skyhook
Installation Steps:
  1. Navigate to GitOps Settings in Skyhook
  2. Under Featured Addons, locate Sealed Secrets controller
  3. Select the clusters where you want secrets support (typically all clusters)
  4. Review the default configuration or customize as needed
  5. Generate and merge the pull request to deploy the controller
Default Configuration:
  • Namespace: kube-system
  • Controller image: quay.io/bitnami/sealed-secrets-controller
  • Key renewal: Enabled (30-day rotation)
  • Service type: ClusterIP

Manual Installation

Alternatively, install Sealed Secrets manually using kubectl or Helm: Using kubectl:
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml
Using Helm:
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm install sealed-secrets sealed-secrets/sealed-secrets --namespace kube-system

Verifying Installation

Check that the controller is running:
kubectl get pods -n kube-system -l name=sealed-secrets-controller
Expected output:
NAME                                         READY   STATUS    RESTARTS   AGE
sealed-secrets-controller-xxxxxxxxxx-xxxxx   1/1     Running   0          5m
Fetch the public key (used by Skyhook for encryption):
kubectl get secret -n kube-system sealed-secrets-key -o jsonpath='{.data.tls\.crt}' | base64 -d

How Skyhook Integrates with Sealed Secrets

Secret Encryption Flow

When a developer adds or updates a secret in Skyhook:
  1. Fetch Public Key: Skyhook retrieves the public certificate from the target cluster’s Sealed Secrets controller
  2. Encrypt Secret: The secret value is encrypted client-side using the public key
  3. Create SealedSecret: A SealedSecret Kubernetes custom resource is generated
  4. Store in Git: The encrypted SealedSecret is committed to the deployment repository
  5. Deploy via GitOps: ArgoCD or another GitOps tool syncs the SealedSecret to the cluster
  6. Controller Decrypts: The Sealed Secrets controller decrypts the SealedSecret into a standard Kubernetes Secret
  7. Pods Consume: Application pods mount the Secret as environment variables or files

Secret Storage in Git

Sealed Secrets are stored in your deployment repository as Kubernetes custom resources:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: my-service-secrets
  namespace: production
spec:
  encryptedData:
    DATABASE_URL: AgA...encrypted...
    STRIPE_API_KEY: AgB...encrypted...
The encryptedData fields contain encrypted values that are safe to commit to Git.

Environment-Specific Secrets

Skyhook creates a SealedSecret resource for each service, named after the service itself (e.g., my-service) and deployed to the service’s namespace. Secrets are environment-specific, with separate encrypted values for each environment (production, staging, development, etc.) stored within the same SealedSecret resource. This ensures:
  • Environment isolation through separate encrypted values
  • Different credentials per environment
  • Cluster-specific encryption (production cluster can’t decrypt staging secrets if using different controllers)

Reloader Integration

Overview

Kubernetes does not automatically restart pods when ConfigMaps or Secrets change. Reloader solves this by monitoring configuration resources and triggering rolling restarts when changes are detected.

How Reloader Works

  1. Watch for Changes: Reloader watches ConfigMaps and Secrets referenced by Deployments, StatefulSets, and DaemonSets
  2. Detect Updates: When a ConfigMap or Secret is updated, Reloader detects the change
  3. Trigger Restart: Reloader updates an annotation on the Deployment, triggering a rolling restart
  4. Zero Downtime: Pods restart gracefully with the new configuration

Installing Reloader

Via Skyhook GitOps:
  1. Go to GitOps Settings
  2. Locate Reloader under Featured Addons
  3. Select target clusters
  4. Generate and merge the pull request
Manual Installation:
kubectl apply -f https://raw.githubusercontent.com/stakater/Reloader/master/deployments/kubernetes/reloader.yaml

Enabling Per-Service

Developers can enable auto-reload for individual services in the Env Vars & Secrets tab by checking Reload on Environment Variables or Secrets Update. This adds the necessary annotations to the service’s Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    reloader.stakater.com/auto: "true"
spec:
  # ...

Troubleshooting

Secrets Not Decrypting

Symptoms: SealedSecret exists but no corresponding Secret is created Causes:
  • Sealed Secrets controller not installed or not running
  • Wrong cluster public key used for encryption
  • Controller doesn’t have RBAC permissions
Resolution:
# Check controller status
kubectl get pods -n kube-system -l name=sealed-secrets-controller

# Check controller logs
kubectl logs -n kube-system -l name=sealed-secrets-controller

# Verify SealedSecret resource
kubectl get sealedsecret -n <namespace>
kubectl describe sealedsecret <name> -n <namespace>

Key Rotation Issues

Symptoms: Old secrets work but newly created secrets fail to decrypt Causes:
  • Cluster certificate was rotated but Skyhook is using old public key
  • Manual certificate replacement without proper key backup
Resolution:
  • Ensure Skyhook refetches the cluster’s current public key
  • Verify the controller’s certificate:
    kubectl get secret -n kube-system sealed-secrets-key -o yaml
    

Pods Not Reloading

Symptoms: Secrets updated but pods still use old values Causes:
  • Reloader not installed
  • Reloader annotations missing or incorrect
  • Reloader not watching the correct namespace
Resolution:
# Check if Reloader is running
kubectl get pods -n reloader

# Verify Deployment annotations
kubectl get deployment <deployment-name> -n <namespace> -o yaml | grep reloader

# Check Reloader logs
kubectl logs -n reloader -l app=reloader

kubectl Integration

Sealing Secrets Manually

The kubeseal CLI can manually encrypt secrets: Install kubeseal:
# macOS
brew install kubeseal

# Linux
wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/kubeseal-linux-amd64
chmod +x kubeseal-linux-amd64
sudo mv kubeseal-linux-amd64 /usr/local/bin/kubeseal
Seal a secret:
# Create a Secret (don't apply it)
kubectl create secret generic my-secret \
  --from-literal=DB_PASSWORD=supersecret \
  --dry-run=client -o yaml > secret.yaml

# Seal the secret
kubeseal --format yaml < secret.yaml > sealed-secret.yaml

# Apply the SealedSecret
kubectl apply -f sealed-secret.yaml

Backing Up Encryption Keys

Important: Back up the Sealed Secrets controller’s private key to prevent data loss during cluster migrations. Export private key:
kubectl get secret -n kube-system sealed-secrets-key -o yaml > sealed-secrets-key-backup.yaml
Restore private key (on new cluster):
kubectl apply -f sealed-secrets-key-backup.yaml
kubectl delete pod -n kube-system -l name=sealed-secrets-controller  # Restart controller

Security Best Practices

Key Management

  • Backup private keys: Store sealed-secrets controller keys securely for disaster recovery
  • Rotate keys regularly: Enable automatic key rotation (default: 30 days)
  • Separate keys per cluster: Never share private keys between clusters

Access Control

  • Limit RBAC: Restrict who can view/edit Secrets and SealedSecrets
  • Audit changes: Review Git history for unauthorized secret modifications
  • Encrypt Git repos: Use private repositories with strict access controls

Secret Hygiene

  • Rotate secrets: Update API keys, passwords, and tokens periodically
  • Delete unused secrets: Remove deprecated secrets to reduce attack surface
  • Monitor usage: Track which secrets are actually being used

Additional Resources