iT邦幫忙

2025 iThome 鐵人賽

DAY 9
1

賢者大叔的容器修煉手札系列 第 9 篇

Secret - 守護你的機密資料,讓密碼不再裸奔 🔐

還記得上一章我們學會了 ConfigMap,讓應用配置不再寫死在程式碼裡嗎?但是各位,有沒有發現一個問題:ConfigMap 裡的資料都是明文的!如果你把資料庫密碼、API Key、SSL 憑證這些機密資料放在 ConfigMap 裡,那就像是把保險箱密碼貼在保險箱上一樣危險!
就像你是一家銀行的 IT 主管,員工的工作證可以公開展示(ConfigMap),但是金庫密碼、客戶資料加密金鑰這些絕對不能讓所有人都看到。Secret 就是 K8s 專門為了保護這些機密資料而設計的!

今日學習目標 🎯

✅ 理解 Secret 與 ConfigMap 的差異:學會區分公開配置與機密資料

✅ 掌握 Secret 的四種類型:Generic、TLS、Docker Registry、Service Account

✅ 實作機密資料的安全管理:加密儲存、權限控制、輪替機制

✅ 培養安全意識思維:從「能用就好」到「安全第一」的架構轉變

為什麼 ConfigMap 不能存放機密資料?🤔

ConfigMap 的安全風險場景

想像你是一家電商公司的架構師,團隊把所有配置都放在 ConfigMap:

# ❌ 危險的做法:機密資料放在 ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: dangerous-config
data:
  database_host: "mysql.company.com"
  database_port: "3306"
  database_user: "admin"
  database_password: "SuperSecret123!"  # 😱 密碼明文儲存
  jwt_secret: "my-super-secret-key"     # 😱 JWT 密鑰明文儲存
  api_key: "sk-1234567890abcdef"        # 😱 API Key 明文儲存

這會造成什麼災難?

🔴 明文儲存風險:任何能訪問 etcd 的人都能看到密碼

# 任何人都能輕易查看
kubectl get configmap dangerous-config -o yaml
# 密碼直接暴露!

🔴 權限控制困難:無法針對機密資料設定特殊權限

# 能看 ConfigMap 的人就能看到所有內容
kubectl get configmap -A  # 所有機密資料一覽無遺

🔴 日誌洩漏風險:機密資料可能出現在各種日誌中

# ConfigMap 變更會被記錄在 audit log
kubectl logs kube-apiserver | grep dangerous-config

🔴 版本控制風險:機密資料被提交到 Git 倉庫

# 一旦提交到 Git,就永遠留在歷史記錄中
git log --oneline | grep "add database config"

Secret:Kubernetes 的專業保鑣 🛡️

Secret 就像銀行的保險箱系統:

🏦 銀行保險箱系統 = Secret 機制
├─ 🔐 加密儲存:內容經過 base64 編碼(基礎保護)
├─ 🎫 權限控制:只有授權人員能訪問
├─ 📋 訪問記錄:每次存取都有日誌
├─ 🔄 定期輪替:支援密鑰輪替機制
└─ 🚫 最小權限:按需分配訪問權限

Secret 的四種類型詳解 🎭

  1. Generic Secret - 通用機密資料 🔑
    最常用的類型,適合儲存密碼、API Key 等,搭配權限控制實踐,或是搭配 Secret 輪替機制。我們介紹這個類型就好。

想像你是一家五星級飯店的總管,手上管理著各種重要鑰匙:客房鑰匙、保險箱密碼、員工通道卡、VIP 貴賓室密碼。你不可能把這些鑰匙隨便放在櫃檯讓所有人都看得到,而是要分門別類、加密保管,只給有需要的員工相對應的權限。Generic Secret 就是 Kubernetes 世界裡的「總管鑰匙系統」!

Generic Secret 是 Kubernetes 中最靈活、最常用的機密資料儲存方式,就像是一個高級保險箱:

