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功能。
在集群信息页面开启
登录容器服务管理控制台,在左侧导航栏选择集群列表。
在集群列表页面,单击目标集群名称,然后在左侧导航栏,选择集群信息。
在基本信息页签的安全与审计区域,单击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容器与应用程序容器一起部署到阿里云的ACK容器服务。
在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。
登录容器服务管理控制台,在左侧导航栏选择集群列表。
在集群列表页面,单击目标集群名称,然后在左侧导航栏,单击组件管理。
在组件管理页面,单击应用管理页签。在ack-kruise区域,单击安装。
在提示对话框确认组件信息后,单击确定。
设置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
,应用容器只能获取到有这个标签的凭据,没有权限获取其他凭据。登录密钥管理服务控制台,在顶部菜单栏选择地域后,在左侧导航栏单击 。
单击通用凭据页签,选择实例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)) }