全部產品
Search
文件中心

Elastic Container Instance:為ECI Pod綁定RAM角色

更新時間:Jul 06, 2024

RAM角色是一種具備某些許可權的虛擬使用者,可以被ECI Pod扮演,從而使得ECI Pod獲得相應的許可權。本文介紹如何為ECI Pod(即ECI執行個體)綁定RAM角色,以便ECI Pod內部的應用程式可以基於STS(Security Token Service)臨時憑證訪問其他阿里雲產品的API。

背景資訊

ECI Pod上部署的應用程式在雲產品通訊中,通過雲帳號或者RAM使用者的AccessKey訪問阿里雲其他雲產品(例如OSS、VPC、RDS等)的API。為了方便和快速地調用,部分使用者直接把AccessKey固化在執行個體中,例如直接寫在設定檔中。這種方式存在許可權過高、泄露資訊和難以維護等問題。

執行個體RAM角色能夠避免上述問題。通過RAM角色授權,無需在ECI Pod中儲存AccessKey。通過修改RAM角色的許可權即可變更ECI Pod的許可權,在使用上安全便捷。更多關於RAM角色的資訊,請參見RAM角色概覽

建立RAM角色並授權

  1. 建立RAM角色。具體操作,請參見建立可信實體為阿里雲服務的RAM角色

    建立RAM角色時,可信實體類型選擇阿里雲服務,受信服務為雲端服務器

    RAM角色.png

  2. 授權RAM角色。

    1. 建立權限原則。具體操作,請參見建立自訂權限原則

    2. 為RAM角色添加權限原則。具體操作,請參見為RAM角色授權

  3. (可選)授權RAM使用者使用RAM角色。

    如果通過RAM使用者使用RAM角色,請確保RAM使用者具有ram:passRole許可權。許可權內容參考如下,其中ECIRamRoleTest為要授權的ram:PassRole許可權的RAM角色名稱。

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

為ECI Pod綁定RAM角色

建立ECI Pod時,可以通過k8s.aliyun.com/eci-ram-role-name的Annotation為Pod綁定RAM角色,授予Pod訪問阿里雲產品的能力。

重要
  • Annotation請添加在Pod的metadata下,例如:建立Deployment時,Annotation需添加在spec>template>metadata下。

  • 僅支援在建立ECI Pod時添加ECI相關Annotation來生效ECI功能,更新ECI Pod時添加或者修改ECI相關Annotation均不會生效。

配置樣本如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test
  labels:
    app: test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test
  template:
    metadata:
      name: test
      labels:
        app: test
        alibabacloud.com/eci: "true" 
      annotations:
        k8s.aliyun.com/eci-ram-role-name : "${your_ram_role_name}"   #綁定RAM角色
    spec:
      containers:
      - name: test
        image: registry.cn-shanghai.aliyuncs.com/eci_open/centos:7
        command: ["sleep"]
        args: ["3600"]

擷取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
		}
	}
}