Kubernetes Secret 使用指南

Kubernetes Secret 使用指南

什麼是 Secrets?

Kubernetes Secrets 是一個 API 物件,用於存儲和管理敏感資訊,如密碼、OAuth 權杖、SSH 金鑰等。Secrets 類似於 ConfigMap,但專門設計用於保存機密資料,提供了額外的安全性保護。

Secrets 的主要功能:

  • 敏感資料存儲:安全地存儲密碼、金鑰、憑證等敏感資訊
  • 資料加密:在 etcd 中以 Base64 編碼存儲(可配置加密)
  • 存取控制:透過 RBAC 控制對敏感資料的存取
  • 多種類型支援:支援不同類型的機密資料格式

為什麼使用 Secrets?

相較於將敏感資料硬編碼在應用程式或 ConfigMap 中,Secrets 提供了更安全的管理方式:

  1. 安全性:敏感資料與應用程式程式碼分離
  2. 存取控制:精細的權限管理和審計功能
  3. 靈活性:支援多種掛載和使用方式
  4. 合規性:符合企業安全和合規要求

Secrets 類型和配置

Opaque Secrets(通用型)

最常用的 Secret 類型,用於存儲任意的使用者定義資料:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Secret
metadata:
name: dockertest-secret
namespace: dockertest-namespace
labels:
app: dockertest
type: Opaque
data:
# Base64 編碼的資料
username: cm9vdA== # root
password: MTIzNDU2 # 123456
api-key: YWJjZGVmZ2hpams= # abcdefghijk

# 或使用 stringData(自動編碼)
stringData:
database-url: "postgresql://user:pass@localhost:5432/db"
secret-token: "my-secret-token-value"

Docker Registry Secrets

用於私有映像倉庫的身份驗證:

1
2
3
4
5
6
7
8
apiVersion: v1
kind: Secret
metadata:
name: docker-registry-secret
namespace: dockertest-namespace
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: eyJhdXRocyI6eyJyZWdpc3RyeS5leGFtcGxlLmNvbSI6eyJ1c2VybmFtZSI6InVzZXIiLCJwYXNzd29yZCI6InBhc3MiLCJhdXRoIjoiZFhObGNqcHdZWE56In19fQ==

TLS Secrets

用於存儲 TLS 憑證和私鑰:

1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
namespace: dockertest-namespace
type: kubernetes.io/tls
data:
tls.crt: LS0tLS1CRUdJTi... # Base64 編碼的憑證
tls.key: LS0tLS1CRUdJTi... # Base64 編碼的私鑰

Service Account Token Secrets

用於服務帳戶權杖:

1
2
3
4
5
6
7
8
apiVersion: v1
kind: Secret
metadata:
name: service-account-secret
namespace: dockertest-namespace
annotations:
kubernetes.io/service-account.name: "my-service-account"
type: kubernetes.io/service-account-token

在 Pod 中使用 Secrets

方法一:作為環境變數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
apiVersion: apps/v1
kind: Deployment
metadata:
name: dockertest-deployment
namespace: dockertest-namespace
spec:
replicas: 1
selector:
matchLabels:
app: dockertest
template:
metadata:
labels:
app: dockertest
spec:
containers:
- name: dockertest
image: docker.io/pcion123/tinydocker:0.0.10
env:
# 引用特定的 Secret 鍵
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: dockertest-secret
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: dockertest-secret
key: password
# 引用整個 Secret 作為環境變數
envFrom:
- secretRef:
name: dockertest-secret

方法二:作為檔案掛載

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-with-secret-files
namespace: dockertest-namespace
spec:
replicas: 1
selector:
matchLabels:
app: app-with-secrets
template:
metadata:
labels:
app: app-with-secrets
spec:
containers:
- name: app
image: nginx:alpine
volumeMounts:
# 掛載整個 Secret 為目錄
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
# 掛載特定檔案到指定位置
- name: secret-token
mountPath: /etc/token/secret-token
subPath: secret-token
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: dockertest-secret
# 設定檔案權限
defaultMode: 0400
- name: secret-token
secret:
secretName: dockertest-secret
items:
- key: secret-token
path: secret-token
mode: 0444

