ConfigMap 的安全死角
上一篇 ConfigMap 漂亮地解決了「設定寫死」問題。但隨手做個實驗:
$ kubectl create configmap db-config \
--from-literal=DB_PASSWORD=my-secret-pw
$ kubectl get configmap db-config -o yaml
apiVersion: v1
data:
DB_PASSWORD: my-secret-pw # ← 明文!
kind: ConfigMap
任何能跑 kubectl 的人都看得到密碼。如果這份 YAML 還 commit 到 Git,全世界都看得到。
ConfigMap 只能放非敏感設定。密碼 / API Key / 憑證要用 Secret。
Secret 跟 ConfigMap 哪裡不一樣?
短答:用法幾乎一樣,但有兩個關鍵差別。
| 項目 | ConfigMap | Secret |
|---|---|---|
| 儲存格式 | 明文 | Base64 編碼 |
kubectl describe | 直接顯示值 | 只顯示大小 |
| 用途 | 一般設定 | 密碼、API Key、TLS |
| RBAC 預設 | 不嚴格 | 嚴格控管 |
建立 Secret
最快的方式:
$ kubectl create secret generic db-cred \
--from-literal=username=admin \
--from-literal=password=my-secret-pw
注意 generic 對應 type: Opaque(最常用的通用型)。
$ kubectl describe secret db-cred
Type: Opaque
Data
====
password: 12 bytes # ← 不顯示值
username: 5 bytes
跟 ConfigMap 不一樣,describe 不會直接秀值。算是一道防線(防止操作失誤把密碼貼上 Slack)。
⚠️ Base64 ≠ 加密
新手最容易誤會的地方。試一下:
$ kubectl get secret db-cred -o yaml
apiVersion: v1
data:
password: bXktc2VjcmV0LXB3
username: YWRtaW4=
kind: Secret
# 隨便挑一個 decode
$ echo "bXktc2VjcmV0LXB3" | base64 -d
my-secret-pw # ← 馬上還原
Base64 只是編碼(換個格式呈現),不是加密。任何人拿到 Secret 都能 decode。
那 Secret 的安全靠什麼? 靠 RBAC(Role-Based Access Control)— 控制誰能 kubectl get secret。
RBAC:誰能讀 Secret?
預設情況,誰只要能跑 kubectl 對著叢集,就能讀所有 Secret。這是不對的。
正式環境的做法:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-cred"] # ← 只允許讀這個 Secret
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: alice-secret-reader
namespace: production
subjects:
- kind: User
name: alice
roleRef:
kind: Role
name: secret-reader
apiGroup: rbac.authorization.k8s.io
Alice 在 production namespace 只能讀 db-cred 這一個 Secret,其他 Secret 連列表都看不到。
RBAC 細節到 第七堂的 RBAC 篇展開,這裡先知道:Secret 的安全靠的不是 Base64,是 RBAC。
Secret 三種類型
$ kubectl get secret -A
NAME TYPE
db-cred Opaque
default-token-x kubernetes.io/service-account-token
myapp-tls kubernetes.io/tls
docker-pull kubernetes.io/dockerconfigjson
| 類型 | 用途 | 怎麼建 |
|---|---|---|
| Opaque | 通用(密碼、API Key) | kubectl create secret generic |
| kubernetes.io/tls | TLS 憑證(Ingress HTTPS) | kubectl create secret tls |
| kubernetes.io/dockerconfigjson | 拉私有 Image 的帳密 | kubectl create secret docker-registry |
注入到 Pod:跟 ConfigMap 一樣
語法幾乎相同,只是把 configMapRef 換成 secretRef、configMapKeyRef 換成 secretKeyRef。
envFrom:整包注入
spec:
containers:
- name: app
image: yanchen184/k8s-demo-app:latest
envFrom:
- configMapRef:
name: app-config # ← 一般設定
- secretRef:
name: db-cred # ← 密碼
env.valueFrom:單一 key
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-cred
key: password
MySQL 範例:完整配 Secret
apiVersion: v1
kind: Secret
metadata:
name: mysql-cred
type: Opaque
stringData: # ← 用 stringData 不用自己 base64
MYSQL_ROOT_PASSWORD: my-secret-pw
MYSQL_USER: appuser
MYSQL_PASSWORD: app-pw
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
envFrom:
- secretRef:
name: mysql-cred # ← 帳密整包進來
ports:
- containerPort: 3306
💡stringData是寫 YAML 時的便利,K8s 收到後會自動轉成data的 base64。手寫時用這個比較直覺,不用自己echo -n 'xxx' | base64。
驗證:
$ kubectl apply -f mysql-secret.yaml
$ kubectl exec -it deploy/mysql -- mysql -u appuser -p
Enter password: app-pw
mysql>
⚠️ 不要把 Secret YAML commit 到 Git
stringData: my-secret-pw 直接 commit 上去 = 全世界都看得到密碼。
正式環境兩個做法:
1. Sealed Secret(推薦)
Sealed Secret 用 RSA 把 Secret 加密成 SealedSecret,只有叢集裡的 controller 能解密。
$ kubeseal -f mysql-secret.yaml -o yaml > mysql-sealed.yaml
mysql-sealed.yaml 裡面是加密過的字串,安全 commit 到 Git。部署時 controller 自動解密成真正的 Secret。
2. 外部 Secret 管理(HashiCorp Vault / AWS Secrets Manager)
把密碼存在專業的密碼管理服務,K8s 用 External Secrets Operator 同步進來。適合大型組織。
對照 Docker
| Docker | Kubernetes |
|---|---|
-e DB_PASSWORD=xxx | Secret + envFrom |
docker login 個人 .docker/config.json | kubernetes.io/dockerconfigjson |
| TLS 憑證手動放容器 | kubernetes.io/tls |
| Vault / dotenv 放本機 | Sealed Secret 上 Git |
.env」。K8s 把這件事做成資源類型,更結構化。
重點整理
- 密碼用 Secret 不是 ConfigMap,至少
describe不會直接秀值 - Base64 不是加密,安全靠 RBAC
- 用法幾乎跟 ConfigMap 一樣(換
secretRef/secretKeyRef) - 寫 YAML 用
stringData比較方便 - Secret YAML 不要 直接 commit 到 Git,用 Sealed Secret
下一步
ConfigMap + Secret 都會了。但工作上常常兩個一起用 — Ingress + ConfigMap 設置 nginx config + Secret 放憑證,整個串起來。
下一篇實戰:Ingress + ConfigMap + Secret 整合實作。
📅 下一篇:Ingress + ConfigMap + Secret 整合實作
把這篇學的 Secret 跟 ConfigMap、Ingress 三者串起來——一條真實微服務的設定組合。
📚 完整系列總覽:K8s 系列教學首頁(共 40 課,按學習路徑順序排)