從外部機密庫同步機密
安裝 External Secrets Operator,連接 OpenBao、Vault、1Password 或 AWS Secrets Manager,將機密同步到叢集。
資料庫密碼放在 OpenBao、1Password 或 AWS Secrets Manager 時,External Secrets Operator 會把它複製成叢集裡的 Secret,服務照一般機密讀取。之後在機密庫換密碼,它會依設定的間隔更新 Secret。
-
叢集要先裝好 cert-manager,它替 webhook 簽發憑證。
-
準備外部機密庫和唯讀認證資訊。下方範例使用
shopNamespace。 -
找出 API server 在叢集網路上的 IPv4 位址。RKE2 就是 server 節點的 IP:
kubectl get endpointslices -n default -l kubernetes.io/service-name=kubernetes \-o jsonpath='{.items[0].endpoints[*].addresses[0]}'
只有一台 server 時,kube-api-cidr 填 <ip>/32,有多台時填涵蓋這些位址的 CIDR。
打開 叢集 → 元件,在 目錄 找到 External Secrets Operator,按 安裝。它要填 kube-api-cidr,所以只能 自己執行:填好值,再執行產生的內容,見檢查並安裝叢集元件。
裝好後不會建立任何 SecretStore,在你加上之前,它不會連到任何機密庫。Catalog 安裝 chart 2.12.0,映像檔 ghcr.io/external-secrets/external-secrets:v2.12.0 以 digest 釘選。
開放到機密庫的連線
Section titled “開放到機密庫的連線”external-secrets Namespace 預設拒絕所有流量,只開放 controller 和 webhook 連 API server(TCP 443、6443)、controller 查 DNS(kube-system 的 kube-dns,53),以及 API server 呼叫 webhook(TCP 10250)。controller 要連到機密庫,得再加一條 NetworkPolicy。
OpenBao 在 openbao Namespace、使用連接埠 8200 時,套用:
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: to-openbao namespace: external-secretsspec: podSelector: matchLabels: {app.kubernetes.io/name: external-secrets} policyTypes: [Egress] egress: - to: - namespaceSelector: {matchLabels: {kubernetes.io/metadata.name: openbao}} podSelector: {matchLabels: {app.kubernetes.io/name: openbao}} ports: [{protocol: TCP, port: 8200}]連到 1Password 或 AWS 時,NetworkPolicy 比對不了主機名稱,只能開放到公開位址的 HTTPS,其他公開網站也會一起放行:
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: to-internet-https namespace: external-secretsspec: podSelector: matchLabels: {app.kubernetes.io/name: external-secrets} policyTypes: [Egress] egress: - to: - ipBlock: cidr: 0.0.0.0/0 except: [10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 100.64.0.0/10] ports: [{protocol: TCP, port: 443}]連接 OpenBao 或 Vault
Section titled “連接 OpenBao 或 Vault”以下讓 shop Namespace 的 shop-secrets ServiceAccount 用 Kubernetes 驗證登入 OpenBao,只能讀 secret 底下 shop/ 開頭的機密。先在 OpenBao 執行:
bao auth enable kubernetesbao write auth/kubernetes/config kubernetes_host=https://kubernetes.default.svcbao secrets enable -path=secret kv-v2bao policy write shop-read - <<'EOF2'path "secret/data/shop/*" { capabilities = ["read"] }EOF2bao write auth/kubernetes/role/shop bound_service_account_names=shop-secrets \ bound_service_account_namespaces=shop policies=shop-read ttl=1h接著在叢集套用 ServiceAccount 和 SecretStore:
apiVersion: v1kind: ServiceAccountmetadata: name: shop-secrets namespace: shop---apiVersion: external-secrets.io/v1kind: SecretStoremetadata: name: openbao namespace: shopspec: provider: openBao: server: http://openbao.openbao.svc:8200 path: secret version: v2 auth: kubernetes: path: kubernetes role: shop serviceAccountRef: name: shop-secrets使用 HashiCorp Vault 時,provider 改為 vault、CLI 改用 vault,並把 auth.kubernetes 下的 path 欄位改成 mountPath。機密庫在叢集外時,使用 HTTPS 和 caProvider。
連接 1Password
Section titled “連接 1Password”在 1Password 建立只能讀取 shop 保管庫的 service account,把它的權杖存進 Secret。onepasswordSDK 直接用這個權杖,不需要架 Connect server:
kubectl -n shop create secret generic onepassword-token --from-literal=token='<service account token>'apiVersion: external-secrets.io/v1kind: SecretStoremetadata: name: onepassword namespace: shopspec: provider: onepasswordSDK: vault: shop auth: serviceAccountSecretRef: name: onepassword-token key: tokenExternalSecret 的 remoteRef.key 寫成 <項目>/<欄位>,例如 db/password。已經有 1Password Connect server 時,改用 onepassword provider。
連接 AWS Secrets Manager
Section titled “連接 AWS Secrets Manager”建立一個 IAM 使用者,只對這個 Namespace 要用的機密給 secretsmanager:GetSecretValue 和 secretsmanager:DescribeSecret,再把它的存取金鑰存進 Secret:
kubectl -n shop create secret generic aws-sm --from-literal=access-key-id='<id>' --from-literal=secret-access-key='<secret>'apiVersion: external-secrets.io/v1kind: SecretStoremetadata: name: aws namespace: shopspec: provider: aws: service: SecretsManager region: ap-northeast-1 auth: secretRef: accessKeyIDSecretRef: {name: aws-sm, key: access-key-id} secretAccessKeySecretRef: {name: aws-sm, key: secret-access-key}建立 ExternalSecret
Section titled “建立 ExternalSecret”以下範例從 openbao SecretStore 讀取 shop/db 的 password,同步成 shop Namespace 裡的 db Secret:
apiVersion: external-secrets.io/v1kind: ExternalSecretmetadata: name: db namespace: shopspec: refreshInterval: 1h secretStoreRef: {kind: SecretStore, name: openbao} target: {name: db} data: - secretKey: password remoteRef: {key: shop/db, property: password}用其他機密庫時,secretStoreRef.name 換成對應的 SecretStore,remoteRef 依機密庫的格式填,例如 1Password 寫 {key: db/password}。
執行 kubectl -n shop get externalsecret db,確認 READY 是 True、STATUS 是 SecretSynced。之後 db Secret 就會出現,照一般機密給服務使用,見設定環境變數。
在 Zeabur 專案的 物件 頁點開這個 ExternalSecret,狀況 的 Ready 是 True,原因是 SecretSynced,也能確認同步成功。
機密庫裡的變更會在 refreshInterval 內寫進 Secret。服務用環境變數讀機密時,要重新啟動才會拿到新值。
- controller 能讀寫整個叢集的 Secret,也能替任何 ServiceAccount 建立權杖。
external-secretsNamespace 只開放給管理員。 - Chart 把 SecretStore 和 ExternalSecret 的寫入權限加進 Kubernetes 內建的
edit、admin角色。Zeabur 的開發者和檢視者角色沒有這些權限,要由管理員建立。 - 每個 Namespace 用自己的 SecretStore,認證資訊也只能讀這個 Namespace 要用的機密。ClusterSecretStore 開放給所有 Namespace,能建立 ExternalSecret 的人就讀得到它的認證資訊能讀的每個機密。要用時設定
spec.conditions,只列出可以用它的 Namespace。 - 給機密庫的認證資訊只開讀取權限。PushSecret 會把叢集裡的 Secret 寫回機密庫,覆蓋原本的值。
- 同步後的值是一般 Kubernetes Secret,適用相同的 etcd 加密、RBAC 和稽核紀錄,見安全性。