方法三:用於 Image Pull Secrets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: apps/v1
kind: Deployment
metadata:
name: private-image-deployment
namespace: dockertest-namespace
spec:
replicas: 1
selector:
matchLabels:
app: private-app
template:
metadata:
labels:
app: private-app
spec:
# 使用 Image Pull Secret
imagePullSecrets:
- name: docker-registry-secret
containers:
- name: app
image: private-registry.example.com/my-app:latest

建立和管理 Secrets

使用 kubectl 指令建立

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 從字面值建立
kubectl create secret generic my-secret \
--from-literal=username=admin \
--from-literal=password=secretpassword \
-n dockertest-namespace

# 從檔案建立
kubectl create secret generic file-secret \
--from-file=ssh-privatekey=/path/to/ssh/key \
--from-file=ssh-publickey=/path/to/ssh/key.pub \
-n dockertest-namespace

# 建立 Docker Registry Secret
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=myuser \
--docker-password=mypassword \
--docker-email=myemail@example.com \
-n dockertest-namespace

# 建立 TLS Secret
kubectl create secret tls tls-secret \
--cert=path/to/tls.crt \
--key=path/to/tls.key \
-n dockertest-namespace

管理指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看 Secret 列表
kubectl get secrets -n dockertest-namespace

# 查看 Secret 詳細資訊(不顯示敏感資料)
kubectl describe secret dockertest-secret -n dockertest-namespace

# 查看 Secret 的 YAML 格式
kubectl get secret dockertest-secret -n dockertest-namespace -o yaml

# 解碼 Secret 資料
kubectl get secret dockertest-secret -n dockertest-namespace -o jsonpath='{.data.username}' | base64 -d

# 編輯 Secret
kubectl edit secret dockertest-secret -n dockertest-namespace

# 刪除 Secret
kubectl delete secret dockertest-secret -n dockertest-namespace

更新 Secrets

1
2
3
4
5
6
7
8
9
# 方法一:重新應用 YAML 檔案
kubectl apply -f secret.yaml

# 方法二:使用 patch 更新
kubectl patch secret dockertest-secret -n dockertest-namespace \
--type merge -p '{"stringData":{"new-key":"new-value"}}'

# 方法三:替換整個 Secret
kubectl replace -f updated-secret.yaml

實際應用範例

範例一:資料庫連線配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
apiVersion: v1
kind: Secret
metadata:
name: database-secret
namespace: dockertest-namespace
type: Opaque
stringData:
DB_HOST: "postgresql.example.com"
DB_PORT: "5432"
DB_NAME: "myapp"
DB_USERNAME: "appuser"
DB_PASSWORD: "super-secret-password"
DB_URL: "postgresql://appuser:super-secret-password@postgresql.example.com:5432/myapp"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: database-app
namespace: dockertest-namespace
spec:
replicas: 1
selector:
matchLabels:
app: database-app
template:
metadata:
labels:
app: database-app
spec:
containers:
- name: app
image: docker.io/pcion123/tinydocker:0.0.10
envFrom:
- secretRef:
name: database-secret

範例二:多服務認證配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: v1
kind: Secret
metadata:
name: service-credentials
namespace: dockertest-namespace
type: Opaque
stringData:
# API 服務認證
API_KEY: "sk-1234567890abcdef"
API_SECRET: "secret-key-abcdef123456"

# OAuth 配置
OAUTH_CLIENT_ID: "my-client-id"
OAUTH_CLIENT_SECRET: "my-client-secret"

# JWT 金鑰
JWT_SECRET: "jwt-signing-secret-key"

# 外部服務 Token
GITHUB_TOKEN: "ghp_1234567890abcdef"
SLACK_WEBHOOK: "https://hooks.slack.com/services/..."

範例三:SSL/TLS 憑證配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
apiVersion: v1
kind: Secret
metadata:
name: app-tls-secret
namespace: dockertest-namespace
type: kubernetes.io/tls
data:
tls.crt: |
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t...
tls.key: |
LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0t...
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
namespace: dockertest-namespace
spec:
tls:
- hosts:
- app.example.com
secretName: app-tls-secret
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-service
port:
number: 80

安全性最佳實務

