同一份 container image 通常要在開發、測試與正式環境運作,但資料庫位址、功能開關、憑證與密碼不應跟著 image 一起寫死,否則每次要異動都得進入 container 內調整,而重建新的 Pod 設定又會被還原。
Docker Compose 常用 environment variable 或 bind mount 解決這件事,而 Kubernetes 則提供 ConfigMap 與 Secret,讓設定資料在 image 之外被管理。
兩者都可以被 Pod 使用,但目的不同。今天主要會說明這兩者的差異:ConfigMap 放一般設定,Secret 敏感資料。
| ConfigMap | Secret | |
|---|---|---|
| 適合內容 | 非敏感設定:功能開關、一般 URL、log level、應用參數 | 密碼、Token、私鑰、TLS certificate 與 private key 的組合、registry credential |
| Pod 使用方式 | environment variable、command/args、Volume 檔案 | environment variable、Volume 檔案、特定 Kubernetes 功能 |
| data 欄位 | 一般字串設定 | 值必須是 Base64 編碼;建立時也可用 stringData 提供一般字串,API Server 會合併到 data。 |
| 安全意義 | 不提供保密性 | 專門表達敏感資料;可在 RBAC 中與 ConfigMap 分別授權,通常應採更嚴格的最小權限;但不是自動加密保險箱。 |
例如 ASP.NET Core 的一般 log level、非敏感功能開關或服務 URL,可以放在 ConfigMap;資料庫密碼、JWT signing key、客戶憑證私鑰,則應放在 Secret。

ConfigMap 與 Secret 最常見有兩種掛載方式:
ASPNETCORE_ENVIRONMENT、feature flag 或 endpoint。Kubernetes 只負責把資料以環境變數或檔案提供給 container,它不會理解 ASP.NET Core 的 appsettings.json,也不會自動知道程式期望哪個檔名或哪個資料夾。
以 .NET 來舉例,可以把 ConfigMap/Secret 想成部署時提供給設定系統的來源。程式仍必須正確讀取 environment variable 或檔案。
Secret 的 data 欄位需要 Base64 編碼,但 Base64 只是把資料換成文字表示方式,任何拿到資料的人都能解碼。它不是加密,也不能當成防止機密外洩的措施。
text
原始密碼 → Base64 編碼 → Secret.data
│
└─ 可被解碼,不等於安全保存
Kubernetes Secret 是否會以加密形式寫入 etcd,取決於叢集是否設定 encryption at rest。除此之外,還需要搭配 RBAC、Namespace 隔離、最小權限、審計與機密交付流程。
能建立 Pod 的權限本身也值得小心:若某個人能任意建立 Pod 並掛載同 Namespace 的 Secret,他可能不必直接讀取 Secret API,就能讓 container 印出其中內容。
因此 Secret 並不是「直接拿來用就是安安全的」,而是「它讓敏感資料被明確標示、以 Kubernetes 的敏感資料機制傳遞,並能套用相對應的治理」。Kubernetes 不會因為使用 Secret 就自動提供更嚴格的權限或 encryption at rest,這些仍需由叢集設定與 RBAC 明確落實。
ConfigMap 與 Secret 都是 Kubernetes API 資源,也都會經過 RBAC 授權。
Kubernetes 不會因為物件是 Secret,就自動拒絕所有讀取;若某個 Role 同時授與 configmaps 與 secrets 的 get、list 或 watch,被綁定的使用者便能讀取兩者。
真正的差異在於我們可以把它們寫成不同的 RBAC rule,並透過不同的 RoleBinding 綁給不同的人或 ServiceAccount。
例如一般開發人員可以檢視 Namespace 內的 ConfigMap,但沒有任何讀取 Secret 的權限;只有專責機密維運群組能讀取指定的 Secret。
# Namespace: catalog
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: configmap-reader
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: developers-read-configmaps
subjects:
- kind: Group
name: developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: configmap-reader
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: production-db-secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["production-db-credentials"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: security-operators-read-db-secret
subjects:
- kind: Group
name: security-operators
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: production-db-secret-reader
apiGroup: rbac.authorization.k8s.io
藉此看清楚角色邊界:
developers 只被綁定到 ConfigMap Role,不能用 API 讀取 Secret;security-operators 才額外被綁定到指定 Secret 的 get 權限。Role 是「可做什麼」,RoleBinding 是「把誰連到這個角色」;兩者都屬於 Namespace 範圍時,權限也只在該 Namespace 生效。
Secret 特別要小心 list 與 watch。官方文件明確指出,這兩個權限也可能讓主體取得 Secret 內容,所所以設定上也要特別小心。
對於只需要一個已知 Secret 的人或元件,優先考慮以 get 搭配 resourceNames 限定名稱;若不必透過 Kubernetes API 主動讀取 Secret,就不要授與 Pod 的 ServiceAccount secrets 讀取權限。
最後還有一個容易忽略的例外:能在同一個 Namespace 建立 Pod、Deployment 等工作負載的人,可能把該 Namespace 的 Secret 掛進自己建立的 Pod,進而間接讀到內容。
因此 Secret 保護不只看 secrets/get,也要限制工作負載建立權限、採 Namespace 隔離,並配合審計。這也是為什麼「能看 ConfigMap、不能看 Secret」只是最小權限設計的起點。
把所有設定都放進 Secret,表面上看起來比較安全,其實會讓界線變模糊。
反過來,也不要把密碼、私鑰或憑證放入 ConfigMap,只因為「比較容易看」。ConfigMap 官方文件明確指出它不提供保密性或加密。
最常見的是 Opaque,用來放自訂 key-value 敏感資料。Kubernetes 也有一些具明確格式的類型:
kubernetes.io/tls:TLS 憑證與私鑰。kubernetes.io/dockerconfigjson:container registry credential。kubernetes.io/basic-auth:帳號密碼。選擇明確 type 的好處是讓讀者看得懂用途,Kubernetes API 也能對部分必要 key 進行基本檢查;但它仍不會代替我們驗證憑證有效性或確保密碼安全。
另一個案例中,ConfigMap 資源與 Volume 都已成功建立,但掛載路徑的單複數和程式預期不同。Kubernetes 看來沒有錯,container 裡也確實有檔案,只是程式讀的是另一個資料夾,因此設定完全沒有生效。
這類問題很像 .NET 設定檔放在正確硬碟、卻沒有被 ConfigurationBuilder 加入來源:檔案存在不代表程式一定會讀取。部署驗證除了看 kubectl get,也要確認 container 內的實際路徑、檔名與應用設定讀取邏輯。
ConfigMap 用於非敏感設定,Secret 用於敏感資料;兩者都能以環境變數或檔案交給 Pod。Secret 的 data 只是 Base64 編碼,不能當成加密或完整的安全方案;還要透過 RBAC 將 ConfigMap 與 Secret 分別授權,並避免過度授與 Secret 的讀取與工作負載建立權限。