--- name: manage-kubernetes-secrets description: > Implement secure secrets management in Kubernetes using SealedSecrets for GitOps, External Secrets Operator for cloud secret managers, and rotation strategies. Handle TLS certificates, API keys, and credentials with encryption at rest and RBAC controls. Use when storing sensitive configuration for Kubernetes applications, implementing GitOps where secrets must be version-controlled, integrating with AWS Secrets Manager or Azure Key Vault, rotating credentials without downtime, or migrating from plaintext Secrets to encrypted solutions. license: MIT allowed-tools: Read Write Edit Bash Grep Glob metadata: author: Philipp Thoss version: "1.0" domain: devops complexity: intermediate language: multi tags: kubernetes, secrets, sealedsecrets, external-secrets, security --- # Manage Kubernetes Secrets Implement production-grade secrets management for Kubernetes with encryption, rotation, and integration with external secret stores. ## When to Use - Storing sensitive configuration (API keys, passwords, tokens) for Kubernetes applications - Implementing GitOps workflows where secrets must be committed to version control - Integrating Kubernetes with AWS Secrets Manager, Azure Key Vault, GCP Secret Manager - Rotating credentials and certificates without application downtime - Enforcing least-privilege access to secrets across namespaces and teams - Migrating from plaintext Secrets to encrypted or externally managed solutions ## Inputs - **Required**: Kubernetes cluster with admin access - **Required**: Secrets to manage (database credentials, API keys, TLS certificates) - **Optional**: Cloud secret manager (AWS Secrets Manager, Azure Key Vault, GCP Secret Manager) - **Optional**: Certificate authority for TLS certificate generation - **Optional**: GitOps repository for SealedSecrets - **Optional**: Key management service (KMS) for encryption at rest ## Procedure > See [Extended Examples](references/EXAMPLES.md) for complete configuration files and templates. ### Step 1: Enable Kubernetes Secrets Encryption at Rest Configure encryption at rest for Secrets using KMS or local encryption. ```bash # For AWS EKS, enable secrets encryption with KMS cat > encryption-config.yaml < pub-cert.pem # Create a regular Secret (NOT applied to cluster yet) kubectl create secret generic mysecret \ --from-literal=username=admin \ --from-literal=password='sup3rs3cr3t!' \ --dry-run=client \ -o yaml > mysecret.yaml # Seal the secret kubeseal --format=yaml --cert=pub-cert.pem < mysecret.yaml > mysealedsecret.yaml # Inspect sealed secret (safe to commit to Git) cat mysealedsecret.yaml ``` The sealed secret will look like: ```yaml apiVersion: bitnami.com/v1alpha1 kind: SealedSecret metadata: name: mysecret namespace: default spec: encryptedData: username: AgA8V7f3q2... (encrypted data) password: AgBkXp9n1h... (encrypted data) template: metadata: name: mysecret namespace: default ``` Apply and verify: ```bash # Apply sealed secret to cluster kubectl apply -f mysealedsecret.yaml # Verify regular Secret was created automatically kubectl get secret mysecret -o yaml # Decode secret to verify values kubectl get secret mysecret -o jsonpath='{.data.username}' | base64 -d # Commit sealed secret to Git (safe, encrypted) git add mysealedsecret.yaml git commit -m "Add database credentials as sealed secret" ``` **Expected:** Sealed Secrets controller running in kube-system namespace. Public certificate fetched. Kubeseal encrypts Secrets using public key. Sealed Secrets applied to cluster automatically create decrypted Secrets. Only controller can decrypt (has private key). **On failure:** For encryption errors, verify controller is running and pub-cert.pem is valid. For decryption failures, check controller logs with `kubectl logs -n kube-system -l name=sealed-secrets-controller`. For namespace mismatch errors, sealed secrets are namespace-scoped by default; use `--scope cluster-wide` for cross-namespace secrets. If private key lost, sealed secrets cannot be decrypted; backup controller key with `kubectl get secret -n kube-system sealed-secrets-key -o yaml > sealed-secrets-backup.yaml`. ### Step 3: Deploy External Secrets Operator for Cloud Secret Managers Integrate Kubernetes with AWS Secrets Manager, Azure Key Vault, or GCP Secret Manager. ```bash # Install External Secrets Operator via Helm helm repo add external-secrets https://charts.external-secrets.io helm repo update helm install external-secrets \ external-secrets/external-secrets \ --namespace external-secrets-system \ --create-namespace # Verify operator is running kubectl get pods -n external-secrets-system # Create IAM role for AWS Secrets Manager (EKS with IRSA) cat > trust-policy.json < -- env | grep DB_PASSWORD ``` **Expected:** Reloader watches Secrets/ConfigMaps and restarts Pods on changes. Secret rotation updates AWS Secrets Manager, External Secrets Operator syncs to Kubernetes, Reloader triggers rolling restart. Application picks up new credentials without manual intervention. **On failure:** For Reloader not triggering, verify annotation syntax and Reloader is running with `kubectl get pods -n default -l app=reloader-reloader`. For External Secrets sync delays, decrease refreshInterval or manually trigger with `kubectl annotate externalsecret myapp-database force-sync="$(date +%s)" --overwrite`. For application connection failures during rotation, implement graceful secret reload in application code or use connection pooling with retry logic. ### Step 6: Implement RBAC for Secrets Access Control Restrict secret access using Kubernetes RBAC with least-privilege principle. ```yaml # Create namespace for sensitive workloads apiVersion: v1 kind: Namespace metadata: name: production --- # ... (see EXAMPLES.md for complete configuration) ``` Test RBAC: ```bash # Apply RBAC resources kubectl apply -f rbac.yaml # Test as application service account kubectl auth can-i get secret myapp-db-secret --as=system:serviceaccount:production:myapp -n production # Should return "yes" # ... (see EXAMPLES.md for complete configuration) ``` **Expected:** Service accounts have read-only access to specific secrets via resourceNames. Developers cannot view secrets in production namespace. Only secret-admins group can create/update/delete secrets. RBAC denials logged in audit logs. **On failure:** For access denied errors, verify RoleBinding subjects match ServiceAccount name and namespace. For overly permissive roles, remove wildcard verbs and add resourceNames restriction. For audit log gaps, enable Kubernetes audit logging at API server level. Test with `kubectl auth can-i` before deploying changes. ## Validation - [ ] Secrets encrypted at rest in etcd (verify with etcdctl or KMS) - [ ] Sealed Secrets controller running and public certificate fetched - [ ] External Secrets Operator syncing from cloud secret managers - [ ] TLS certificates issued by cert-manager and auto-renewing - [ ] Secret rotation automated with application restarts via Reloader - [ ] RBAC policies enforce least-privilege access to secrets - [ ] No plaintext secrets in Git repositories or container images - [ ] Backup/restore procedure tested for sealed-secrets private key - [ ] Monitoring alerts configured for secret sync failures and expiration ## Common Pitfalls - **Secrets in Git history**: Committing plaintext secrets then later removing them doesn't purge Git history. Use git-filter-repo or BFG to rewrite history, rotate compromised secrets. - **Overly broad RBAC**: Granting `get secrets` on all secrets in namespace. Use resourceNames to restrict access to specific secrets only. - **No rotation strategy**: Secrets never rotated, increasing blast radius of compromise. Implement automated rotation with External Secrets Operator or CronJobs. - **Missing encryption at rest**: Secrets stored in plaintext in etcd. Enable encryption provider or KMS integration before storing sensitive data. - **Application caching secrets**: App reads secret once at startup and never reloads. Implement signal handling (SIGHUP) or file watcher for secret file changes. - **External Secrets refresh too slow**: Default 1h refresh means secrets changes take up to an hour to propagate. Lower refreshInterval for critical secrets, use webhooks for immediate updates. - **No backup of sealed-secrets key**: Controller private key lost, all sealed secrets unrecoverable. Backup with `kubectl get secret -n kube-system sealed-secrets-key -o yaml > backup.yaml` and store securely. - **Certificate renewal failures**: cert-manager unable to renew due to DNS/firewall changes. Monitor certificate expiry with Prometheus metrics and alerts. ## Related Skills - `deploy-to-kubernetes` - Using secrets in Deployments and StatefulSets - `enforce-policy-as-code` - OPA policies for secret access validation - `security-audit-codebase` - Detecting hardcoded secrets in application code - `configure-ingress-networking` - TLS certificate usage in Ingress resources - `implement-gitops-workflow` - Sealed Secrets in ArgoCD/Flux pipelines