assume_role_provider.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. // Package stscreds are credential Providers to retrieve STS AWS credentials.
  2. //
  3. // STS provides multiple ways to retrieve credentials which can be used when making
  4. // future AWS service API operation calls.
  5. //
  6. // The SDK will ensure that per instance of credentials.Credentials all requests
  7. // to refresh the credentials will be synchronized. But, the SDK is unable to
  8. // ensure synchronous usage of the AssumeRoleProvider if the value is shared
  9. // between multiple Credentials or service clients.
  10. //
  11. // # Assume Role
  12. //
  13. // To assume an IAM role using STS with the SDK you can create a new Credentials
  14. // with the SDKs's stscreds package.
  15. //
  16. // // Initial credentials loaded from SDK's default credential chain. Such as
  17. // // the environment, shared credentials (~/.aws/credentials), or EC2 Instance
  18. // // Role. These credentials will be used to to make the STS Assume Role API.
  19. // cfg, err := config.LoadDefaultConfig(context.TODO())
  20. // if err != nil {
  21. // panic(err)
  22. // }
  23. //
  24. // // Create the credentials from AssumeRoleProvider to assume the role
  25. // // referenced by the "myRoleARN" ARN.
  26. // stsSvc := sts.NewFromConfig(cfg)
  27. // creds := stscreds.NewAssumeRoleProvider(stsSvc, "myRoleArn")
  28. //
  29. // cfg.Credentials = aws.NewCredentialsCache(creds)
  30. //
  31. // // Create service client value configured for credentials
  32. // // from assumed role.
  33. // svc := s3.NewFromConfig(cfg)
  34. //
  35. // # Assume Role with custom MFA Token provider
  36. //
  37. // To assume an IAM role with a MFA token you can either specify a custom MFA
  38. // token provider or use the SDK's built in StdinTokenProvider that will prompt
  39. // the user for a token code each time the credentials need to to be refreshed.
  40. // Specifying a custom token provider allows you to control where the token
  41. // code is retrieved from, and how it is refreshed.
  42. //
  43. // With a custom token provider, the provider is responsible for refreshing the
  44. // token code when called.
  45. //
  46. // cfg, err := config.LoadDefaultConfig(context.TODO())
  47. // if err != nil {
  48. // panic(err)
  49. // }
  50. //
  51. // staticTokenProvider := func() (string, error) {
  52. // return someTokenCode, nil
  53. // }
  54. //
  55. // // Create the credentials from AssumeRoleProvider to assume the role
  56. // // referenced by the "myRoleARN" ARN using the MFA token code provided.
  57. // creds := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), "myRoleArn", func(o *stscreds.AssumeRoleOptions) {
  58. // o.SerialNumber = aws.String("myTokenSerialNumber")
  59. // o.TokenProvider = staticTokenProvider
  60. // })
  61. //
  62. // cfg.Credentials = aws.NewCredentialsCache(creds)
  63. //
  64. // // Create service client value configured for credentials
  65. // // from assumed role.
  66. // svc := s3.NewFromConfig(cfg)
  67. //
  68. // # Assume Role with MFA Token Provider
  69. //
  70. // To assume an IAM role with MFA for longer running tasks where the credentials
  71. // may need to be refreshed setting the TokenProvider field of AssumeRoleProvider
  72. // will allow the credential provider to prompt for new MFA token code when the
  73. // role's credentials need to be refreshed.
  74. //
  75. // The StdinTokenProvider function is available to prompt on stdin to retrieve
  76. // the MFA token code from the user. You can also implement custom prompts by
  77. // satisfying the TokenProvider function signature.
  78. //
  79. // Using StdinTokenProvider with multiple AssumeRoleProviders, or Credentials will
  80. // have undesirable results as the StdinTokenProvider will not be synchronized. A
  81. // single Credentials with an AssumeRoleProvider can be shared safely.
  82. //
  83. // cfg, err := config.LoadDefaultConfig(context.TODO())
  84. // if err != nil {
  85. // panic(err)
  86. // }
  87. //
  88. // // Create the credentials from AssumeRoleProvider to assume the role
  89. // // referenced by the "myRoleARN" ARN using the MFA token code provided.
  90. // creds := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), "myRoleArn", func(o *stscreds.AssumeRoleOptions) {
  91. // o.SerialNumber = aws.String("myTokenSerialNumber")
  92. // o.TokenProvider = stscreds.StdinTokenProvider
  93. // })
  94. //
  95. // cfg.Credentials = aws.NewCredentialsCache(creds)
  96. //
  97. // // Create service client value configured for credentials
  98. // // from assumed role.
  99. // svc := s3.NewFromConfig(cfg)
  100. package stscreds
  101. import (
  102. "context"
  103. "fmt"
  104. "time"
  105. "github.com/aws/aws-sdk-go-v2/aws"
  106. "github.com/aws/aws-sdk-go-v2/service/sts"
  107. "github.com/aws/aws-sdk-go-v2/service/sts/types"
  108. )
  109. // StdinTokenProvider will prompt on stdout and read from stdin for a string value.
  110. // An error is returned if reading from stdin fails.
  111. //
  112. // Use this function go read MFA tokens from stdin. The function makes no attempt
  113. // to make atomic prompts from stdin across multiple gorouties.
  114. //
  115. // Using StdinTokenProvider with multiple AssumeRoleProviders, or Credentials will
  116. // have undesirable results as the StdinTokenProvider will not be synchronized. A
  117. // single Credentials with an AssumeRoleProvider can be shared safely
  118. //
  119. // Will wait forever until something is provided on the stdin.
  120. func StdinTokenProvider() (string, error) {
  121. var v string
  122. fmt.Printf("Assume Role MFA token code: ")
  123. _, err := fmt.Scanln(&v)
  124. return v, err
  125. }
  126. // ProviderName provides a name of AssumeRole provider
  127. const ProviderName = "AssumeRoleProvider"
  128. // AssumeRoleAPIClient is a client capable of the STS AssumeRole operation.
  129. type AssumeRoleAPIClient interface {
  130. AssumeRole(ctx context.Context, params *sts.AssumeRoleInput, optFns ...func(*sts.Options)) (*sts.AssumeRoleOutput, error)
  131. }
  132. // DefaultDuration is the default amount of time in minutes that the
  133. // credentials will be valid for. This value is only used by AssumeRoleProvider
  134. // for specifying the default expiry duration of an assume role.
  135. //
  136. // Other providers such as WebIdentityRoleProvider do not use this value, and
  137. // instead rely on STS API's default parameter handing to assign a default
  138. // value.
  139. var DefaultDuration = time.Duration(15) * time.Minute
  140. // AssumeRoleProvider retrieves temporary credentials from the STS service, and
  141. // keeps track of their expiration time.
  142. //
  143. // This credential provider will be used by the SDKs default credential change
  144. // when shared configuration is enabled, and the shared config or shared credentials
  145. // file configure assume role. See Session docs for how to do this.
  146. //
  147. // AssumeRoleProvider does not provide any synchronization and it is not safe
  148. // to share this value across multiple Credentials, Sessions, or service clients
  149. // without also sharing the same Credentials instance.
  150. type AssumeRoleProvider struct {
  151. options AssumeRoleOptions
  152. }
  153. // AssumeRoleOptions is the configurable options for AssumeRoleProvider
  154. type AssumeRoleOptions struct {
  155. // Client implementation of the AssumeRole operation. Required
  156. Client AssumeRoleAPIClient
  157. // IAM Role ARN to be assumed. Required
  158. RoleARN string
  159. // Session name, if you wish to uniquely identify this session.
  160. RoleSessionName string
  161. // Expiry duration of the STS credentials. Defaults to 15 minutes if not set.
  162. Duration time.Duration
  163. // Optional ExternalID to pass along, defaults to nil if not set.
  164. ExternalID *string
  165. // The policy plain text must be 2048 bytes or shorter. However, an internal
  166. // conversion compresses it into a packed binary format with a separate limit.
  167. // The PackedPolicySize response element indicates by percentage how close to
  168. // the upper size limit the policy is, with 100% equaling the maximum allowed
  169. // size.
  170. Policy *string
  171. // The ARNs of IAM managed policies you want to use as managed session policies.
  172. // The policies must exist in the same account as the role.
  173. //
  174. // This parameter is optional. You can provide up to 10 managed policy ARNs.
  175. // However, the plain text that you use for both inline and managed session
  176. // policies can't exceed 2,048 characters.
  177. //
  178. // An AWS conversion compresses the passed session policies and session tags
  179. // into a packed binary format that has a separate limit. Your request can fail
  180. // for this limit even if your plain text meets the other requirements. The
  181. // PackedPolicySize response element indicates by percentage how close the policies
  182. // and tags for your request are to the upper size limit.
  183. //
  184. // Passing policies to this operation returns new temporary credentials. The
  185. // resulting session's permissions are the intersection of the role's identity-based
  186. // policy and the session policies. You can use the role's temporary credentials
  187. // in subsequent AWS API calls to access resources in the account that owns
  188. // the role. You cannot use session policies to grant more permissions than
  189. // those allowed by the identity-based policy of the role that is being assumed.
  190. // For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session)
  191. // in the IAM User Guide.
  192. PolicyARNs []types.PolicyDescriptorType
  193. // The identification number of the MFA device that is associated with the user
  194. // who is making the AssumeRole call. Specify this value if the trust policy
  195. // of the role being assumed includes a condition that requires MFA authentication.
  196. // The value is either the serial number for a hardware device (such as GAHT12345678)
  197. // or an Amazon Resource Name (ARN) for a virtual device (such as arn:aws:iam::123456789012:mfa/user).
  198. SerialNumber *string
  199. // The source identity specified by the principal that is calling the AssumeRole
  200. // operation. You can require users to specify a source identity when they assume a
  201. // role. You do this by using the sts:SourceIdentity condition key in a role trust
  202. // policy. You can use source identity information in CloudTrail logs to determine
  203. // who took actions with a role. You can use the aws:SourceIdentity condition key
  204. // to further control access to Amazon Web Services resources based on the value of
  205. // source identity. For more information about using source identity, see Monitor
  206. // and control actions taken with assumed roles
  207. // (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_monitor.html)
  208. // in the IAM User Guide.
  209. SourceIdentity *string
  210. // Async method of providing MFA token code for assuming an IAM role with MFA.
  211. // The value returned by the function will be used as the TokenCode in the Retrieve
  212. // call. See StdinTokenProvider for a provider that prompts and reads from stdin.
  213. //
  214. // This token provider will be called when ever the assumed role's
  215. // credentials need to be refreshed when SerialNumber is set.
  216. TokenProvider func() (string, error)
  217. // A list of session tags that you want to pass. Each session tag consists of a key
  218. // name and an associated value. For more information about session tags, see
  219. // Tagging STS Sessions
  220. // (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) in the
  221. // IAM User Guide. This parameter is optional. You can pass up to 50 session tags.
  222. Tags []types.Tag
  223. // A list of keys for session tags that you want to set as transitive. If you set a
  224. // tag key as transitive, the corresponding key and value passes to subsequent
  225. // sessions in a role chain. For more information, see Chaining Roles with Session
  226. // Tags
  227. // (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_role-chaining)
  228. // in the IAM User Guide. This parameter is optional.
  229. TransitiveTagKeys []string
  230. }
  231. // NewAssumeRoleProvider constructs and returns a credentials provider that
  232. // will retrieve credentials by assuming a IAM role using STS.
  233. func NewAssumeRoleProvider(client AssumeRoleAPIClient, roleARN string, optFns ...func(*AssumeRoleOptions)) *AssumeRoleProvider {
  234. o := AssumeRoleOptions{
  235. Client: client,
  236. RoleARN: roleARN,
  237. }
  238. for _, fn := range optFns {
  239. fn(&o)
  240. }
  241. return &AssumeRoleProvider{
  242. options: o,
  243. }
  244. }
  245. // Retrieve generates a new set of temporary credentials using STS.
  246. func (p *AssumeRoleProvider) Retrieve(ctx context.Context) (aws.Credentials, error) {
  247. // Apply defaults where parameters are not set.
  248. if len(p.options.RoleSessionName) == 0 {
  249. // Try to work out a role name that will hopefully end up unique.
  250. p.options.RoleSessionName = fmt.Sprintf("aws-go-sdk-%d", time.Now().UTC().UnixNano())
  251. }
  252. if p.options.Duration == 0 {
  253. // Expire as often as AWS permits.
  254. p.options.Duration = DefaultDuration
  255. }
  256. input := &sts.AssumeRoleInput{
  257. DurationSeconds: aws.Int32(int32(p.options.Duration / time.Second)),
  258. PolicyArns: p.options.PolicyARNs,
  259. RoleArn: aws.String(p.options.RoleARN),
  260. RoleSessionName: aws.String(p.options.RoleSessionName),
  261. ExternalId: p.options.ExternalID,
  262. SourceIdentity: p.options.SourceIdentity,
  263. Tags: p.options.Tags,
  264. TransitiveTagKeys: p.options.TransitiveTagKeys,
  265. }
  266. if p.options.Policy != nil {
  267. input.Policy = p.options.Policy
  268. }
  269. if p.options.SerialNumber != nil {
  270. if p.options.TokenProvider != nil {
  271. input.SerialNumber = p.options.SerialNumber
  272. code, err := p.options.TokenProvider()
  273. if err != nil {
  274. return aws.Credentials{}, err
  275. }
  276. input.TokenCode = aws.String(code)
  277. } else {
  278. return aws.Credentials{}, fmt.Errorf("assume role with MFA enabled, but TokenProvider is not set")
  279. }
  280. }
  281. resp, err := p.options.Client.AssumeRole(ctx, input)
  282. if err != nil {
  283. return aws.Credentials{Source: ProviderName}, err
  284. }
  285. return aws.Credentials{
  286. AccessKeyID: *resp.Credentials.AccessKeyId,
  287. SecretAccessKey: *resp.Credentials.SecretAccessKey,
  288. SessionToken: *resp.Credentials.SessionToken,
  289. Source: ProviderName,
  290. CanExpire: true,
  291. Expires: *resp.Credentials.Expiration,
  292. }, nil
  293. }