123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- // Package stscreds are credential Providers to retrieve STS AWS credentials.
- //
- // STS provides multiple ways to retrieve credentials which can be used when making
- // future AWS service API operation calls.
- //
- // The SDK will ensure that per instance of credentials.Credentials all requests
- // to refresh the credentials will be synchronized. But, the SDK is unable to
- // ensure synchronous usage of the AssumeRoleProvider if the value is shared
- // between multiple Credentials or service clients.
- //
- // # Assume Role
- //
- // To assume an IAM role using STS with the SDK you can create a new Credentials
- // with the SDKs's stscreds package.
- //
- // // Initial credentials loaded from SDK's default credential chain. Such as
- // // the environment, shared credentials (~/.aws/credentials), or EC2 Instance
- // // Role. These credentials will be used to to make the STS Assume Role API.
- // cfg, err := config.LoadDefaultConfig(context.TODO())
- // if err != nil {
- // panic(err)
- // }
- //
- // // Create the credentials from AssumeRoleProvider to assume the role
- // // referenced by the "myRoleARN" ARN.
- // stsSvc := sts.NewFromConfig(cfg)
- // creds := stscreds.NewAssumeRoleProvider(stsSvc, "myRoleArn")
- //
- // cfg.Credentials = aws.NewCredentialsCache(creds)
- //
- // // Create service client value configured for credentials
- // // from assumed role.
- // svc := s3.NewFromConfig(cfg)
- //
- // # Assume Role with custom MFA Token provider
- //
- // To assume an IAM role with a MFA token you can either specify a custom MFA
- // token provider or use the SDK's built in StdinTokenProvider that will prompt
- // the user for a token code each time the credentials need to to be refreshed.
- // Specifying a custom token provider allows you to control where the token
- // code is retrieved from, and how it is refreshed.
- //
- // With a custom token provider, the provider is responsible for refreshing the
- // token code when called.
- //
- // cfg, err := config.LoadDefaultConfig(context.TODO())
- // if err != nil {
- // panic(err)
- // }
- //
- // staticTokenProvider := func() (string, error) {
- // return someTokenCode, nil
- // }
- //
- // // Create the credentials from AssumeRoleProvider to assume the role
- // // referenced by the "myRoleARN" ARN using the MFA token code provided.
- // creds := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), "myRoleArn", func(o *stscreds.AssumeRoleOptions) {
- // o.SerialNumber = aws.String("myTokenSerialNumber")
- // o.TokenProvider = staticTokenProvider
- // })
- //
- // cfg.Credentials = aws.NewCredentialsCache(creds)
- //
- // // Create service client value configured for credentials
- // // from assumed role.
- // svc := s3.NewFromConfig(cfg)
- //
- // # Assume Role with MFA Token Provider
- //
- // To assume an IAM role with MFA for longer running tasks where the credentials
- // may need to be refreshed setting the TokenProvider field of AssumeRoleProvider
- // will allow the credential provider to prompt for new MFA token code when the
- // role's credentials need to be refreshed.
- //
- // The StdinTokenProvider function is available to prompt on stdin to retrieve
- // the MFA token code from the user. You can also implement custom prompts by
- // satisfying the TokenProvider function signature.
- //
- // Using StdinTokenProvider with multiple AssumeRoleProviders, or Credentials will
- // have undesirable results as the StdinTokenProvider will not be synchronized. A
- // single Credentials with an AssumeRoleProvider can be shared safely.
- //
- // cfg, err := config.LoadDefaultConfig(context.TODO())
- // if err != nil {
- // panic(err)
- // }
- //
- // // Create the credentials from AssumeRoleProvider to assume the role
- // // referenced by the "myRoleARN" ARN using the MFA token code provided.
- // creds := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), "myRoleArn", func(o *stscreds.AssumeRoleOptions) {
- // o.SerialNumber = aws.String("myTokenSerialNumber")
- // o.TokenProvider = stscreds.StdinTokenProvider
- // })
- //
- // cfg.Credentials = aws.NewCredentialsCache(creds)
- //
- // // Create service client value configured for credentials
- // // from assumed role.
- // svc := s3.NewFromConfig(cfg)
- package stscreds
- import (
- "context"
- "fmt"
- "time"
- "github.com/aws/aws-sdk-go-v2/aws"
- "github.com/aws/aws-sdk-go-v2/service/sts"
- "github.com/aws/aws-sdk-go-v2/service/sts/types"
- )
- // StdinTokenProvider will prompt on stdout and read from stdin for a string value.
- // An error is returned if reading from stdin fails.
- //
- // Use this function go read MFA tokens from stdin. The function makes no attempt
- // to make atomic prompts from stdin across multiple gorouties.
- //
- // Using StdinTokenProvider with multiple AssumeRoleProviders, or Credentials will
- // have undesirable results as the StdinTokenProvider will not be synchronized. A
- // single Credentials with an AssumeRoleProvider can be shared safely
- //
- // Will wait forever until something is provided on the stdin.
- func StdinTokenProvider() (string, error) {
- var v string
- fmt.Printf("Assume Role MFA token code: ")
- _, err := fmt.Scanln(&v)
- return v, err
- }
- // ProviderName provides a name of AssumeRole provider
- const ProviderName = "AssumeRoleProvider"
- // AssumeRoleAPIClient is a client capable of the STS AssumeRole operation.
- type AssumeRoleAPIClient interface {
- AssumeRole(ctx context.Context, params *sts.AssumeRoleInput, optFns ...func(*sts.Options)) (*sts.AssumeRoleOutput, error)
- }
- // DefaultDuration is the default amount of time in minutes that the
- // credentials will be valid for. This value is only used by AssumeRoleProvider
- // for specifying the default expiry duration of an assume role.
- //
- // Other providers such as WebIdentityRoleProvider do not use this value, and
- // instead rely on STS API's default parameter handing to assign a default
- // value.
- var DefaultDuration = time.Duration(15) * time.Minute
- // AssumeRoleProvider retrieves temporary credentials from the STS service, and
- // keeps track of their expiration time.
- //
- // This credential provider will be used by the SDKs default credential change
- // when shared configuration is enabled, and the shared config or shared credentials
- // file configure assume role. See Session docs for how to do this.
- //
- // AssumeRoleProvider does not provide any synchronization and it is not safe
- // to share this value across multiple Credentials, Sessions, or service clients
- // without also sharing the same Credentials instance.
- type AssumeRoleProvider struct {
- options AssumeRoleOptions
- }
- // AssumeRoleOptions is the configurable options for AssumeRoleProvider
- type AssumeRoleOptions struct {
- // Client implementation of the AssumeRole operation. Required
- Client AssumeRoleAPIClient
- // IAM Role ARN to be assumed. Required
- RoleARN string
- // Session name, if you wish to uniquely identify this session.
- RoleSessionName string
- // Expiry duration of the STS credentials. Defaults to 15 minutes if not set.
- Duration time.Duration
- // Optional ExternalID to pass along, defaults to nil if not set.
- ExternalID *string
- // The policy plain text must be 2048 bytes or shorter. However, an internal
- // conversion compresses it into a packed binary format with a separate limit.
- // The PackedPolicySize response element indicates by percentage how close to
- // the upper size limit the policy is, with 100% equaling the maximum allowed
- // size.
- Policy *string
- // The ARNs of IAM managed policies you want to use as managed session policies.
- // The policies must exist in the same account as the role.
- //
- // This parameter is optional. You can provide up to 10 managed policy ARNs.
- // However, the plain text that you use for both inline and managed session
- // policies can't exceed 2,048 characters.
- //
- // An AWS conversion compresses the passed session policies and session tags
- // into a packed binary format that has a separate limit. Your request can fail
- // for this limit even if your plain text meets the other requirements. The
- // PackedPolicySize response element indicates by percentage how close the policies
- // and tags for your request are to the upper size limit.
- //
- // Passing policies to this operation returns new temporary credentials. The
- // resulting session's permissions are the intersection of the role's identity-based
- // policy and the session policies. You can use the role's temporary credentials
- // in subsequent AWS API calls to access resources in the account that owns
- // the role. You cannot use session policies to grant more permissions than
- // those allowed by the identity-based policy of the role that is being assumed.
- // For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session)
- // in the IAM User Guide.
- PolicyARNs []types.PolicyDescriptorType
- // The identification number of the MFA device that is associated with the user
- // who is making the AssumeRole call. Specify this value if the trust policy
- // of the role being assumed includes a condition that requires MFA authentication.
- // The value is either the serial number for a hardware device (such as GAHT12345678)
- // or an Amazon Resource Name (ARN) for a virtual device (such as arn:aws:iam::123456789012:mfa/user).
- SerialNumber *string
- // The source identity specified by the principal that is calling the AssumeRole
- // operation. You can require users to specify a source identity when they assume a
- // role. You do this by using the sts:SourceIdentity condition key in a role trust
- // policy. You can use source identity information in CloudTrail logs to determine
- // who took actions with a role. You can use the aws:SourceIdentity condition key
- // to further control access to Amazon Web Services resources based on the value of
- // source identity. For more information about using source identity, see Monitor
- // and control actions taken with assumed roles
- // (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_monitor.html)
- // in the IAM User Guide.
- SourceIdentity *string
- // Async method of providing MFA token code for assuming an IAM role with MFA.
- // The value returned by the function will be used as the TokenCode in the Retrieve
- // call. See StdinTokenProvider for a provider that prompts and reads from stdin.
- //
- // This token provider will be called when ever the assumed role's
- // credentials need to be refreshed when SerialNumber is set.
- TokenProvider func() (string, error)
- // A list of session tags that you want to pass. Each session tag consists of a key
- // name and an associated value. For more information about session tags, see
- // Tagging STS Sessions
- // (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) in the
- // IAM User Guide. This parameter is optional. You can pass up to 50 session tags.
- Tags []types.Tag
- // A list of keys for session tags that you want to set as transitive. If you set a
- // tag key as transitive, the corresponding key and value passes to subsequent
- // sessions in a role chain. For more information, see Chaining Roles with Session
- // Tags
- // (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_role-chaining)
- // in the IAM User Guide. This parameter is optional.
- TransitiveTagKeys []string
- }
- // NewAssumeRoleProvider constructs and returns a credentials provider that
- // will retrieve credentials by assuming a IAM role using STS.
- func NewAssumeRoleProvider(client AssumeRoleAPIClient, roleARN string, optFns ...func(*AssumeRoleOptions)) *AssumeRoleProvider {
- o := AssumeRoleOptions{
- Client: client,
- RoleARN: roleARN,
- }
- for _, fn := range optFns {
- fn(&o)
- }
- return &AssumeRoleProvider{
- options: o,
- }
- }
- // Retrieve generates a new set of temporary credentials using STS.
- func (p *AssumeRoleProvider) Retrieve(ctx context.Context) (aws.Credentials, error) {
- // Apply defaults where parameters are not set.
- if len(p.options.RoleSessionName) == 0 {
- // Try to work out a role name that will hopefully end up unique.
- p.options.RoleSessionName = fmt.Sprintf("aws-go-sdk-%d", time.Now().UTC().UnixNano())
- }
- if p.options.Duration == 0 {
- // Expire as often as AWS permits.
- p.options.Duration = DefaultDuration
- }
- input := &sts.AssumeRoleInput{
- DurationSeconds: aws.Int32(int32(p.options.Duration / time.Second)),
- PolicyArns: p.options.PolicyARNs,
- RoleArn: aws.String(p.options.RoleARN),
- RoleSessionName: aws.String(p.options.RoleSessionName),
- ExternalId: p.options.ExternalID,
- SourceIdentity: p.options.SourceIdentity,
- Tags: p.options.Tags,
- TransitiveTagKeys: p.options.TransitiveTagKeys,
- }
- if p.options.Policy != nil {
- input.Policy = p.options.Policy
- }
- if p.options.SerialNumber != nil {
- if p.options.TokenProvider != nil {
- input.SerialNumber = p.options.SerialNumber
- code, err := p.options.TokenProvider()
- if err != nil {
- return aws.Credentials{}, err
- }
- input.TokenCode = aws.String(code)
- } else {
- return aws.Credentials{}, fmt.Errorf("assume role with MFA enabled, but TokenProvider is not set")
- }
- }
- resp, err := p.options.Client.AssumeRole(ctx, input)
- if err != nil {
- return aws.Credentials{Source: ProviderName}, err
- }
- return aws.Credentials{
- AccessKeyID: *resp.Credentials.AccessKeyId,
- SecretAccessKey: *resp.Credentials.SecretAccessKey,
- SessionToken: *resp.Credentials.SessionToken,
- Source: ProviderName,
- CanExpire: true,
- Expires: *resp.Credentials.Expiration,
- }, nil
- }
|