ecs_ram_role_credentials_provider.go raw

   1  package credentials
   2  
   3  import (
   4  	"encoding/json"
   5  	"fmt"
   6  	"strconv"
   7  	"time"
   8  
   9  	"github.com/alibabacloud-go/tea/tea"
  10  	"github.com/aliyun/credentials-go/credentials/internal/utils"
  11  	"github.com/aliyun/credentials-go/credentials/request"
  12  )
  13  
  14  var securityCredURL = "http://100.100.100.200/latest/meta-data/ram/security-credentials/"
  15  var securityCredTokenURL = "http://100.100.100.200/latest/api/token"
  16  
  17  const defaultMetadataTokenDuration = int(21600)
  18  
  19  // ECSRAMRoleCredentialsProvider is a kind of credentials provider
  20  type ECSRAMRoleCredentialsProvider struct {
  21  	*credentialUpdater
  22  	RoleName              string
  23  	EnableIMDSv2          bool
  24  	MetadataTokenDuration int
  25  	sessionCredential     *sessionCredential
  26  	runtime               *utils.Runtime
  27  	metadataToken         string
  28  	staleTime             int64
  29  }
  30  
  31  type ecsRAMRoleResponse struct {
  32  	Code            string `json:"Code" xml:"Code"`
  33  	AccessKeyId     string `json:"AccessKeyId" xml:"AccessKeyId"`
  34  	AccessKeySecret string `json:"AccessKeySecret" xml:"AccessKeySecret"`
  35  	SecurityToken   string `json:"SecurityToken" xml:"SecurityToken"`
  36  	Expiration      string `json:"Expiration" xml:"Expiration"`
  37  }
  38  
  39  func newEcsRAMRoleCredentialWithEnableIMDSv2(roleName string, enableIMDSv2 bool, metadataTokenDuration int, inAdvanceScale float64, runtime *utils.Runtime) *ECSRAMRoleCredentialsProvider {
  40  	credentialUpdater := new(credentialUpdater)
  41  	if inAdvanceScale < 1 && inAdvanceScale > 0 {
  42  		credentialUpdater.inAdvanceScale = inAdvanceScale
  43  	}
  44  	return &ECSRAMRoleCredentialsProvider{
  45  		RoleName:              roleName,
  46  		EnableIMDSv2:          enableIMDSv2,
  47  		MetadataTokenDuration: metadataTokenDuration,
  48  		credentialUpdater:     credentialUpdater,
  49  		runtime:               runtime,
  50  	}
  51  }
  52  
  53  func (e *ECSRAMRoleCredentialsProvider) GetCredential() (credentials *CredentialModel, err error) {
  54  	if e.sessionCredential == nil || e.needUpdateCredential() {
  55  		err = e.updateCredential()
  56  		if err != nil {
  57  			if e.credentialExpiration > (int(time.Now().Unix()) - int(e.lastUpdateTimestamp)) {
  58  				// 虽然有错误,但是已有的 credentials 还有效
  59  			} else {
  60  				return
  61  			}
  62  		}
  63  	}
  64  
  65  	credentials = &CredentialModel{
  66  		AccessKeyId:     tea.String(e.sessionCredential.AccessKeyId),
  67  		AccessKeySecret: tea.String(e.sessionCredential.AccessKeySecret),
  68  		SecurityToken:   tea.String(e.sessionCredential.SecurityToken),
  69  		Type:            tea.String("ecs_ram_role"),
  70  	}
  71  
  72  	return
  73  }
  74  
  75  // GetAccessKeyId reutrns  EcsRAMRoleCredential's AccessKeyId
  76  // if AccessKeyId is not exist or out of date, the function will update it.
  77  func (e *ECSRAMRoleCredentialsProvider) GetAccessKeyId() (accessKeyId *string, err error) {
  78  	c, err := e.GetCredential()
  79  	if err != nil {
  80  		return
  81  	}
  82  
  83  	accessKeyId = c.AccessKeyId
  84  	return
  85  }
  86  
  87  // GetAccessSecret reutrns  EcsRAMRoleCredential's AccessKeySecret
  88  // if AccessKeySecret is not exist or out of date, the function will update it.
  89  func (e *ECSRAMRoleCredentialsProvider) GetAccessKeySecret() (accessKeySecret *string, err error) {
  90  	c, err := e.GetCredential()
  91  	if err != nil {
  92  		return
  93  	}
  94  
  95  	accessKeySecret = c.AccessKeySecret
  96  	return
  97  }
  98  
  99  // GetSecurityToken reutrns  EcsRAMRoleCredential's SecurityToken
 100  // if SecurityToken is not exist or out of date, the function will update it.
 101  func (e *ECSRAMRoleCredentialsProvider) GetSecurityToken() (securityToken *string, err error) {
 102  	c, err := e.GetCredential()
 103  	if err != nil {
 104  		return
 105  	}
 106  
 107  	securityToken = c.SecurityToken
 108  	return
 109  }
 110  
 111  // GetBearerToken is useless for EcsRAMRoleCredential
 112  func (e *ECSRAMRoleCredentialsProvider) GetBearerToken() *string {
 113  	return tea.String("")
 114  }
 115  
 116  // GetType reutrns  EcsRAMRoleCredential's type
 117  func (e *ECSRAMRoleCredentialsProvider) GetType() *string {
 118  	return tea.String("ecs_ram_role")
 119  }
 120  
 121  func getRoleName() (string, error) {
 122  	runtime := utils.NewRuntime(1, 1, "", "")
 123  	request := request.NewCommonRequest()
 124  	request.URL = securityCredURL
 125  	request.Method = "GET"
 126  	content, err := doAction(request, runtime)
 127  	if err != nil {
 128  		return "", err
 129  	}
 130  	return string(content), nil
 131  }
 132  
 133  func (e *ECSRAMRoleCredentialsProvider) getMetadataToken() (err error) {
 134  	if e.needToRefresh() {
 135  		if e.MetadataTokenDuration <= 0 {
 136  			e.MetadataTokenDuration = defaultMetadataTokenDuration
 137  		}
 138  		tmpTime := time.Now().Unix() + int64(e.MetadataTokenDuration*1000)
 139  		request := request.NewCommonRequest()
 140  		request.URL = securityCredTokenURL
 141  		request.Method = "PUT"
 142  		request.Headers["X-aliyun-ecs-metadata-token-ttl-seconds"] = strconv.Itoa(e.MetadataTokenDuration)
 143  		content, err := doAction(request, e.runtime)
 144  		if err != nil {
 145  			return err
 146  		}
 147  		e.staleTime = tmpTime
 148  		e.metadataToken = string(content)
 149  	}
 150  	return
 151  }
 152  
 153  func (e *ECSRAMRoleCredentialsProvider) updateCredential() (err error) {
 154  	if e.runtime == nil {
 155  		e.runtime = new(utils.Runtime)
 156  	}
 157  	request := request.NewCommonRequest()
 158  	if e.RoleName == "" {
 159  		e.RoleName, err = getRoleName()
 160  		if err != nil {
 161  			return fmt.Errorf("refresh Ecs sts token err: %s", err.Error())
 162  		}
 163  	}
 164  	if e.EnableIMDSv2 {
 165  		err = e.getMetadataToken()
 166  		if err != nil {
 167  			return fmt.Errorf("failed to get token from ECS Metadata Service: %s", err.Error())
 168  		}
 169  		request.Headers["X-aliyun-ecs-metadata-token"] = e.metadataToken
 170  	}
 171  	request.URL = securityCredURL + e.RoleName
 172  	request.Method = "GET"
 173  	content, err := doAction(request, e.runtime)
 174  	if err != nil {
 175  		return fmt.Errorf("refresh Ecs sts token err: %s", err.Error())
 176  	}
 177  	var resp *ecsRAMRoleResponse
 178  	err = json.Unmarshal(content, &resp)
 179  	if err != nil {
 180  		return fmt.Errorf("refresh Ecs sts token err: Json Unmarshal fail: %s", err.Error())
 181  	}
 182  	if resp.Code != "Success" {
 183  		return fmt.Errorf("refresh Ecs sts token err: Code is not Success")
 184  	}
 185  	if resp.AccessKeyId == "" || resp.AccessKeySecret == "" || resp.SecurityToken == "" || resp.Expiration == "" {
 186  		return fmt.Errorf("refresh Ecs sts token err: AccessKeyId: %s, AccessKeySecret: %s, SecurityToken: %s, Expiration: %s", resp.AccessKeyId, resp.AccessKeySecret, resp.SecurityToken, resp.Expiration)
 187  	}
 188  
 189  	expirationTime, err := time.Parse("2006-01-02T15:04:05Z", resp.Expiration)
 190  	e.lastUpdateTimestamp = time.Now().Unix()
 191  	e.credentialExpiration = int(expirationTime.Unix() - time.Now().Unix())
 192  	e.sessionCredential = &sessionCredential{
 193  		AccessKeyId:     resp.AccessKeyId,
 194  		AccessKeySecret: resp.AccessKeySecret,
 195  		SecurityToken:   resp.SecurityToken,
 196  	}
 197  
 198  	return
 199  }
 200  
 201  func (e *ECSRAMRoleCredentialsProvider) needToRefresh() (needToRefresh bool) {
 202  	needToRefresh = time.Now().Unix() >= e.staleTime
 203  	return
 204  }
 205