通过STS实现跨账号访问日志服务资源

阿里云账号(主账号)可以通过创建并授权RAM角色的方式授权其他云账号一定的资源权限,其他云账号扮演该角色,并为其名下的RAM用户授予AssumeRole权限之后,其他云账号或RAM用户可以通过访问STS接口获取临时AK和Token令牌,调用日志服务API接口。

背景信息

出于业务隔离或项目外包等需求,云账号A希望将部分日志服务业务授权给云账号B,由云账号B操作维护这部分业务。基本需求如下:

  • 云账号B拥有向企业A的日志服务中写入数据和使用消费组的权限。

  • 云账号B的指定RAM用户也拥有日志服务的写入和消费组权限。

  • 云账号B可获取STS临时凭证,访问日志服务API接口。具体操作,请参见STS

操作流程

  1. 云账号A创建RAM角色,并指定云账号B扮演该角色并赋予日志服务的指定权限。

  2. 云账号B创建RAM用户B1,并为其赋予AliyunSTSAssumeRoleAccess(调用STS AssumeRole接口)的系统策略。

  3. RAM账号B1调用STS AssumeRole接口访问日志服务API接口,操作日志服务资源。

步骤1:云账号A为云账号B创建RAM角色并授权

云账号A创建RAM角色,并指定云账号B扮演该角色并为角色赋予日志服务的指定权限。

可以通过RAM控制台创建RAM角色。具体操作,请参见创建RAM用户及授权。您也可以通过RAM的API CreateRole创建RAM角色。具体操作,请参见CreateRole。以下以控制台创建为例进行详细步骤说明。

  1. 使用阿里云账号(主账号)或RAM管理员登录RAM控制台

  2. 云账号A创建RAM角色,并指定云账号B扮演该角色。

    1. 在左侧导航栏,选择身份管理 > 角色

    2. 在角色页面,单击创建角色

    3. 创建角色面板,选择可信实体类型为阿里云账号,单击下一步

    4. 输入角色名称备注选择信任的云账号其他云账号,填写云账号B的账号ID,单击完成

      说明

      将鼠标悬停在控制台右上角头像的位置,即可查询主账号ID。

      以上步骤中创建的RAM角色详情如下。

      {
        "Statement": [
          {
            "Action": "sts:AssumeRole",
            "Effect": "Allow",
            "Principal": {
              "RAM": [
                "acs:ram::<云账号B的账号ID>:root"
              ]
            }
          }
        ],
        "Version": "1"
      }
  3. 创建权限策略。

    1. 在左侧导航栏,选择权限管理 > 权限策略

    2. 权限策略页面,单击创建权限策略

    3. 单击脚本编辑

    4. 创建权限策略页面的脚本编辑页签中,将配置框中的原有脚本替换为如下内容。

      此处配置的权限策略为云账号A授予云账号B的权限内容。

      如果仅需要写数据,具体权限如下:

      {
        "Version": "1",
        "Statement": [
          {
            "Action": "log:PostLogStoreLogs",
            "Resource": "*",
            "Effect": "Allow"
          }
        ]
      }

      如果需要通过协同消费库获取数据,具体权限如下:

      {
        "Version": "1",
        "Statement": [
          {
            "Action": [
               "log:GetCursorOrData",
               "log:CreateConsumerGroup",
               "log:ListConsumerGroup",
               "log:ConsumerGroupUpdateCheckPoint",
               "log:ConsumerGroupHeartBeat",
               "log:GetConsumerGroupCheckPoint",
               "log:UpdateConsumerGroup"
            ],
            "Resource": "*",
            "Effect": "Allow"
          }
        ]
      }

      以上两类资源都是授权指定用户的所有Project和Logstore,如果您需要授权指定Project和Logstore,请参考如下内容:

      • 授权指定Project:acs:log::{projectOwnerAliUid}:project/

      • 授权指定Logstore:acs:log::{projectOwnerAliUid}:project/{projectName}/logstore/{logstoreName}/

      完整的资源说明请参见日志服务RAM资源

    5. 编辑完成脚本后,单击继续编辑基本信息,配置如下参数:

      参数

      说明

      名称

      输入策略名称。

      备注

      输入您需要备注的内容。

    6. 单击确定

  4. 云账号A为RAM角色授权。

    1. 在左侧导航栏,选择身份管理 > 角色

    2. 角色列表下,找到目标RAM角色,单击新增授权

    3. 选择权限选择自定义策略并在列表中选中步骤3创建的权限策略,单击确定

    4. 单击完成,即完成授权。

步骤2:云账号B创建RAM用户B1并授权

云账号B创建RAM用户B1,并为其授予AliyunSTSAssumeRoleAccess(调用STS AssumeRole接口)的系统策略。

  1. 使用阿里云账号(主账号)或RAM管理员登录RAM控制台

  2. 在左侧导航栏中,选择身份管理 > 用户

  3. 用户页面,单击创建用户

  4. 创建用户页面,参考如下说明进行参数配置并单击确定

    参数

    说明

    用户账号信息

    请输入登录名称显示名称

    访问方式

    选中控制台访问OpenAPI 调用访问

  5. 返回用户页面,在用户登录名称/显示名称列表中,找到目标RAM用户,单击添加权限

  6. 添加权限页面,授权主体会自动填入。选中系统策略,在权限策略名称列表下,单击需要授予RAM角色的权限策略AliyunSTSAssumeRoleAccess,单击确定

  7. 单击完成

