Kubernetes Security

Kubernetes Secrets Management: Vault, Sealed Secrets, SOPS, and External Secrets Compared

Kubernetes Secrets are base64-encoded, not encrypted. That is the start of the problem. Here is a no-nonsense comparison of the tools that actually solve secrets management in Kubernetes.

Alex
Security Architect
7 min read

Kubernetes Secrets are not secret. They are base64-encoded YAML stored in etcd. Anyone with RBAC access to read secrets in a namespace can decode them instantly. Etcd encryption at rest helps, but that only protects against someone stealing the etcd disk. It does nothing for the ten other ways secrets leak.

The ecosystem has produced several competing solutions. Each makes different tradeoffs. Picking the wrong one for your environment creates operational pain that leads teams to cut corners, which defeats the purpose.

The Problem Space

Kubernetes secrets management has four distinct challenges:

  1. Storage: Where does the secret material live at rest?
  2. Distribution: How do secrets get into the pods that need them?
  3. Rotation: How do you change secrets without downtime?
  4. Audit: Who accessed which secret and when?

No single tool solves all four equally well.

HashiCorp Vault

Vault is the most capable and most complex option. It is a dedicated secrets management platform with its own storage, access control, and audit logging.

Architecture

Vault runs as a separate service (or cluster) that pods authenticate to at runtime. Secrets never exist in Kubernetes YAML or etcd. The pod requests a secret from Vault, Vault validates the pod's identity (via Kubernetes service account JWT), and returns the secret directly.

Integration Patterns

Vault Agent Sidecar Injector: Injects a sidecar that authenticates to Vault and writes secrets to a shared volume:

annotations:
  vault.hashicorp.com/agent-inject: "true"
  vault.hashicorp.com/role: "app-role"
  vault.hashicorp.com/agent-inject-secret-db-password: "secret/data/db/password"

Vault CSI Provider: Mounts secrets via the Secrets Store CSI driver:

volumes:
- name: secrets
  csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
      secretProviderClass: vault-db-password

Vault Secrets Operator (VSO): The newest approach. A Kubernetes operator that syncs Vault secrets to native Kubernetes Secrets:

apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
  name: db-password
spec:
  vaultAuthRef: default
  mount: secret
  path: db/password
  destination:
    name: db-password
    create: true
  refreshAfter: 30s

Strengths

  • Dynamic secrets (database credentials created on-demand with TTL)
  • Fine-grained access policies
  • Comprehensive audit logging
  • Secret rotation built in
  • Encryption as a service

Weaknesses

  • Operational complexity. Vault itself needs HA, backup, unsealing, and monitoring.
  • Latency. Every secret access is a network call.
  • Learning curve. Vault's policy language, auth methods, and secret engines require dedicated expertise.
  • Cost. HashiCorp Vault Enterprise pricing is significant. Open-source version lacks namespaces and replication.

Best For

Organizations with dedicated platform teams, compliance requirements for audit trails, and workloads that benefit from dynamic secrets (database-per-pod credentials, short-lived TLS certificates).

Sealed Secrets

Bitnami's Sealed Secrets takes the opposite approach to Vault. Instead of a runtime secret retrieval, it encrypts secrets at rest in Git.

Architecture

A cluster-side controller generates a public/private key pair. Developers encrypt secrets using the public key with kubeseal, producing a SealedSecret resource that can safely live in Git. The controller decrypts it and creates a regular Kubernetes Secret.

# Encrypt a secret
kubectl create secret generic db-password \
  --from-literal=password=s3cret \
  --dry-run=client -o json | \
  kubeseal --controller-namespace kube-system \
           --controller-name sealed-secrets \
           -o yaml > sealed-db-password.yaml
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: db-password
spec:
  encryptedData:
    password: AgBxj3k8...encrypted...blob==

Strengths

  • Secrets can live in Git repos (GitOps compatible)
  • Simple mental model: encrypt locally, commit to Git, controller decrypts in cluster
  • Low operational overhead—just the controller
  • No external dependencies

Weaknesses

  • No rotation mechanism. Changing a secret requires re-encrypting and redeploying.
  • No audit trail beyond Git history.
  • Cluster-bound encryption. Sealed secrets encrypted for cluster A cannot be decrypted by cluster B.
  • Secret material still ends up as plain Kubernetes Secrets in etcd.
  • Key rotation for the controller itself requires re-encrypting all sealed secrets.

