Blame vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds/assume_role_provider.go

Packit 63bb0d
/*
Packit 63bb0d
Package stscreds are credential Providers to retrieve STS AWS credentials.
Packit 63bb0d
Packit 63bb0d
STS provides multiple ways to retrieve credentials which can be used when making
Packit 63bb0d
future AWS service API operation calls.
Packit 63bb0d
Packit 63bb0d
The SDK will ensure that per instance of credentials.Credentials all requests
Packit 63bb0d
to refresh the credentials will be synchronized. But, the SDK is unable to
Packit 63bb0d
ensure synchronous usage of the AssumeRoleProvider if the value is shared
Packit 63bb0d
between multiple Credentials, Sessions or service clients.
Packit 63bb0d
Packit 63bb0d
Assume Role
Packit 63bb0d
Packit 63bb0d
To assume an IAM role using STS with the SDK you can create a new Credentials
Packit 63bb0d
with the SDKs's stscreds package.
Packit 63bb0d
Packit 63bb0d
	// Initial credentials loaded from SDK's default credential chain. Such as
Packit 63bb0d
	// the environment, shared credentials (~/.aws/credentials), or EC2 Instance
Packit 63bb0d
	// Role. These credentials will be used to to make the STS Assume Role API.
Packit 63bb0d
	sess := session.Must(session.NewSession())
Packit 63bb0d
Packit 63bb0d
	// Create the credentials from AssumeRoleProvider to assume the role
Packit 63bb0d
	// referenced by the "myRoleARN" ARN.
Packit 63bb0d
	creds := stscreds.NewCredentials(sess, "myRoleArn")
Packit 63bb0d
Packit 63bb0d
	// Create service client value configured for credentials
Packit 63bb0d
	// from assumed role.
Packit 63bb0d
	svc := s3.New(sess, &aws.Config{Credentials: creds})
Packit 63bb0d
Packit 63bb0d
Assume Role with static MFA Token
Packit 63bb0d
Packit 63bb0d
To assume an IAM role with a MFA token you can either specify a MFA token code
Packit 63bb0d
directly or provide a function to prompt the user each time the credentials
Packit 63bb0d
need to refresh the role's credentials. Specifying the TokenCode should be used
Packit 63bb0d
for short lived operations that will not need to be refreshed, and when you do
Packit 63bb0d
not want to have direct control over the user provides their MFA token.
Packit 63bb0d
Packit 63bb0d
With TokenCode the AssumeRoleProvider will be not be able to refresh the role's
Packit 63bb0d
credentials.
Packit 63bb0d
Packit 63bb0d
	// Create the credentials from AssumeRoleProvider to assume the role
Packit 63bb0d
	// referenced by the "myRoleARN" ARN using the MFA token code provided.
Packit 63bb0d
	creds := stscreds.NewCredentials(sess, "myRoleArn", func(p *stscreds.AssumeRoleProvider) {
Packit 63bb0d
		p.SerialNumber = aws.String("myTokenSerialNumber")
Packit 63bb0d
		p.TokenCode = aws.String("00000000")
Packit 63bb0d
	})
Packit 63bb0d
Packit 63bb0d
	// Create service client value configured for credentials
Packit 63bb0d
	// from assumed role.
Packit 63bb0d
	svc := s3.New(sess, &aws.Config{Credentials: creds})
Packit 63bb0d
Packit 63bb0d
Assume Role with MFA Token Provider
Packit 63bb0d
Packit 63bb0d
To assume an IAM role with MFA for longer running tasks where the credentials
Packit 63bb0d
may need to be refreshed setting the TokenProvider field of AssumeRoleProvider
Packit 63bb0d
will allow the credential provider to prompt for new MFA token code when the
Packit 63bb0d
role's credentials need to be refreshed.
Packit 63bb0d
Packit 63bb0d
The StdinTokenProvider function is available to prompt on stdin to retrieve
Packit 63bb0d
the MFA token code from the user. You can also implement custom prompts by
Packit 63bb0d
satisfing the TokenProvider function signature.
Packit 63bb0d
Packit 63bb0d
Using StdinTokenProvider with multiple AssumeRoleProviders, or Credentials will
Packit 63bb0d
have undesirable results as the StdinTokenProvider will not be synchronized. A
Packit 63bb0d
single Credentials with an AssumeRoleProvider can be shared safely.
Packit 63bb0d
Packit 63bb0d
	// Create the credentials from AssumeRoleProvider to assume the role
Packit 63bb0d
	// referenced by the "myRoleARN" ARN. Prompting for MFA token from stdin.
Packit 63bb0d
	creds := stscreds.NewCredentials(sess, "myRoleArn", func(p *stscreds.AssumeRoleProvider) {
Packit 63bb0d
		p.SerialNumber = aws.String("myTokenSerialNumber")
Packit 63bb0d
		p.TokenProvider = stscreds.StdinTokenProvider
Packit 63bb0d
	})
Packit 63bb0d
Packit 63bb0d
	// Create service client value configured for credentials
Packit 63bb0d
	// from assumed role.
Packit 63bb0d
	svc := s3.New(sess, &aws.Config{Credentials: creds})
Packit 63bb0d
Packit 63bb0d
*/
Packit 63bb0d
package stscreds
Packit 63bb0d
Packit 63bb0d
import (
Packit 63bb0d
	"fmt"
Packit 63bb0d
	"os"
Packit 63bb0d
	"time"
Packit 63bb0d
Packit 63bb0d
	"github.com/aws/aws-sdk-go/aws"
Packit 63bb0d
	"github.com/aws/aws-sdk-go/aws/awserr"
Packit 63bb0d
	"github.com/aws/aws-sdk-go/aws/client"
Packit 63bb0d
	"github.com/aws/aws-sdk-go/aws/credentials"
Packit 63bb0d
	"github.com/aws/aws-sdk-go/internal/sdkrand"
Packit 63bb0d
	"github.com/aws/aws-sdk-go/service/sts"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
// StdinTokenProvider will prompt on stderr and read from stdin for a string value.
Packit 63bb0d
// An error is returned if reading from stdin fails.
Packit 63bb0d
//
Packit 63bb0d
// Use this function go read MFA tokens from stdin. The function makes no attempt
Packit 63bb0d
// to make atomic prompts from stdin across multiple gorouties.
Packit 63bb0d
//
Packit 63bb0d
// Using StdinTokenProvider with multiple AssumeRoleProviders, or Credentials will
Packit 63bb0d
// have undesirable results as the StdinTokenProvider will not be synchronized. A
Packit 63bb0d
// single Credentials with an AssumeRoleProvider can be shared safely
Packit 63bb0d
//
Packit 63bb0d
// Will wait forever until something is provided on the stdin.
Packit 63bb0d
func StdinTokenProvider() (string, error) {
Packit 63bb0d
	var v string
Packit 63bb0d
	fmt.Fprintf(os.Stderr, "Assume Role MFA token code: ")
Packit 63bb0d
	_, err := fmt.Scanln(&v)
Packit 63bb0d
Packit 63bb0d
	return v, err
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ProviderName provides a name of AssumeRole provider
Packit 63bb0d
const ProviderName = "AssumeRoleProvider"
Packit 63bb0d
Packit 63bb0d
// AssumeRoler represents the minimal subset of the STS client API used by this provider.
Packit 63bb0d
type AssumeRoler interface {
Packit 63bb0d
	AssumeRole(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// DefaultDuration is the default amount of time in minutes that the credentials
Packit 63bb0d
// will be valid for.
Packit 63bb0d
var DefaultDuration = time.Duration(15) * time.Minute
Packit 63bb0d
Packit 63bb0d
// AssumeRoleProvider retrieves temporary credentials from the STS service, and
Packit 63bb0d
// keeps track of their expiration time.
Packit 63bb0d
//
Packit 63bb0d
// This credential provider will be used by the SDKs default credential change
Packit 63bb0d
// when shared configuration is enabled, and the shared config or shared credentials
Packit 63bb0d
// file configure assume role. See Session docs for how to do this.
Packit 63bb0d
//
Packit 63bb0d
// AssumeRoleProvider does not provide any synchronization and it is not safe
Packit 63bb0d
// to share this value across multiple Credentials, Sessions, or service clients
Packit 63bb0d
// without also sharing the same Credentials instance.
Packit 63bb0d
type AssumeRoleProvider struct {
Packit 63bb0d
	credentials.Expiry
Packit 63bb0d
Packit 63bb0d
	// STS client to make assume role request with.
Packit 63bb0d
	Client AssumeRoler
Packit 63bb0d
Packit 63bb0d
	// Role to be assumed.
Packit 63bb0d
	RoleARN string
Packit 63bb0d
Packit 63bb0d
	// Session name, if you wish to reuse the credentials elsewhere.
Packit 63bb0d
	RoleSessionName string
Packit 63bb0d
Packit 63bb0d
	// Expiry duration of the STS credentials. Defaults to 15 minutes if not set.
Packit 63bb0d
	Duration time.Duration
Packit 63bb0d
Packit 63bb0d
	// Optional ExternalID to pass along, defaults to nil if not set.
Packit 63bb0d
	ExternalID *string
Packit 63bb0d
Packit 63bb0d
	// The policy plain text must be 2048 bytes or shorter. However, an internal
Packit 63bb0d
	// conversion compresses it into a packed binary format with a separate limit.
Packit 63bb0d
	// The PackedPolicySize response element indicates by percentage how close to
Packit 63bb0d
	// the upper size limit the policy is, with 100% equaling the maximum allowed
Packit 63bb0d
	// size.
Packit 63bb0d
	Policy *string
Packit 63bb0d
Packit 63bb0d
	// The identification number of the MFA device that is associated with the user
Packit 63bb0d
	// who is making the AssumeRole call. Specify this value if the trust policy
Packit 63bb0d
	// of the role being assumed includes a condition that requires MFA authentication.
Packit 63bb0d
	// The value is either the serial number for a hardware device (such as GAHT12345678)
Packit 63bb0d
	// or an Amazon Resource Name (ARN) for a virtual device (such as arn:aws:iam::123456789012:mfa/user).
Packit 63bb0d
	SerialNumber *string
Packit 63bb0d
Packit 63bb0d
	// The value provided by the MFA device, if the trust policy of the role being
Packit 63bb0d
	// assumed requires MFA (that is, if the policy includes a condition that tests
Packit 63bb0d
	// for MFA). If the role being assumed requires MFA and if the TokenCode value
Packit 63bb0d
	// is missing or expired, the AssumeRole call returns an "access denied" error.
Packit 63bb0d
	//
Packit 63bb0d
	// If SerialNumber is set and neither TokenCode nor TokenProvider are also
Packit 63bb0d
	// set an error will be returned.
Packit 63bb0d
	TokenCode *string
Packit 63bb0d
Packit 63bb0d
	// Async method of providing MFA token code for assuming an IAM role with MFA.
Packit 63bb0d
	// The value returned by the function will be used as the TokenCode in the Retrieve
Packit 63bb0d
	// call. See StdinTokenProvider for a provider that prompts and reads from stdin.
Packit 63bb0d
	//
Packit 63bb0d
	// This token provider will be called when ever the assumed role's
Packit 63bb0d
	// credentials need to be refreshed when SerialNumber is also set and
Packit 63bb0d
	// TokenCode is not set.
Packit 63bb0d
	//
Packit 63bb0d
	// If both TokenCode and TokenProvider is set, TokenProvider will be used and
Packit 63bb0d
	// TokenCode is ignored.
Packit 63bb0d
	TokenProvider func() (string, error)
Packit 63bb0d
Packit 63bb0d
	// ExpiryWindow will allow the credentials to trigger refreshing prior to
Packit 63bb0d
	// the credentials actually expiring. This is beneficial so race conditions
Packit 63bb0d
	// with expiring credentials do not cause request to fail unexpectedly
Packit 63bb0d
	// due to ExpiredTokenException exceptions.
Packit 63bb0d
	//
Packit 63bb0d
	// So a ExpiryWindow of 10s would cause calls to IsExpired() to return true
Packit 63bb0d
	// 10 seconds before the credentials are actually expired.
Packit 63bb0d
	//
Packit 63bb0d
	// If ExpiryWindow is 0 or less it will be ignored.
Packit 63bb0d
	ExpiryWindow time.Duration
Packit 63bb0d
Packit 63bb0d
	// MaxJitterFrac reduces the effective Duration of each credential requested
Packit 63bb0d
	// by a random percentage between 0 and MaxJitterFraction. MaxJitterFrac must
Packit 63bb0d
	// have a value between 0 and 1. Any other value may lead to expected behavior.
Packit 63bb0d
	// With a MaxJitterFrac value of 0, default) will no jitter will be used.
Packit 63bb0d
	//
Packit 63bb0d
	// For example, with a Duration of 30m and a MaxJitterFrac of 0.1, the
Packit 63bb0d
	// AssumeRole call will be made with an arbitrary Duration between 27m and
Packit 63bb0d
	// 30m.
Packit 63bb0d
	//
Packit 63bb0d
	// MaxJitterFrac should not be negative.
Packit 63bb0d
	MaxJitterFrac float64
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NewCredentials returns a pointer to a new Credentials object wrapping the
Packit 63bb0d
// AssumeRoleProvider. The credentials will expire every 15 minutes and the
Packit 63bb0d
// role will be named after a nanosecond timestamp of this operation.
Packit 63bb0d
//
Packit 63bb0d
// Takes a Config provider to create the STS client. The ConfigProvider is
Packit 63bb0d
// satisfied by the session.Session type.
Packit 63bb0d
//
Packit 63bb0d
// It is safe to share the returned Credentials with multiple Sessions and
Packit 63bb0d
// service clients. All access to the credentials and refreshing them
Packit 63bb0d
// will be synchronized.
Packit 63bb0d
func NewCredentials(c client.ConfigProvider, roleARN string, options ...func(*AssumeRoleProvider)) *credentials.Credentials {
Packit 63bb0d
	p := &AssumeRoleProvider{
Packit 63bb0d
		Client:   sts.New(c),
Packit 63bb0d
		RoleARN:  roleARN,
Packit 63bb0d
		Duration: DefaultDuration,
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	for _, option := range options {
Packit 63bb0d
		option(p)
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return credentials.NewCredentials(p)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NewCredentialsWithClient returns a pointer to a new Credentials object wrapping the
Packit 63bb0d
// AssumeRoleProvider. The credentials will expire every 15 minutes and the
Packit 63bb0d
// role will be named after a nanosecond timestamp of this operation.
Packit 63bb0d
//
Packit 63bb0d
// Takes an AssumeRoler which can be satisfied by the STS client.
Packit 63bb0d
//
Packit 63bb0d
// It is safe to share the returned Credentials with multiple Sessions and
Packit 63bb0d
// service clients. All access to the credentials and refreshing them
Packit 63bb0d
// will be synchronized.
Packit 63bb0d
func NewCredentialsWithClient(svc AssumeRoler, roleARN string, options ...func(*AssumeRoleProvider)) *credentials.Credentials {
Packit 63bb0d
	p := &AssumeRoleProvider{
Packit 63bb0d
		Client:   svc,
Packit 63bb0d
		RoleARN:  roleARN,
Packit 63bb0d
		Duration: DefaultDuration,
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	for _, option := range options {
Packit 63bb0d
		option(p)
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return credentials.NewCredentials(p)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Retrieve generates a new set of temporary credentials using STS.
Packit 63bb0d
func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) {
Packit 63bb0d
	// Apply defaults where parameters are not set.
Packit 63bb0d
	if p.RoleSessionName == "" {
Packit 63bb0d
		// Try to work out a role name that will hopefully end up unique.
Packit 63bb0d
		p.RoleSessionName = fmt.Sprintf("%d", time.Now().UTC().UnixNano())
Packit 63bb0d
	}
Packit 63bb0d
	if p.Duration == 0 {
Packit 63bb0d
		// Expire as often as AWS permits.
Packit 63bb0d
		p.Duration = DefaultDuration
Packit 63bb0d
	}
Packit 63bb0d
	jitter := time.Duration(sdkrand.SeededRand.Float64() * p.MaxJitterFrac * float64(p.Duration))
Packit 63bb0d
	input := &sts.AssumeRoleInput{
Packit 63bb0d
		DurationSeconds: aws.Int64(int64((p.Duration - jitter) / time.Second)),
Packit 63bb0d
		RoleArn:         aws.String(p.RoleARN),
Packit 63bb0d
		RoleSessionName: aws.String(p.RoleSessionName),
Packit 63bb0d
		ExternalId:      p.ExternalID,
Packit 63bb0d
	}
Packit 63bb0d
	if p.Policy != nil {
Packit 63bb0d
		input.Policy = p.Policy
Packit 63bb0d
	}
Packit 63bb0d
	if p.SerialNumber != nil {
Packit 63bb0d
		if p.TokenCode != nil {
Packit 63bb0d
			input.SerialNumber = p.SerialNumber
Packit 63bb0d
			input.TokenCode = p.TokenCode
Packit 63bb0d
		} else if p.TokenProvider != nil {
Packit 63bb0d
			input.SerialNumber = p.SerialNumber
Packit 63bb0d
			code, err := p.TokenProvider()
Packit 63bb0d
			if err != nil {
Packit 63bb0d
				return credentials.Value{ProviderName: ProviderName}, err
Packit 63bb0d
			}
Packit 63bb0d
			input.TokenCode = aws.String(code)
Packit 63bb0d
		} else {
Packit 63bb0d
			return credentials.Value{ProviderName: ProviderName},
Packit 63bb0d
				awserr.New("AssumeRoleTokenNotAvailable",
Packit 63bb0d
					"assume role with MFA enabled, but neither TokenCode nor TokenProvider are set", nil)
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	roleOutput, err := p.Client.AssumeRole(input)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return credentials.Value{ProviderName: ProviderName}, err
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	// We will proactively generate new credentials before they expire.
Packit 63bb0d
	p.SetExpiration(*roleOutput.Credentials.Expiration, p.ExpiryWindow)
Packit 63bb0d
Packit 63bb0d
	return credentials.Value{
Packit 63bb0d
		AccessKeyID:     *roleOutput.Credentials.AccessKeyId,
Packit 63bb0d
		SecretAccessKey: *roleOutput.Credentials.SecretAccessKey,
Packit 63bb0d
		SessionToken:    *roleOutput.Credentials.SessionToken,
Packit 63bb0d
		ProviderName:    ProviderName,
Packit 63bb0d
	}, nil
Packit 63bb0d
}