260 lines
7.3 KiB
Go
260 lines
7.3 KiB
Go
package session
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
|
"github.com/aws/aws-sdk-go/aws/credentials/processcreds"
|
|
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
|
"github.com/aws/aws-sdk-go/aws/defaults"
|
|
"github.com/aws/aws-sdk-go/aws/request"
|
|
"github.com/aws/aws-sdk-go/internal/shareddefaults"
|
|
)
|
|
|
|
func resolveCredentials(cfg *aws.Config,
|
|
envCfg envConfig, sharedCfg sharedConfig,
|
|
handlers request.Handlers,
|
|
sessOpts Options,
|
|
) (*credentials.Credentials, error) {
|
|
|
|
switch {
|
|
case len(sessOpts.Profile) != 0:
|
|
// User explicitly provided an Profile in the session's configuration
|
|
// so load that profile from shared config first.
|
|
// Github(aws/aws-sdk-go#2727)
|
|
return resolveCredsFromProfile(cfg, envCfg, sharedCfg, handlers, sessOpts)
|
|
|
|
case envCfg.Creds.HasKeys():
|
|
// Environment credentials
|
|
return credentials.NewStaticCredentialsFromCreds(envCfg.Creds), nil
|
|
|
|
case len(envCfg.WebIdentityTokenFilePath) != 0:
|
|
// Web identity token from environment, RoleARN required to also be
|
|
// set.
|
|
return assumeWebIdentity(cfg, handlers,
|
|
envCfg.WebIdentityTokenFilePath,
|
|
envCfg.RoleARN,
|
|
envCfg.RoleSessionName,
|
|
)
|
|
|
|
default:
|
|
// Fallback to the "default" credential resolution chain.
|
|
return resolveCredsFromProfile(cfg, envCfg, sharedCfg, handlers, sessOpts)
|
|
}
|
|
}
|
|
|
|
// WebIdentityEmptyRoleARNErr will occur if 'AWS_WEB_IDENTITY_TOKEN_FILE' was set but
|
|
// 'AWS_IAM_ROLE_ARN' was not set.
|
|
var WebIdentityEmptyRoleARNErr = awserr.New(stscreds.ErrCodeWebIdentity, "role ARN is not set", nil)
|
|
|
|
// WebIdentityEmptyTokenFilePathErr will occur if 'AWS_IAM_ROLE_ARN' was set but
|
|
// 'AWS_WEB_IDENTITY_TOKEN_FILE' was not set.
|
|
var WebIdentityEmptyTokenFilePathErr = awserr.New(stscreds.ErrCodeWebIdentity, "token file path is not set", nil)
|
|
|
|
func assumeWebIdentity(cfg *aws.Config, handlers request.Handlers,
|
|
filepath string,
|
|
roleARN, sessionName string,
|
|
) (*credentials.Credentials, error) {
|
|
|
|
if len(filepath) == 0 {
|
|
return nil, WebIdentityEmptyTokenFilePathErr
|
|
}
|
|
|
|
if len(roleARN) == 0 {
|
|
return nil, WebIdentityEmptyRoleARNErr
|
|
}
|
|
|
|
creds := stscreds.NewWebIdentityCredentials(
|
|
&Session{
|
|
Config: cfg,
|
|
Handlers: handlers.Copy(),
|
|
},
|
|
roleARN,
|
|
sessionName,
|
|
filepath,
|
|
)
|
|
|
|
return creds, nil
|
|
}
|
|
|
|
func resolveCredsFromProfile(cfg *aws.Config,
|
|
envCfg envConfig, sharedCfg sharedConfig,
|
|
handlers request.Handlers,
|
|
sessOpts Options,
|
|
) (creds *credentials.Credentials, err error) {
|
|
|
|
switch {
|
|
case sharedCfg.SourceProfile != nil:
|
|
// Assume IAM role with credentials source from a different profile.
|
|
creds, err = resolveCredsFromProfile(cfg, envCfg,
|
|
*sharedCfg.SourceProfile, handlers, sessOpts,
|
|
)
|
|
|
|
case sharedCfg.Creds.HasKeys():
|
|
// Static Credentials from Shared Config/Credentials file.
|
|
creds = credentials.NewStaticCredentialsFromCreds(
|
|
sharedCfg.Creds,
|
|
)
|
|
|
|
case len(sharedCfg.CredentialProcess) != 0:
|
|
// Get credentials from CredentialProcess
|
|
creds = processcreds.NewCredentials(sharedCfg.CredentialProcess)
|
|
|
|
case len(sharedCfg.CredentialSource) != 0:
|
|
creds, err = resolveCredsFromSource(cfg, envCfg,
|
|
sharedCfg, handlers, sessOpts,
|
|
)
|
|
|
|
case len(sharedCfg.WebIdentityTokenFile) != 0:
|
|
// Credentials from Assume Web Identity token require an IAM Role, and
|
|
// that roll will be assumed. May be wrapped with another assume role
|
|
// via SourceProfile.
|
|
return assumeWebIdentity(cfg, handlers,
|
|
sharedCfg.WebIdentityTokenFile,
|
|
sharedCfg.RoleARN,
|
|
sharedCfg.RoleSessionName,
|
|
)
|
|
|
|
default:
|
|
// Fallback to default credentials provider, include mock errors for
|
|
// the credential chain so user can identify why credentials failed to
|
|
// be retrieved.
|
|
creds = credentials.NewCredentials(&credentials.ChainProvider{
|
|
VerboseErrors: aws.BoolValue(cfg.CredentialsChainVerboseErrors),
|
|
Providers: []credentials.Provider{
|
|
&credProviderError{
|
|
Err: awserr.New("EnvAccessKeyNotFound",
|
|
"failed to find credentials in the environment.", nil),
|
|
},
|
|
&credProviderError{
|
|
Err: awserr.New("SharedCredsLoad",
|
|
fmt.Sprintf("failed to load profile, %s.", envCfg.Profile), nil),
|
|
},
|
|
defaults.RemoteCredProvider(*cfg, handlers),
|
|
},
|
|
})
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(sharedCfg.RoleARN) > 0 {
|
|
cfgCp := *cfg
|
|
cfgCp.Credentials = creds
|
|
return credsFromAssumeRole(cfgCp, handlers, sharedCfg, sessOpts)
|
|
}
|
|
|
|
return creds, nil
|
|
}
|
|
|
|
// valid credential source values
|
|
const (
|
|
credSourceEc2Metadata = "Ec2InstanceMetadata"
|
|
credSourceEnvironment = "Environment"
|
|
credSourceECSContainer = "EcsContainer"
|
|
)
|
|
|
|
func resolveCredsFromSource(cfg *aws.Config,
|
|
envCfg envConfig, sharedCfg sharedConfig,
|
|
handlers request.Handlers,
|
|
sessOpts Options,
|
|
) (creds *credentials.Credentials, err error) {
|
|
|
|
switch sharedCfg.CredentialSource {
|
|
case credSourceEc2Metadata:
|
|
p := defaults.RemoteCredProvider(*cfg, handlers)
|
|
creds = credentials.NewCredentials(p)
|
|
|
|
case credSourceEnvironment:
|
|
creds = credentials.NewStaticCredentialsFromCreds(envCfg.Creds)
|
|
|
|
case credSourceECSContainer:
|
|
if len(os.Getenv(shareddefaults.ECSCredsProviderEnvVar)) == 0 {
|
|
return nil, ErrSharedConfigECSContainerEnvVarEmpty
|
|
}
|
|
|
|
p := defaults.RemoteCredProvider(*cfg, handlers)
|
|
creds = credentials.NewCredentials(p)
|
|
|
|
default:
|
|
return nil, ErrSharedConfigInvalidCredSource
|
|
}
|
|
|
|
return creds, nil
|
|
}
|
|
|
|
func credsFromAssumeRole(cfg aws.Config,
|
|
handlers request.Handlers,
|
|
sharedCfg sharedConfig,
|
|
sessOpts Options,
|
|
) (*credentials.Credentials, error) {
|
|
|
|
if len(sharedCfg.MFASerial) != 0 && sessOpts.AssumeRoleTokenProvider == nil {
|
|
// AssumeRole Token provider is required if doing Assume Role
|
|
// with MFA.
|
|
return nil, AssumeRoleTokenProviderNotSetError{}
|
|
}
|
|
|
|
return stscreds.NewCredentials(
|
|
&Session{
|
|
Config: &cfg,
|
|
Handlers: handlers.Copy(),
|
|
},
|
|
sharedCfg.RoleARN,
|
|
func(opt *stscreds.AssumeRoleProvider) {
|
|
opt.RoleSessionName = sharedCfg.RoleSessionName
|
|
opt.Duration = sessOpts.AssumeRoleDuration
|
|
|
|
// Assume role with external ID
|
|
if len(sharedCfg.ExternalID) > 0 {
|
|
opt.ExternalID = aws.String(sharedCfg.ExternalID)
|
|
}
|
|
|
|
// Assume role with MFA
|
|
if len(sharedCfg.MFASerial) > 0 {
|
|
opt.SerialNumber = aws.String(sharedCfg.MFASerial)
|
|
opt.TokenProvider = sessOpts.AssumeRoleTokenProvider
|
|
}
|
|
},
|
|
), nil
|
|
}
|
|
|
|
// AssumeRoleTokenProviderNotSetError is an error returned when creating a
|
|
// session when the MFAToken option is not set when shared config is configured
|
|
// load assume a role with an MFA token.
|
|
type AssumeRoleTokenProviderNotSetError struct{}
|
|
|
|
// Code is the short id of the error.
|
|
func (e AssumeRoleTokenProviderNotSetError) Code() string {
|
|
return "AssumeRoleTokenProviderNotSetError"
|
|
}
|
|
|
|
// Message is the description of the error
|
|
func (e AssumeRoleTokenProviderNotSetError) Message() string {
|
|
return fmt.Sprintf("assume role with MFA enabled, but AssumeRoleTokenProvider session option not set.")
|
|
}
|
|
|
|
// OrigErr is the underlying error that caused the failure.
|
|
func (e AssumeRoleTokenProviderNotSetError) OrigErr() error {
|
|
return nil
|
|
}
|
|
|
|
// Error satisfies the error interface.
|
|
func (e AssumeRoleTokenProviderNotSetError) Error() string {
|
|
return awserr.SprintError(e.Code(), e.Message(), "", nil)
|
|
}
|
|
|
|
type credProviderError struct {
|
|
Err error
|
|
}
|
|
|
|
func (c credProviderError) Retrieve() (credentials.Value, error) {
|
|
return credentials.Value{}, c.Err
|
|
}
|
|
func (c credProviderError) IsExpired() bool {
|
|
return true
|
|
}
|