Best For

Small to medium teams using GitOps who need a simple, low-overhead way to keep secrets out of plain-text YAML in Git repositories.

SOPS (Secrets OPerationS)

Mozilla's SOPS encrypts files (YAML, JSON, ENV, INI) using external key management services. It integrates with AWS KMS, GCP Cloud KMS, Azure Key Vault, age, and PGP.

Architecture

SOPS encrypts the values in a structured file while leaving keys and structure visible:

db:
  password: ENC[AES256_GCM,data:abc123==,iv:xyz==,tag:def==,type:str]
  host: ENC[AES256_GCM,data:ghi789==,iv:uvw==,tag:jkl==,type:str]
sops:
  kms:
  - arn: arn:aws:kms:us-east-1:123456789:key/abcd-1234
  encrypted_regex: ^(password|host)$

Kubernetes Integration

SOPS alone does not create Kubernetes Secrets. You need a tool like Flux or ArgoCD to decrypt and apply:

# Flux Kustomization with SOPS decryption
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
spec:
  decryption:
    provider: sops
    secretRef:
      name: sops-age-key

Strengths

  • Cloud-native KMS integration (secrets encrypted by your cloud provider's HSMs)
  • File-level encryption means you can encrypt any structured file
  • Multi-key support (multiple KMS keys, threshold schemes)
  • Works outside Kubernetes too

Weaknesses

  • Requires a GitOps tool for Kubernetes integration
  • No runtime rotation. Same re-encrypt-and-deploy cycle as Sealed Secrets.
  • Partial encryption can confuse tools that parse the files.
  • Key management shifts to the KMS provider.

Best For

Teams already using cloud KMS and GitOps (Flux or ArgoCD) who want to encrypt Kubernetes manifests alongside other configuration files.

External Secrets Operator

ESO is a Kubernetes operator that synchronizes secrets from external providers into Kubernetes Secrets. It supports AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, HashiCorp Vault, and a dozen other backends.

Architecture

You define an ExternalSecret resource that references a secret in an external store. The operator fetches it and creates a Kubernetes Secret:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-password
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets-manager
    kind: ClusterSecretStore
  target:
    name: db-password
  data:
  - secretKey: password
    remoteRef:
      key: prod/db/password

Strengths

  • Provider-agnostic. Swap backends without changing application code.
  • Automatic refresh. Secrets update on a schedule without redeployment.
  • Template support. Transform and combine secrets from multiple sources.
  • Mature project with broad community adoption.

Weaknesses

  • Secrets still materialize as Kubernetes Secrets in etcd.
  • Depends on network connectivity to external providers.
  • Refresh interval means secrets are eventually consistent, not immediately consistent.
  • Operator needs broad permissions to create secrets across namespaces.

Best For

Multi-cloud organizations that want a single Kubernetes interface for secrets from diverse backends, with automatic refresh and no application-level changes.

Decision Matrix

| Requirement | Vault | Sealed Secrets | SOPS | ESO | |---|---|---|---|---| | Dynamic secrets | Yes | No | No | Depends on backend | | GitOps compatible | Via VSO | Yes | Yes | Yes | | Audit trail | Built-in | Git only | Git only | Backend-dependent | | Rotation | Automated | Manual | Manual | Automated refresh | | Operational overhead | High | Low | Low | Medium | | Multi-cluster | Yes | Per-cluster | Yes (with KMS) | Yes | | Avoids etcd storage | Sidecar/CSI modes | No | No | No |

The Hybrid Approach

Most mature teams combine tools:

  • Vault for dynamic database credentials and PKI
  • External Secrets Operator for static configuration secrets from cloud secret managers
  • SOPS for encrypting non-secret configuration that still should not be plain text in Git

This is not overengineering. It is matching the tool to the secret type.

How Safeguard.sh Helps

Safeguard.sh audits your Kubernetes secrets management posture across clusters. It identifies secrets stored without encryption at rest, flags overly broad RBAC permissions for secret access, detects secrets exposed through environment variables (which appear in logs and process listings), and verifies that your chosen secrets management solution is properly configured. The platform tracks secret rotation compliance and alerts when secrets exceed their maximum age, ensuring your management tooling is not just installed but actively maintaining your security posture.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.