web_identity_provider.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. package stscreds
  2. import (
  3. "context"
  4. "fmt"
  5. "io/ioutil"
  6. "strconv"
  7. "time"
  8. "github.com/aws/aws-sdk-go-v2/aws"
  9. "github.com/aws/aws-sdk-go-v2/aws/retry"
  10. "github.com/aws/aws-sdk-go-v2/internal/sdk"
  11. "github.com/aws/aws-sdk-go-v2/service/sts"
  12. "github.com/aws/aws-sdk-go-v2/service/sts/types"
  13. )
  14. var invalidIdentityTokenExceptionCode = (&types.InvalidIdentityTokenException{}).ErrorCode()
  15. const (
  16. // WebIdentityProviderName is the web identity provider name
  17. WebIdentityProviderName = "WebIdentityCredentials"
  18. )
  19. // AssumeRoleWithWebIdentityAPIClient is a client capable of the STS AssumeRoleWithWebIdentity operation.
  20. type AssumeRoleWithWebIdentityAPIClient interface {
  21. AssumeRoleWithWebIdentity(ctx context.Context, params *sts.AssumeRoleWithWebIdentityInput, optFns ...func(*sts.Options)) (*sts.AssumeRoleWithWebIdentityOutput, error)
  22. }
  23. // WebIdentityRoleProvider is used to retrieve credentials using
  24. // an OIDC token.
  25. type WebIdentityRoleProvider struct {
  26. options WebIdentityRoleOptions
  27. }
  28. // WebIdentityRoleOptions is a structure of configurable options for WebIdentityRoleProvider
  29. type WebIdentityRoleOptions struct {
  30. // Client implementation of the AssumeRoleWithWebIdentity operation. Required
  31. Client AssumeRoleWithWebIdentityAPIClient
  32. // JWT Token Provider. Required
  33. TokenRetriever IdentityTokenRetriever
  34. // IAM Role ARN to assume. Required
  35. RoleARN string
  36. // Session name, if you wish to uniquely identify this session.
  37. RoleSessionName string
  38. // Expiry duration of the STS credentials. STS will assign a default expiry
  39. // duration if this value is unset. This is different from the Duration
  40. // option of AssumeRoleProvider, which automatically assigns 15 minutes if
  41. // Duration is unset.
  42. //
  43. // See the STS AssumeRoleWithWebIdentity API reference guide for more
  44. // information on defaults.
  45. // https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html
  46. Duration time.Duration
  47. // An IAM policy in JSON format that you want to use as an inline session policy.
  48. Policy *string
  49. // The Amazon Resource Names (ARNs) of the IAM managed policies that you
  50. // want to use as managed session policies. The policies must exist in the
  51. // same account as the role.
  52. PolicyARNs []types.PolicyDescriptorType
  53. }
  54. // IdentityTokenRetriever is an interface for retrieving a JWT
  55. type IdentityTokenRetriever interface {
  56. GetIdentityToken() ([]byte, error)
  57. }
  58. // IdentityTokenFile is for retrieving an identity token from the given file name
  59. type IdentityTokenFile string
  60. // GetIdentityToken retrieves the JWT token from the file and returns the contents as a []byte
  61. func (j IdentityTokenFile) GetIdentityToken() ([]byte, error) {
  62. b, err := ioutil.ReadFile(string(j))
  63. if err != nil {
  64. return nil, fmt.Errorf("unable to read file at %s: %v", string(j), err)
  65. }
  66. return b, nil
  67. }
  68. // NewWebIdentityRoleProvider will return a new WebIdentityRoleProvider with the
  69. // provided stsiface.ClientAPI
  70. func NewWebIdentityRoleProvider(client AssumeRoleWithWebIdentityAPIClient, roleARN string, tokenRetriever IdentityTokenRetriever, optFns ...func(*WebIdentityRoleOptions)) *WebIdentityRoleProvider {
  71. o := WebIdentityRoleOptions{
  72. Client: client,
  73. RoleARN: roleARN,
  74. TokenRetriever: tokenRetriever,
  75. }
  76. for _, fn := range optFns {
  77. fn(&o)
  78. }
  79. return &WebIdentityRoleProvider{options: o}
  80. }
  81. // Retrieve will attempt to assume a role from a token which is located at
  82. // 'WebIdentityTokenFilePath' specified destination and if that is empty an
  83. // error will be returned.
  84. func (p *WebIdentityRoleProvider) Retrieve(ctx context.Context) (aws.Credentials, error) {
  85. b, err := p.options.TokenRetriever.GetIdentityToken()
  86. if err != nil {
  87. return aws.Credentials{}, fmt.Errorf("failed to retrieve jwt from provide source, %w", err)
  88. }
  89. sessionName := p.options.RoleSessionName
  90. if len(sessionName) == 0 {
  91. // session name is used to uniquely identify a session. This simply
  92. // uses unix time in nanoseconds to uniquely identify sessions.
  93. sessionName = strconv.FormatInt(sdk.NowTime().UnixNano(), 10)
  94. }
  95. input := &sts.AssumeRoleWithWebIdentityInput{
  96. PolicyArns: p.options.PolicyARNs,
  97. RoleArn: &p.options.RoleARN,
  98. RoleSessionName: &sessionName,
  99. WebIdentityToken: aws.String(string(b)),
  100. }
  101. if p.options.Duration != 0 {
  102. // If set use the value, otherwise STS will assign a default expiration duration.
  103. input.DurationSeconds = aws.Int32(int32(p.options.Duration / time.Second))
  104. }
  105. if p.options.Policy != nil {
  106. input.Policy = p.options.Policy
  107. }
  108. resp, err := p.options.Client.AssumeRoleWithWebIdentity(ctx, input, func(options *sts.Options) {
  109. options.Retryer = retry.AddWithErrorCodes(options.Retryer, invalidIdentityTokenExceptionCode)
  110. })
  111. if err != nil {
  112. return aws.Credentials{}, fmt.Errorf("failed to retrieve credentials, %w", err)
  113. }
  114. // InvalidIdentityToken error is a temporary error that can occur
  115. // when assuming an Role with a JWT web identity token.
  116. value := aws.Credentials{
  117. AccessKeyID: aws.ToString(resp.Credentials.AccessKeyId),
  118. SecretAccessKey: aws.ToString(resp.Credentials.SecretAccessKey),
  119. SessionToken: aws.ToString(resp.Credentials.SessionToken),
  120. Source: WebIdentityProviderName,
  121. CanExpire: true,
  122. Expires: *resp.Credentials.Expiration,
  123. }
  124. return value, nil
  125. }