ACK场景下应用程序访问云资源最佳实践
在ACK场景下应用程序如何做到无AK访问后端资源。本方案会提供最佳方案用于指导客户在ACK场景下更好的使用无AK。
方案概述
对于云上资源的访问是部署在K8S集群节点上应用的常见需求,比如应用程序中会调用OSS的OpenAPI读取存储在指定bucket中的数据。在传统方式下,应用程序要想访问其授权范围内的云上资源,要么使用账号AK,要么通过访问ECS meta server扮演指定节点上绑定的RAM角色,获取STS token作为凭证访问对应资源服务的OpenAPI。
对于使用固定AK直接访问阿里云API,往往需要将AK写入Secret,但存在泄漏风险。云上更推荐使用临时AK方案,如果使用ECS meta server的实例角色方案且能够保证节点上运行的应用均来自同一个可信的租户,那么不同应用共享同一个角色STS token在安全上是可以接受的。而在多租场景下,节点上绑定的角色授权需要满足来自不同租户应用的云资源使用需求,这样在一个租户应用中只需要访问meta server的角色token接口就可以轻松获得节点上所有租户应用的云资源访问权限,直接引发跨租户的越权访问。
本文档则介绍了一种针对阿里云ACK应用场景的无AK方案,解决集群上多租户针对数据面的应用云资源访问权限隔离的解决方案。
方案优势
与K8S有很好的兼容性
不需要在集群Node节点上绑定任何的RAM角色,也不需要在集群Node节点上运行额外的Agent。
多租户最小化授权
在同一套ACK集群上可以同时运行多个业务程序,支持按不同Namespace、不同应用授权。权限管理能够最小化粒度,避免过度授权。
提供应用使用规范
提供一套标准化的可落地文档与代码,用于指导客户快速切换到无AK实现。
客户场景
容器场景的AK防泄露
场景描述
如何避免永久访问密钥的使用,最好的办法就是不使用固定AK。这个场景特定当客户使用容器场景下,如何规避应用程序泄露AK。
适用客户
- 使用容器的企业客户
- 单个K8S集群跑了多个租户业务
- 对AK治理要求高的客户
客户案例
客户背景
某知名快递企业X,作为国内快递龙头企业,每日处理的订单及包裹数据据达到千万级别。客户对云上身份安全非常重视。
客户痛点(或客户诉求)
客户全部的业务跑在一套集群里面,通过不同的Namespace隔离。不同业务应用在不同的Namespace里。有些应用需要操作云资源(如写日志、提交图片到OSS等),起初各个应用都是直接把AK/SK硬编码在应用程序中,后面出现过研发离职之后把代码带出去,导致应用对应的AK泄露。客户希望在ACK场景下有一套最佳AK管理实践。
实施方案
- 每个应用分配一个单独的RAM Role,最小化授权。
- 修改Pod部署YAML文件,添加OIDC Token的Volume。
- 应用程序完成改造,由硬编码AK改变造成临时AK。
客户收益
- 应用程序中不再出现硬编码AK,防止研发将AK泄露。
- 当出现某个临时Token泄露之后,可以非常快速处理,不需要改代码重新部署。
方案架构
整个方案涉及到的产品主要是两块,一块是K8S集群,一块是RAM访问控制,下面从静态配置这部分来介绍整个方案。
静态配置
因为涉及到K8S容器服务和RAM访问控制,所以配置也在两边同时进行:
- 在RAM访问控制侧,需要配置一个新身份提供商(IdP),所以这里需要在RAM访问控制侧添加基于OIDC的身份提供商定义,比如OIDC的issuer url,aud等,同时配置一个该IdP的某一身份能扮演的角色,在角色扮演的授信方限制OIDC的相关身份信息,比如audience,subject等,同时定义该角色能访问的资源和相关action;
- 在K8S集群侧,配置相关OIDC的属性,比如自身的OIDC标识,颁发ID_Token使用的公私钥等,同时给目标Pod定义响应的Service Account,把该Pod需要扮演的角色名称等配置好,这样在Pod启动后在对应卷目录下就可以获取到动态替换的ID_Token,结合在RAM侧定义的角色ARN,通过RAM/STS服务获取到阿里云临时安全令牌(Security Token Service,STS),从而最终访问阿里云其他云产品服务;
从最终实现体来看,就是K8S里的一个Service Account定义,里面包含RoleName和OIDC ID_Token,RoleName映射到RAM里面的角色名称,然后该角色扮演的授信体,通过ID_Token里的aud,sub来匹配。目前OIDC这部分配置,ACK产品已经能够实现一键配置,无需手工配置。
架构总结
在K8S集群上配置OIDC属性,使该集群可以对特殊的Service Account产生标准的ID_Token并把这些token注入到使用特殊Service Account的Pod中去,同时RAM访问控制可以配置集群上的OIDC provider作为标准的IdP,并信任这类IdP颁发出来的ID_Token做为身份代表,进而把这些身份作为授信实体绑定到不同的角色上,最终Pod里的应用可以使用这些ID_Token,去扮演预定义的角色获取到STS去访问其他云服务。
方案延展
除了在密钥本身的优化和提升,对于现有的已经存在的永久密钥怎么样更好的使用,提高安全风险防御能力,也是需要考虑和优化的部分。这部分涵盖的内容,也会有好几块,比如密钥轮转,密钥托管等方案。建议可以查阅Landing Zone在身份权限管理的其他方案。
产品费用及名词
产品费用
产品名称 |
产品说明 |
产品费用 |
RAM |
访问控制(RAM)是阿里云提供的一项管理用户身份与资源访问权限的服务。使用RAM,您可以创建、管理RAM用户(例如员工、系统或应用程序),并可以控制这些RAM用户对资源的操作权限。 |
免费,详情参见产品定价。 |
STS |
阿里云临时安全令牌(Security Token Service,STS)是阿里云提供的一种临时访问权限管理服务。 |
免费,详情参见产品定价。 |
容器服务ACK |
阿里云容器服务Kubernetes版(Alibaba Cloud Container Service for Kubernetes,简称容器服务ACK)是全球首批通过Kubernetes一致性认证的服务平台,提供高性能的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理,让您轻松高效地在云端运行Kubernetes容器化应用。 |
收费,详情参见产品定价。 |
名词解释
名称 |
说明 |
OIDC |
OIDC是一套业界内比较认可的用于用户身份认证的协议,在我们生活中比较常见的场景是使用支付宝授权登录某个三方应用。OIDC使用授权协议(通常为OAuth 2)来进行身份认证,因为可以简单的理解为OIDC是通过OAuth 2来实现的。 |
OAuth2 |
OAuth2这种授权协议是什么,这里借鉴网上最常见的例子:某人A去机构B,科室C办事,前台就会拦住A,检查A是否预约,之后进行登记,然后给A一个通行证,A就可以正常通行了。这里的前台检查是否预约,之后给通行证就是授权的过程。这里面的A就可以看做业务系统(或者说是第三方应用),科室C就是三方软件想访问的资源。因此授权协议,就是为了保证第三方在获取了授权之后,才能访问资源。 |
AccessKey |
在调用阿里云API时您需要使用AccessKey完成身份验证。AccessKey包括AccessKey ID和AccessKey Secret,需要一起使用。 |
安全性
Role精细化授权
可以通过创建自定义策略指定RAM角色具体的权限。对于程序访问场景,某个业务应用程序的逻辑是可以预知的,明确能够知道程序会访问云上的哪些资源,做哪些操作。建议在应用维度进行精细化授权。如一个业务程序需要对xxxbucket进行图片的上传下载,那么精细化授权后,策略示例如下所示:
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": ["oss:GetObject", "oss:PutObject"],
"Resource": "acs:oss:cn-hangzhou:1234567890:xxxbucket/*"
}
]
}
同时,同一个应用的不同环境访问的资源也不应该相同,建议一个应用的一个环境对应具体的一个Role,避免过度授权,也避免不同环境的业务互相影响。
实际针对K8S场景,不建议测试环境和生产环境混在同一个账号的同一个集群下。建议测试环境的业务都单独运行在测试环境账号下的集群。生产环境的集群则运行在生产环境账号下的集群。
如果想要获取进一步精细化授权,也可以限制控制调用端的SourceIp。
STS Token权限范围
STS Token的权限:默认与指定扮演的角色权限相同。
若在调用AssumeRole接口时不设置Policy参数,则返回的STS Token将拥有指定角色的所有权限。
注意事项
STS服务调用次数是否有上限
AssumeRole接口调用次数上限:6000次/分钟、100次/秒。一个阿里云账号及该账号下的RAM用户、RAM角色共用该接口调用次数上限。当请求量超过上限时,会触发流控,影响业务正常运行。客户需要准确评估应用的调用频次,如果确实需要提升容量,则可以提交工单申请配额。每个STS Token都有过期时间,建议在有效期内,在客户端进行缓存,避免每次调用阿里云API都重新生成STS Token造成限流影响业务使用。
STS Token发生泄露时处理
如果通过扮演RAM角色获取的安全令牌(STS Token)发生泄露,您可以按以下步骤回收所有已经颁发的STS Token。
- 使用阿里云账号登录RAM控制台。
- 移除RAM角色的所有权限策略。具体操作,请参见为RAM角色移除权限。
- 删除RAM角色。具体操作,请参见删除RAM角色。删除RAM角色后,所有通过扮演该RAM角色获取的且未过期的STS Token都将立即失效。
如果您还需要使用该角色,您可以重新创建同名角色并授权相同的权限策略,使用新创建的角色继续完成您的任务。
ACK集群版本要求
RRSA功能目前只支持1.22及以上版本的集群(即ACK标准版、ACK Pro版、ASK标准版和ASK Pro版)。
实施步骤
实施准备
- 确保已开通了ACK集群。
- 确保已在ACK集群中创建好相应的K8S资源,包括namespace / serviceaccount。
实施时长
在实施准备工作完成的情况下,本方案实施预计时长:45分钟。
数据规划
以下操作过程参考如下数据规划:
资源对象 |
资源二级类型 |
值 |
ACK |
namespace |
default |
serviceaccount |
app |
|
ram |
role |
ACK-APP-STS-Role |
操作步骤
启用RRSA功能
本步骤配置容器服务的RRSA功能,若已配置参数可跳过该步骤。
- 登录容器服务管理控制台。
- 在控制台左侧导航栏中,单击集群。
- 在集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情。
- 在集群详情页面,单击基本信息页签,单击RRSA OIDC提供商URL右侧的启用RRSA。
- 在弹出的启用RRSA对话框中,单击确定。当集群状态由更新中变为运行中时,说明该集群的RRSA特性已变更完成,RRSA OIDC提供商URL右侧会显示OIDC提供商的URL链接。
使用RRSA功能
集群开启RRSA功能后,按照以下步骤来赋予集群内应用通过RRSA功能获取访问云资源OpenAPI的临时凭证的能力。
- 创建RAM角色
需要为应用所使用的服务账户(Service Account)创建一个RAM角色。后续应用将获取一个扮演这个RAM角色的临时凭证。在创建角色过程中,需要选择身份提供商类型为OIDC,身份提供商选择对应集群ID生成的身份提供商(格式一般为ack-rrsa-$集群ID),限制条件中,oidc:sub
格式为system:serviceaccount:<namespace>:<service_account>
。更多信息,请参见创建可信实体为阿里云账号的RAM角色。
最终创建出来的RAM角色的信任策略类似如下格式:
{
"Statement": [
{
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"oidc:aud": "sts.aliyuncs.com",
/*替换为当前集群的OIDC提供商URL,该URL可以在集群详情的基本信息页签获取。*/
"oidc:iss": "",
/*替换为应用所在的命名空间,本示例为:default。*/
/*替换为应用使用的服务账户,本示例为:app。*/
"oidc:sub": "system:serviceaccount::"
}
},
"Effect": "Allow",
"Principal": {
"Federated": [
/*替换为阿里云主账号UID。替换为ACK集群ID。*/
"acs:ram:::oidc-provider/ack-rrsa-"
]
}
}
],
"Version": "1"
}
- 为RAM角色授权。您可以通过为这个RAM角色授权的方式,指定这个RAM角色可以访问的云资源。更多信息,请参见为RAM角色授权。
注意这个角色,就是应用程序实际扮演的角色,需要赋上对应云资源操作权限。
- 部署应用
部署应用时,需要修改应用的模板内容,自动生成OIDC Token。具体操作,请参见部署服务账户令牌卷投影。
模板示例:
apiVersion: v1
kind: Pod
metadata:
name: test-rrsa
spec:
containers:
- image: alpine:3.14
command:
- sh
- -c
- 'sleep inf'
name: test
volumeMounts:
- mountPath: /var/run/secrets/tokens
name: oidc-token
serviceAccountName: build-robot
volumes:
- name: oidc-token # 新增的配置项。
projected:
sources:
- serviceAccountToken:
path: oidc-token
expirationSeconds: 7200 # oidc token过期时间(单位:秒)。
audience: "sts.aliyuncs.com"
注意!
- audience字段的值必须是 sts.aliyuncs.com。
- expirationSeconds 的值必须小于43200(12小时),如果设置的值比该值大,实际的OIDC Token会使用12小时作为过期时间。
您的应用就可以使用容器内挂载的OIDC Token(上面示例中的/var/run/secrets/tokens/oidc-token文件内容)调用STS的AssumeRoleWithOIDC接口来获取一个扮演前面创建的RAM角色的STS临时凭证,然后使用临时凭证访问云资源OpenAPI。
应用程序使用RRSA OIDC Token认证
目前阿里云官方SDK中,Go、Java、Node.js/TypeScript 已经支持了这种认证方式,无需开发人员手动调用AssumeRoleWithOIDC接口,就可以实现直接获取到STS Token完成阿里云API调用。
请参考下一个章节,查看对应代码示例。
故障排除
为什么使用STS时会报错?
如果一个RAM用户使用API、CLI或SDK调用AssumeRole获取STS Token时,出现如下报错信息:
Error message: You are not authorized to do this action. You should be authorized by RAM.
问题原因和解决方法如下:
- 该RAM用户缺少允许STS扮演角色的权限策略:请为该RAM用户添加系统策略(AliyunSTSAssumeRoleAccess)或自定义策略,详情请参见能否指定RAM用户具体可以扮演哪个RAM角色。
- RAM角色的信任策略不包含您正在使用的RAM用户,即RAM角色不允许该RAM用户扮演:请为RAM角色添加允许该RAM用户扮演的信任策略,详情请参见修改RAM角色的信任策略。