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, andAttachPolicyToRolein RAM, andCreateContainerGroupin ECI(Optional) If using a RAM user to create the elastic container instance, the RAM user must also have
ram:PassRolepermission on the target role
How it works
Create an instance RAM role and configure its trust policy to allow ECS to assume it.
Create a permission policy and attach it to the role.
(Optional) Grant a RAM user permission to pass the role to an instance.
Assign the role to an elastic container instance via
CreateContainerGroup.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:
ECIRamRoleTestPolicyPolicyDocument: 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:
| Parameter | Value |
|---|---|
PolicyName | ECIRamRoleTestPolicy |
PolicyType | Custom |
RoleName | ECIRamRoleTest |
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.
Log on to the RAM console using an Alibaba Cloud account or a RAM user with admin permissions.
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/ECIRamRoleTestReplace 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"
}| Field | Description |
|---|---|
AccessKeyId | Temporary access key ID (prefixed with STS.) |
AccessKeySecret | Temporary access key secret |
SecurityToken | Session token required alongside the AccessKeyId and AccessKeySecret when calling service APIs |
Expiration | UTC timestamp when the token expires; STS tokens are automatically and regularly updated |
LastUpdated | UTC timestamp when the token was last refreshed |
Code | Success 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
To manage RAM roles and policies in the console instead of via API, see RAM role overview.
To control which RAM users can create elastic container instances and assign roles, see Grant permissions to a RAM user.