| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- package providers
- import (
- "encoding/json"
- "errors"
- "fmt"
- "net/http"
- "net/url"
- "os"
- "strconv"
- "strings"
- "time"
- httputil "github.com/aliyun/credentials-go/credentials/internal/http"
- "github.com/aliyun/credentials-go/credentials/internal/utils"
- )
- type assumedRoleUser struct {
- }
- type credentials struct {
- SecurityToken *string `json:"SecurityToken"`
- Expiration *string `json:"Expiration"`
- AccessKeySecret *string `json:"AccessKeySecret"`
- AccessKeyId *string `json:"AccessKeyId"`
- }
- type assumeRoleResponse struct {
- RequestID *string `json:"RequestId"`
- AssumedRoleUser *assumedRoleUser `json:"AssumedRoleUser"`
- Credentials *credentials `json:"Credentials"`
- }
- type sessionCredentials struct {
- AccessKeyId string
- AccessKeySecret string
- SecurityToken string
- Expiration string
- }
- type HttpOptions struct {
- Proxy string
- // Connection timeout, in milliseconds.
- ConnectTimeout int
- // Read timeout, in milliseconds.
- ReadTimeout int
- }
- type RAMRoleARNCredentialsProvider struct {
- // for previous credentials
- accessKeyId string
- accessKeySecret string
- securityToken string
- credentialsProvider CredentialsProvider
- roleArn string
- roleSessionName string
- durationSeconds int
- policy string
- externalId string
- // for sts endpoint
- stsRegionId string
- enableVpc bool
- stsEndpoint string
- // for http options
- httpOptions *HttpOptions
- // inner
- expirationTimestamp int64
- lastUpdateTimestamp int64
- previousProviderName string
- sessionCredentials *sessionCredentials
- }
- type RAMRoleARNCredentialsProviderBuilder struct {
- provider *RAMRoleARNCredentialsProvider
- }
- func NewRAMRoleARNCredentialsProviderBuilder() *RAMRoleARNCredentialsProviderBuilder {
- return &RAMRoleARNCredentialsProviderBuilder{
- provider: &RAMRoleARNCredentialsProvider{},
- }
- }
- func (builder *RAMRoleARNCredentialsProviderBuilder) WithAccessKeyId(accessKeyId string) *RAMRoleARNCredentialsProviderBuilder {
- builder.provider.accessKeyId = accessKeyId
- return builder
- }
- func (builder *RAMRoleARNCredentialsProviderBuilder) WithAccessKeySecret(accessKeySecret string) *RAMRoleARNCredentialsProviderBuilder {
- builder.provider.accessKeySecret = accessKeySecret
- return builder
- }
- func (builder *RAMRoleARNCredentialsProviderBuilder) WithSecurityToken(securityToken string) *RAMRoleARNCredentialsProviderBuilder {
- builder.provider.securityToken = securityToken
- return builder
- }
- func (builder *RAMRoleARNCredentialsProviderBuilder) WithCredentialsProvider(credentialsProvider CredentialsProvider) *RAMRoleARNCredentialsProviderBuilder {
- builder.provider.credentialsProvider = credentialsProvider
- return builder
- }
- func (builder *RAMRoleARNCredentialsProviderBuilder) WithRoleArn(roleArn string) *RAMRoleARNCredentialsProviderBuilder {
- builder.provider.roleArn = roleArn
- return builder
- }
- func (builder *RAMRoleARNCredentialsProviderBuilder) WithStsRegionId(regionId string) *RAMRoleARNCredentialsProviderBuilder {
- builder.provider.stsRegionId = regionId
- return builder
- }
- func (builder *RAMRoleARNCredentialsProviderBuilder) WithEnableVpc(enableVpc bool) *RAMRoleARNCredentialsProviderBuilder {
- builder.provider.enableVpc = enableVpc
- return builder
- }
- func (builder *RAMRoleARNCredentialsProviderBuilder) WithStsEndpoint(endpoint string) *RAMRoleARNCredentialsProviderBuilder {
- builder.provider.stsEndpoint = endpoint
- return builder
- }
- func (builder *RAMRoleARNCredentialsProviderBuilder) WithRoleSessionName(roleSessionName string) *RAMRoleARNCredentialsProviderBuilder {
- builder.provider.roleSessionName = roleSessionName
- return builder
- }
- func (builder *RAMRoleARNCredentialsProviderBuilder) WithPolicy(policy string) *RAMRoleARNCredentialsProviderBuilder {
- builder.provider.policy = policy
- return builder
- }
- func (builder *RAMRoleARNCredentialsProviderBuilder) WithExternalId(externalId string) *RAMRoleARNCredentialsProviderBuilder {
- builder.provider.externalId = externalId
- return builder
- }
- func (builder *RAMRoleARNCredentialsProviderBuilder) WithDurationSeconds(durationSeconds int) *RAMRoleARNCredentialsProviderBuilder {
- builder.provider.durationSeconds = durationSeconds
- return builder
- }
- func (builder *RAMRoleARNCredentialsProviderBuilder) WithHttpOptions(httpOptions *HttpOptions) *RAMRoleARNCredentialsProviderBuilder {
- builder.provider.httpOptions = httpOptions
- return builder
- }
- func (builder *RAMRoleARNCredentialsProviderBuilder) Build() (provider *RAMRoleARNCredentialsProvider, err error) {
- if builder.provider.credentialsProvider == nil {
- if builder.provider.accessKeyId != "" && builder.provider.accessKeySecret != "" && builder.provider.securityToken != "" {
- builder.provider.credentialsProvider, err = NewStaticSTSCredentialsProviderBuilder().
- WithAccessKeyId(builder.provider.accessKeyId).
- WithAccessKeySecret(builder.provider.accessKeySecret).
- WithSecurityToken(builder.provider.securityToken).
- Build()
- if err != nil {
- return
- }
- } else if builder.provider.accessKeyId != "" && builder.provider.accessKeySecret != "" {
- builder.provider.credentialsProvider, err = NewStaticAKCredentialsProviderBuilder().
- WithAccessKeyId(builder.provider.accessKeyId).
- WithAccessKeySecret(builder.provider.accessKeySecret).
- Build()
- if err != nil {
- return
- }
- } else {
- err = errors.New("must specify a previous credentials provider to assume role")
- return
- }
- }
- if builder.provider.roleArn == "" {
- if roleArn := os.Getenv("ALIBABA_CLOUD_ROLE_ARN"); roleArn != "" {
- builder.provider.roleArn = roleArn
- } else {
- err = errors.New("the RoleArn is empty")
- return
- }
- }
- if builder.provider.roleSessionName == "" {
- if roleSessionName := os.Getenv("ALIBABA_CLOUD_ROLE_SESSION_NAME"); roleSessionName != "" {
- builder.provider.roleSessionName = roleSessionName
- } else {
- builder.provider.roleSessionName = "credentials-go-" + strconv.FormatInt(time.Now().UnixNano()/1000, 10)
- }
- }
- // duration seconds
- if builder.provider.durationSeconds == 0 {
- // default to 3600
- builder.provider.durationSeconds = 3600
- }
- if builder.provider.durationSeconds < 900 {
- err = errors.New("session duration should be in the range of 900s - max session duration")
- return
- }
- // sts endpoint
- if builder.provider.stsEndpoint == "" {
- if !builder.provider.enableVpc {
- builder.provider.enableVpc = strings.ToLower(os.Getenv("ALIBABA_CLOUD_VPC_ENDPOINT_ENABLED")) == "true"
- }
- prefix := "sts"
- if builder.provider.enableVpc {
- prefix = "sts-vpc"
- }
- if builder.provider.stsRegionId != "" {
- builder.provider.stsEndpoint = fmt.Sprintf("%s.%s.aliyuncs.com", prefix, builder.provider.stsRegionId)
- } else if region := os.Getenv("ALIBABA_CLOUD_STS_REGION"); region != "" {
- builder.provider.stsEndpoint = fmt.Sprintf("%s.%s.aliyuncs.com", prefix, region)
- } else {
- builder.provider.stsEndpoint = "sts.aliyuncs.com"
- }
- }
- provider = builder.provider
- return
- }
- func (provider *RAMRoleARNCredentialsProvider) getCredentials(cc *Credentials) (session *sessionCredentials, err error) {
- method := "POST"
- req := &httputil.Request{
- Method: method,
- Protocol: "https",
- Host: provider.stsEndpoint,
- Headers: map[string]string{},
- }
- queries := make(map[string]string)
- queries["Version"] = "2015-04-01"
- queries["Action"] = "AssumeRole"
- queries["Format"] = "JSON"
- queries["Timestamp"] = utils.GetTimeInFormatISO8601()
- queries["SignatureMethod"] = "HMAC-SHA1"
- queries["SignatureVersion"] = "1.0"
- queries["SignatureNonce"] = utils.GetNonce()
- queries["AccessKeyId"] = cc.AccessKeyId
- if cc.SecurityToken != "" {
- queries["SecurityToken"] = cc.SecurityToken
- }
- bodyForm := make(map[string]string)
- bodyForm["RoleArn"] = provider.roleArn
- if provider.policy != "" {
- bodyForm["Policy"] = provider.policy
- }
- if provider.externalId != "" {
- bodyForm["ExternalId"] = provider.externalId
- }
- bodyForm["RoleSessionName"] = provider.roleSessionName
- bodyForm["DurationSeconds"] = strconv.Itoa(provider.durationSeconds)
- req.Form = bodyForm
- // caculate signature
- signParams := make(map[string]string)
- for key, value := range queries {
- signParams[key] = value
- }
- for key, value := range bodyForm {
- signParams[key] = value
- }
- stringToSign := utils.GetURLFormedMap(signParams)
- stringToSign = strings.Replace(stringToSign, "+", "%20", -1)
- stringToSign = strings.Replace(stringToSign, "*", "%2A", -1)
- stringToSign = strings.Replace(stringToSign, "%7E", "~", -1)
- stringToSign = url.QueryEscape(stringToSign)
- stringToSign = method + "&%2F&" + stringToSign
- secret := cc.AccessKeySecret + "&"
- queries["Signature"] = utils.ShaHmac1(stringToSign, secret)
- req.Queries = queries
- // set headers
- req.Headers["Accept-Encoding"] = "identity"
- req.Headers["Content-Type"] = "application/x-www-form-urlencoded"
- req.Headers["x-acs-credentials-provider"] = cc.ProviderName
- connectTimeout := 5 * time.Second
- readTimeout := 10 * time.Second
- if provider.httpOptions != nil && provider.httpOptions.ConnectTimeout > 0 {
- connectTimeout = time.Duration(provider.httpOptions.ConnectTimeout) * time.Millisecond
- }
- if provider.httpOptions != nil && provider.httpOptions.ReadTimeout > 0 {
- readTimeout = time.Duration(provider.httpOptions.ReadTimeout) * time.Millisecond
- }
- if provider.httpOptions != nil && provider.httpOptions.Proxy != "" {
- req.Proxy = provider.httpOptions.Proxy
- }
- req.ConnectTimeout = connectTimeout
- req.ReadTimeout = readTimeout
- res, err := httpDo(req)
- if err != nil {
- return
- }
- if res.StatusCode != http.StatusOK {
- err = errors.New("refresh session token failed: " + string(res.Body))
- return
- }
- var data assumeRoleResponse
- err = json.Unmarshal(res.Body, &data)
- if err != nil {
- err = fmt.Errorf("refresh RoleArn sts token err, json.Unmarshal fail: %s", err.Error())
- return
- }
- if data.Credentials == nil {
- err = fmt.Errorf("refresh RoleArn sts token err, fail to get credentials")
- return
- }
- if data.Credentials.AccessKeyId == nil || data.Credentials.AccessKeySecret == nil || data.Credentials.SecurityToken == nil {
- err = fmt.Errorf("refresh RoleArn sts token err, fail to get credentials")
- return
- }
- session = &sessionCredentials{
- AccessKeyId: *data.Credentials.AccessKeyId,
- AccessKeySecret: *data.Credentials.AccessKeySecret,
- SecurityToken: *data.Credentials.SecurityToken,
- Expiration: *data.Credentials.Expiration,
- }
- return
- }
- func (provider *RAMRoleARNCredentialsProvider) needUpdateCredential() (result bool) {
- if provider.expirationTimestamp == 0 {
- return true
- }
- return provider.expirationTimestamp-time.Now().Unix() <= 180
- }
- func (provider *RAMRoleARNCredentialsProvider) GetCredentials() (cc *Credentials, err error) {
- if provider.sessionCredentials == nil || provider.needUpdateCredential() {
- // 获取前置凭证
- previousCredentials, err1 := provider.credentialsProvider.GetCredentials()
- if err1 != nil {
- return nil, err1
- }
- sessionCredentials, err2 := provider.getCredentials(previousCredentials)
- if err2 != nil {
- return nil, err2
- }
- expirationTime, err := time.Parse("2006-01-02T15:04:05Z", sessionCredentials.Expiration)
- if err != nil {
- return nil, err
- }
- provider.expirationTimestamp = expirationTime.Unix()
- provider.lastUpdateTimestamp = time.Now().Unix()
- provider.previousProviderName = previousCredentials.ProviderName
- provider.sessionCredentials = sessionCredentials
- }
- cc = &Credentials{
- AccessKeyId: provider.sessionCredentials.AccessKeyId,
- AccessKeySecret: provider.sessionCredentials.AccessKeySecret,
- SecurityToken: provider.sessionCredentials.SecurityToken,
- ProviderName: fmt.Sprintf("%s/%s", provider.GetProviderName(), provider.previousProviderName),
- }
- return
- }
- func (provider *RAMRoleARNCredentialsProvider) GetProviderName() string {
- return "ram_role_arn"
- }
|