🏨 五星級飯店總管系統 = Generic Secret
├─ 🔐 分類管理:不同類型的機密資料分開儲存
├─ 🎫 權限分級:房務只能拿客房鑰匙,財務只能拿保險箱密碼
├─ 📋 使用記錄:誰在什麼時候取用了哪把鑰匙
├─ 🔄 定期更換:定期更換密碼和鑰匙
└─ 🚫 最小授權:只給必要的權限,不多給一分
  1. TLS Secret - SSL/TLS 憑證管理 🔒
    專門用於管理 HTTPS 憑證

  2. Docker Registry Secret - 私有映像庫認證 🐳
    用於訪問私有 Docker Registry

  3. Service Account Secret - 服務帳戶認證 👤
    用於 Pod 與 Kubernetes API 的認證

實戰演練

你是「賢者金融科技」的 DevOps 工程師,負責部署一套即時交易系統,需要管理:

  • 🏦 資料庫連線密碼
  • 🔑 JWT 簽名金鑰
  • 💳 第三方支付 API Key

步驟一:創建 Generic Secret 🛠️

# 創建命名空間
kubectl create namespace fintech-trading

# 從文件創建
# 先創建機密資料檔案
mkdir -p /tmp/secrets
echo 'SuperSecretDB123!' > /tmp/secrets/db-password
echo 'my-jwt-signing-key-2024' > /tmp/secrets/jwt-secret
echo 'sk-live-abcd1234567890' > /tmp/secrets/payment-api-key
echo 'mail-service-pwd' > /tmp/secrets/smtp-password

# 從檔案創建 Secret
kubectl create secret generic trading-secrets-from-file \
  --from-file=/tmp/secrets/ \
  --namespace=fintech-trading

# 清理臨時文件(重要!)
rm -rf /tmp/secrets
echo "✅ 從機密資料檔案創建的 Secret 完成,臨時文件已清理!"
echo "✅ Generic Secret 創建完成!"

步驟二:建立 Secret 📝

trading-secrets.yaml

# trading-secrets.yaml
apiVersion: v1
kind: Secret
metadata:
  name: trading-secrets-yaml
  namespace: fintech-trading
  labels:
    app: trading-system
    environment: production
    security-level: high
type: Opaque  # Generic Secret 的類型
data:
  # 注意:這裡的值必須是 base64 encode
  db-password: U3VwZXJTZWNyZXREQjEyMyE=        # SuperSecretDB123!
  jwt-secret: bXktand0LXNpZ25pbmcta2V5LTIwMjQ=   # my-jwt-signing-key-2024
  payment-api-key: c2stbGl2ZS1hYmNkMTIzNDU2Nzg5MA== # sk-live-abcd1234567890
# 應用 YAML 配置
kubectl apply -f trading-secrets.yaml
echo "✅ YAML 方式的 Secret 創建完成!"

步驟三:在 Pod 中使用 Secret 🚀

trading-app-deployment.yaml

# trading-app-deployment-corrected.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: trading-app
  namespace: fintech-trading
spec:
  replicas: 3
  selector:
    matchLabels:
      app: trading-app
  template:
    metadata:
      labels:
        app: trading-app
    spec:
      containers:
      - name: trading-service
        image: nginx:1.21-alpine
        ports:
        - containerPort: 80
        
        # 使用正確的 Secret 名稱
        env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: trading-secrets-from-file  # ✅ 使用實際存在的 Secret
              key: db-password
        - name: JWT_SECRET
          valueFrom:
            secretKeyRef:
              name: trading-secrets-from-file  # ✅ 使用實際存在的 Secret
              key: jwt-secret
        - name: PAYMENT_API_KEY
          valueFrom:
            secretKeyRef:
              name: trading-secrets-from-file  # ✅ 使用實際存在的 Secret
              key: payment-api-key
        
        # 文件掛載方式
        volumeMounts:
        - name: secret-volume
          mountPath: /etc/secrets
          readOnly: true
        - name: html-volume
          mountPath: /usr/share/nginx/html
          
      volumes:
      - name: secret-volume
        secret:
          secretName: trading-secrets-from-file  # ✅ 使用實際存在的 Secret
          defaultMode: 0400  # 文件權限設定
      - name: html-volume
        configMap:
          name: trading-app-config

---
# 創建 ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: trading-app-config
  namespace: fintech-trading
