As of July 4, 2026, the current stable upstream Kubernetes line is Kubernetes v1.36, with v1.36.2 released on June 9, 2026. Kubernetes v1.37 is in release-cycle/pre-release status, not the stable baseline yet. Kubernetes currently maintains the latest three minor release branches: 1.36, 1.35, and 1.34. (Kubernetes)
1. ConfigMap vs Secret — First Compare
| Area | ConfigMap | Secret |
|---|---|---|
| Main purpose | Store non-sensitive configuration | Store sensitive configuration |
| Examples | App mode, log level, feature flags, URLs, config files | Passwords, API keys, tokens, TLS certs, registry credentials |
| Kubernetes object kind | ConfigMap | Secret |
| Namespace scoped? | Yes | Yes |
| Stored under | data and optional binaryData | data and optional stringData |
| Encoding behavior | data is plain UTF-8 text; binaryData is base64 | data must be base64-encoded; stringData accepts plain text and Kubernetes converts it |
| Is base64 encryption? | No | No |
| Encrypted by default in upstream Kubernetes? | No, unless API data encryption is configured | No, unless API data encryption is configured |
| Best for Git? | Usually safe if no secrets | Unsafe unless encrypted with SOPS, Sealed Secrets, Vault, External Secrets, etc. |
| Access control | RBAC | RBAC, but must be stricter |
| Can be consumed as env vars? | Yes | Yes |
| Can be mounted as files? | Yes | Yes |
| Immutable support | Yes, using immutable: true | Yes, using immutable: true |
| Maximum object size | Should stay small; practical limit around Kubernetes object size | Same; Secrets are intended for small sensitive values |
A ConfigMap stores configuration for applications and has data for UTF-8 strings and binaryData for base64-encoded binary data. A Secret is meant for confidential data; values in the data field must be base64-encoded, while stringData lets you submit plain strings that the API server converts into data. (Kubernetes)
The biggest trap: Kubernetes Secret base64 is not encryption. Anyone who can run kubectl get secret can decode it.
2. Hands-on: Prove ConfigMap Is Plain and Secret Is Encoded
Create a test namespace:
kubectl create namespace cm-secret-lab
Code language: PHP (php)
2.1 Create a ConfigMap
kubectl -n cm-secret-lab create configmap app-config \
--from-literal=APP_MODE=prod \
--from-literal=LOG_LEVEL=debug \
--from-literal=DB_HOST=mysql.dev.svc.cluster.local
Code language: JavaScript (javascript)
View it:
kubectl -n cm-secret-lab get configmap app-config -o yaml
Code language: JavaScript (javascript)
Expected output style:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: cm-secret-lab
data:
APP_MODE: prod
DB_HOST: mysql.dev.svc.cluster.local
LOG_LEVEL: debug
Code language: CSS (css)
Notice: values are plain text.
You can also print one value:
kubectl -n cm-secret-lab get configmap app-config \
-o jsonpath='{.data.DB_HOST}'
Code language: PHP (php)
Output:
mysql.dev.svc.cluster.local
Code language: CSS (css)
2.2 Create a Secret
kubectl -n cm-secret-lab create secret generic app-secret \
--from-literal=DB_PASSWORD='P@ssw0rd!' \
--from-literal=API_KEY='abc123'
Code language: JavaScript (javascript)
View it:
kubectl -n cm-secret-lab get secret app-secret -o yaml
Code language: JavaScript (javascript)
Expected output style:
apiVersion: v1
kind: Secret
metadata:
name: app-secret
namespace: cm-secret-lab
type: Opaque
data:
API_KEY: YWJjMTIz
DB_PASSWORD: UEBzc3cwcmQh
Code language: PHP (php)
Here:
abc123 -> YWJjMTIz
P@ssw0rd! -> UEBzc3cwcmQh
Code language: CSS (css)
That is base64 encoding, not encryption.
2.3 Compare Side by Side
echo "===== ConfigMap ====="
kubectl -n cm-secret-lab get configmap app-config -o yaml
echo "===== Secret ====="
kubectl -n cm-secret-lab get secret app-secret -o yaml
Code language: PHP (php)
You will see:
# ConfigMap
data:
APP_MODE: prod
LOG_LEVEL: debug
Code language: PHP (php)
But Secret shows:
# Secret
data:
API_KEY: YWJjMTIz
DB_PASSWORD: UEBzc3cwcmQh
Code language: PHP (php)
3. Secret Encoding Mechanism
Kubernetes Secrets have two common input styles.
3.1 data: You provide base64 yourself
apiVersion: v1
kind: Secret
metadata:
name: manual-secret
type: Opaque
data:
username: YWRtaW4=
password: UEBzc3cwcmQh
Decode mentally:
YWRtaW4= -> admin
UEBzc3cwcmQh -> P@ssw0rd!
Apply:
kubectl apply -f manual-secret.yaml
Code language: CSS (css)
3.2 stringData: You provide plain text, Kubernetes converts it
apiVersion: v1
kind: Secret
metadata:
name: stringdata-secret
type: Opaque
stringData:
username: admin
password: P@ssw0rd!
Code language: CSS (css)
Apply it:
kubectl -n cm-secret-lab apply -f stringdata-secret.yaml
Code language: CSS (css)
Now view it:
kubectl -n cm-secret-lab get secret stringdata-secret -o yaml
Code language: JavaScript (javascript)
You will see data, not stringData:
data:
password: UEBzc3cwcmQh
username: YWRtaW4=
stringData is only a convenience input field. The Kubernetes API stores the result in data, and data values are base64-encoded strings. (Kubernetes)
4. How to Decode Kubernetes Secrets
4.1 Decode one key
kubectl -n cm-secret-lab get secret app-secret \
-o jsonpath='{.data.DB_PASSWORD}' | base64 --decode
Code language: PHP (php)
Output:
P@ssw0rd!
Code language: CSS (css)
On some systems, use:
kubectl -n cm-secret-lab get secret app-secret \
-o jsonpath='{.data.DB_PASSWORD}' | base64 -d
Code language: PHP (php)
4.2 Decode API key
kubectl -n cm-secret-lab get secret app-secret \
-o jsonpath='{.data.API_KEY}' | base64 --decode
Code language: PHP (php)
Output:
abc123
4.3 Decode all keys using jq
kubectl -n cm-secret-lab get secret app-secret -o json \
| jq -r '.data | map_values(@base64d)'
Code language: JavaScript (javascript)
Output:
{
"API_KEY": "abc123",
"DB_PASSWORD": "P@ssw0rd!"
}
Code language: JSON / JSON with Comments (json)
4.4 Decode all secrets in a namespace — audit style
Careful: this prints sensitive values.
kubectl -n cm-secret-lab get secrets -o json \
| jq -r '.items[] |
"SECRET: \(.metadata.name)\n" +
(.data // {} | to_entries[]? | "\(.key)=\(.value | @base64d)") +
"\n"'
Code language: PHP (php)
4.5 Decode manually
echo 'UEBzc3cwcmQh' | base64 --decode
Code language: PHP (php)
Output:
P@ssw0rd!
Code language: CSS (css)
4.6 Decode in PowerShell
[System.Text.Encoding]::UTF8.GetString(
[System.Convert]::FromBase64String("UEBzc3cwcmQh")
)
Code language: CSS (css)
Output:
P@ssw0rd!
Code language: CSS (css)
5. Consuming ConfigMap and Secret in a Pod
5.1 Use both as environment variables
apiVersion: v1
kind: Pod
metadata:
name: cm-secret-env-demo
namespace: cm-secret-lab
spec:
containers:
- name: demo
image: busybox:1.36
command: ["sh", "-c", "env | sort && sleep 3600"]
env:
- name: APP_MODE
valueFrom:
configMapKeyRef:
name: app-config
key: APP_MODE
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secret
key: DB_PASSWORD
Code language: CSS (css)
Apply:
kubectl apply -f pod-env.yaml
Code language: CSS (css)
Check env:
kubectl -n cm-secret-lab logs cm-secret-env-demo | grep -E 'APP_MODE|DB_PASSWORD'
Code language: JavaScript (javascript)
Output:
APP_MODE=prod
DB_PASSWORD=P@ssw0rd!
Inside the Pod, Kubernetes gives the application the decoded value.
5.2 Mount both as files
apiVersion: v1
kind: Pod
metadata:
name: cm-secret-volume-demo
namespace: cm-secret-lab
spec:
containers:
- name: demo
image: busybox:1.36
command: ["sh", "-c", "ls -R /etc/app-config /etc/app-secret && sleep 3600"]
volumeMounts:
- name: config-volume
mountPath: /etc/app-config
readOnly: true
- name: secret-volume
mountPath: /etc/app-secret
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
- name: secret-volume
secret:
secretName: app-secret
Code language: JavaScript (javascript)
Apply:
kubectl apply -f pod-volume.yaml
Code language: CSS (css)
Read mounted values:
kubectl -n cm-secret-lab exec cm-secret-volume-demo -- cat /etc/app-config/APP_MODE
kubectl -n cm-secret-lab exec cm-secret-volume-demo -- cat /etc/app-secret/DB_PASSWORD
Output:
prod
P@ssw0rd!
Code language: CSS (css)
For sensitive values, volume mounts are often better than environment variables because env vars can leak through process dumps, debug output, accidental logs, and application diagnostics.
6. Secret Types
Common Kubernetes Secret types:
| Type | Use |
|---|---|
Opaque | Generic key-value secrets |
kubernetes.io/tls | TLS certificate and private key |
kubernetes.io/dockerconfigjson | Private container registry credentials |
kubernetes.io/basic-auth | Username/password |
kubernetes.io/ssh-auth | SSH private key |
kubernetes.io/service-account-token | Legacy service account token Secret |
Example TLS Secret:
kubectl -n cm-secret-lab create secret tls my-tls-secret \
--cert=tls.crt \
--key=tls.key
Example Docker registry Secret:
kubectl -n cm-secret-lab create secret docker-registry regcred \
--docker-server=index.docker.io \
--docker-username=myuser \
--docker-password=mypassword \
--docker-email=myemail@example.com
7. Important Security Reality
7.1 Base64 is not encryption
This:
P@ssw0rd! -> UEBzc3cwcmQh
Code language: CSS (css)
is reversible without any key:
echo 'UEBzc3cwcmQh' | base64 -d
Code language: PHP (php)
So Kubernetes Secret gives you:
API object separation + RBAC + special handling
It does not automatically give you cryptographic protection in a self-managed upstream cluster unless you configure encryption at rest.
7.2 Anyone with get secret can decode it
This RBAC permission is dangerous:
resources: ["secrets"]
verbs: ["get", "list", "watch"]
Code language: CSS (css)
A user with get secrets can do:
kubectl get secret app-secret -o yaml
Code language: JavaScript (javascript)
Then decode the values.
For production, give get secrets only to tightly controlled service accounts, operators, and administrators.
8. Kubernetes Native Encryption at Rest
Kubernetes supports encryption of API resource data before it is stored in etcd. This is different from base64 encoding. Native encryption protects stored API data such as Secrets in etcd; it does not encrypt files mounted into containers or data inside application volumes. (Kubernetes)
Kubernetes supports KMS provider-based encryption. In Kubernetes 1.36, KMS v2 is the recommended approach where feasible; KMS v1 has been deprecated since Kubernetes 1.28 and disabled by default since Kubernetes 1.29. (Kubernetes)
8.1 Self-managed cluster: basic encryption with aescbc
Generate a 32-byte key:
head -c 32 /dev/urandom | base64
Example output:
qAj0N9HjJxWkKpE4Vg1u7W8dKJmYj9w2vN0bQ9xJx2A=
Create an encryption config file on every control-plane node:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: qAj0N9HjJxWkKpE4Vg1u7W8dKJmYj9w2vN0bQ9xJx2A=
- identity: {}
Save as:
/etc/kubernetes/encryption-config.yaml
Then configure the API server with:
--encryption-provider-config=/etc/kubernetes/encryption-config.yaml
Code language: JavaScript (javascript)
For kubeadm-style static Pods, this usually means editing:
/etc/kubernetes/manifests/kube-apiserver.yaml
Add volume mount:
- --encryption-provider-config=/etc/kubernetes/encryption-config.yaml
Code language: JavaScript (javascript)
And mount file:
volumeMounts:
- name: encryption-config
mountPath: /etc/kubernetes/encryption-config.yaml
readOnly: true
Code language: JavaScript (javascript)
volumes:
- name: encryption-config
hostPath:
path: /etc/kubernetes/encryption-config.yaml
type: File
Code language: JavaScript (javascript)
After the API server restarts, new Secrets are encrypted before being written to etcd.
8.2 Re-encrypt existing Secrets
After enabling encryption, existing Secrets are not always rewritten automatically in self-managed clusters. Force rewrite:
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
Code language: JavaScript (javascript)
For ConfigMaps too, if you configured encryption for ConfigMaps:
kubectl get configmaps --all-namespaces -o json | kubectl replace -f -
Code language: JavaScript (javascript)
8.3 Decryption with native Kubernetes encryption
Normally, you do not manually decrypt etcd values. The flow is:
Client -> kube-apiserver -> decrypts from etcd -> returns normal Kubernetes API object
Code language: JavaScript (javascript)
So this still works:
kubectl get secret app-secret -o yaml
Code language: JavaScript (javascript)
The API server decrypts the stored data, then returns the Kubernetes Secret object with base64-encoded data.
For disaster recovery, you must keep the encryption config or KMS key available. If you lose the encryption key, encrypted etcd data may become unrecoverable.
9. External Encrypt / Decrypt Options
For production, you normally combine multiple layers:
Git encryption + Kubernetes RBAC + etcd encryption + cloud KMS + runtime secret delivery
9.1 SOPS — best for GitOps encrypted YAML
SOPS encrypts YAML, JSON, ENV, INI, and binary files and supports AWS KMS, GCP KMS, Azure Key Vault, age, and PGP. It keeps the file structure visible while encrypting sensitive values. (GitHub)
Install tools
brew install sops age
Generate age key:
age-keygen -o age.key
Code language: CSS (css)
Show public recipient:
grep public age.key
Code language: CSS (css)
Example:
# public key: age1abcxyz...
Code language: PHP (php)
Create .sops.yaml:
creation_rules:
- path_regex: .*secret.*\.yaml$
age: age1abcxyz...
Create Secret manifest:
apiVersion: v1
kind: Secret
metadata:
name: app-secret
namespace: cm-secret-lab
type: Opaque
stringData:
DB_PASSWORD: P@ssw0rd!
API_KEY: abc123
Code language: CSS (css)
Encrypt:
sops --encrypt app-secret.yaml > app-secret.enc.yaml
Code language: CSS (css)
Decrypt:
SOPS_AGE_KEY_FILE=age.key sops --decrypt app-secret.enc.yaml
Apply without saving decrypted file:
SOPS_AGE_KEY_FILE=age.key sops --decrypt app-secret.enc.yaml | kubectl apply -f -
Good for:
GitOps + Argo CD + Flux + CI/CD
Risk:
CI/CD runner needs decrypt permission.
9.2 Sealed Secrets — encrypt Secret for one cluster
Bitnami Sealed Secrets lets you encrypt a Kubernetes Secret into a SealedSecret; only the controller running in the target cluster can decrypt it. It is designed for safely storing encrypted Secrets in Git. (GitHub)
Install controller:
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm repo update
helm install sealed-secrets sealed-secrets/sealed-secrets \
-n kube-system
Code language: JavaScript (javascript)
Create a normal Secret manifest locally, then seal it:
kubectl -n cm-secret-lab create secret generic app-secret \
--from-literal=DB_PASSWORD='P@ssw0rd!' \
--dry-run=client \
-o yaml \
| kubeseal --format yaml > app-secret-sealed.yaml
Code language: JavaScript (javascript)
Apply sealed secret:
kubectl apply -f app-secret-sealed.yaml
Code language: CSS (css)
The cluster controller decrypts it and creates a normal Kubernetes Secret.
Good for:
GitOps where encrypted Secret is bound to a cluster.
Risk:
If controller private key is lost, old SealedSecrets cannot be decrypted.
Code language: PHP (php)
9.3 External Secrets Operator — sync from real secret managers
External Secrets Operator synchronizes secrets from external secret systems such as AWS Secrets Manager, HashiCorp Vault, Google Secret Manager, and Azure Key Vault into Kubernetes Secrets. (External Secrets)
Install:
helm repo add external-secrets https://charts.external-secrets.io
helm repo update
helm install external-secrets external-secrets/external-secrets \
-n external-secrets \
--create-namespace \
--set installCRDs=true
Code language: JavaScript (javascript)
Example ExternalSecret:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: app-secret
namespace: cm-secret-lab
spec:
refreshInterval: 1h
secretStoreRef:
name: cloud-secret-store
kind: SecretStore
target:
name: app-secret
creationPolicy: Owner
data:
- secretKey: DB_PASSWORD
remoteRef:
key: prod/app/db
property: password
Code language: PHP (php)
Good for:
Central secret manager + rotation + audit + multi-cluster usage
Risk:
By default, it still creates a native Kubernetes Secret unless you design otherwise.
Code language: JavaScript (javascript)
9.4 Secrets Store CSI Driver — mount external secrets as files
The Secrets Store CSI Driver lets Kubernetes mount secrets, keys, and certificates from enterprise secret stores into Pods as volumes. Once attached, the secret data is mounted into the container filesystem. (Secrets Store CSI Driver)
Typical pattern:
External Secret Store -> CSI Driver -> Pod volume
Example Pod volume shape:
volumes:
- name: secrets-store-inline
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: app-secret-provider
Code language: CSS (css)
Good for:
Avoiding persistent Kubernetes Secret objects.
Risk:
Application must read secrets from files, not env vars.
Code language: JavaScript (javascript)
9.5 HashiCorp Vault Agent Injector
Vault Agent Injector is a Kubernetes admission webhook that injects Vault Agent containers into Pods so workloads can consume Vault secrets. It commonly authenticates using the Pod’s Kubernetes service account. (HashiCorp Developer)
Example annotation style:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "myapp"
vault.hashicorp.com/agent-inject-secret-db-password: "secret/data/prod/db"
Code language: JavaScript (javascript)
Good for:
Dynamic credentials, short-lived database passwords, strict audit.
Risk:
Needs Vault availability and correct auth/policy design.
10. Cloud Provider Improvements
Modern managed Kubernetes platforms improve Secrets in four major ways:
1. Managed control-plane encryption
2. Cloud KMS integration
3. Cloud-native secret stores
4. Workload identity / IAM-based access instead of static credentials
Code language: JavaScript (javascript)
11. EKS — Amazon Elastic Kubernetes Service
11.1 EKS secret encryption status
Amazon EKS now uses KMS v2 default envelope encryption for all Kubernetes API data in managed control planes for clusters running Kubernetes 1.28 or higher. For EKS clusters running Kubernetes 1.27 or lower, the older procedure to enable Secrets encryption with AWS KMS applies. (AWS Documentation)
AWS recommends using AWS KMS for envelope encryption of Kubernetes Secrets. In that model, a data encryption key encrypts the data, and the data encryption key is encrypted by a key encryption key in AWS KMS. (AWS Documentation)
11.2 EKS old clusters: enable KMS encryption
For EKS Kubernetes 1.27 or lower:
aws eks associate-encryption-config \
--cluster-name my-cluster \
--encryption-config '[{"resources":["secrets"],"provider":{"keyArn":"arn:aws:kms:ap-northeast-1:111122223333:key/abcd-1234"}}]'
Code language: JavaScript (javascript)
Using eksctl:
eksctl utils enable-secrets-encryption \
--cluster my-cluster \
--key-arn arn:aws:kms:ap-northeast-1:111122223333:key/abcd-1234
11.3 EKS with AWS Secrets Manager using External Secrets Operator
Use IAM Roles for Service Accounts or EKS Pod Identity so the External Secrets Operator can read AWS Secrets Manager without static AWS keys.
Example ClusterSecretStore shape:
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: aws-secrets-manager
spec:
provider:
aws:
service: SecretsManager
region: ap-northeast-1
auth:
jwt:
serviceAccountRef:
name: external-secrets
namespace: external-secrets
Code language: PHP (php)
Example app secret:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: app-secret
namespace: cm-secret-lab
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: app-secret
data:
- secretKey: DB_PASSWORD
remoteRef:
key: prod/app/db
property: password
Code language: PHP (php)
Best EKS production pattern:
AWS Secrets Manager / SSM Parameter Store
+ IAM Roles for Service Accounts or EKS Pod Identity
+ External Secrets Operator or AWS Secrets Store CSI provider
+ EKS default KMS v2 envelope encryption
+ strict Kubernetes RBAC
Code language: JavaScript (javascript)
12. AKS — Azure Kubernetes Service
12.1 AKS secret encryption status
AKS stores Kubernetes Secrets in etcd and supports optional KMS provider encryption using Azure Key Vault. Azure describes this as an additional KMS provider layer over platform encryption, where Secrets stored in etcd can be encrypted using Azure Key Vault keys. (Azure Documentation)
Microsoft’s AKS KMS documentation shows how to enable encryption at rest using Azure Key Vault and the Kubernetes KMS plugin, including bring-your-own-key and key rotation scenarios. (Microsoft Learn)
12.2 Enable AKS KMS encryption with Azure Key Vault
Create or use an Azure Key Vault key, then create AKS with KMS:
az aks create \
--name myAKSCluster \
--resource-group myResourceGroup \
--assign-identity "$IDENTITY_RESOURCE_ID" \
--enable-azure-keyvault-kms \
--azure-keyvault-kms-key-vault-network-access "Public" \
--azure-keyvault-kms-key-id "$KEY_ID" \
--generate-ssh-keys
Code language: JavaScript (javascript)
Enable on an existing cluster:
az aks update \
--name myAKSCluster \
--resource-group myResourceGroup \
--enable-azure-keyvault-kms \
--azure-keyvault-kms-key-vault-network-access "Public" \
--azure-keyvault-kms-key-id "$KEY_ID"
Code language: JavaScript (javascript)
For private Key Vault:
az aks update \
--name myAKSCluster \
--resource-group myResourceGroup \
--enable-azure-keyvault-kms \
--azure-keyvault-kms-key-id "$KEY_ID" \
--azure-keyvault-kms-key-vault-network-access "Private" \
--azure-keyvault-kms-key-vault-resource-id "$KEY_VAULT_RESOURCE_ID"
Code language: JavaScript (javascript)
Critical warning: do not delete or expire the Key Vault key used for AKS KMS encryption; Microsoft warns that doing so can make the API server unable to work with encrypted data. (Microsoft Learn)
12.3 AKS with Azure Key Vault Provider for Secrets Store CSI Driver
AKS has a managed add-on for Azure Key Vault Provider for Secrets Store CSI Driver. Microsoft documents enabling it with --enable-addons azure-keyvault-secrets-provider; the add-on creates a user-assigned managed identity for Key Vault access. (Microsoft Learn)
Enable during cluster creation:
az aks create \
--resource-group myResourceGroup \
--name myAKSCluster \
--enable-addons azure-keyvault-secrets-provider \
--enable-oidc-issuer \
--enable-workload-identity \
--generate-ssh-keys
Enable on existing cluster:
az aks enable-addons \
--addons azure-keyvault-secrets-provider \
--name myAKSCluster \
--resource-group myResourceGroup
Best AKS production pattern:
Azure Key Vault
+ AKS Workload Identity
+ Secrets Store CSI Driver or External Secrets Operator
+ AKS KMS etcd encryption
+ Azure RBAC / Kubernetes RBAC
+ Key Vault soft delete + purge protection
Code language: JavaScript (javascript)
13. GKE / “GKS” — Google Kubernetes Engine
I’m assuming by GKS you mean GKE, Google Kubernetes Engine.
13.1 GKE secret encryption status
Google Cloud encrypts data at rest by default, including GKE data, but GKE also supports application-layer Secrets encryption using a customer-managed key in Cloud KMS. Google’s current GKE documentation describes encrypting Kubernetes Secrets at the application layer with a Cloud KMS key that you manage. (Google Cloud)
13.2 Create GKE cluster with Cloud KMS application-layer Secrets encryption
Create KMS key ring and key:
gcloud kms keyrings create gke-secrets-ring \
--location=global
gcloud kms keys create gke-secrets-key \
--location=global \
--keyring=gke-secrets-ring \
--purpose=encryption
Code language: PHP (php)
Grant GKE service account access to the key:
gcloud kms keys add-iam-policy-binding gke-secrets-key \
--location=global \
--keyring=gke-secrets-ring \
--member="serviceAccount:SERVICE_ACCOUNT_EMAIL" \
--role="roles/cloudkms.cryptoKeyEncrypterDecrypter"
Code language: PHP (php)
Create cluster:
gcloud container clusters create my-gke-cluster \
--region=asia-northeast1 \
--database-encryption-key=projects/PROJECT_ID/locations/global/keyRings/gke-secrets-ring/cryptoKeys/gke-secrets-key
Code language: PHP (php)
Update existing cluster:
gcloud container clusters update my-gke-cluster \
--region=asia-northeast1 \
--database-encryption-key=projects/PROJECT_ID/locations/global/keyRings/gke-secrets-ring/cryptoKeys/gke-secrets-key
Code language: PHP (php)
13.3 GKE with Google Secret Manager
Recommended pattern:
Google Secret Manager
+ Workload Identity Federation for GKE
+ External Secrets Operator or Secrets Store CSI Driver provider
+ Cloud KMS application-layer Secrets encryption
+ Kubernetes RBAC
Example External Secret concept:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: app-secret
namespace: cm-secret-lab
spec:
refreshInterval: 1h
secretStoreRef:
name: google-secret-manager
kind: ClusterSecretStore
target:
name: app-secret
data:
- secretKey: DB_PASSWORD
remoteRef:
key: prod-app-db-password
Code language: PHP (php)
Best GKE production pattern:
Google Secret Manager
+ GKE Workload Identity
+ Cloud KMS application-layer secret encryption
+ External Secrets Operator or CSI mount
+ Binary Authorization / Policy Controller for guardrails
14. OpenShift
14.1 OpenShift secret encryption status
In OpenShift Container Platform 4.19 documentation, Red Hat states that etcd data is not encrypted by default. When etcd encryption is enabled, OpenShift encrypts resources including Secrets, ConfigMaps, Routes, OAuth access tokens, and OAuth authorize tokens. The docs also note that encryption protects values, not keys; resource types, namespaces, and object names remain unencrypted. (Red Hat Documentation)
14.2 Enable etcd encryption in OpenShift
Check current setting:
oc get apiserver cluster -o yaml
Code language: JavaScript (javascript)
Enable encryption:
oc patch apiserver cluster \
--type=merge \
-p '{"spec":{"encryption":{"type":"aescbc"}}}'
Code language: JavaScript (javascript)
Watch progress:
oc get clusteroperators kube-apiserver openshift-apiserver authentication
Code language: JavaScript (javascript)
Check API server status:
oc get apiserver cluster -o jsonpath='{.status.conditions}'
Code language: PHP (php)
Depending on OpenShift version, supported encryption types can include AES-CBC and AES-GCM. Always confirm the exact supported values for your OpenShift release before changing production clusters.
14.3 OpenShift External Secrets Operator
Red Hat provides an External Secrets Operator for OpenShift. It fetches secrets from external providers including AWS Secrets Manager, HashiCorp Vault, Google Secret Manager, Azure Key Vault, IBM Cloud Secrets Manager, and AWS Systems Manager Parameter Store. (Red Hat Documentation)
Production pattern:
External provider
-> External Secrets Operator
-> Kubernetes Secret
-> Application
14.4 OpenShift Secrets Store CSI Driver
OpenShift / OKD documentation describes the Secrets Store CSI Driver Operator as a way to mount secrets, keys, and certificates from external secret stores into Pods as volumes. Listed providers include AWS Secrets Manager, AWS Systems Manager Parameter Store, Azure Key Vault, and HashiCorp Vault. (OKD Documentation)
Production pattern:
External secret manager
-> CSI driver
-> Pod volume
This is useful when you do not want the secret value persisted as a normal Kubernetes Secret.
Best OpenShift production pattern:
OpenShift etcd encryption
+ External Secrets Operator or Secrets Store CSI Driver
+ Vault / AWS Secrets Manager / Azure Key Vault / Google Secret Manager
+ strict OpenShift RBAC
+ SCC / admission policy
+ audit logging
15. Best Practice Architecture by Security Level
Level 1 — Basic lab
ConfigMap for config
Secret for password
RBAC limited
Good for learning, not enough for production.
Level 2 — Standard production
ConfigMap for non-sensitive config
Secret for sensitive values
etcd encryption enabled
RBAC restricted
Secrets mounted as files where possible
Audit logging enabled
Code language: JavaScript (javascript)
Level 3 — GitOps production
SOPS or Sealed Secrets
Git stores only encrypted secret manifests
CI/CD decrypts or controller decrypts
Cluster has etcd encryption
RBAC restricted
Level 4 — Enterprise cloud-native
AWS Secrets Manager / Azure Key Vault / Google Secret Manager / Vault
External Secrets Operator or Secrets Store CSI Driver
Workload identity, not static cloud keys
Cloud KMS envelope encryption
Secret rotation
Audit logging
Policy enforcement
Code language: JavaScript (javascript)
Level 5 — High-security / regulated
External secret manager is source of truth
No long-lived static secrets in Git
No secrets as env vars
Runtime mount or dynamic injection
Short-lived credentials
Automatic rotation
KMS/HSM-backed keys
Namespace isolation
Dedicated service accounts
Admission policy blocks unsafe Secrets
Full audit trail
Code language: PHP (php)
16. Recommended Decision Matrix
| Requirement | Best choice |
|---|---|
| Non-sensitive app config | ConfigMap |
| Password/API key for simple app | Secret |
| Need encrypted GitOps | SOPS or Sealed Secrets |
| Need central rotation | External Secrets Operator |
| Need avoid native Kubernetes Secret persistence | Secrets Store CSI Driver |
| Need dynamic DB credentials | HashiCorp Vault |
| EKS production | AWS Secrets Manager + IRSA/EKS Pod Identity + KMS |
| AKS production | Azure Key Vault + Workload Identity + KMS |
| GKE production | Google Secret Manager + Workload Identity + Cloud KMS |
| OpenShift production | etcd encryption + ESO/CSI + Vault/cloud secret manager |
17. Real Interview / Exam Style Summary
ConfigMap
ConfigMap stores non-confidential configuration data in key-value form.
It is visible in plain text through kubectl.
Use it for app settings, URLs, flags, and config files.
Do not store passwords or API keys in ConfigMaps.
Code language: PHP (php)
Secret
Secret stores confidential data such as passwords, tokens, and keys.
Secret data is base64-encoded, not encrypted by default.
Anyone with permission to read the Secret can decode it.
For production, enable encryption at rest and use external secret managers where possible.
Code language: JavaScript (javascript)
Encoding vs Encryption
Encoding = reversible format transformation, no key needed.
Encryption = cryptographic protection, key required.
Kubernetes Secret data uses base64 encoding.
Kubernetes etcd encryption or cloud KMS provides real encryption.
18. Cleanup Lab
kubectl delete namespace cm-secret-lab
Code language: JavaScript (javascript)
19. Final Recommended Production Pattern
For modern Kubernetes in 2026, I would use this:
ConfigMap:
Only non-sensitive app configuration.
Secret:
Only when application requires Kubernetes-native Secret.
Git:
Never store plain Secret YAML.
Use SOPS or Sealed Secrets.
Cluster:
Enable etcd / API data encryption.
Use KMS v2 where available.
Cloud:
Use AWS Secrets Manager, Azure Key Vault, Google Secret Manager, or Vault.
Runtime:
Prefer Workload Identity / IRSA / managed identity.
Avoid static cloud access keys.
Prefer CSI mount or External Secrets Operator.
Rotate secrets regularly.
RBAC:
Treat "get secrets" as near-admin-level permission.
Code language: JavaScript (javascript)
The one-line golden rule:
ConfigMap is for configuration. Secret is for sensitive data. But Secret is only safe when RBAC, encryption at rest, external secret management, and rotation are designed properly.
I’m a DevOps/SRE/DevSecOps/Cloud Expert passionate about sharing knowledge and experiences. I have worked at Cotocus. I share tech blog at DevOps School, travel stories at Holiday Landmark, stock market tips at Stocks Mantra, health and fitness guidance at My Medic Plus, product reviews at TrueReviewNow , and SEO strategies at Wizbrand.
Do you want to learn Quantum Computing?
Please find my social handles as below;
Rajesh Kumar Personal Website
Rajesh Kumar at YOUTUBE
Rajesh Kumar at INSTAGRAM
Rajesh Kumar at X
Rajesh Kumar at FACEBOOK
Rajesh Kumar at LINKEDIN
Rajesh Kumar at WIZBRAND
Find Trusted Cardiac Hospitals
Compare heart hospitals by city and services — all in one place.
Explore Hospitals