Use an instance RAM role using API operations

更新时间:
复制 MD 格式

Assign a RAM role to an elastic container instance so that applications running on the instance can call other Alibaba Cloud service APIs using automatically rotated Security Token Service (STS) credentials — no AccessKey pairs stored on the instance.

Why use an instance RAM role

Hardcoding AccessKey pairs in configuration files creates two problems: credentials can leak, and updating permissions requires modifying every instance. Instance RAM roles solve both:

  • No credentials on disk. Applications retrieve short-lived STS tokens from instance metadata at runtime.

  • Centralized permission management. To change what the instance can access, update the RAM role — not the instance.

  • Automatic token rotation. STS tokens expire and are refreshed automatically, limiting the blast radius of any exposure.

Prerequisites

Before you begin, ensure that you have:

  • An Alibaba Cloud account or a RAM user with permissions to call CreateRole, CreatePolicy, and AttachPolicyToRole in RAM, and CreateContainerGroup in ECI

  • (Optional) If using a RAM user to create the elastic container instance, the RAM user must also have ram:PassRole permission on the target role

How it works

  1. Create an instance RAM role and configure its trust policy to allow ECS to assume it.

  2. Create a permission policy and attach it to the role.

  3. (Optional) Grant a RAM user permission to pass the role to an instance.

  4. Assign the role to an elastic container instance via CreateContainerGroup.

  5. Retrieve STS credentials from instance metadata inside the pod, then use them to call other Alibaba Cloud services.

Step 1: Create an instance RAM role

Call CreateRole with the following parameters:

  • RoleName: A descriptive name for the role. This example uses ECIRamRoleTest.

  • AssumeRolePolicyDocument: The trust policy that grants ECS the right to assume this role.

{
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "ecs.aliyuncs.com"
        ]
      }
    }
  ],
  "Version": "1"
}
The trusted entity must be set to Elastic Container Instance and the trusted service must be set to Elastic Compute Service (ECS), represented by the ecs.aliyuncs.com principal.

Step 2: Attach a permission policy to the role

2a. Create a custom policy

Call CreatePolicy with the following parameters:

  • PolicyName: ECIRamRoleTestPolicy

  • PolicyDocument: The permissions to grant. The following example grants read access to all Object Storage Service (OSS) buckets:

{
  "Statement": [
    {
      "Action": [
        "oss:Get*",
        "oss:List*"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ],
  "Version": "1"
}

Follow the principle of least privilege — grant only the permissions the application actually needs.

2b. Attach the policy to the role

Call AttachPolicyToRole with the following parameters:

ParameterValue
PolicyNameECIRamRoleTestPolicy
PolicyTypeCustom
RoleNameECIRamRoleTest

Step 3 (optional): Authorize a RAM user to pass the role

Skip this step if you are using an Alibaba Cloud account to create the elastic container instance.

If a RAM user creates the elastic container instance and assigns the role, the RAM user needs ram:PassRole permission on that role. Without it, the RAM user cannot delegate the role to the instance.

  1. Log on to the RAM console using an Alibaba Cloud account or a RAM user with admin permissions.

  2. Create a custom policy with the following document, then attach it to the RAM user. For details, see Grant permissions to a RAM user.

{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "ram:PassRole",
      "Resource": "acs:ram:*:*:role/ECIRamRoleTest"
    }
  ],
  "Version": "1"
}

Replace ECIRamRoleTest with the actual role name if you used a different name in Step 1.

Step 4: Assign the role to an elastic container instance

When calling CreateContainerGroup, set the RamRoleName parameter to the role name created in Step 1.

Each elastic container instance supports only one instance RAM role. If a role is already assigned, attempting to assign another role returns an error.

Step 5: Retrieve and use STS credentials

Retrieve the STS token

From inside the pod, query the instance metadata endpoint:

curl http://100.100.100.200/latest/meta-data/ram/security-credentials/ECIRamRoleTest

Replace ECIRamRoleTest with your actual role name. The response looks like:

