作者:张医博
RAM 背景由来
给予 AWS 上的 Code space (代码与软件管理平台)系统数据和备份数据一并被攻击者删除。
企业上云的安全威胁排名
part1 - 云账号及其安全
云平台上多租户隔离的基本主体
- 1、阿里云现在所有云产品彼此之前都是隔离,主账号和 RAM 子账号也都是隔离的,彼此不能互访
- 2、同一个主账号 UID 下,不同的云产品默认不能互访,需要在主账号授权的跨产品授权的权限下才能访问
- 3、未经过主账号授权的情况下,其他主账号 uid 是不能访问用户自己的云产品和控制台。
认识云账户
- 1、统一出账
- 2、统一开票
- 3、共享信誉额度
云账户安全
- 1、云账户安全就是要保护云资源以防止未授权访问,即便是同个云账号下的不通产品也不能互访,除非云账号自身允许
- 2、认识云账号凭证(Credentials)
- 2.1、登陆密码验证(password)
- 2.2、mfa 多因素验。
- 2.3、api 访问问(ak/sk)
part2 - 用户身份管理与访问控制
谁是 user
1、用户自己登陆
2、用户授权自己的 ram 子账号登陆。
3、用户授权别人加的 ram 账号登陆
RAM 核心功能
集中用户管理
- 所有的用户都可以在控制台上统一的可视化界面处理,统一的 api 接入
谁是应用
1、比如阿里云提供的工具,类似 oss 的 brower
2、客户的应用程序代码(app、服务端程序)通过 sts 或者 云账号的 ak/sk
3、ecs 的 meta 网关信息也可以操作
https://www.alibabacloud.com/help/zh/doc-detail/54579.htm
阿里云RAM的特色
ABAC模型:AttributeBasedAccessControl . 这种是我们常用的自定义 policy 需要自己写控制语句
ABACvsACL: 就是我们常用系统策略,权限粒度小,都是一个管理权限或者只读、只写的权限。
一个实际的授权场景
{
"Version":"1",
"Statement":[ {
"Effect":"Allow", "Action":"ecs:StopInstance", "Resource":"acs:ecs:cn-hangzhou:*:*", "Condition":{
"StringEquals":{ "ecs:tag/env":"production"
}, "Bool":{
"acs:MFAPresent":"true" },
"IpAddress":{ "acs:SourceIp":"42.120.88.0/24"
} }
} ]
}
以上策略意思是 针对 42.120.88.0,允许操作 production 组内的 ecs 实例进行停止操作。
为了方便我们给他分开三块看,这样会比较清晰。
第一块:固定的外层语法不变,即使有多条策略也是在者一个 statement 内部,用 "," 分开。
{
"Sersion":"1"
"Statement":[
"这里是第二块"
]
}
第二块:我们简称三板斧,因为内容是固定的,只不过变化 value 而已。
1、三板斧就是 effect ,action,resource ,这三个是一组,包含在一个 {} 内,第二条语句要用 "," 隔开写在第二个 {} 内
2、effect :只有 Allow 和 Deny
3、action :可以写多条时要用 [] 包括,比如 ["acs:ecs:cn-hangzhou:1982222:instance/i-zxxxxesd" , "acs:oss:cn-beijing:1299:bucket/prefix/objet"]
4、action :填写的是你要限制对应的产品的 API 名称,写多个时要用 [] 包括主,比如 [ “ecs:CreateInstance”,"ecs:StopInstance"]
5、product:填写产品名称 slb、ecs 、oss、vpc 等。
6、regionID:cn-shanghai、cn-hangzhou 等
7、uid:云账号 uid
{
"Sersion":"1"
"Statement":[
{
"Effect":"Allow / deny",
"Resource":"acs:product:regionid:uid:*",
"Action:":"apiname"
},
{
"设置并行的第二条语句"
}
]
}
Part 3: 最佳实践
下面我实际操作如果新建 ram 子账号、授权策略
新建账户
系统策略
自定义策略
使用 RAM 子账号 ak sk
App 安全天使 STS
为什么说是安全天使
当前端上的 APP 不可能直接使用客户的 ak sk ,风险性极高,一但恶意攻击者那到你 APP 数据包,揭秘出源码中的 ak sk ,您的云产品将暴露,任何人都可以操作您子账号下所有授权的产品,即使删除 ak sk 也可能导致服务端的其他业务出现链接异常,由此 sts 应运而生。
临时、最小粒度、可控
- 1、临时: sts 的令牌有效期是 900-3600 秒,一但过期将失去效力。
- 2、最小粒度:sts 只能操作角色扮演了策略的对应产品,简单说就是,把用户想授权的各类云产品抽象出各种角色,给每个角色赋予不通的权限,ram 子账号扮演了哪种角色就可以有哪种权限,及时 sts 信息泄露,客户只要删除 sts 角色即可,或者将角色和 ram 子账号解绑,盗取者也没有用了,而且并不影响用户其他使用 ak sk 的服务端业务。
- 3、可控:生成 sts 是放在用户自己的服务器上所以安全可用。
sts 创建、代码实践
由于 sts 也要新建 ram 子账号存在与 part3 重复的地方,所以建立 ram 子账号的位置我就不演示了。
- 新建角色,选择用户角色,当前账号,如果选择其他云账号是给其他 云账号下的 子账号授权访问自己的云产品,要区分概念。这里我们给自己的云账号授权,所以默认。
- 给角色创建一条自定义策略或者系统策略都行
- 给角色绑定我们刚才自定义的 policy
- 让 ram 有权调用角色,这样 ram 子账号就有了角色对应的产品策略,所以要让角色和 ram 关联
- 1、最后一部利用服务端的代码,填入我门刚才建立 ram 、角色时得到的所有信息,就能生成 sts 令牌
- 2、rolearn 就是我们在创建角色时控制台看到的。
- 3、rolesession 就是角色名称
public class StsServiceSample {
public static void main(String[] args) {
String endpoint = "sts.aliyuncs.com";
String accessKeyId = "<access-key-id>";
String accessKeySecret = "<access-key-secret>";
String roleArn = "<role-arn>";
String roleSessionName = "session-name";
String policy = "{\n" +
" \"Version\": \"1\", \n" +
" \"Statement\": [\n" +
" {\n" +
" \"Action\": [\n" +
" \"oss:*\"\n" +
" ], \n" +
" \"Resource\": [\n" +
" \"acs:oss:*:*:*\" \n" +
" ], \n" +
" \"Effect\": \"Allow\"\n" +
" }\n" +
" ]\n" +
"}";
try {
// 添加endpoint(直接使用STS endpoint,前两个参数留空,无需添加region ID)
DefaultProfile.addEndpoint("", "", "Sts", endpoint);
// 构造default profile(参数留空,无需添加region ID)
IClientProfile profile = DefaultProfile.getProfile("", accessKeyId, accessKeySecret);
// 用profile构造client
DefaultAcsClient client = new DefaultAcsClient(profile);
final AssumeRoleRequest request = new AssumeRoleRequest();
request.setMethod(MethodType.POST);
request.setRoleArn(roleArn);
request.setRoleSessionName(roleSessionName);
request.setPolicy(policy); // Optional
final AssumeRoleResponse response = client.getAcsResponse(request);
System.out.println("Expiration: " + response.getCredentials().getExpiration());
System.out.println("Access Key Id: " + response.getCredentials().getAccessKeyId());
System.out.println("Access Key Secret: " + response.getCredentials().getAccessKeySecret());
System.out.println("Security Token: " + response.getCredentials().getSecurityToken());
System.out.println("RequestId: " + response.getRequestId());
} catch (ClientException e) {
System.out.println("Failed:");
System.out.println("Error code: " + e.getErrCode());
System.out.println("Error message: " + e.getErrMsg());
System.out.println("RequestId: " + e.getRequestId());
}
}
}