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