github.com/aliyun/credentials-go@v1.4.7/credentials/ecs_ram_role_credentials_provider.go (about)

     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  }