Back to all blogs
Cloud & DevOpsJuly 1, 20269 min read

Kubernetes Secret Management: How to Stop Leaking Credentials Before They Burn Down Your Production Environment

Most teams treat Kubernetes secrets like an afterthought — until credentials leak, infrastructure burns, and the post-mortem is brutal. Here's the engineering playbook to do it right.

L
Lucas Bennett
UI/UX Design Director
Kubernetes Secret Management: How to Stop Leaking Credentials Before They Burn Down Your Production Environment
TL;DR Quick Answer: Native Kubernetes Secrets are base64-encoded, not encrypted — storing them in Git or relying on etcd defaults is a critical vulnerability. Production-grade Kubernetes Secret Management requires envelope encryption at rest, dynamic secret injection via tools like HashiCorp Vault or AWS Secrets Manager, and GitOps-safe workflows using External Secrets Operator (ESO) or Sealed Secrets. This article walks you through the full architecture, toolchain, and hardened configurations you need to ship with confidence.

The Dirty Truth About Kubernetes Secrets Nobody Talks About

Every engineering team that has ever deployed a containerised application has brushed up against Kubernetes Secret Management. Most of them got it wrong on the first attempt — and some of them paid for it in production. The uncomfortable truth is that a native kubectl create secret command does not encrypt your data. It base64-encodes it. That is not encryption. That is obfuscation with a lowercase "o", and any attacker with read access to your etcd cluster or your Git history can reverse it in milliseconds.

At Apargo, we run multi-tenant SaaS platforms, AI inference pipelines, and customer-facing automation products like AI Greentick — all of which carry credentials, API keys, and database connection strings that would be catastrophic if exposed. We have invested deeply in hardening our secret management posture, and this article is the distilled engineering playbook from that work.

Why Default Kubernetes Secret Management Is a Security Liability

Before we discuss solutions, it is worth understanding exactly what the default behaviour gives you — and why it falls short.

Base64 Is Not Encryption

When you run kubectl create secret generic db-creds --from-literal=password=supersecret, Kubernetes stores the value as c3VwZXJzZWNyZXQ= in etcd. Any engineer with kubectl get secret permissions can decode it instantly:

# Decoding a "secret" takes exactly one command
echo "c3VwZXJzZWNyZXQ=" | base64 --decode
# Output: supersecret

This means that RBAC misconfiguration, a compromised CI/CD pipeline token, or a developer with overly broad cluster access can silently exfiltrate every credential in your namespace.

etcd Is Unencrypted by Default

Even if your RBAC is airtight, etcd — the key-value store backing your cluster state — stores secrets in plaintext unless you explicitly configure encryption at rest. According to the official Kubernetes documentation, encryption at rest is an opt-in feature, and the majority of self-managed clusters never enable it. A single etcd snapshot in the wrong hands is a full credential dump.

GitOps Workflows Amplify the Risk

Modern teams use GitOps tools like ArgoCD or Flux to declaratively manage cluster state. If you commit a raw Kubernetes Secret manifest to Git — even a private repository — you have permanently baked credentials into your version history. git log does not forget. Neither do attackers.

The Production-Grade Kubernetes Secret Management Architecture

A robust Kubernetes Secret Management architecture has four distinct layers:

  1. Encryption at Rest — Ensure etcd encrypts secret objects using AES-GCM or KMS provider envelopes.
  2. Centralised Secret Store — Use an external vault (HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager) as the source of truth.
  3. GitOps-Safe Injection — Use External Secrets Operator or Sealed Secrets to safely reference secrets in Git without exposing values.
  4. Least-Privilege Access Controls — Enforce tight RBAC, namespace isolation, and audit logging on every secret read.

Layer 1: Enabling Envelope Encryption at Rest in etcd

The first line of defence in any serious Kubernetes Secret Management setup is encrypting secrets inside etcd. Kubernetes supports this via an EncryptionConfiguration manifest applied to the API server.

# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      # KMS envelope encryption (recommended for production)
      - kms:
          name: aws-kms-provider
          endpoint: unix:///var/run/kmsplugin/socket.sock
          cachesize: 1000
          timeout: 3s
      # AES-GCM fallback (acceptable for non-KMS setups)
      - aesgcm:
          keys:
            - name: key1
              secret: 
      # Identity provider MUST be last — it stores plaintext
      - identity: {}

Pass this configuration to the kube-apiserver with the flag --encryption-provider-config=/etc/kubernetes/encryption-config.yaml. After applying, run a forced re-encryption of all existing secrets:

# Re-encrypt all existing secrets in all namespaces
kubectl get secrets --all-namespaces -o json | kubectl replace -f -

With AWS KMS as the envelope key provider, you gain hardware-backed key management, automatic key rotation, and CloudTrail audit logs for every decrypt operation — reducing your blast radius by an estimated 80% compared to a plaintext etcd setup.

Layer 2: HashiCorp Vault as Your Centralised Secret Store

Encryption at rest hardens etcd, but it does not solve the problem of secret lifecycle management — rotation, revocation, dynamic generation, and audit trails. That is where HashiCorp Vault becomes indispensable.

Vault's Kubernetes Auth Method

Vault integrates natively with Kubernetes via its kubernetes auth method. Pods authenticate to Vault using their projected service account tokens (PSAT), and Vault returns a short-lived token scoped to specific secret paths. No static credentials are ever stored in the cluster.

# Enable the Kubernetes auth method in Vault
vault auth enable kubernetes

# Configure it with your cluster's API server details
vault write auth/kubernetes/config \
  kubernetes_host="https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT" \
  kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
  token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"

# Create a policy that grants read access to a specific secret path
vault policy write app-db-policy - <

With this setup, a pod running under app-service-account in the production namespace can authenticate to Vault and retrieve credentials. The token expires in 1 hour, meaning a compromised token has a dramatically limited window of usefulness — typically reducing credential exposure risk by 90%+ compared to long-lived static secrets.

Dynamic Database Secrets: The Gold Standard

Vault's database secrets engine can generate ephemeral, per-pod database credentials with a configurable TTL. Each application instance gets a unique username and password that are automatically revoked when the lease expires. There are no shared static passwords — and no credential reuse across environments.

# Enable the database secrets engine
vault secrets enable database

# Configure a PostgreSQL connection
vault write database/config/production-postgres \
  plugin_name=postgresql-database-plugin \
  allowed_roles="app-role" \
  connection_url="postgresql://{{username}}:{{password}}@postgres.internal:5432/appdb" \
  username="vault-root" \
  password="vault-root-password"

# Create a role that generates credentials with a 1-hour TTL
vault write database/roles/app-role \
  db_name=production-postgres \
  creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
  default_ttl="1h" \
  max_ttl="24h"

Every database connection is now traceable, time-bounded, and automatically cleaned up. This is the kind of secret hygiene that separates production-grade Kubernetes Secret Management from amateur-hour setups.

Layer 3: GitOps-Safe Secret Injection with External Secrets Operator

The External Secrets Operator (ESO) is the cleanest solution for teams running GitOps workflows. ESO is a Kubernetes operator that synchronises secrets from external providers (Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault) into native Kubernetes Secret objects — without ever storing the secret values in Git.

Installing ESO with Helm

# Add the ESO Helm repository
helm repo add external-secrets https://charts.external-secrets.io

# Install ESO into your cluster
helm install external-secrets \
  external-secrets/external-secrets \
  -n external-secrets \
  --create-namespace \
  --set installCRDs=true

Defining a SecretStore and ExternalSecret

# SecretStore — defines the connection to AWS Secrets Manager
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secretsmanager
  namespace: production
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        jwt:
          serviceAccountRef:
            name: external-secrets-sa