data:
  index.html: |
    <!DOCTYPE html>
    <html>
    <head>
        <title>🏦 賢者金融交易系統</title>
        <style>
            body { 
                font-family: 'Segoe UI', Arial, sans-serif; 
                margin: 0; 
                padding: 40px;
                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                color: white;
                min-height: 100vh;
            }
            .container { 
                max-width: 800px;
                margin: 0 auto;
                background: rgba(255,255,255,0.1); 
                padding: 40px; 
                border-radius: 20px; 
                backdrop-filter: blur(15px);
                box-shadow: 0 8px 32px rgba(0,0,0,0.1);
            }
            .status { 
                background: rgba(46, 204, 113, 0.2); 
                padding: 25px; 
                border-radius: 15px; 
                margin: 25px 0;
                border-left: 5px solid #2ecc71;
            }
            .secret-info {
                background: rgba(52, 152, 219, 0.2);
                padding: 20px;
                border-radius: 12px;
                margin: 15px 0;
                border-left: 5px solid #3498db;
            }
            .pod-info {
                background: rgba(155, 89, 182, 0.2);
                padding: 15px;
                border-radius: 10px;
                margin: 10px 0;
                border-left: 5px solid #9b59b6;
                font-family: 'Courier New', monospace;
            }
            h1 { font-size: 2.5em; margin-bottom: 10px; }
            h2 { color: #2ecc71; }
            h3 { color: #3498db; }
            .time { font-weight: bold; color: #f39c12; }
        </style>
    </head>
    <body>
        <div class="container">
            <h1>🏦 賢者金融交易系統</h1>
            <p style="font-size: 1.2em; opacity: 0.9;">Kubernetes Secret 管理示範應用</p>
            
            <div class="status">
                <h2>✅ 系統狀態:正常運行</h2>
                <p>🚀 服務版本:v1.0</p>
                <p>🐳 容器映像:nginx:1.21-alpine</p>
                <p>📊 當前時間:<span id="time" class="time"></span></p>
                <p>🌐 Pod 名稱:<span id="hostname">載入中...</span></p>
            </div>
            
            <div class="secret-info">
                <h3>🔐 Secret 管理狀態</h3>
                <p>✅ Secret 來源:<code>trading-secrets-from-file</code></p>
                <p>✅ 環境變數方式:已啟用</p>
                <p>✅ 文件掛載方式:已啟用</p>
                <p>📁 Secret 掛載路徑:<code>/etc/secrets</code></p>
                <p>🔒 文件權限:<code>0400</code> (只讀)</p>
            </div>
            
            <div class="secret-info">
                <h3>🔑 可用的 Secret Keys</h3>
                <ul>
                    <li><code>db-password</code> - 資料庫密碼</li>
                    <li><code>jwt-secret</code> - JWT 簽名金鑰</li>
                    <li><code>payment-api-key</code> - 支付 API 金鑰</li>
                </ul>
            </div>
            
            <div class="pod-info">
                <h3>🎯 測試命令</h3>
                <p>查看環境變數:</p>
                <code>kubectl exec -n fintech-trading [POD_NAME] -- env | grep -E "(DB_PASSWORD|JWT_SECRET)"</code>
                <br><br>
                <p>查看 Secret 文件:</p>
                <code>kubectl exec -n fintech-trading [POD_NAME] -- ls -la /etc/secrets/</code>
            </div>
        </div>
        
        <script>
            function updateTime() {
                document.getElementById('time').textContent = new Date().toLocaleString('zh-TW', {
                    year: 'numeric',
                    month: '2-digit',
                    day: '2-digit',
                    hour: '2-digit',
                    minute: '2-digit',
                    second: '2-digit'
                });
            }
            
            function updateHostname() {
                document.getElementById('hostname').textContent = 'trading-app-xxx-' + Math.random().toString(36).substr(2, 5);
            }
            
            updateTime();
            updateHostname();
            setInterval(updateTime, 1000);
        </script>
    </body>
    </html>

    ```
    
其中
```yaml
volumes:
- name: secret-volume              # Volume 的名稱(可自定義)
  secret:
    secretName: trading-secrets-from-file  # 引用的 Secret 名稱
    defaultMode: 0400              # 文件權限設定

這段配置就像是在告訴 Kubernetes:

"請幫我準備一個名為 secret-volume 的文件櫃,裡面放的是 trading-secrets-from-file 這個保險箱的內容,而且所有文件的權限都設為 0400"

# Linux 文件權限解讀
0400 = 0 + 4 + 0 + 0
│      │   │   │   │
│      │   │   │   └─ 其他用戶權限:0 (無權限)
│      │   │   └─ 群組權限:0 (無權限)  
│      │   └─ 擁有者權限:4 (只讀)
│      └─ 特殊權限:0 (無特殊權限)
└─ 八進位表示法

# 實際效果
-r--------  1 root root  16 Aug 23 14:24 db-password
# │││││││││
# ││││││││└─ 其他用戶:無權限
# │││││││└─ 其他用戶:無權限  
# ││││││└─ 其他用戶:無權限
# │││││└─ 群組:無權限
# ││││└─ 群組:無權限
# │││└─ 群組:無權限
# ││└─ 擁有者:無權限
# │└─ 擁有者:無權限
# └─ 擁有者:可讀取

trading-app-secure-deployment.yaml

spec:
  serviceAccountName: trading-app-sa  # 使用專用的 ServiceAccount
  securityContext:                    # Pod 層級安全設定
    runAsNonRoot: true                # 不以 root 身份運行
    runAsUser: 1000                   # 指定用戶 ID
    runAsGroup: 1000                  # 指定群組 ID
    fsGroup: 1000                     # 文件系統群組
  containers:
  - name: trading-service
    image: nginx:1.21-alpine
    securityContext:                  # Container 層級安全設定
      allowPrivilegeEscalation: false # 禁止權限提升
      readOnlyRootFilesystem: true    # 根文件系統只讀
      capabilities:
        drop:
        - ALL                         # 移除所有 capabilities



### 步驟四:驗證 Secret 的使用 🔍

```bash
# 部署應用
> kubectl apply -f trading-app-deployment.yaml

# 等待 Pod 啟動
> kubectl wait --for=condition=ready pod -l app=trading-app -n fintech-trading --timeout=60s

# 檢查 Pod 中的環境變數
> POD_NAME=$(kubectl get pods -n fintech-trading -l app=trading-app -o jsonpath='{.items[0].metadata.name}')

echo "=== 檢查環境變數方式 ==="
> kubectl exec -n fintech-trading $POD_NAME -- env | grep -E "(DB_PASSWORD|JWT_SECRET|PAYMENT_API_KEY)"

DB_PASSWORD=SuperSecretDB123!
JWT_SECRET=my-jwt-signing-key-2024
PAYMENT_API_KEY=sk-live-abcd1234567890


> echo -e "\n=== 檢查文件掛載方式 ==="
> kubectl exec -n fintech-trading $POD_NAME -- ls -la /etc/secrets/

total 8
drwxrwxrwt    3 root     root           160 Aug 23 14:24 .
drwxr-xr-x    1 root     root          4096 Aug 23 14:24 ..
drwxr-xr-x    2 root     root           120 Aug 23 14:24 ..2025_08_23_14_24_50.1599870018
lrwxrwxrwx    1 root     root            32 Aug 23 14:24 ..data -> ..2025_08_23_14_24_50.1599870018
lrwxrwxrwx    1 root     root            18 Aug 23 14:24 db-password -> ..data/db-password
lrwxrwxrwx    1 root     root            17 Aug 23 14:24 jwt-secret -> ..data/jwt-secret
lrwxrwxrwx    1 root     root            22 Aug 23 14:24 payment-api-key -> ..data/payment-api-key
lrwxrwxrwx    1 root     root            20 Aug 23 14:24 smtp-password -> ..data/smtp-password

> kubectl exec -n fintech-trading $POD_NAME -- cat /etc/secrets/db-password

SuperSecretDB123!

總結

安全不是一次性的工作,而是持續的過程。就像銀行的保險箱需要定期檢查和維護一樣,你的 Secret 管理系統也需要持續的關注和改進!

因此我們能考慮︰

  • 在項目中實施 Secret 分類管理
  • 建立 Secret 輪替的自動化流程
  • 設定 Secret 使用的監控告警
  • 制定團隊的 Secret 管理規範

https://ithelp.ithome.com.tw/upload/images/20250823/20104930PgyaV4wNx3.png

明天再接續學
🔜 ServiceAccount
🔜 RBAC 權限控制


上一篇
ConfigMap - 讓你的應用配置不再寫死在程式碼裡 ⚙️
下一篇
Secret 權限控制 - ServiceAccount 與 RBAC 🎭
系列文
賢者大叔的容器修煉手札19
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言