KMS支援您在ACK叢集中以Sidecar形式部署KMS Agent,Agent通過RRSA機制向KMS擷取憑據值,業務應用通過本地介面向Agent擷取KMS憑據。該方式無需整合SDK,可以降低應用改造成本,確保統一的整合標準,適用於大規模應用訪問KMS的情境。本文介紹如何在ACK容器環境中部署KMS Agent擷取憑據。
前置概念
在您瞭解本最佳實務前,請先瞭解以下內容:
什麼是KMS Agent:KMS Agent是一個HTTP代理服務,負責擷取KMS服務中的憑據值並緩衝在記憶體中,應用通過HTTP請求向KMS Agent擷取憑據值。
通過RRSA配置ServiceAccount的RAM許可權實現Pod許可權隔離:如果您的應用程式部署在阿里雲ACK容器叢集上,則可以基於RRSA(RAM Roles for Service Accounts)功能,在容器叢集內實現應用隔離的RAM角色功能,各個應用可以扮演獨立的RAM角色,訪問阿里雲OpenAPI。
應用情境
在ACK容器環境中部署KMS Agent擷取憑據,使用RRSA作為訪問憑證,適用於以下業務情境。
企業需要解決last key問題。
企業將敏感憑證託管至阿里雲KMS以提升安全性,但由於訪問KMS本身仍需進行身份認證,若長期依賴固定AccessKey訪問雲端服務,會導致KMS的訪問憑證成為新的潛在安全隱患。
通過在阿里雲ACK叢集上開啟RRSA功能,基於RRSA可以為不同Pod關聯不同的RAM角色,不同Pod內的各個應用可以扮演獨立的RAM角色並使用擷取的臨時憑證訪問雲資源,從而實現應用RAM許可權最小化以及無AccessKey訪問阿里雲OpenAPI,避免AccessKey泄露。
企業需要隔離不同應用對憑據的存取權限。
例如同一應用的測試與正式環境以及不同應用間,都需要進行許可權隔離,避免存取權限擴大,憑據被非法擷取。
RRSA基於RAM策略,不同的應用運行在不同的namespace和service account,綁定不同的ramrole,通過為ramrole授予不同的許可權,實現不同應用對KMS憑據的存取權限控制。
降低應用與KMS的整合的研發成本。
如果只有少數幾個應用需要訪問KMS,應用可以整合SDK,但對於大中型企業來說,面對成百上千的應用,讓每個應用改造去整合SDK,並且都按照一致的標準去設計容災和緩衝是非常困難的。同時由於一般中國內地企業的安全、營運、開發分屬不同團隊,已經存在的開源external secret方式在中國內地企業也不太適用,其更適合於集開發營運一體的devops模式。
此時您可以通過KMS Agent來擷取憑據。在ACK容器叢集環境中,KMS Agent作為Sidecar容器運行在與業務容器相同的Pod裡。當ACK叢集啟動RRSA後,KMS Agent會自動以RRSA RamRole的身份訪問KMS,不需要再設定任何訪問憑證。Agent從遠端KMS擷取憑證並緩衝到記憶體,業務應用再從Agent擷取憑據。
解決方案架構
下圖以使用者擁有兩個應用dev和prod為例,通過ACK容器環境中部署KMS Agent擷取憑據,不同環境的應用只能訪問各自對應的KMS憑據。
使用限制
ACK叢集支援ACK託管叢集、ACK專有叢集、ACK註冊叢集、ACK Serverless叢集。
ACK叢集與KMS執行個體需要在同一地區。
操作步驟
以下流程示範了ACK、RAM、KMS如何配置,以實現業務應用通過KMS Agent擷取憑據。研發只需要在KMS建立憑據,然後在應用裡從本地檢索即可,其餘配置由營運和安全團隊提前配置。整個過程中,研發不需要在業務代碼裡整合SDK,也不需要關心應用訪問KMS的認證鑒權以及緩衝容災等。
步驟一:構建KMS Agent可執行檔
安裝Golang環境。具體操作,請參見Go安裝指南。
下載源碼和依賴。
請訪問Git官網,下載並安裝Git工具。
執行以下命令下載源碼和依賴。
git clone https://github.com/aliyun/alibabacloud-kms-agent go mod download
在專案根目錄下,執行
go build .命令編譯可執行檔。檔案預設名稱alibabacloud-kms-agent,預設儲存在專案根目錄下 。編譯環境與部署環境一致,執行
go build .命令即可。如果編譯環境和部署環境不一致,請參考以下命令進行跨平台編譯,產生64位可執行檔。編譯環境
部署環境為Mac
部署環境為Linux
部署環境為Windows
Mac
go build .CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build .CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build .Linux
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build .go build .CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build .Windows
SET CGO_ENABLED=0 SET GOOS=darwin SET GOARCH=amd64 go build .SET CGO_ENABLED=0 SET GOOS=linux SET GOARCH=amd64 go build .go build .在專案根目錄下,查看是否存在可執行檔alibabacloud-kms-agent。

