github.com/aavshr/aws-sdk-go@v1.41.3/aws/credentials/stscreds/assume_role_provider.go (about)

     1  /*
     2  Package stscreds are credential Providers to retrieve STS AWS credentials.
     3  
     4  STS provides multiple ways to retrieve credentials which can be used when making
     5  future AWS service API operation calls.
     6  
     7  The SDK will ensure that per instance of credentials.Credentials all requests
     8  to refresh the credentials will be synchronized. But, the SDK is unable to
     9  ensure synchronous usage of the AssumeRoleProvider if the value is shared
    10  between multiple Credentials, Sessions or service clients.
    11  
    12  Assume Role
    13  
    14  To assume an IAM role using STS with the SDK you can create a new Credentials
    15  with the SDKs's stscreds package.
    16  
    17  	// Initial credentials loaded from SDK's default credential chain. Such as
    18  	// the environment, shared credentials (~/.aws/credentials), or EC2 Instance
    19  	// Role. These credentials will be used to to make the STS Assume Role API.
    20  	sess := session.Must(session.NewSession())
    21  
    22  	// Create the credentials from AssumeRoleProvider to assume the role
    23  	// referenced by the "myRoleARN" ARN.
    24  	creds := stscreds.NewCredentials(sess, "myRoleArn")
    25  
    26  	// Create service client value configured for credentials
    27  	// from assumed role.
    28  	svc := s3.New(sess, &aws.Config{Credentials: creds})
    29  
    30  Assume Role with static MFA Token
    31  
    32  To assume an IAM role with a MFA token you can either specify a MFA token code
    33  directly or provide a function to prompt the user each time the credentials
    34  need to refresh the role's credentials. Specifying the TokenCode should be used
    35  for short lived operations that will not need to be refreshed, and when you do
    36  not want to have direct control over the user provides their MFA token.
    37  
    38  With TokenCode the AssumeRoleProvider will be not be able to refresh the role's
    39  credentials.
    40  
    41  	// Create the credentials from AssumeRoleProvider to assume the role
    42  	// referenced by the "myRoleARN" ARN using the MFA token code provided.
    43  	creds := stscreds.NewCredentials(sess, "myRoleArn", func(p *stscreds.AssumeRoleProvider) {
    44  		p.SerialNumber = aws.String("myTokenSerialNumber")
    45  		p.TokenCode = aws.String("00000000")
    46  	})
    47  
    48  	// Create service client value configured for credentials
    49  	// from assumed role.
    50  	svc := s3.New(sess, &aws.Config{Credentials: creds})
    51  
    52  Assume Role with MFA Token Provider
    53  
    54  To assume an IAM role with MFA for longer running tasks where the credentials
    55  may need to be refreshed setting the TokenProvider field of AssumeRoleProvider
    56  will allow the credential provider to prompt for new MFA token code when the
    57  role's credentials need to be refreshed.
    58  
    59  The StdinTokenProvider function is available to prompt on stdin to retrieve
    60  the MFA token code from the user. You can also implement custom prompts by
    61  satisfing the TokenProvider function signature.
    62  
    63  Using StdinTokenProvider with multiple AssumeRoleProviders, or Credentials will
    64  have undesirable results as the StdinTokenProvider will not be synchronized. A
    65  single Credentials with an AssumeRoleProvider can be shared safely.
    66  
    67  	// Create the credentials from AssumeRoleProvider to assume the role
    68  	// referenced by the "myRoleARN" ARN. Prompting for MFA token from stdin.
    69  	creds := stscreds.NewCredentials(sess, "myRoleArn", func(p *stscreds.AssumeRoleProvider) {
    70  		p.SerialNumber = aws.String("myTokenSerialNumber")
    71  		p.TokenProvider = stscreds.StdinTokenProvider
    72  	})
    73  
    74  	// Create service client value configured for credentials
    75  	// from assumed role.
    76  	svc := s3.New(sess, &aws.Config{Credentials: creds})
    77  
    78  */
    79  package stscreds
    80  
    81  import (
    82  	"fmt"
    83  	"os"
    84  	"time"
    85  
    86  	"github.com/aavshr/aws-sdk-go/aws"
    87  	"github.com/aavshr/aws-sdk-go/aws/awserr"
    88  	"github.com/aavshr/aws-sdk-go/aws/client"
    89  	"github.com/aavshr/aws-sdk-go/aws/credentials"
    90  	"github.com/aavshr/aws-sdk-go/aws/request"
    91  	"github.com/aavshr/aws-sdk-go/internal/sdkrand"
    92  	"github.com/aavshr/aws-sdk-go/service/sts"
    93  )
    94  
    95  // StdinTokenProvider will prompt on stderr and read from stdin for a string value.
    96  // An error is returned if reading from stdin fails.
    97  //
    98  // Use this function to read MFA tokens from stdin. The function makes no attempt
    99  // to make atomic prompts from stdin across multiple gorouties.
   100  //
   101  // Using StdinTokenProvider with multiple AssumeRoleProviders, or Credentials will
   102  // have undesirable results as the StdinTokenProvider will not be synchronized. A
   103  // single Credentials with an AssumeRoleProvider can be shared safely
   104  //
   105  // Will wait forever until something is provided on the stdin.
   106  func StdinTokenProvider() (string, error) {
   107  	var v string
   108  	fmt.Fprintf(os.Stderr, "Assume Role MFA token code: ")
   109  	_, err := fmt.Scanln(&v)
   110  
   111  	return v, err
   112  }
   113  
   114  // ProviderName provides a name of AssumeRole provider
   115  const ProviderName = "AssumeRoleProvider"
   116  
   117  // AssumeRoler represents the minimal subset of the STS client API used by this provider.
   118  type AssumeRoler interface {
   119  	AssumeRole(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error)
   120  }
   121  
   122  type assumeRolerWithContext interface {
   123  	AssumeRoleWithContext(aws.Context, *sts.AssumeRoleInput, ...request.Option) (*sts.AssumeRoleOutput, error)
   124  }
   125  
   126  // DefaultDuration is the default amount of time in minutes that the credentials
   127  // will be valid for.
   128  var DefaultDuration = time.Duration(15) * time.Minute
   129  
   130  // AssumeRoleProvider retrieves temporary credentials from the STS service, and
   131  // keeps track of their expiration time.
   132  //
   133  // This credential provider will be used by the SDKs default credential change
   134  // when shared configuration is enabled, and the shared config or shared credentials
   135  // file configure assume role. See Session docs for how to do this.
   136  //
   137  // AssumeRoleProvider does not provide any synchronization and it is not safe
   138  // to share this value across multiple Credentials, Sessions, or service clients
   139  // without also sharing the same Credentials instance.
   140  type AssumeRoleProvider struct {
   141  	credentials.Expiry
   142  
   143  	// STS client to make assume role request with.
   144  	Client AssumeRoler
   145  
   146  	// Role to be assumed.
   147  	RoleARN string
   148  
   149  	// Session name, if you wish to reuse the credentials elsewhere.
   150  	RoleSessionName string
   151  
   152  	// Optional, you can pass tag key-value pairs to your session. These tags are called session tags.
   153  	Tags []*sts.Tag
   154  
   155  	// A list of keys for session tags that you want to set as transitive.
   156  	// If you set a tag key as transitive, the corresponding key and value passes to subsequent sessions in a role chain.
   157  	TransitiveTagKeys []*string
   158  
   159  	// Expiry duration of the STS credentials. Defaults to 15 minutes if not set.
   160  	Duration time.Duration
   161  
   162  	// Optional ExternalID to pass along, defaults to nil if not set.
   163  	ExternalID *string
   164  
   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  
   172  	// The ARNs of IAM managed policies you want to use as managed session policies.
   173  	// The policies must exist in the same account as the role.
   174  	//
   175  	// This parameter is optional. You can provide up to 10 managed policy ARNs.
   176  	// However, the plain text that you use for both inline and managed session
   177  	// policies can't exceed 2,048 characters.
   178  	//
   179  	// An AWS conversion compresses the passed session policies and session tags
   180  	// into a packed binary format that has a separate limit. Your request can fail
   181  	// for this limit even if your plain text meets the other requirements. The
   182  	// PackedPolicySize response element indicates by percentage how close the policies
   183  	// and tags for your request are to the upper size limit.
   184  	//
   185  	// Passing policies to this operation returns new temporary credentials. The
   186  	// resulting session's permissions are the intersection of the role's identity-based
   187  	// policy and the session policies. You can use the role's temporary credentials
   188  	// in subsequent AWS API calls to access resources in the account that owns
   189  	// the role. You cannot use session policies to grant more permissions than
   190  	// those allowed by the identity-based policy of the role that is being assumed.
   191  	// For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session)
   192  	// in the IAM User Guide.
   193  	PolicyArns []*sts.PolicyDescriptorType
   194  
   195  	// The identification number of the MFA device that is associated with the user
   196  	// who is making the AssumeRole call. Specify this value if the trust policy
   197  	// of the role being assumed includes a condition that requires MFA authentication.
   198  	// The value is either the serial number for a hardware device (such as GAHT12345678)
   199  	// or an Amazon Resource Name (ARN) for a virtual device (such as arn:aws:iam::123456789012:mfa/user).
   200  	SerialNumber *string
   201  
   202  	// The value provided by the MFA device, if the trust policy of the role being
   203  	// assumed requires MFA (that is, if the policy includes a condition that tests
   204  	// for MFA). If the role being assumed requires MFA and if the TokenCode value
   205  	// is missing or expired, the AssumeRole call returns an "access denied" error.
   206  	//
   207  	// If SerialNumber is set and neither TokenCode nor TokenProvider are also
   208  	// set an error will be returned.
   209  	TokenCode *string
   210  
   211  	// Async method of providing MFA token code for assuming an IAM role with MFA.
   212  	// The value returned by the function will be used as the TokenCode in the Retrieve
   213  	// call. See StdinTokenProvider for a provider that prompts and reads from stdin.
   214  	//
   215  	// This token provider will be called when ever the assumed role's
   216  	// credentials need to be refreshed when SerialNumber is also set and
   217  	// TokenCode is not set.
   218  	//
   219  	// If both TokenCode and TokenProvider is set, TokenProvider will be used and
   220  	// TokenCode is ignored.
   221  	TokenProvider func() (string, error)
   222  
   223  	// ExpiryWindow will allow the credentials to trigger refreshing prior to
   224  	// the credentials actually expiring. This is beneficial so race conditions
   225  	// with expiring credentials do not cause request to fail unexpectedly
   226  	// due to ExpiredTokenException exceptions.
   227  	//
   228  	// So a ExpiryWindow of 10s would cause calls to IsExpired() to return true
   229  	// 10 seconds before the credentials are actually expired.
   230  	//
   231  	// If ExpiryWindow is 0 or less it will be ignored.
   232  	ExpiryWindow time.Duration
   233  
   234  	// MaxJitterFrac reduces the effective Duration of each credential requested
   235  	// by a random percentage between 0 and MaxJitterFraction. MaxJitterFrac must
   236  	// have a value between 0 and 1. Any other value may lead to expected behavior.
   237  	// With a MaxJitterFrac value of 0, default) will no jitter will be used.
   238  	//
   239  	// For example, with a Duration of 30m and a MaxJitterFrac of 0.1, the
   240  	// AssumeRole call will be made with an arbitrary Duration between 27m and
   241  	// 30m.
   242  	//
   243  	// MaxJitterFrac should not be negative.
   244  	MaxJitterFrac float64
   245  }
   246  
   247  // NewCredentials returns a pointer to a new Credentials value wrapping the
   248  // AssumeRoleProvider. The credentials will expire every 15 minutes and the
   249  // role will be named after a nanosecond timestamp of this operation. The
   250  // Credentials value will attempt to refresh the credentials using the provider
   251  // when Credentials.Get is called, if the cached credentials are expiring.
   252  //
   253  // Takes a Config provider to create the STS client. The ConfigProvider is
   254  // satisfied by the session.Session type.
   255  //
   256  // It is safe to share the returned Credentials with multiple Sessions and
   257  // service clients. All access to the credentials and refreshing them
   258  // will be synchronized.
   259  func NewCredentials(c client.ConfigProvider, roleARN string, options ...func(*AssumeRoleProvider)) *credentials.Credentials {
   260  	p := &AssumeRoleProvider{
   261  		Client:   sts.New(c),
   262  		RoleARN:  roleARN,
   263  		Duration: DefaultDuration,
   264  	}
   265  
   266  	for _, option := range options {
   267  		option(p)
   268  	}
   269  
   270  	return credentials.NewCredentials(p)
   271  }
   272  
   273  // NewCredentialsWithClient returns a pointer to a new Credentials value wrapping the
   274  // AssumeRoleProvider. The credentials will expire every 15 minutes and the
   275  // role will be named after a nanosecond timestamp of this operation. The
   276  // Credentials value will attempt to refresh the credentials using the provider
   277  // when Credentials.Get is called, if the cached credentials are expiring.
   278  //
   279  // Takes an AssumeRoler which can be satisfied by the STS client.
   280  //
   281  // It is safe to share the returned Credentials with multiple Sessions and
   282  // service clients. All access to the credentials and refreshing them
   283  // will be synchronized.
   284  func NewCredentialsWithClient(svc AssumeRoler, roleARN string, options ...func(*AssumeRoleProvider)) *credentials.Credentials {
   285  	p := &AssumeRoleProvider{
   286  		Client:   svc,
   287  		RoleARN:  roleARN,
   288  		Duration: DefaultDuration,
   289  	}
   290  
   291  	for _, option := range options {
   292  		option(p)
   293  	}
   294  
   295  	return credentials.NewCredentials(p)
   296  }
   297  
   298  // Retrieve generates a new set of temporary credentials using STS.
   299  func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) {
   300  	return p.RetrieveWithContext(aws.BackgroundContext())
   301  }
   302  
   303  // RetrieveWithContext generates a new set of temporary credentials using STS.
   304  func (p *AssumeRoleProvider) RetrieveWithContext(ctx credentials.Context) (credentials.Value, error) {
   305  	// Apply defaults where parameters are not set.
   306  	if p.RoleSessionName == "" {
   307  		// Try to work out a role name that will hopefully end up unique.
   308  		p.RoleSessionName = fmt.Sprintf("%d", time.Now().UTC().UnixNano())
   309  	}
   310  	if p.Duration == 0 {
   311  		// Expire as often as AWS permits.
   312  		p.Duration = DefaultDuration
   313  	}
   314  	jitter := time.Duration(sdkrand.SeededRand.Float64() * p.MaxJitterFrac * float64(p.Duration))
   315  	input := &sts.AssumeRoleInput{
   316  		DurationSeconds:   aws.Int64(int64((p.Duration - jitter) / time.Second)),
   317  		RoleArn:           aws.String(p.RoleARN),
   318  		RoleSessionName:   aws.String(p.RoleSessionName),
   319  		ExternalId:        p.ExternalID,
   320  		Tags:              p.Tags,
   321  		PolicyArns:        p.PolicyArns,
   322  		TransitiveTagKeys: p.TransitiveTagKeys,
   323  	}
   324  	if p.Policy != nil {
   325  		input.Policy = p.Policy
   326  	}
   327  	if p.SerialNumber != nil {
   328  		if p.TokenCode != nil {
   329  			input.SerialNumber = p.SerialNumber
   330  			input.TokenCode = p.TokenCode
   331  		} else if p.TokenProvider != nil {
   332  			input.SerialNumber = p.SerialNumber
   333  			code, err := p.TokenProvider()
   334  			if err != nil {
   335  				return credentials.Value{ProviderName: ProviderName}, err
   336  			}
   337  			input.TokenCode = aws.String(code)
   338  		} else {
   339  			return credentials.Value{ProviderName: ProviderName},
   340  				awserr.New("AssumeRoleTokenNotAvailable",
   341  					"assume role with MFA enabled, but neither TokenCode nor TokenProvider are set", nil)
   342  		}
   343  	}
   344  
   345  	var roleOutput *sts.AssumeRoleOutput
   346  	var err error
   347  
   348  	if c, ok := p.Client.(assumeRolerWithContext); ok {
   349  		roleOutput, err = c.AssumeRoleWithContext(ctx, input)
   350  	} else {
   351  		roleOutput, err = p.Client.AssumeRole(input)
   352  	}
   353  
   354  	if err != nil {
   355  		return credentials.Value{ProviderName: ProviderName}, err
   356  	}
   357  
   358  	// We will proactively generate new credentials before they expire.
   359  	p.SetExpiration(*roleOutput.Credentials.Expiration, p.ExpiryWindow)
   360  
   361  	return credentials.Value{
   362  		AccessKeyID:     *roleOutput.Credentials.AccessKeyId,
   363  		SecretAccessKey: *roleOutput.Credentials.SecretAccessKey,
   364  		SessionToken:    *roleOutput.Credentials.SessionToken,
   365  		ProviderName:    ProviderName,
   366  	}, nil
   367  }