步骤3:RAM账号B1获取STS临时凭证访问日志服务

  1. 调用STS AssumeRole接口获取临时AK和Token。更多信息,请参见AssumeRole

    您可以选择以下方式调用该接口:

    通过STS SDK调用。更多信息,请参见STS SDK概览

  2. 调用日志服务接口,关于日志服务SDK请参见SDK参考

示例代码

基于Java SDK,以RAM账号B1通过STS向用户A的Project写入数据为例。示例代码如下所示:

package com.aliyun.openservices.log.sample;

import java.util.Date;
import java.util.Vector;

import com.aliyun.openservices.log.Client;
import com.aliyun.openservices.log.common.LogItem;
import com.aliyun.openservices.log.exception.LogException;
import com.aliyun.openservices.log.request.PutLogsRequest;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;

public class StsSample {
	  // now sts only support "cn-hangzhou"
	  public static final String REGION_CN_HANGZHOU = "cn-hangzhou";
	  // current sts api version
	  public static final String STS_API_VERSION = "2015-04-01";
	  static AssumeRoleResponse assumeRole(String accessKeyId, String accessKeySecret,
	                                       String roleArn, String roleSessionName, String policy,
	                                       ProtocolType protocolType) throws ClientException {
	    try {
	      // construct Aliyun Acs Client to ivoke OpenAPI
	      IClientProfile profile = DefaultProfile.getProfile(REGION_CN_HANGZHOU, accessKeyId, accessKeySecret);
	      DefaultAcsClient client = new DefaultAcsClient(profile);
	      // create AssumeRoleRequest object
	      final AssumeRoleRequest request = new AssumeRoleRequest();
	      request.setVersion(STS_API_VERSION);
	      request.setMethod(MethodType.POST);
	      request.setProtocol(protocolType);
	      request.setRoleArn(roleArn);
	      request.setRoleSessionName(roleSessionName);
	      request.setPolicy(policy);
	      // send request
	      final AssumeRoleResponse response = client.getAcsResponse(request);
	      return response;
	    } catch (ClientException e) {
	      throw e;
	    }
	  }
	  public static void main(String[] args) {
	    // only RAM user(sub account)can invoke AssumeRole interface
	    // Aliyun root account's AccessKeys can't invoke AssumeRole
	    // please create sub account in RAM web console(https://ram.console.aliyun.com), and create AK for this sub account
	    String accessKeyId = "<subaccountaccesskey>";
	    String accessKeySecret = "<subaccountaccesssecret>";
	    // AssumeRole API parameter: RoleArn, RoleSessionName, Policy, and DurationSeconds
	    // RoleArn can retrieve in RAM web console
	    // https://ram.console.aliyun.com/#/role/detail/< specifid rolename>/info
	    String roleArn = "<rolearn found in web console>";
	    // RoleSessionName is  temporary Token(mainly used for audit)
	    String roleSessionName = "bluemix-001";
	    String policy = "{\n" +
	            "    \"Version\": \"1\", \n" +
	            "    \"Statement\": [\n" +
	            "        {\n" +
	            "            \"Action\": \"log:PostLogStoreLogs\",\n" +
	            "            \"Resource\": \"*\",\n" +
	            "            \"Effect\": \"Allow\"\n" +
	            "        }\n" +
	            "    ]\n" +
	            "}";
	    System.out.println(policy);
	    // only support HTTPS here
	    ProtocolType protocolType = ProtocolType.HTTPS;
	    AssumeRoleResponse response = new AssumeRoleResponse();
	    try {
	      response = assumeRole(accessKeyId, accessKeySecret,
	              roleArn, roleSessionName, policy, protocolType);
	      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());
	    } catch (ClientException e) {
	      System.out.println("Failed to get a token.");
	      System.out.println("Error code: " + e.getErrCode());
	      System.out.println("Error message: " + e.getErrMsg());
	    }
	    
	    // log service parameter
	    // log service endpoint doc: https://help.aliyun.com/document_detail/29008.html
	    String logServiceEndpoint = "cn-hangzhou.log.aliyuncs.com";
	    // means project region must be cn-hangzhou
	    String project = "<log service project name>";
	    String logstore = "<log service logstore name>";
	    
	    // construct log service client object
	    Client client = new Client(logServiceEndpoint, 
	    		response.getCredentials().getAccessKeyId(), 
	    		response.getCredentials().getAccessKeySecret());
	    // notice: the AK & Security Token will be expire in 1hour
	    // so you must invoke asumeRole interface when expired
	    client.SetSecurityToken(response.getCredentials().getSecurityToken());
	    Vector<LogItem> logGroup = new Vector<LogItem>();
	    LogItem logItem = new LogItem((int) (new Date().getTime() / 1000));
	    logItem.PushBack("StsSample", "Send Data");
	    logGroup.add(logItem);
	    
	    PutLogsRequest req2 = new PutLogsRequest(project, logstore, "", "", logGroup);
	    try {
	    	client.PutLogs(req2);
	    } catch (LogException e) {
			System.out.println("Failed to send data.");
			System.out.println("Error code: " + e.GetErrorCode());
			System.out.println("Error message: " + e.GetErrorMessage());
	    }
	  }
}