{
  "AccessKeyId": "STS.******",
  "AccessKeySecret": "******",
  "Expiration": "2023-06-22T19:13:58Z",
  "SecurityToken": "******",
  "LastUpdated": "2023-06-22T13:13:58Z",
  "Code": "Success"
}
FieldDescription
AccessKeyIdTemporary access key ID (prefixed with STS.)
AccessKeySecretTemporary access key secret
SecurityTokenSession token required alongside the AccessKeyId and AccessKeySecret when calling service APIs
ExpirationUTC timestamp when the token expires; STS tokens are automatically and regularly updated
LastUpdatedUTC timestamp when the token was last refreshed
CodeSuccess indicates valid credentials

Use the STS token to access Alibaba Cloud services

The following Go example retrieves credentials from instance metadata and uses them to list objects in an OSS bucket. Credentials are read from the metadata endpoint — no static AccessKey pairs are in the code.

This example demonstrates the credential retrieval pattern. In production, adapt the code to your application's requirements and refer to the SDK documentation for the target service.
package main

import (
	"encoding/json"
	"flag"
	"log"
	"os/exec"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
)

const (
	securityCredUrl = "http://100.100.100.200/latest/meta-data/ram/security-credentials/"
)

var (
	ossEndpoint   string
	ossBucketName string
)

func init() {
	flag.StringVar(&ossEndpoint, "endpoint", "oss-cn-hangzhou-internal.aliyuncs.com", "OSS endpoint (internal endpoint recommended, e.g. oss-cn-hangzhou-internal.aliyuncs.com)")
	flag.StringVar(&ossBucketName, "bucket", "", "OSS bucket name")
}

type AssumedRoleUserCredentialsWithServiceIdentity struct {
	AccessKeyId     string `json:"AccessKeyId" xml:"AccessKeyId"`
	AccessKeySecret string `json:"AccessKeySecret" xml:"AccessKeySecret"`
	Expiration      string `json:"Expiration" xml:"Expiration"`
	SecurityToken   string `json:"SecurityToken" xml:"SecurityToken"`
	LastUpdated     string `json:"LastUpdated" xml:"LastUpdated"`
	Code            string `json:"Code" xml:"Code"`
}

func main() {
	flag.Parse()

	if ossEndpoint == "" {
		log.Fatal("OSS endpoint is required, e.g. oss-cn-hangzhou-internal.aliyuncs.com")
	}
	if ossBucketName == "" {
		log.Fatal("OSS bucket name is required")
	}

	output, err := exec.Command("curl", securityCredUrl).Output()
	if err != nil {
		log.Fatalf("Failed to get RAM role name from metadata server: %s", err)
	}

	output, err = exec.Command("curl", securityCredUrl+string(output)).Output()
	if err != nil {
		log.Fatalf("Failed to get STS credentials from metadata server: %s", err)
	}

	authServiceIdentity := new(AssumedRoleUserCredentialsWithServiceIdentity)
	if err := json.Unmarshal(output, authServiceIdentity); err != nil {
		log.Fatalf("Failed to parse STS credentials: %s", err)
	}

	// Create an OSS client using STS credentials. In production, refresh the client
	// before the token expires to avoid access failures.
	ossClient, err := oss.New(ossEndpoint, authServiceIdentity.AccessKeyId,
		authServiceIdentity.AccessKeySecret, oss.SecurityToken(authServiceIdentity.SecurityToken))
	if err != nil {
		log.Fatalf("Failed to create OSS client: %s", err)
	}

	// Get the bucket handle.
	bucket, err := ossClient.Bucket(ossBucketName)
	if err != nil {
		log.Fatalf("Failed to get bucket %q: %s", ossBucketName, err)
	}

	// List objects in the bucket. Up to 100 objects are returned per call by default.
	marker := ""
	for {
		lsRes, err := bucket.ListObjects(oss.Marker(marker))
		if err != nil {
			log.Fatalf("Failed to list objects in bucket %q: %s", ossBucketName, err)
		}
		for _, object := range lsRes.Objects {
			log.Println("Object:", object.Key)
		}
		if lsRes.IsTruncated {
			marker = lsRes.NextMarker
		} else {
			break
		}
	}
}

What's next