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  }