Secret 管理

  1. 最小權限原則:只授予必要的 Secret 存取權限
  2. 定期輪換:定期更新密碼和金鑰
  3. 版本控制:避免將 Secret 內容提交到版本控制系統
  4. 環境分離:為不同環境使用獨立的 Secret

存取控制

  1. RBAC 配置:使用 Role-Based Access Control 限制存取
  2. 命名空間隔離:利用命名空間隔離不同應用的 Secret
  3. ServiceAccount 管理:為不同應用使用專用的 ServiceAccount
  4. 審計日誌:啟用 Secret 存取的審計記錄

加密和存儲

  1. 靜態加密:啟用 etcd 中 Secret 的加密
  2. 傳輸加密:確保 API 通信使用 TLS
  3. 記憶體保護:避免將 Secret 寫入磁碟交換區
  4. 備份安全:確保備份中的 Secret 也被加密

應用程式整合

  1. 環境變數 vs 檔案:根據安全要求選擇適當的掛載方式
  2. 檔案權限:設定適當的檔案權限和擁有者
  3. 程序隔離:避免在日誌或錯誤訊息中洩露 Secret
  4. 清理策略:應用程式退出時清理記憶體中的敏感資料

疑難排解

常見問題

1. Pod 無法存取 Secret

1
2
3
4
5
6
7
8
# 檢查 Secret 是否存在
kubectl get secret dockertest-secret -n dockertest-namespace

# 檢查 Pod 的服務帳戶權限
kubectl describe pod <pod-name> -n dockertest-namespace

# 查看 RBAC 配置
kubectl auth can-i get secrets --as=system:serviceaccount:dockertest-namespace:default

2. Secret 資料解碼問題

1
2
3
4
5
6
7
8
# 檢查 Secret 內容
kubectl get secret dockertest-secret -n dockertest-namespace -o yaml

# 手動解碼 Base64 資料
echo "cm9vdA==" | base64 -d

# 檢查編碼格式是否正確
kubectl get secret dockertest-secret -n dockertest-namespace -o jsonpath='{.data.username}' | base64 -d

3. Image Pull Secret 失效

1
2
3
4
5
6
7
8
9
10
11
12
13
# 測試 Docker Registry 連線
docker login registry.example.com

# 重新建立 Image Pull Secret
kubectl delete secret regcred -n dockertest-namespace
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=newuser \
--docker-password=newpassword \
-n dockertest-namespace

# 檢查 Pod 使用的 Image Pull Secret
kubectl describe pod <pod-name> -n dockertest-namespace

4. Secret 更新後 Pod 未重新載入

1
2
3
4
5
6
7
8
# 重啟 Deployment 以載入新的 Secret
kubectl rollout restart deployment dockertest-deployment -n dockertest-namespace

# 檢查重啟狀態
kubectl rollout status deployment dockertest-deployment -n dockertest-namespace

# 驗證新 Secret 是否生效
kubectl exec -it <pod-name> -n dockertest-namespace -- env | grep DB_PASSWORD

進階配置

使用外部 Secret 管理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 使用 External Secrets Operator
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
namespace: dockertest-namespace
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
version: "v2"
auth:
tokenSecretRef:
name: "vault-token"
key: "token"
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: vault-secret
namespace: dockertest-namespace
spec:
refreshInterval: 15s
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: myapp-secret
creationPolicy: Owner
data:
- secretKey: password
remoteRef:
key: secret/myapp
property: password

自動 Secret 輪換

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: batch/v1
kind: CronJob
metadata:
name: secret-rotation
namespace: dockertest-namespace
spec:
schedule: "0 2 * * 0" # 每週日凌晨 2 點
jobTemplate:
spec:
template:
spec:
containers:
- name: rotate-secrets
image: secret-rotator:latest
command:
- /bin/sh
- -c
- |
# 更新 Secret 的腳本邏輯
kubectl patch secret dockertest-secret \
--type merge \
-p '{"stringData":{"password":"'$(generate-new-password)'"}}'
restartPolicy: OnFailure

透過正確使用和管理 Kubernetes Secrets,您可以安全地處理應用程式的敏感資料,確保符合企業安全標準,同時保持應用程式的靈活性和可維護性。