Skip to content

🎓 LEVEL 37 DEBRIEF: Base64 Encoding ≠ Encryption

Congratulations! You've discovered a critical security lesson about Kubernetes Secrets!


The Critical Lesson: Base64 is NOT Security

What You Saw:

data:
  username: YWRtaW4=  # Looks encrypted... but it's not!
  password: c2VjcmV0cGFzczEyMw==  # Anyone can decode this!

What Actually Happens:

# Anyone with kubectl access can decode instantly:
$ kubectl get secret db-credentials -n k8squest -o jsonpath='{.data.username}' | base64 -d
admin

$ kubectl get secret db-credentials -n k8squest -o jsonpath='{.data.password}' | base64 -d
secretpass123

Result: Zero security - base64 is just encoding, not encryption!


🔍 Understanding the Difference

Base64 Encoding (What Kubernetes Uses)

Purpose: Convert binary data to text format

Characteristics: - ✅ Handles binary data (certs, keys) - ✅ Avoids YAML special characters - ❌ Provides ZERO security - ❌ Trivially reversible (no key needed) - ❌ Anyone with kubectl access can decode

Example:

echo -n "admin" | base64          # Encode: YWRtaW4=
echo "YWRtaW4=" | base64 -d       # Decode: admin (no key needed!)

Encryption (What You Actually Need)

Purpose: Hide data from unauthorized access

Characteristics: - ✅ Requires a secret key to decrypt - ✅ Cannot be reversed without the key - ✅ Actually provides security - ✅ Protects against unauthorized access

Example:

# With AES encryption (requires key!)
echo "admin" | openssl enc -aes-256-cbc -k "secret-key" -base64
# Can only decrypt with the correct key

Method 2: Kubernetes Auto-Encoding (stringData)

apiVersion: v1
kind: Secret
metadata:
  name: db-creds
type: Opaque
stringData:  # ✅ Kubernetes encodes for you!
  username: admin  # Plain text OK here
  password: secret  # Plain text OK here

Encoding Commands

# Encode
echo -n "admin" | base64
# YWRtaW4=

# Decode
echo "YWRtaW4=" | base64 -d
# admin

# IMPORTANT: Use -n to avoid encoding newline!
echo "admin" | base64     # ❌ Includes newline
echo -n "admin" | base64  # ✅ Correct

💥 Common Mistakes

Mistake 1: Forgetting -n Flag

echo "password" | base64
# cGFzc3dvcmQK  # ❌ Has extra newline encoded!

echo -n "password" | base64
# cGFzc3dvcmQ=  # ✅ Correct

Mistake 2: Double Encoding

# First encoding
ENCODED=$(echo -n "secret" | base64)
# Second encoding (wrong!)
echo $ENCODED | base64  # ❌ Encoded twice!

Mistake 3: Plain Text in data Field

data:
  password: mysecret  # ❌ Should be base64!
# The API rejects values that are not valid base64.
# Use stringData for unencoded text input instead.

🛡️ Best Practices

  1. Use stringData for simplicity:

    stringData:  # No manual encoding needed
      password: my-secret
    

  2. Use kubectl create secret:

    kubectl create secret generic db-creds \
      --from-literal=username=admin \
      --from-literal=password=secret
    # Automatically base64 encoded
    

  3. Validate encoding:

    # Check secret
    kubectl get secret db-creds -o jsonpath='{.data.password}' | base64 -d
    

  4. Never commit decoded secrets:

    # ❌ Don't do this
    echo "password: mysecret" > secret.yaml
    
    # ✅ Do this
    echo -n "mysecret" | base64
    # Copy output to yaml
    


🔐 Real Secret Security Solutions

Since base64 provides no security, here's what you should actually use:

1. Encryption at Rest (etcd encryption)

# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
    - secrets
    providers:
    - aescbc:
        keys:
        - name: key1
          secret: <base64-encoded-32-byte-key>
    - identity: {}
✅ Secrets encrypted in etcd, not just base64

2. Sealed Secrets (Bitnami)

# Encrypt secret that can only be decrypted in-cluster
kubeseal --format=yaml < secret.yaml > sealed-secret.yaml
# Safe to commit sealed-secret.yaml to git!
✅ Public-key cryptography, safe for version control

3. External Secrets Operator

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-creds
spec:
  secretStoreRef:
    name: aws-secrets-manager
  target:
    name: db-credentials
  data:
  - secretKey: password
    remoteRef:
      key: prod/db/password
✅ Secrets stored in AWS Secrets Manager / Vault / Azure Key Vault

4. HashiCorp Vault

# Inject secrets directly into pods
vault kv put secret/db username=admin password=secret
# Pods use Vault Agent Injector
✅ Centralized secret management, auditing, rotation

5. RBAC (Role-Based Access Control)

# Restrict who can read secrets
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "list"]
  resourceNames: ["specific-secret"]  # Limit to specific secrets
✅ Principle of least privilege


🎯 Key Takeaways

  1. Base64 is encoding, NOT encryption - Provides zero security
  2. Anyone with kubectl access can decode secrets - RBAC is critical
  3. Secrets in etcd are just base64 by default - Enable encryption at rest
  4. Use external secret managers - Vault, Sealed Secrets, ESO
  5. Defense in depth - RBAC + encryption at rest + external secrets
  6. Never commit decoded secrets to git - Use Sealed Secrets or ESO

Well done! You understand why Kubernetes Secrets need additional security layers! 🎉