github.com/mponton/terratest@v0.44.0/modules/aws/auth.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "os" 6 "time" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/aws/credentials" 10 "github.com/aws/aws-sdk-go/aws/credentials/stscreds" 11 "github.com/aws/aws-sdk-go/aws/session" 12 "github.com/aws/aws-sdk-go/service/iam" 13 "github.com/aws/aws-sdk-go/service/sts" 14 "github.com/pquerna/otp/totp" 15 ) 16 17 const ( 18 AuthAssumeRoleEnvVar = "TERRATEST_IAM_ROLE" // OS environment variable name through which Assume Role ARN may be passed for authentication 19 ) 20 21 // NewAuthenticatedSession creates an AWS session following to standard AWS authentication workflow. 22 // If AuthAssumeIamRoleEnvVar environment variable is set, assumes IAM role specified in it. 23 func NewAuthenticatedSession(region string) (*session.Session, error) { 24 if assumeRoleArn, ok := os.LookupEnv(AuthAssumeRoleEnvVar); ok { 25 return NewAuthenticatedSessionFromRole(region, assumeRoleArn) 26 } else { 27 return NewAuthenticatedSessionFromDefaultCredentials(region) 28 } 29 } 30 31 // NewAuthenticatedSessionFromDefaultCredentials gets an AWS Session, checking that the user has credentials properly configured in their environment. 32 func NewAuthenticatedSessionFromDefaultCredentials(region string) (*session.Session, error) { 33 awsConfig := aws.NewConfig().WithRegion(region) 34 35 sessionOptions := session.Options{ 36 Config: *awsConfig, 37 SharedConfigState: session.SharedConfigEnable, 38 } 39 40 sess, err := session.NewSessionWithOptions(sessionOptions) 41 if err != nil { 42 return nil, err 43 } 44 45 if _, err = sess.Config.Credentials.Get(); err != nil { 46 return nil, CredentialsError{UnderlyingErr: err} 47 } 48 49 return sess, nil 50 } 51 52 // NewAuthenticatedSessionFromRole returns a new AWS Session after assuming the 53 // role whose ARN is provided in roleARN. If the credentials are not properly 54 // configured in the underlying environment, an error is returned. 55 func NewAuthenticatedSessionFromRole(region string, roleARN string) (*session.Session, error) { 56 sess, err := CreateAwsSessionFromRole(region, roleARN) 57 if err != nil { 58 return nil, err 59 } 60 61 if _, err = sess.Config.Credentials.Get(); err != nil { 62 return nil, CredentialsError{UnderlyingErr: err} 63 } 64 65 return sess, nil 66 } 67 68 // CreateAwsSessionFromRole returns a new AWS session after assuming the role 69 // whose ARN is provided in roleARN. 70 func CreateAwsSessionFromRole(region string, roleARN string) (*session.Session, error) { 71 sess, err := session.NewSession(aws.NewConfig().WithRegion(region)) 72 if err != nil { 73 return nil, err 74 } 75 sess = AssumeRole(sess, roleARN) 76 return sess, err 77 } 78 79 // AssumeRole mutates the provided session by obtaining new credentials by 80 // assuming the role provided in roleARN. 81 func AssumeRole(sess *session.Session, roleARN string) *session.Session { 82 sess.Config.Credentials = stscreds.NewCredentials(sess, roleARN) 83 return sess 84 } 85 86 // CreateAwsSessionWithCreds creates a new AWS session using explicit credentials. This is useful if you want to create an IAM User dynamically and 87 // create an AWS session authenticated as the new IAM User. 88 func CreateAwsSessionWithCreds(region string, accessKeyID string, secretAccessKey string) (*session.Session, error) { 89 creds := CreateAwsCredentials(accessKeyID, secretAccessKey) 90 return session.NewSession(aws.NewConfig().WithRegion(region).WithCredentials(creds)) 91 } 92 93 // CreateAwsSessionWithMfa creates a new AWS session authenticated using an MFA token retrieved using the given STS client and MFA Device. 94 func CreateAwsSessionWithMfa(region string, stsClient *sts.STS, mfaDevice *iam.VirtualMFADevice) (*session.Session, error) { 95 tokenCode, err := GetTimeBasedOneTimePassword(mfaDevice) 96 if err != nil { 97 return nil, err 98 } 99 100 output, err := stsClient.GetSessionToken(&sts.GetSessionTokenInput{ 101 SerialNumber: mfaDevice.SerialNumber, 102 TokenCode: aws.String(tokenCode), 103 }) 104 if err != nil { 105 return nil, err 106 } 107 108 accessKeyID := *output.Credentials.AccessKeyId 109 secretAccessKey := *output.Credentials.SecretAccessKey 110 sessionToken := *output.Credentials.SessionToken 111 112 creds := CreateAwsCredentialsWithSessionToken(accessKeyID, secretAccessKey, sessionToken) 113 return session.NewSession(aws.NewConfig().WithRegion(region).WithCredentials(creds)) 114 } 115 116 // CreateAwsCredentials creates an AWS Credentials configuration with specific AWS credentials. 117 func CreateAwsCredentials(accessKeyID string, secretAccessKey string) *credentials.Credentials { 118 creds := credentials.Value{AccessKeyID: accessKeyID, SecretAccessKey: secretAccessKey} 119 return credentials.NewStaticCredentialsFromCreds(creds) 120 } 121 122 // CreateAwsCredentialsWithSessionToken creates an AWS Credentials configuration with temporary AWS credentials by including a session token (used for 123 // authenticating with MFA). 124 func CreateAwsCredentialsWithSessionToken(accessKeyID, secretAccessKey, sessionToken string) *credentials.Credentials { 125 creds := credentials.Value{ 126 AccessKeyID: accessKeyID, 127 SecretAccessKey: secretAccessKey, 128 SessionToken: sessionToken, 129 } 130 return credentials.NewStaticCredentialsFromCreds(creds) 131 } 132 133 // GetTimeBasedOneTimePassword gets a One-Time Password from the given mfaDevice. Per the RFC 6238 standard, this value will be different every 30 seconds. 134 func GetTimeBasedOneTimePassword(mfaDevice *iam.VirtualMFADevice) (string, error) { 135 base32StringSeed := string(mfaDevice.Base32StringSeed) 136 137 otp, err := totp.GenerateCode(base32StringSeed, time.Now()) 138 if err != nil { 139 return "", err 140 } 141 142 return otp, nil 143 } 144 145 // ReadPasswordPolicyMinPasswordLength returns the minimal password length. 146 func ReadPasswordPolicyMinPasswordLength(iamClient *iam.IAM) (int, error) { 147 output, err := iamClient.GetAccountPasswordPolicy(&iam.GetAccountPasswordPolicyInput{}) 148 if err != nil { 149 return -1, err 150 } 151 152 return int(*output.PasswordPolicy.MinimumPasswordLength), nil 153 } 154 155 // CredentialsError is an error that occurs because AWS credentials can't be found. 156 type CredentialsError struct { 157 UnderlyingErr error 158 } 159 160 func (err CredentialsError) Error() string { 161 return fmt.Sprintf("Error finding AWS credentials. Did you set the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables or configure an AWS profile? Underlying error: %v", err.UnderlyingErr) 162 }