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  }