執行個體RAM角色允許您將一個角色關聯到ECI執行個體,在執行個體內部基於STS(Security Token Service)臨時憑證訪問其他雲產品的API。本文介紹如何通過API建立、授權執行個體RAM角色,並將該角色授予給ECI執行個體。
應用情境
ECI執行個體上部署的應用程式在雲產品通訊中,通過雲帳號或者RAM使用者的AccessKey訪問阿里雲其他雲產品(例如OSS、VPC、RDS等)的API。為了方便和快速地調用,部分使用者直接把AccessKey固化在執行個體中,例如直接寫在設定檔中。這種方式存在許可權過高、泄露資訊和難以維護等問題。執行個體RAM角色能夠避免此類問題。
RAM角色是一種具備某些許可權的虛擬使用者,可以被ECI執行個體扮演,從而使得ECI執行個體獲得相應的許可權。使用RAM角色,無需在執行個體中儲存AccessKey,通過修改RAM角色的許可權即可變更ECI執行個體的許可權,在使用上安全便捷。更多關於RAM角色的資訊,請參見RAM角色概覽。
使用流程
使用執行個體RAM角色的步驟如下:
調用CreateRole建立執行個體RAM角色,設定允許扮演該角色的可信實體為ECI服務(受信服務為Elastic Compute Service)。
根據需要調用CreatePolicy建立權限原則,然後調用AttachPolicyToRole將該權限原則授予給執行個體RAM角色。
如果您使用RAM使用者建立ECI執行個體並指定執行個體RAM角色,則必須先授權RAM使用者可以使用執行個體RAM角色。
調用CreateContainerGroup建立ECI執行個體,通過RamRoleName參數為ECI執行個體授予執行個體RAM角色,使得ECI執行個體獲得對應的許可權。一個ECI執行個體只能授予一個執行個體RAM角色。
為ECI執行個體授予了執行個體RAM角色後,如果需要在ECI執行個體內部部署的應用程式中訪問雲產品的API,您需要通過執行個體中繼資料擷取執行個體RAM角色的STS Token。
建立執行個體RAM角色
調用CreateRole可以建立一個執行個體RAM角色。具體參數資訊,請參見CreateRole。
您可以自訂設定RoleName來指定角色名稱(假設為ECIRamRoleTest),然後按如下策略文本設定AssumeRolePolicyDocument。
{
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": [
"ecs.aliyuncs.com"
]
}
}
],
"Version": "1"
}
授權執行個體RAM角色
調用CreatePolicy建立一個自訂權限原則。
調用時,需設定以下參數:
PolicyName:權限原則名稱(假設為ECIRamRoleTestPolicy)。
PolicyDocument:權限原則內容。
{ "Statement": [ { "Action": [ "oss:Get*", "oss:List*" ], "Effect": "Allow", "Resource": "*" } ], "Version": "1" }
更多資訊,請參見CreatePolicy。
調用AttachPolicyToRole為RAM角色添加權限原則。
調用時,需設定以下參數:
PolicyName:指定權限原則名稱,例如ECIRamRoleTestPolicy。
PolicyType:權限原則類型,配置為Custom,表示是自訂權限原則。
RoleName:指定RAM角色,例如ECIRamRoleTest。
更多資訊,請參見AttachPolicyToRole。
授權RAM使用者使用執行個體RAM角色
如果您使用RAM使用者,則必須先授權RAM使用者具備該執行個體RAM角色的ram:PassRole
許可權,RAM使用者才可以使用執行個體RAM角色。ram:PassRole
許可權決定RAM使用者能否直接執行角色策略賦予的許可權。
為ECI執行個體授予執行個體RAM角色
調用CreateContainerGroup建立ECI執行個體時,可以通過RamRoleName參數來指定RAM角色。更多資訊,請參見CreateContainerGroup。
一個ECI執行個體只能授予一個執行個體RAM角色。如果ECI執行個體已有RAM角色,則會報錯提示您不能附加新的角色。
擷取STS Token
在ECI執行個體內部,您可以訪問中繼資料URL來擷取RAM角色的STS Token,該STS Token可以執行RAM角色的許可權和資源,並且該STS Token會自動周期性地更新。
curl http://100.100.100.200/latest/meta-data/ram/security-credentials/${your_ram_role_name}
請使用實際RAM角色名稱替換${your_ram_role_name}
,假設RAM角色名稱為ECIRamRoleTest,命令樣本如下:
curl http://100.100.100.200/latest/meta-data/ram/security-credentials/ECIRamRoleTest
返回結果中可以擷取STS Token,樣本如下:
{
"AccessKeyId" : "STS.******",
"AccessKeySecret" : "******",
"Expiration" : "2023-06-22T19:13:58Z",
"SecurityToken" : "******",
"LastUpdated" : "2023-06-22T13:13:58Z",
"Code" : "Success"
}
基於STS Token訪問雲端服務
以下樣本以使用Go SDK訪問OSS為例介紹如何基於STS Token訪問雲端服務,該樣本可以實現基於STS Token訪問某個OSS Bucket並列舉其中的所有檔案。
該樣本僅用於示範基於STS Token訪問雲端服務的方法,實際情境中請根據自身業務編寫代碼,具體請參考您要使用的雲端服務的SDK。
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", "Please input oss endpoint, Recommended internal endpoint, eg: oss-cn-hangzhou-internal.aliyuncs.com")
flag.StringVar(&ossBucketName, "bucket", "", "Please input 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("Please input oss endpoint, eg: oss-cn-hangzhou-internal.aliyuncs.com")
}
if ossBucketName == "" {
log.Fatal("Please input oss endpoint")
}
output, err := exec.Command("curl", securityCredUrl).Output()
if err != nil {
log.Fatalf("Failed to get ramrole name from metaserver: %s", err)
}
output, err = exec.Command("curl", securityCredUrl+string(output)).Output()
if err != nil {
log.Fatalf("Failed to get security credentials from metaserver: %s", err)
}
authServiceIdentity := new(AssumedRoleUserCredentialsWithServiceIdentity)
if err := json.Unmarshal(output, authServiceIdentity); err != nil {
log.Fatalf("Failed to Unmarshal to AssumedRoleUserCredentialsWithServiceIdentity: %s", err)
}
// 建立OSS Client執行個體, 生產環境使用需要定時更新OSS Client,防止SecurityToken到期訪問雲產品失敗
ossClient, err := oss.New(ossEndpoint, authServiceIdentity.AccessKeyId,
authServiceIdentity.AccessKeySecret, oss.SecurityToken(authServiceIdentity.SecurityToken))
if err != nil {
log.Fatalf("Failed to new oss client: %s", err)
}
// 擷取儲存空間
bucket, err := ossClient.Bucket(ossBucketName)
if err != nil {
log.Fatalf("Failed to get bucket %q: %s", ossBucketName, err)
}
// 列舉儲存空間中檔案
marker := ""
for {
lsRes, err := bucket.ListObjects(oss.Marker(marker))
if err != nil {
log.Fatalf("Failed to list object from bucket %q: %s", ossBucketName, err)
}
// 列印列舉檔案,預設情況下一次返回100條記錄。
for _, object := range lsRes.Objects {
log.Println("Bucket: ", object.Key)
}
if lsRes.IsTruncated {
marker = lsRes.NextMarker
} else {
break
}
}
}