credentials/ecs_ram_role_credentials_provider.go (170 lines of code) (raw):
package credentials
import (
"encoding/json"
"fmt"
"strconv"
"time"
"github.com/alibabacloud-go/tea/tea"
"github.com/aliyun/credentials-go/credentials/internal/utils"
"github.com/aliyun/credentials-go/credentials/request"
)
var securityCredURL = "http://100.100.100.200/latest/meta-data/ram/security-credentials/"
var securityCredTokenURL = "http://100.100.100.200/latest/api/token"
const defaultMetadataTokenDuration = int(21600)
// ECSRAMRoleCredentialsProvider is a kind of credentials provider
type ECSRAMRoleCredentialsProvider struct {
*credentialUpdater
RoleName string
EnableIMDSv2 bool
MetadataTokenDuration int
sessionCredential *sessionCredential
runtime *utils.Runtime
metadataToken string
staleTime int64
}
type ecsRAMRoleResponse struct {
Code string `json:"Code" xml:"Code"`
AccessKeyId string `json:"AccessKeyId" xml:"AccessKeyId"`
AccessKeySecret string `json:"AccessKeySecret" xml:"AccessKeySecret"`
SecurityToken string `json:"SecurityToken" xml:"SecurityToken"`
Expiration string `json:"Expiration" xml:"Expiration"`
}
func newEcsRAMRoleCredentialWithEnableIMDSv2(roleName string, enableIMDSv2 bool, metadataTokenDuration int, inAdvanceScale float64, runtime *utils.Runtime) *ECSRAMRoleCredentialsProvider {
credentialUpdater := new(credentialUpdater)
if inAdvanceScale < 1 && inAdvanceScale > 0 {
credentialUpdater.inAdvanceScale = inAdvanceScale
}
return &ECSRAMRoleCredentialsProvider{
RoleName: roleName,
EnableIMDSv2: enableIMDSv2,
MetadataTokenDuration: metadataTokenDuration,
credentialUpdater: credentialUpdater,
runtime: runtime,
}
}
func (e *ECSRAMRoleCredentialsProvider) GetCredential() (credentials *CredentialModel, err error) {
if e.sessionCredential == nil || e.needUpdateCredential() {
err = e.updateCredential()
if err != nil {
if e.credentialExpiration > (int(time.Now().Unix()) - int(e.lastUpdateTimestamp)) {
// 虽然有错误,但是已有的 credentials 还有效
} else {
return
}
}
}
credentials = &CredentialModel{
AccessKeyId: tea.String(e.sessionCredential.AccessKeyId),
AccessKeySecret: tea.String(e.sessionCredential.AccessKeySecret),
SecurityToken: tea.String(e.sessionCredential.SecurityToken),
Type: tea.String("ecs_ram_role"),
}
return
}
// GetAccessKeyId reutrns EcsRAMRoleCredential's AccessKeyId
// if AccessKeyId is not exist or out of date, the function will update it.
func (e *ECSRAMRoleCredentialsProvider) GetAccessKeyId() (accessKeyId *string, err error) {
c, err := e.GetCredential()
if err != nil {
return
}
accessKeyId = c.AccessKeyId
return
}
// GetAccessSecret reutrns EcsRAMRoleCredential's AccessKeySecret
// if AccessKeySecret is not exist or out of date, the function will update it.
func (e *ECSRAMRoleCredentialsProvider) GetAccessKeySecret() (accessKeySecret *string, err error) {
c, err := e.GetCredential()
if err != nil {
return
}
accessKeySecret = c.AccessKeySecret
return
}
// GetSecurityToken reutrns EcsRAMRoleCredential's SecurityToken
// if SecurityToken is not exist or out of date, the function will update it.
func (e *ECSRAMRoleCredentialsProvider) GetSecurityToken() (securityToken *string, err error) {
c, err := e.GetCredential()
if err != nil {
return
}
securityToken = c.SecurityToken
return
}
// GetBearerToken is useless for EcsRAMRoleCredential
func (e *ECSRAMRoleCredentialsProvider) GetBearerToken() *string {
return tea.String("")
}
// GetType reutrns EcsRAMRoleCredential's type
func (e *ECSRAMRoleCredentialsProvider) GetType() *string {
return tea.String("ecs_ram_role")
}
func getRoleName() (string, error) {
runtime := utils.NewRuntime(1, 1, "", "")
request := request.NewCommonRequest()
request.URL = securityCredURL
request.Method = "GET"
content, err := doAction(request, runtime)
if err != nil {
return "", err
}
return string(content), nil
}
func (e *ECSRAMRoleCredentialsProvider) getMetadataToken() (err error) {
if e.needToRefresh() {
if e.MetadataTokenDuration <= 0 {
e.MetadataTokenDuration = defaultMetadataTokenDuration
}
tmpTime := time.Now().Unix() + int64(e.MetadataTokenDuration*1000)
request := request.NewCommonRequest()
request.URL = securityCredTokenURL
request.Method = "PUT"
request.Headers["X-aliyun-ecs-metadata-token-ttl-seconds"] = strconv.Itoa(e.MetadataTokenDuration)
content, err := doAction(request, e.runtime)
if err != nil {
return err
}
e.staleTime = tmpTime
e.metadataToken = string(content)
}
return
}
func (e *ECSRAMRoleCredentialsProvider) updateCredential() (err error) {
if e.runtime == nil {
e.runtime = new(utils.Runtime)
}
request := request.NewCommonRequest()
if e.RoleName == "" {
e.RoleName, err = getRoleName()
if err != nil {
return fmt.Errorf("refresh Ecs sts token err: %s", err.Error())
}
}
if e.EnableIMDSv2 {
err = e.getMetadataToken()
if err != nil {
return fmt.Errorf("failed to get token from ECS Metadata Service: %s", err.Error())
}
request.Headers["X-aliyun-ecs-metadata-token"] = e.metadataToken
}
request.URL = securityCredURL + e.RoleName
request.Method = "GET"
content, err := doAction(request, e.runtime)
if err != nil {
return fmt.Errorf("refresh Ecs sts token err: %s", err.Error())
}
var resp *ecsRAMRoleResponse
err = json.Unmarshal(content, &resp)
if err != nil {
return fmt.Errorf("refresh Ecs sts token err: Json Unmarshal fail: %s", err.Error())
}
if resp.Code != "Success" {
return fmt.Errorf("refresh Ecs sts token err: Code is not Success")
}
if resp.AccessKeyId == "" || resp.AccessKeySecret == "" || resp.SecurityToken == "" || resp.Expiration == "" {
return fmt.Errorf("refresh Ecs sts token err: AccessKeyId: %s, AccessKeySecret: %s, SecurityToken: %s, Expiration: %s", resp.AccessKeyId, resp.AccessKeySecret, resp.SecurityToken, resp.Expiration)
}
expirationTime, err := time.Parse("2006-01-02T15:04:05Z", resp.Expiration)
e.lastUpdateTimestamp = time.Now().Unix()
e.credentialExpiration = int(expirationTime.Unix() - time.Now().Unix())
e.sessionCredential = &sessionCredential{
AccessKeyId: resp.AccessKeyId,
AccessKeySecret: resp.AccessKeySecret,
SecurityToken: resp.SecurityToken,
}
return
}
func (e *ECSRAMRoleCredentialsProvider) needToRefresh() (needToRefresh bool) {
needToRefresh = time.Now().Unix() >= e.staleTime
return
}