---
# ExternalSecret — declares which secret to sync and how
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
  namespace: production
spec:
  refreshInterval: 1h          # Re-sync every hour automatically
  secretStoreRef:
    name: aws-secretsmanager
    kind: SecretStore
  target:
    name: db-credentials        # Name of the resulting Kubernetes Secret
    creationPolicy: Owner
  data:
    - secretKey: DB_PASSWORD    # Key in the Kubernetes Secret
      remoteRef:
        key: production/db-creds
        property: password      # Property inside the AWS secret JSON

What you commit to Git is the ExternalSecret manifest — a reference, not a value. The actual credential never touches your repository. ESO polls AWS Secrets Manager every hour and automatically rotates the Kubernetes Secret when the upstream value changes, giving you zero-touch secret rotation with no redeployment required.

Layer 4: Sealed Secrets for Air-Gapped and Offline GitOps

For teams that cannot use an external secret store — air-gapped environments, strict compliance regimes, or cost-constrained setups — Bitnami Sealed Secrets offers a pragmatic alternative. A SealedSecret is encrypted with a cluster-specific public key and can only be decrypted by the Sealed Secrets controller running inside that cluster. You can safely commit the encrypted manifest to Git.

# Install the Sealed Secrets controller
helm install sealed-secrets \
  sealed-secrets/sealed-secrets \
  -n kube-system

# Seal a secret using the cluster's public key
kubectl create secret generic db-creds \
  --from-literal=password=supersecret \
  --dry-run=client -o yaml | \
  kubeseal --format yaml > sealed-db-creds.yaml

# The output (safe to commit to Git):
# apiVersion: bitnami.com/v1alpha1
# kind: SealedSecret
# metadata:
#   name: db-creds
# spec:
#   encryptedData:
#     password: AgBy8hgF3k... (RSA-OAEP encrypted ciphertext)

The tradeoff: Sealed Secrets does not give you dynamic rotation or centralised audit trails. It is a Git-safety tool, not a full secrets lifecycle platform. Use it as a stepping stone, not a destination.

RBAC Hardening: Least-Privilege Access for Secrets

Even with encryption and external stores in place, misconfigured RBAC remains one of the top vectors for secret exposure. Apply these rules without exception:

  • Never grant wildcard secret access. Avoid resources: ["secrets"], verbs: ["*"] in any Role or ClusterRole.
  • Scope by namespace. Use Role (not ClusterRole) for application service accounts — they should only see secrets in their own namespace.
  • Audit list and watch verbs. A service account that can list secrets can enumerate every secret in the namespace. Grant only get on specific named resources.
  • Enable Kubernetes audit logging and route it to a SIEM. Every secret read should produce an audit event.
  • Use automountServiceAccountToken: false on pods that do not need API server access, reducing the attack surface for token-based auth exploits.
Share this article:
Cloud & DevOpsApargo Lab

Related Articles

Explore more insights from our engineering and product teams.

View all blogs
Online Document Verification: Detect Fake, Edited & AI-Generated Files Instantly
May 1, 2026
Engineering

Online Document Verification: Detect Fake, Edited & AI-Generated Files Instantly

Learn how to verify documents online and detect fake, forged, edited, or AI-generated files instantly using VerifyDocs. Fast, secure, and AI-powered.

Online Document Verification: Detect Fake, Edited & AI-Generated Files Instantly
May 1, 2026
Engineering

Online Document Verification: Detect Fake, Edited & AI-Generated Files Instantly

Learn how to verify documents online and detect fake, forged, edited, or AI-generated files instantly using VerifyDocs. Fast, secure, and AI-powered.

Top 10 Ways to Detect Fake Documents Online (Complete Guide)
May 2, 2026
Engineering

Top 10 Ways to Detect Fake Documents Online (Complete Guide)

Discover the top 10 ways to detect fake, forged, edited, or AI-generated documents online. Learn expert tips and use VerifyDocs for instant verification.