github.com/darmach/terratest@v0.34.8-0.20210517103231-80931f95e3ff/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 sess, err := session.NewSession(aws.NewConfig().WithRegion(region)) 34 if err != nil { 35 return nil, err 36 } 37 38 if _, err = sess.Config.Credentials.Get(); err != nil { 39 return nil, CredentialsError{UnderlyingErr: err} 40 } 41 42 return sess, nil 43 } 44 45 // NewAuthenticatedSessionFromRole returns a new AWS Session after assuming the 46 // role whose ARN is provided in roleARN. If the credentials are not properly 47 // configured in the underlying environment, an error is returned. 48 func NewAuthenticatedSessionFromRole(region string, roleARN string) (*session.Session, error) { 49 sess, err := CreateAwsSessionFromRole(region, roleARN) 50 if err != nil { 51 return nil, err 52 } 53 54 if _, err = sess.Config.Credentials.Get(); err != nil { 55 return nil, CredentialsError{UnderlyingErr: err} 56 } 57 58 return sess, nil 59 } 60 61 // CreateAwsSessionFromRole returns a new AWS session after assuming the role 62 // whose ARN is provided in roleARN. 63 func CreateAwsSessionFromRole(region string, roleARN string) (*session.Session, error) { 64 sess, err := session.NewSession(aws.NewConfig().WithRegion(region)) 65 if err != nil { 66 return nil, err 67 } 68 sess = AssumeRole(sess, roleARN) 69 return sess, err 70 } 71 72 // AssumeRole mutates the provided session by obtaining new credentials by 73 // assuming the role provided in roleARN. 74 func AssumeRole(sess *session.Session, roleARN string) *session.Session { 75 sess.Config.Credentials = stscreds.NewCredentials(sess, roleARN) 76 return sess 77 } 78 79 // CreateAwsSessionWithCreds creates a new AWS session using explicit credentials. This is useful if you want to create an IAM User dynamically and 80 // create an AWS session authenticated as the new IAM User. 81 func CreateAwsSessionWithCreds(region string, accessKeyID string, secretAccessKey string) (*session.Session, error) { 82 creds := CreateAwsCredentials(accessKeyID, secretAccessKey) 83 return session.NewSession(aws.NewConfig().WithRegion(region).WithCredentials(creds)) 84 } 85 86 // CreateAwsSessionWithMfa creates a new AWS session authenticated using an MFA token retrieved using the given STS client and MFA Device. 87 func CreateAwsSessionWithMfa(region string, stsClient *sts.STS, mfaDevice *iam.VirtualMFADevice) (*session.Session, error) { 88 tokenCode, err := GetTimeBasedOneTimePassword(mfaDevice) 89 if err != nil { 90 return nil, err 91 } 92 93 output, err := stsClient.GetSessionToken(&sts.GetSessionTokenInput{ 94 SerialNumber: mfaDevice.SerialNumber, 95 TokenCode: aws.String(tokenCode), 96 }) 97 if err != nil { 98 return nil, err 99 } 100 101 accessKeyID := *output.Credentials.AccessKeyId 102 secretAccessKey := *output.Credentials.SecretAccessKey 103 sessionToken := *output.Credentials.SessionToken 104 105 creds := CreateAwsCredentialsWithSessionToken(accessKeyID, secretAccessKey, sessionToken) 106 return session.NewSession(aws.NewConfig().WithRegion(region).WithCredentials(creds)) 107 } 108 109 // CreateAwsCredentials creates an AWS Credentials configuration with specific AWS credentials. 110 func CreateAwsCredentials(accessKeyID string, secretAccessKey string) *credentials.Credentials { 111 creds := credentials.Value{AccessKeyID: accessKeyID, SecretAccessKey: secretAccessKey} 112 return credentials.NewStaticCredentialsFromCreds(creds) 113 } 114 115 // CreateAwsCredentialsWithSessionToken creates an AWS Credentials configuration with temporary AWS credentials by including a session token (used for 116 // authenticating with MFA). 117 func CreateAwsCredentialsWithSessionToken(accessKeyID, secretAccessKey, sessionToken string) *credentials.Credentials { 118 creds := credentials.Value{ 119 AccessKeyID: accessKeyID, 120 SecretAccessKey: secretAccessKey, 121 SessionToken: sessionToken, 122 } 123 return credentials.NewStaticCredentialsFromCreds(creds) 124 } 125 126 // GetTimeBasedOneTimePassword gets a One-Time Password from the given mfaDevice. Per the RFC 6238 standard, this value will be different every 30 seconds. 127 func GetTimeBasedOneTimePassword(mfaDevice *iam.VirtualMFADevice) (string, error) { 128 base32StringSeed := string(mfaDevice.Base32StringSeed) 129 130 otp, err := totp.GenerateCode(base32StringSeed, time.Now()) 131 if err != nil { 132 return "", err 133 } 134 135 return otp, nil 136 } 137 138 // ReadPasswordPolicyMinPasswordLength returns the minimal password length. 139 func ReadPasswordPolicyMinPasswordLength(iamClient *iam.IAM) (int, error) { 140 output, err := iamClient.GetAccountPasswordPolicy(&iam.GetAccountPasswordPolicyInput{}) 141 if err != nil { 142 return -1, err 143 } 144 145 return int(*output.PasswordPolicy.MinimumPasswordLength), nil 146 } 147 148 // CredentialsError is an error that occurs because AWS credentials can't be found. 149 type CredentialsError struct { 150 UnderlyingErr error 151 } 152 153 func (err CredentialsError) Error() string { 154 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) 155 }