ram_role_arn.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. package providers
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "net/http"
  7. "net/url"
  8. "os"
  9. "strconv"
  10. "strings"
  11. "time"
  12. httputil "github.com/aliyun/credentials-go/credentials/internal/http"
  13. "github.com/aliyun/credentials-go/credentials/internal/utils"
  14. )
  15. type assumedRoleUser struct {
  16. }
  17. type credentials struct {
  18. SecurityToken *string `json:"SecurityToken"`
  19. Expiration *string `json:"Expiration"`
  20. AccessKeySecret *string `json:"AccessKeySecret"`
  21. AccessKeyId *string `json:"AccessKeyId"`
  22. }
  23. type assumeRoleResponse struct {
  24. RequestID *string `json:"RequestId"`
  25. AssumedRoleUser *assumedRoleUser `json:"AssumedRoleUser"`
  26. Credentials *credentials `json:"Credentials"`
  27. }
  28. type sessionCredentials struct {
  29. AccessKeyId string
  30. AccessKeySecret string
  31. SecurityToken string
  32. Expiration string
  33. }
  34. type HttpOptions struct {
  35. Proxy string
  36. // Connection timeout, in milliseconds.
  37. ConnectTimeout int
  38. // Read timeout, in milliseconds.
  39. ReadTimeout int
  40. }
  41. type RAMRoleARNCredentialsProvider struct {
  42. // for previous credentials
  43. accessKeyId string
  44. accessKeySecret string
  45. securityToken string
  46. credentialsProvider CredentialsProvider
  47. roleArn string
  48. roleSessionName string
  49. durationSeconds int
  50. policy string
  51. externalId string
  52. // for sts endpoint
  53. stsRegionId string
  54. enableVpc bool
  55. stsEndpoint string
  56. // for http options
  57. httpOptions *HttpOptions
  58. // inner
  59. expirationTimestamp int64
  60. lastUpdateTimestamp int64
  61. previousProviderName string
  62. sessionCredentials *sessionCredentials
  63. }
  64. type RAMRoleARNCredentialsProviderBuilder struct {
  65. provider *RAMRoleARNCredentialsProvider
  66. }
  67. func NewRAMRoleARNCredentialsProviderBuilder() *RAMRoleARNCredentialsProviderBuilder {
  68. return &RAMRoleARNCredentialsProviderBuilder{
  69. provider: &RAMRoleARNCredentialsProvider{},
  70. }
  71. }
  72. func (builder *RAMRoleARNCredentialsProviderBuilder) WithAccessKeyId(accessKeyId string) *RAMRoleARNCredentialsProviderBuilder {
  73. builder.provider.accessKeyId = accessKeyId
  74. return builder
  75. }
  76. func (builder *RAMRoleARNCredentialsProviderBuilder) WithAccessKeySecret(accessKeySecret string) *RAMRoleARNCredentialsProviderBuilder {
  77. builder.provider.accessKeySecret = accessKeySecret
  78. return builder
  79. }
  80. func (builder *RAMRoleARNCredentialsProviderBuilder) WithSecurityToken(securityToken string) *RAMRoleARNCredentialsProviderBuilder {
  81. builder.provider.securityToken = securityToken
  82. return builder
  83. }
  84. func (builder *RAMRoleARNCredentialsProviderBuilder) WithCredentialsProvider(credentialsProvider CredentialsProvider) *RAMRoleARNCredentialsProviderBuilder {
  85. builder.provider.credentialsProvider = credentialsProvider
  86. return builder
  87. }
  88. func (builder *RAMRoleARNCredentialsProviderBuilder) WithRoleArn(roleArn string) *RAMRoleARNCredentialsProviderBuilder {
  89. builder.provider.roleArn = roleArn
  90. return builder
  91. }
  92. func (builder *RAMRoleARNCredentialsProviderBuilder) WithStsRegionId(regionId string) *RAMRoleARNCredentialsProviderBuilder {
  93. builder.provider.stsRegionId = regionId
  94. return builder
  95. }
  96. func (builder *RAMRoleARNCredentialsProviderBuilder) WithEnableVpc(enableVpc bool) *RAMRoleARNCredentialsProviderBuilder {
  97. builder.provider.enableVpc = enableVpc
  98. return builder
  99. }
  100. func (builder *RAMRoleARNCredentialsProviderBuilder) WithStsEndpoint(endpoint string) *RAMRoleARNCredentialsProviderBuilder {
  101. builder.provider.stsEndpoint = endpoint
  102. return builder
  103. }
  104. func (builder *RAMRoleARNCredentialsProviderBuilder) WithRoleSessionName(roleSessionName string) *RAMRoleARNCredentialsProviderBuilder {
  105. builder.provider.roleSessionName = roleSessionName
  106. return builder
  107. }
  108. func (builder *RAMRoleARNCredentialsProviderBuilder) WithPolicy(policy string) *RAMRoleARNCredentialsProviderBuilder {
  109. builder.provider.policy = policy
  110. return builder
  111. }
  112. func (builder *RAMRoleARNCredentialsProviderBuilder) WithExternalId(externalId string) *RAMRoleARNCredentialsProviderBuilder {
  113. builder.provider.externalId = externalId
  114. return builder
  115. }
  116. func (builder *RAMRoleARNCredentialsProviderBuilder) WithDurationSeconds(durationSeconds int) *RAMRoleARNCredentialsProviderBuilder {
  117. builder.provider.durationSeconds = durationSeconds
  118. return builder
  119. }
  120. func (builder *RAMRoleARNCredentialsProviderBuilder) WithHttpOptions(httpOptions *HttpOptions) *RAMRoleARNCredentialsProviderBuilder {
  121. builder.provider.httpOptions = httpOptions
  122. return builder
  123. }
  124. func (builder *RAMRoleARNCredentialsProviderBuilder) Build() (provider *RAMRoleARNCredentialsProvider, err error) {
  125. if builder.provider.credentialsProvider == nil {
  126. if builder.provider.accessKeyId != "" && builder.provider.accessKeySecret != "" && builder.provider.securityToken != "" {
  127. builder.provider.credentialsProvider, err = NewStaticSTSCredentialsProviderBuilder().
  128. WithAccessKeyId(builder.provider.accessKeyId).
  129. WithAccessKeySecret(builder.provider.accessKeySecret).
  130. WithSecurityToken(builder.provider.securityToken).
  131. Build()
  132. if err != nil {
  133. return
  134. }
  135. } else if builder.provider.accessKeyId != "" && builder.provider.accessKeySecret != "" {
  136. builder.provider.credentialsProvider, err = NewStaticAKCredentialsProviderBuilder().
  137. WithAccessKeyId(builder.provider.accessKeyId).
  138. WithAccessKeySecret(builder.provider.accessKeySecret).
  139. Build()
  140. if err != nil {
  141. return
  142. }
  143. } else {
  144. err = errors.New("must specify a previous credentials provider to assume role")
  145. return
  146. }
  147. }
  148. if builder.provider.roleArn == "" {
  149. if roleArn := os.Getenv("ALIBABA_CLOUD_ROLE_ARN"); roleArn != "" {
  150. builder.provider.roleArn = roleArn
  151. } else {
  152. err = errors.New("the RoleArn is empty")
  153. return
  154. }
  155. }
  156. if builder.provider.roleSessionName == "" {
  157. if roleSessionName := os.Getenv("ALIBABA_CLOUD_ROLE_SESSION_NAME"); roleSessionName != "" {
  158. builder.provider.roleSessionName = roleSessionName
  159. } else {
  160. builder.provider.roleSessionName = "credentials-go-" + strconv.FormatInt(time.Now().UnixNano()/1000, 10)
  161. }
  162. }
  163. // duration seconds
  164. if builder.provider.durationSeconds == 0 {
  165. // default to 3600
  166. builder.provider.durationSeconds = 3600
  167. }
  168. if builder.provider.durationSeconds < 900 {
  169. err = errors.New("session duration should be in the range of 900s - max session duration")
  170. return
  171. }
  172. // sts endpoint
  173. if builder.provider.stsEndpoint == "" {
  174. if !builder.provider.enableVpc {
  175. builder.provider.enableVpc = strings.ToLower(os.Getenv("ALIBABA_CLOUD_VPC_ENDPOINT_ENABLED")) == "true"
  176. }
  177. prefix := "sts"
  178. if builder.provider.enableVpc {
  179. prefix = "sts-vpc"
  180. }
  181. if builder.provider.stsRegionId != "" {
  182. builder.provider.stsEndpoint = fmt.Sprintf("%s.%s.aliyuncs.com", prefix, builder.provider.stsRegionId)
  183. } else if region := os.Getenv("ALIBABA_CLOUD_STS_REGION"); region != "" {
  184. builder.provider.stsEndpoint = fmt.Sprintf("%s.%s.aliyuncs.com", prefix, region)
  185. } else {
  186. builder.provider.stsEndpoint = "sts.aliyuncs.com"
  187. }
  188. }
  189. provider = builder.provider
  190. return
  191. }
  192. func (provider *RAMRoleARNCredentialsProvider) getCredentials(cc *Credentials) (session *sessionCredentials, err error) {
  193. method := "POST"
  194. req := &httputil.Request{
  195. Method: method,
  196. Protocol: "https",
  197. Host: provider.stsEndpoint,
  198. Headers: map[string]string{},
  199. }
  200. queries := make(map[string]string)
  201. queries["Version"] = "2015-04-01"
  202. queries["Action"] = "AssumeRole"
  203. queries["Format"] = "JSON"
  204. queries["Timestamp"] = utils.GetTimeInFormatISO8601()
  205. queries["SignatureMethod"] = "HMAC-SHA1"
  206. queries["SignatureVersion"] = "1.0"
  207. queries["SignatureNonce"] = utils.GetNonce()
  208. queries["AccessKeyId"] = cc.AccessKeyId
  209. if cc.SecurityToken != "" {
  210. queries["SecurityToken"] = cc.SecurityToken
  211. }
  212. bodyForm := make(map[string]string)
  213. bodyForm["RoleArn"] = provider.roleArn
  214. if provider.policy != "" {
  215. bodyForm["Policy"] = provider.policy
  216. }
  217. if provider.externalId != "" {
  218. bodyForm["ExternalId"] = provider.externalId
  219. }
  220. bodyForm["RoleSessionName"] = provider.roleSessionName
  221. bodyForm["DurationSeconds"] = strconv.Itoa(provider.durationSeconds)
  222. req.Form = bodyForm
  223. // caculate signature
  224. signParams := make(map[string]string)
  225. for key, value := range queries {
  226. signParams[key] = value
  227. }
  228. for key, value := range bodyForm {
  229. signParams[key] = value
  230. }
  231. stringToSign := utils.GetURLFormedMap(signParams)
  232. stringToSign = strings.Replace(stringToSign, "+", "%20", -1)
  233. stringToSign = strings.Replace(stringToSign, "*", "%2A", -1)
  234. stringToSign = strings.Replace(stringToSign, "%7E", "~", -1)
  235. stringToSign = url.QueryEscape(stringToSign)
  236. stringToSign = method + "&%2F&" + stringToSign
  237. secret := cc.AccessKeySecret + "&"
  238. queries["Signature"] = utils.ShaHmac1(stringToSign, secret)
  239. req.Queries = queries
  240. // set headers
  241. req.Headers["Accept-Encoding"] = "identity"
  242. req.Headers["Content-Type"] = "application/x-www-form-urlencoded"
  243. req.Headers["x-acs-credentials-provider"] = cc.ProviderName
  244. connectTimeout := 5 * time.Second
  245. readTimeout := 10 * time.Second
  246. if provider.httpOptions != nil && provider.httpOptions.ConnectTimeout > 0 {
  247. connectTimeout = time.Duration(provider.httpOptions.ConnectTimeout) * time.Millisecond
  248. }
  249. if provider.httpOptions != nil && provider.httpOptions.ReadTimeout > 0 {
  250. readTimeout = time.Duration(provider.httpOptions.ReadTimeout) * time.Millisecond
  251. }
  252. if provider.httpOptions != nil && provider.httpOptions.Proxy != "" {
  253. req.Proxy = provider.httpOptions.Proxy
  254. }
  255. req.ConnectTimeout = connectTimeout
  256. req.ReadTimeout = readTimeout
  257. res, err := httpDo(req)
  258. if err != nil {
  259. return
  260. }
  261. if res.StatusCode != http.StatusOK {
  262. err = errors.New("refresh session token failed: " + string(res.Body))
  263. return
  264. }
  265. var data assumeRoleResponse
  266. err = json.Unmarshal(res.Body, &data)
  267. if err != nil {
  268. err = fmt.Errorf("refresh RoleArn sts token err, json.Unmarshal fail: %s", err.Error())
  269. return
  270. }
  271. if data.Credentials == nil {
  272. err = fmt.Errorf("refresh RoleArn sts token err, fail to get credentials")
  273. return
  274. }
  275. if data.Credentials.AccessKeyId == nil || data.Credentials.AccessKeySecret == nil || data.Credentials.SecurityToken == nil {
  276. err = fmt.Errorf("refresh RoleArn sts token err, fail to get credentials")
  277. return
  278. }
  279. session = &sessionCredentials{
  280. AccessKeyId: *data.Credentials.AccessKeyId,
  281. AccessKeySecret: *data.Credentials.AccessKeySecret,
  282. SecurityToken: *data.Credentials.SecurityToken,
  283. Expiration: *data.Credentials.Expiration,
  284. }
  285. return
  286. }
  287. func (provider *RAMRoleARNCredentialsProvider) needUpdateCredential() (result bool) {
  288. if provider.expirationTimestamp == 0 {
  289. return true
  290. }
  291. return provider.expirationTimestamp-time.Now().Unix() <= 180
  292. }
  293. func (provider *RAMRoleARNCredentialsProvider) GetCredentials() (cc *Credentials, err error) {
  294. if provider.sessionCredentials == nil || provider.needUpdateCredential() {
  295. // 获取前置凭证
  296. previousCredentials, err1 := provider.credentialsProvider.GetCredentials()
  297. if err1 != nil {
  298. return nil, err1
  299. }
  300. sessionCredentials, err2 := provider.getCredentials(previousCredentials)
  301. if err2 != nil {
  302. return nil, err2
  303. }
  304. expirationTime, err := time.Parse("2006-01-02T15:04:05Z", sessionCredentials.Expiration)
  305. if err != nil {
  306. return nil, err
  307. }
  308. provider.expirationTimestamp = expirationTime.Unix()
  309. provider.lastUpdateTimestamp = time.Now().Unix()
  310. provider.previousProviderName = previousCredentials.ProviderName
  311. provider.sessionCredentials = sessionCredentials
  312. }
  313. cc = &Credentials{
  314. AccessKeyId: provider.sessionCredentials.AccessKeyId,
  315. AccessKeySecret: provider.sessionCredentials.AccessKeySecret,
  316. SecurityToken: provider.sessionCredentials.SecurityToken,
  317. ProviderName: fmt.Sprintf("%s/%s", provider.GetProviderName(), provider.previousProviderName),
  318. }
  319. return
  320. }
  321. func (provider *RAMRoleARNCredentialsProvider) GetProviderName() string {
  322. return "ram_role_arn"
  323. }