全部产品
Search
文档中心

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

更新时间:Dec 16, 2024

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

方案概览

假设企业A拥有阿里云账号A,企业B拥有阿里云账号B。出于业务隔离或项目外包等需求,企业A希望将部分日志服务业务授权给企业B维护。需求如下:

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

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

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

基于以上需求,您可按照以下步骤配置。

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

步骤二:阿里云账号B创建RAM用户user-b并授权

步骤三:RAM用户user-b获取STS临时凭证访问日志服务

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

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

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

  1. 使用阿里云账号A登录RAM控制台

  2. 创建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. 创建一个自定义权限策略,其中在脚本编辑页签,请使用以下脚本替换配置框中的原有内容。具体操作,请参见通过脚本编辑模式创建自定义权限策略

    如果仅需要写数据,授权脚本如下所示:

    {
      "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}/

    完整的资源说明请参见资源列表

  4. 为RAM角色添加创建的自定义权限。具体操作,请参见为RAM角色授权

步骤二:阿里云账号B创建RAM用户user-b并授权

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

  1. 使用阿里云账号B登录RAM控制台

  2. 创建一个RAM用户user-b访问方式选择控制台访问使用永久AccessKey访问具体操作,请参见创建RAM用户

    重要
    • RAM用户的AccessKey Secret只在创建时显示,不支持查看,请妥善保管。

    • 访问密钥(AccessKey)是一种长期有效的程序访问凭证。AccessKey泄露会威胁该账号下所有资源的安全。建议优先采用STS Token临时凭证方案,降低凭证泄露的风险。更多信息,请参见使用访问凭据访问阿里云OpenAPI最佳实践

  3. 为RAM用户添加AliyunSTSAssumeRoleAccess权限,使其可以通过扮演RAM角色来获取临时身份凭证。具体操作,请参见为RAM用户授权

步骤三:RAM用户user-b获取STS临时凭证访问日志服务

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

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

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

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

示例代码

基于Java SDK,以RAM账号user-b通过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/zh/sls/developer-reference/api-sls-2020-12-30-endpoint
	    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());
	    }
	  }
}