設定在環境中的重要資訊 (⁎⁍̴̛ᴗ⁍̴̛⁎)
在 Serverless 架構中,常會面臨到應用程式變數應該如何配置的問題。尤其是像 Token、API Key、憑證等敏感資訊,更不能隨意存放,也不應該直接寫入程式碼中,必須採取安全的管理方式。
一般會將資訊儲存在幾種地方:
環境變數 (Environment Variables)
通常在會在部署時指定,並在服務啟動時透過程式碼存取
外部儲存
可能是資料庫或其他儲存服務,會在服務啟動或者需要時執行讀取
配置管理服務
雲平台供應商都會提供相對應的儲存方案
這些工具提供了安全的儲存方式,並且確保應用程式可以動態且安全地讀取這些資訊。
Configuration Files
某些情況下,變數可以透過部署工具的配置檔案進行管理。多半適用於雲平台,透過 yaml 設定檔將變數管理從程式碼中抽離出來,與工具做關聯,使服務在部署時能透過工具讀取變數,再注入到執行環境中。
這些方法提供了靈活性和安全性,確保 Serverless 應用程式能夠安全、動態地管理應用程式運行需要的敏感資訊。
集中管理 Pod 使用到的環境變數。
設定方式:
kubectl create configmap \
<config-name> --from-literal=<key1>=<value1> \
--from-literal=<key2>=<value2>
# 範例
kubectl create configmap \
app-config --from-literal=APP_ENV=prod \
--from-literal=APP_COLOR=blue
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
APP_ENV: prod
APP_COLOR: blue
這邊要特別留意,使用的是 data
而不是 spec
。
有三種設定方式
apiVersion: v1
kind: Pod
metadata:
name: webapp-color-pod
spec:
containers:
- name: webapp-color
image: webapp-color
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: app-config
apiVersion: v1
kind: Pod
metadata:
name: webapp-color-pod
spec:
containers:
- name: webapp-color
image: webapp-color
ports:
- containerPort: 8080
env:
- name: APP_COLOR
valueFrom:
configMapKeyRef:
name: app-config
key: APP_COLOR
apiVersion: v1
kind: Pod
metadata:
name: webapp-color-pod
spec:
containers:
- name: webapp-color
image: webapp-color
ports:
- containerPort: 8080
volumes:
- name: app-config-volume
configMap:
name: app-config
透過 volumes 配置的 ConfigMap ,每一個 Key 都會被轉換為一個單獨的文件,文件名是 Key,文件的內容是 Value。讀取方式需要透過文件路徑,與另外兩種設定方式有所不同,需特別留意。
volumes 配置適用於 複雜的設定 或者 需要頻繁訪問和更新 的情境
保存敏感資訊,例如密碼、API Key和憑證。
設定方式:
# key-value
kubectl create secret generic <secret-name> --from-literal=key=value
# from file
kubectl create secret generic <secret-name> --from-file=<file-path>
apiVersion: v1
kind: Secret
metadata:
name: app-secret
data:
DB_HOST: mysql
DB_USER: root
DB_PASSWORD: password
(ConfigMap 說明過這邊就不再贅述)
apiVersion: v1
kind: Pod
metadata:
name: webapp-color-pod
spec:
containers:
- name: webapp-color
image: webapp-color
ports:
- containerPort: 8080
# ===== envFrom =====
envFrom:
- secretRef:
name: db-secret
# ===== env =====
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secret
key: DB_PASSWORD
# ===== volume =====
volumes:
- name: app-secret-volume
secret:
secretName: app-secret
通常介紹是這樣寫,但其實 Secret 一點都不 Secret
相關討論可搭配這篇文章:Shhhh... Kubernetes Secrets Are Not Really Secret!
事情是這樣的:Secret 在預設情況下根本就沒有加密。
它只是有經過 base64
編碼,雖然不是明目張膽的存明碼,但也說不上是加密資訊。
並且,透過 Secret 儲存的資訊是存在 ETCD 中,只要有 kube-apiserver 對應存取權
或 ETCD存取權
的人或服務,都有權限可以讀取、修改 Secret 值。
就算透過 Namespace 做環境隔離,也無法阻擋同一個 Namespace 中的 Pod 進行 Secret 存取。
那怎麼辦!!!
視執行平台而定。
CSI
機制。Secrets Store CSI Driver 和 Secrets Store CSI Provider 是配套使用的插件,用來實現 Kubernetes 從外部讀取 Secrets 的機制。
這個方式是在 Pod 啟動(或重新啟動)時執行,透過 CSI Driver 使用 gRPC 與 CSI Provider 進行通訊,從自訂外部資源中將擷取指定的內容寫入磁碟區(volume),最後再將該磁碟區掛載到 Pod 中。
一旦 Pod 資源消失,Secret 也會跟著消失。
不但保證了 Pod 使用時不需要直接讀取外部儲存區,也確保了 Secret 在 Cluster 中的安全隱密性。
能不能將資訊直接存入其他雲端服務中,再 Import 進 Secret 使用?
技術上可行,但不建議這樣做。
這只是避免了直接在 Secret 設定的 yaml 中直接出現機敏資訊,卻沒有解決 Secret 中的值本身安全性不足的問題。
相關安全基準可參考:CIS BenchmarksRef: 在GKE安全地使用帳密(GCP KMS)
其實關於 Secret 的使用有不少相關討論,在不同的環境中也有不同的解決方案。同時兼顧安全性與實用性才能使系統運行更加穩定。
畢竟,沒人想看到為了做資訊的加解密而導致系統延遲或是效能不彰吧?