步驟二:啟用ACK RRSA並授權其訪問特定的憑據
啟用ACK RRSA功能。
建立叢集時開啟
建立ACK託管叢集和ACK Edge叢集時,您可以在叢集配置的進階選項(選填)地區,選中開啟RRSA功能。

在叢集資訊頁面開啟
登入Container Service管理主控台,在左側導覽列選擇叢集列表。
在叢集列表頁面,單擊目的地組群名稱,然後在左側導覽列,選擇叢集資訊。
在基本資料頁簽的安全與審計地區,單擊RRSA OIDC右側的開啟。

在彈出的啟用RRSA對話方塊,單擊確定。
在基本資料地區,當叢集狀態由更新中變為運行中後,表明該叢集的RRSA特性已變更完成。
開啟叢集詳情頁,在基本資料頁簽的安全與審計地區,將滑鼠懸浮至RRSA OIDC右側已開啟上面,查看供應商的URL連結和ARN資訊。

建立一個可信實體為身份供應商的RAM角色。
使用阿里雲帳號登入RAM控制台。
在左側導覽列,選擇,然後在角色頁面,單擊建立角色。
在建立角色面板,選擇可信實體類型為身份供應商,並單擊切換編輯器。

在建立角色頁面的可視化編輯,配置如下角色資訊後,單擊確定。
配置項
描述
效果
選擇允許。
主體
選擇身份提供者。
身份提供者類型:選擇OIDC。
身份提供者:開啟RRSA後,ACK叢集會預設建立身份提供者,命名格式為ack-rrsa-<cluster_id>。其中,<cluster_id>為您的叢集ID。
操作
保持預設。即勾選sts:AssumeRole。
條件
在預設的oidc:iss和oidc:aud限制條件基礎上,新增一個限制條件:
條件鍵:選擇oidc:sub。
運算子:選擇StringEquals。
條件值:system:serviceaccount:<namespace>:<serviceAccountName>。
<namespace>:應用所在的命名空間。
<serviceAccountName>:服務賬戶名稱。
本文以
system:serviceaccount:rrsa-dev:dev-sa為例, 其中 rsa-dev 是下一步需要建立 k8s namespace,dev-sa 是下一步需要建立的 k8s service account。
在建立角色對話方塊中,設定角色名稱,然後單擊確定。本文角色名稱以dev-role-for-rrsa為例。
查看dev-role-for-rrsa這個RAM角色的信任策略。
該策略表示允許特定的服務賬戶通過阿里雲RRSA(RAM Roles for Service Accounts) ,在滿足 OIDC 身分識別驗證條件後,擔任某個RAM角色。
{ "Statement": [ { "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "oidc:aud": [ "sts.aliyuncs.com" ], "oidc:iss": [ "https://oidc-ack-cn-hongkong.oss-cn-hongkong.aliyuncs.com/cf01******" ], "oidc:sub": [ "system:serviceaccount:rrsa-dev:dev-sa" ] } }, "Effect": "Allow", "Principal": { "Federated": [ "acs:ram::5269************:oidc-provider/ack-rrsa-cf01******" ] } } ], "Version": "1" }
建立權限原則並授權給RAM角色。
權限原則名稱以dev-role-for-rrsa-kms-policy為例,策略內容以僅允許訪問帶有
env:dev標籤的憑據為例。
權限原則內容如下所示:
{ "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "kms:Decrypt", "kms:GetSecretValue" ], "Resource": "*", "Condition": { "StringEqualsIgnoreCase": { "kms:tag/env": [ "dev" ] } } } ] }將dev-role-for-rrsa-kms-policy權限原則,授權給dev-role-for-rrsa角色。

步驟三:在ACK中建立Namespace和Service Account
Namespace將ACK叢集劃分為邏輯隔離的虛擬空間,用於區分開發、測試、生產等環境,不同Namespace中的應用預設無法互訪資源,為RAM角色綁定提供物理邊界。Service Account為Pod提供身份標識,通過RRSA機制與RAM角色動態綁定。
通過YAML檔案建立Namespace。
Namespace名稱以rrsa-dev為例,YAML檔案名稱以namespace.yaml為例。
apiVersion: v1 kind: Namespace metadata: name: rrsa-dev執行如下命令在ACK叢集中建立名為
rrsa-dev的 Namespace。kubectl apply -f namespace.yaml查看Namespace是否建立成功:
kubectl get namespaces若輸出包含
rrsa-dev,即代表建立成功。通過YAML檔案建立Service Account。
Service Account名稱以dev-sa為例,YAML檔案名稱以serviceaccount.yaml為例。
apiVersion: v1 kind: ServiceAccount metadata: name: dev-sa namespace: rrsa-dev執行如下命令在
rrsa-dev中建立一個名為dev-sa的ServiceAccount。kubectl apply -f serviceaccount.yaml查看 ServiceAccount 是否建立成功:
kubectl get serviceaccount -n rrsa-dev若輸出包含
dev-sa,即代表建立成功。
步驟四:製作sidecar容器鏡像
將KMS Agent作為sidecar容器與應用程式容器一起部署到阿里雲的ACKContainer Service。
在config.toml中配置KMS Agent的運行參數。
說明config.toml是Agent的設定檔,您可以在步驟一擷取的alibabacloud-kms-agent源碼的configs目錄下查看到該檔案。
[Server] HttpPort = 2025 [KMS] Region = "cn-hangzhou" [Cache] CacheType = "InMemory" CacheSize = 1000 TtlSeconds = 300 [Log] LogLevel = "Debug" LogPath = "./logs/" MaxSize = 100 MaxBackups = 2建立Dockerfile檔案。
以Agent部署在
deploy/ack/agent路徑下為例,在該路徑下建立Dockerfile。檔案內容,請參考agent_Dockerfile。# Use the centos image as the base FROM centos:centos7 # Set the working directory inside the container WORKDIR /usr/local/alibabacloudkmsagent # Copy the binary and config to the container COPY alibabacloud-kms-agent . COPY config.toml . # Set the entry point to run the kms agent ENTRYPOINT ["./alibabacloud-kms-agent", "agent", "./config.toml"]將步驟1和步驟2建立的檔案拷貝到deploy/ack/agent目錄下。
deploy/ack/agent ├── alibabacloud-kms-agent ├── config.toml ├── Dockerfile.agent編譯sidecar容器鏡像並上傳到阿里雲鏡像服務。
# 編譯 docker build -t registry.cn-hangzhou.aliyuncs.com/<ns>/<repo>:kmsagent-v1.0 . # 上傳 docker push registry.cn-hangzhou.aliyuncs.com/<ns>/<repo>:kmsagent-v1.0
步驟五:製作業務容器鏡像
以業務應用部署在
deploy/ack/app路徑下為例,在該路徑下編寫應用程式容器鏡像的Dockerfile。# Use the centos image as the base FROM centos:centos7 # Set the working directory inside the container WORKDIR / # Set the entry point to run the kms agent ENTRYPOINT ["sleep", "360000"]編譯應用程式容器鏡像,並將鏡像上傳到阿里雲鏡像倉庫。
# 編譯 docker build -t registry.cn-hangzhou.aliyuncs.com/<ns>/<repo>:app-v1.0 . # 上傳 docker push registry.cn-hangzhou.aliyuncs.com/<ns>/<repo>:app-v1.0
步驟六:發布應用
方法一:自訂Deployment模板
編寫一個自訂的deployment模板,將KMS Agent與應用程式容器部署在同一個Pod中作為附加容器。如下所示,將app與sidecar容器放到同一個Pod或者Deployment裡一起部署。如何運行您的容器應用,請參見建立無狀態工作負載Deployment。
KMS_TOKEN:指定Agent啟動時產生的SSRF Token檔案的儲存路徑。
ALIBABA_CLOUD_ROLE_ARN:RAM角色的ARN。
ALIBABA_CLOUD_OIDC_PROVIDER_ARN :OIDC身份供應商的ARN,此處為ACK叢集ARN。
ALIBABA_CLOUD_OIDC_TOKEN_FILE:包含OIDC Token的檔案路徑。
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: rrsa-dev
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
serviceAccountName: dev-sa
containers:
- name: kmsagent
image: registry-vpc.cn-hangzhou.aliyuncs.com/<ns>/<repo>:kmsagent-v1.0
env:
- name: ALIBABA_CLOUD_ROLE_ARN
value: acs:ram::<uid>:role/dev-role-for-rrsa
- name: ALIBABA_CLOUD_OIDC_PROVIDER_ARN
value: acs:ram::<uid>:oidc-provider/ack-rrsa-<ackClusterId>
- name: ALIBABA_CLOUD_OIDC_TOKEN_FILE
value: /var/run/secrets/ack.alibabacloud.com/rrsa-tokens/token
- name: KMS_TOKEN
value: file:///var/run/kmstoken/token
volumeMounts:
- name: shared-volume
mountPath: /var/run/kmstoken
- name: rrsa-oidc-token
mountPath: /var/run/secrets/ack.alibabacloud.com/rrsa-tokens
readOnly: true
command: ["/bin/sh", "-c", "./alibabacloud-kms-agent token /var/run/kmstoken/token && ./alibabacloud-kms-agent agent config.toml"]
- name: my-app
image: registry-vpc.cn-hangzhou.aliyuncs.com/<ns/<repo>:app-v1.0
volumeMounts:
- name: shared-volume
mountPath: /var/run/kmstoken
volumes:
- name: shared-volume
emptyDir: {}
- name: rrsa-oidc-token
projected:
defaultMode: 420
sources:
- serviceAccountToken:
audience: sts.aliyuncs.com
expirationSeconds: 3600
path: token方法二:安裝OpenKruise,配置注入規則
OpenKruise是基於Kubernetes的一個標準向外延展群組件,可以配合原生Kubernetes使用,高效管理應用程式容器、Sidecar容器及鏡像分發。詳細介紹,請參見使用OpenKruise部署雲原生應用。
安裝OpenKruise。

登入Container Service管理主控台,在左側導覽列選擇叢集列表。
在叢集列表頁面,單擊目的地組群名稱,然後在左側導覽列,單擊組件管理。
在組件管理頁面,單擊應用管理頁簽。在ack-kruise地區,單擊安裝。
在安裝ack-cruise對話方塊確認組件資訊後,單擊確認。
設定Sidecar注入規則。
設定Sidecar容器注入規則,為帶有標籤label: app的Pod注入Sidecar容器。
環境變數定義:
KMS_TOKEN:指定Agent啟動時產生的SSRF Token檔案的儲存路徑。
ALIBABA_CLOUD_ROLE_ARN:RAM角色的ARN。
ALIBABA_CLOUD_OIDC_PROVIDER_ARN :OIDC身份供應商的ARN,此處為ACK叢集ARN。
ALIBABA_CLOUD_OIDC_TOKEN_FILE:包含OIDC Token的檔案路徑。
selector 定義:選擇需要注入sidecar的Pod,可以根據實際情況修改,本文以對帶有標籤app的Pod注入sidecar容器為例。
apiVersion: apps.kruise.io/v1alpha1 kind: SidecarSet metadata: name: kms-agent-sidecarset namespace: rrsa-dev spec: serviceAccountName: dev-sa containers: - name: kms-agent image: registry-vpc.cn-hangzhou.aliyuncs.com/<ns>/<repo>:kmsagent-v1.0 env: - name: ALIBABA_CLOUD_ROLE_ARN value: acs:ram::<uid>:role/dev-role-for-rrsa - name: ALIBABA_CLOUD_OIDC_PROVIDER_ARN value: acs:ram::<uid>:oidc-provider/ack-rrsa-<ackClusterId> - name: ALIBABA_CLOUD_OIDC_TOKEN_FILE value: /var/run/secrets/ack.alibabacloud.com/rrsa-tokens/token - name: KMS_TOKEN value: file:///var/run/kmstoken/token volumeMounts: - name: shared-volume mountPath: /var/run/kmstoken - name: rrsa-oidc-token mountPath: /var/run/secrets/ack.alibabacloud.com/rrsa-tokens readOnly: true command: ["/bin/sh", "-c", "./alibabacloud-kms-agent token /var/run/kmstoken/token && ./alibabacloud-kms-agent agent config.toml"] # 用於選擇需要注入 sidecar 的 Pod,可以根據實際情況修改 selector: matchLabels: app: app volumes: - name: shared-volume emptyDir: {} - name: rrsa-oidc-token projected: defaultMode: 420 sources: - serviceAccountToken: audience: sts.aliyuncs.com expirationSeconds: 3600 path: token自動注入KMS Agent。
編寫Deployment檔案,在ACK上發布應用,labels標註上
app: app,才能被注入Sidecar容器。apiVersion: apps/v1 kind: Deployment metadata: name: app-with-kmsagent namespace: rrsa-dev spec: replicas: 1 selector: matchLabels: app: app template: metadata: labels: app: app spec: serviceAccountName: dev-sa containers: - name: my-app image: registry-vpc.cn-hangzhou.aliyuncs.com/<ns>/<repo>:app-v1.0 volumeMounts: - name: shared-volume mountPath: /var/run/kmstoken volumes: - name: shared-volume emptyDir: {}運行您的容器應用。具體操作,請參見建立無狀態工作負載Deployment。
應用啟動,可以看到在應用所在Pod裡,已經被注入了kms agent代理容器。

步驟七:建立並擷取憑據值
在KMS建立憑據。
本文樣本中建立憑據時需要加上標籤
env:dev,應用程式容器只能擷取到有這個標籤的憑據,沒有許可權擷取其他憑據。登入Key Management Service控制台,在頂部功能表列選擇地區後,在左側導覽列單擊。
單擊通用凭据頁簽,選擇实例ID後,單擊创建凭据,完成各項配置後單擊確定。
配置項
說明
凭据名称
自訂的憑據名稱。憑據名稱在當前地區內唯一。
设置凭据值
根據您要託管的敏感性資料類型,選擇憑據鍵/值或純文字。
長度不超過30720位元組(30KB)。
初始版本号
憑據的初始版本號碼。預設為v1,也支援自訂版本號碼。
加密主密钥
選擇用於加密憑據值的密鑰。
重要密鑰和憑據需要屬於同一個KMS執行個體,且密鑰必須為對稱金鑰。關於KMS支援哪些對稱金鑰,請參見密鑰管理類型和密鑰規格。
如果是RAM使用者、RAM角色,需要具備使用加密主要金鑰執行GenerateDataKey操作的許可權。
標籤
憑據的標籤,方便您對憑據進行分類管理。每個標籤由一個索引值對(Key:Value)組成,包含標籤鍵(Key)、標籤值(Value)。
說明標籤鍵和標籤值的格式:最多支援128個字元,可以包含英文大小寫字母、數字、正斜線(/)、反斜線(\)、底線(_)、短劃線(-)、半形句號(.)、加號(+)、等號(=)、半形冒號(:)、字元at(@)、空格。
標籤鍵不能以aliyun或acs:開頭。
每個憑據最多可以設定20個標籤索引值對。
描述信息
憑據的描述資訊。
憑據的策略配置。詳細介紹,請參見憑據策略概述。
您可以先選擇預設策略,建立憑據後根據業務需要再修改策略。
擷取憑據值。
使用curl命令。
curl -v -H "X-Vault-Token:$(</var/run/kmstoken/token)" 'http://localhost:2025/secretsmanager/get?secretId=app/dev/secret-1'在業務代碼裡使用HTTP GET請求擷取憑據。
package main import ( "fmt" "io/ioutil" "net/http" ) func main() { url := fmt.Sprintf("http://localhost:2025/secretsmanager/get?secretId=%s", "app/dev/secret-1") token, err := ioutil.ReadFile("/var/run/kmstoken/token") if err != nil { fmt.Printf("error reading token file: %v\n", err) } req, err := http.NewRequest("GET", url, nil) if err != nil { fmt.Printf("error creating request: %v\n", err) } req.Header.Add("X-KMS-Token", string(token)) client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Printf("error sending request: %v \n", err) } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) fmt.Printf("status code %d - %s \n", resp.StatusCode, string(body)) }