Volume - Secret
什麼是 Secret?
在 Kubernetes 中,Secret 是一種資源物件,用於安全地存儲敏感數據,例如密碼、OAuth token、SSH key 等。與 ConfigMap 類似,Secret 能夠以鍵值對的形式存儲數據,但它的數據會被加密或編碼以提高安全性。
Secret 的主要特點包括:
- 敏感數據存儲:避免將敏感數據直接存放在 Pod 規範中。
- 靈活使用:可作為環境變量、Volume 或文件的形式掛載到 Pod 中。
- 安全性:通過 Base64 編碼存儲,並可以與 Kubernetes 中的 RBAC 一起使用來保護數據。
Secret 的類型
Kubernetes 提供了多種類型的 Secret,可以根據需求選擇合適的類型:
Secret 類型 | 描述 |
---|---|
Opaque | 默認類型,用於存儲任意鍵值對數據。 |
kubernetes.io/service-account-token | 用於存儲服務帳號的 token,自動創建並分配給 Pod。 |
kubernetes.io/dockercfg | 用於存儲 Docker 鏡像拉取的 ~/.dockercfg 文件。 |
kubernetes.io/dockerconfigjson | 用於存儲 Docker 鏡像拉取的 JSON 格式認證信息。 |
kubernetes.io/tls | 用於存儲 TLS 私鑰和證書(PEM 格式)。 |
bootstrap.kubernetes.io/token | 用於集群引導過程中的 token。 |
Secret 的生命週期
- 創建 Secret: Secret 可以通過 YAML 文件、Kubernetes CLI 或程序化的方式創建。
- 掛載到 Pod: Secret 可作為環境變量或 Volume 掛載到 Pod 中,供容器使用。
- 更新 Secret: Secret 可以被更新,更新後相關 Pod 將獲取新的數據。
- 刪除 Secret: 如果 Secret 被刪除,相關的 Pod 將無法訪問這些敏感數據。
Secret 的使用方式
1. 作為環境變量
Secret 可以作為 Pod 的環境變量,容器內應用程序可直接通過環境變量訪問。
2. 掛載為 Volume
Secret 可作為文件掛載到 Pod 的文件系統,應用程序可以從指定路徑讀取。
3. 使用於容器映像憑據
Secret 可以用來存儲 Docker 鏡像的認證信息,以便 Kubernetes 拉取私有鏡像。
注意事項
- Base64 編碼: Secret 資料是以 Base64 編碼的形式存儲,但這不是加密,因此不要將其視為完全安全的保護機制。
- 安全性:
- 使用 Kubernetes 的 RBAC 設定適當的存取權限。
- 確保 Kubernetes 集群的 etcd 數據存儲是加密的。
- 更新影響: Secret 更新後,會立即影響掛載該 Secret 的 Pod。
Secret 的創立
使用指令建立
- 直接使用指令建立 Secret
kubectl.exe create secret generic test-secret --from-literal='username=my-account' --from-literal='password=my-password'
---
secret/test-secret created
- 查看 Secret,我們會發現 data 只顯示大小,不顯示內容。
kubectl.exe describe secrets test-secret
---
Name: test-secret
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password: 11 bytes
username: 10 bytes
使用 yaml 建立
在建立 Secret 的值時,我們必需先使用 base64 編碼,而 kubernetes 在我們正確掛載後會自動幫我們解碼回原本的值。
- 首先假設我們有 my-account 和 my-password 這兩個字串要當成我們的 username 和 password,首先我們需要對他做 base64 加密。
echo -n "my-account" | base64
echo -n "my-password" | base64
---
bXktYWNjb3VudA==
bXktcGFzc3dvcmQ=
[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("my-account"))
[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("my-password"))
---
bXktYWNjb3VudA==
bXktcGFzc3dvcmQ=
- 建立一個 test-secret.yaml,裡面的 kind 設定為 Secret,並且在 data 儲存 key-value,並且 value 必須要是 base64 後的數值。
apiVersion: v1
kind: Secret
metadata:
name: test-secret
data:
username: bXktYWNjb3VudA==
password: bXktcGFzc3dvcmQ=
- 透過 kubectl 來執行他。
kubectl.exe apply -f test-secret.yaml
---
secret/test-secret created
- 並且查看一下結果,我們可以發現他只會顯示加密過後的大小,不會顯示內容。
kubectl.exe describe secrets test-secret
---
Name: test-secret
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password: 11 bytes
username: 10 bytes
實作 Secret
使用 Secret 作為環境變量
- 在上面建立 test-secret 之後,我們撰寫一個 pod 來使用這些 secret,我們可以使用 secretKeyRef,指定 Secret 名稱和 Key,就會自動映射到對應的環境變數。
apiVersion: v1
kind: Pod
metadata:
name: secret-test-pod
spec:
containers:
- name: test-container
image: nginx
env:
- name: USERNAME
valueFrom:
secretKeyRef: # 從 Secret 中引用值來設定環境變數
name: test-secret # Secret 的名稱為 test-secret
key: username # 取 Secret 中的 key 為 username 的值
- name: PASSWORD
valueFrom:
secretKeyRef:
name: test-secret # Secret 的名稱為 test-secret
key: password # 取 Secret 中的 key 為 password 的值
- 透過指令來運行這個 Pod
kubectl.exe apply -f .\secret-test-pod-key-value.yaml
---
pod/secret-test-pod created
- 我們可以連進去 Pod 查看是否成功注入
kubectl.exe exec -it pods/secret-test-pod -- bash
---
root@secret-test-pod:/#
- 透過輸出環境變數來查看是否成功 base64 解碼。
root@secret-test-pod:/# echo $USERNAME
root@secret-test-pod:/# echo $PASSWORD
---
my-account
my-password
使用 Secret 作為 Volume 掛載
- 在上面建立 test-secret 之後,我們撰寫一個 pod 來使用這些 secret,我們可以使用 Volume 掛載成檔案。
apiVersion: v1
kind: Pod
metadata:
name: secret-test-pod
spec:
containers:
- name: test-container
image: nginx
volumeMounts:
- name: secret-volume
mountPath: /etc/secret-volume
volumes:
- name: secret-volume
secret:
secretName: test-secret
- 透過指令來執行他。
kubectl.exe apply -f secret-test-pod-volume.yaml
---
pod/secret-test-pod created
- 進到 pod 裡面查看,會發現 Secret 被映射成 檔案
kubectl.exe exec -it secret-test-pod -- bash
---
root@secret-test-pod:/#
root@secret-test-pod:/# ls /etc/secret-volume/
---
password username
- 查看裡面的內容會發現 kubernetes 已經幫我們成功解密。
root@secret-test-pod:/# cat /etc/secret-volume/username
root@secret-test-pod:/# cat /etc/secret-volume/password
---
my-account
my-password
延伸來聊聊 Secret 的安全性挑戰與限制
雖然 Kubernetes 的 Secret 提供了一種管理敏感數據的方式,但它本身並非完全安全,仍存在一些潛在的安全挑戰和限制。以下是一些 Secret 不那麼安全的原因,以及可能造成的影響:
1. Base64 只是編碼,非加密
Secret 的數據存儲於 etcd 中時是以 Base64 編碼 的形式保存,而不是加密。這意味著:
- Base64 僅僅是將數據轉換為一種易於存儲和傳輸的格式,並不具備安全保護能力。
- 如果有人取得了 Secret 的數據內容,解碼 Base64 是非常簡單且快速的。
建議
在創建 Secret 時,使用外部工具對敏感數據進行加密,再將加密後的內容存入 Secret 中。
2. etcd 中的存儲未必加密
Kubernetes 的 Secret 資料存儲於集群的 etcd 中。如果 etcd 沒有啟用加密存儲,這些數據將以明文的形式保存於磁碟上:
- 攻擊者如果能存取 etcd,就能直接讀取到所有的 Secret 資料。
- 預設情況下,etcd 的加密功能並未自動啟用,需要額外配置。
建議
啟用 etcd 數據存儲加密,並妥善保護 etcd 的存取權限(例如:限制誰可以存取 etcd 資料)。
3. 過於依賴 RBAC
Kubernetes 使用 RBAC(Role-Based Access Control) 來限制誰可以 存取 Secret。但在以下情況下,RBAC 的保護可能失效:
- RBAC 配置錯誤,允許不必要的角色訪問敏感數據。
- 攻擊者獲得高權限的帳號,繞過了 Secret 的保護。
- 過多的使用者擁有 Secret 的存取權,增加了資料洩露的風險。
建議
- 嚴格配置 RBAC,限制 Secret 的存取權限,只允許最小必要的角色存取。
- 定期審核 RBAC 規則,檢查是否有多餘的存取權。
4. Pod 中的掛載存在風險
Secret 通常以環境變量或 Volume 的形式掛載到 Pod 中,但這些方法可能導致數據洩露:
- 如果 Pod 內的應用程序被攻擊或接管,攻擊者可以直接讀取 Secret 的內容。
- 使用環境變量存取 Secret 時,敏感數據可能出現在應用程序的日誌中,因為很多應用程序會記錄環境變量。
建議
- 優先使用 Volume 掛載,而非環境變量。
- 確保應用程序避免將 Secret 的內容寫入日誌中。
5. Secret 無法應對節點級攻擊
如果攻擊者取得了 Kubernetes 節點的控制權,他們可能通過以下方式獲取 Secret:
- 攻擊 kubelet,進一步取得 etcd 的存取權限。
- 直接讀取掛載到容器內的 Secret 文件。
建議
- 加強節點層級的安全防護,例如使用強化的節點映像、防火牆規則,以及啟用安全模組(如 SELinux 或 AppArmor)。
- 定期更新和修補 Kubernetes 節點的安全漏洞。
6. Secret 更新的分發延遲
當 Secret 被更新時,Pod 並不會立刻獲取更新的內容。這可能導致:
- Pod 在一段時間內仍然使用舊的 Secret 數據,無法及時應對敏感數據的變更。
- 攻擊者可能利用這段時間窗口來攻擊系統。
建議
- 配置 Pod 的自動重啟機制,確保更新 Secret 後,相關 Pod 能及時加載新的 Secret。
- 使用側車容器(Sidecar)監控 Secret 的變更並動態加載。
結論
Kubernetes 的 Secret 提供了一個集中化管理敏感數據的方式,但並非完全安全。在實際使用中,應該結合其他安全機制來保護 Secret,例如:
- 啟用 etcd 加密 。
- 嚴格管理 RBAC 訪問權限。
- 在 Pod 中謹慎掛載和使用 Secret。
- 配合外部工具(如 AWS Key Management、Google Cloud KMS)進行更高級別的數據加密和管理。