forked from gruntwork-io/terratest
-
Notifications
You must be signed in to change notification settings - Fork 0
/
auth.go
140 lines (116 loc) · 5.18 KB
/
auth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package aws
import (
"fmt"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/pquerna/otp/totp"
)
// NewAuthenticatedSession gets an AWS Session, checking that the user has credentials properly configured in their environment.
func NewAuthenticatedSession(region string) (*session.Session, error) {
sess, err := session.NewSession(aws.NewConfig().WithRegion(region))
if err != nil {
return nil, err
}
if _, err = sess.Config.Credentials.Get(); err != nil {
return nil, CredentialsError{UnderlyingErr: err}
}
return sess, nil
}
// NewAuthenticatedSessionFromRole returns a new AWS Session after assuming the
// role whose ARN is provided in roleARN. If the credentials are not properly
// configured in the underlying environment, an error is returned.
func NewAuthenticatedSessionFromRole(region string, roleARN string) (*session.Session, error) {
sess, err := CreateAwsSessionFromRole(region, roleARN)
if err != nil {
return nil, err
}
if _, err = sess.Config.Credentials.Get(); err != nil {
return nil, CredentialsError{UnderlyingErr: err}
}
return sess, nil
}
// CreateAwsSessionFromRole returns a new AWS session after assuming the role
// whose ARN is provided in roleARN.
func CreateAwsSessionFromRole(region string, roleARN string) (*session.Session, error) {
sess, err := session.NewSession(aws.NewConfig().WithRegion(region))
if err != nil {
return nil, err
}
sess = AssumeRole(sess, roleARN)
return sess, err
}
// AssumeRole mutates the provided session by obtaining new credentials by
// assuming the role provided in roleARN.
func AssumeRole(sess *session.Session, roleARN string) *session.Session {
sess.Config.Credentials = stscreds.NewCredentials(sess, roleARN)
return sess
}
// CreateAwsSessionWithCreds creates a new AWS session using explicit credentials. This is useful if you want to create an IAM User dynamically and
// create an AWS session authenticated as the new IAM User.
func CreateAwsSessionWithCreds(region string, accessKeyID string, secretAccessKey string) (*session.Session, error) {
creds := CreateAwsCredentials(accessKeyID, secretAccessKey)
return session.NewSession(aws.NewConfig().WithRegion(region).WithCredentials(creds))
}
// CreateAwsSessionWithMfa creates a new AWS session authenticated using an MFA token retrieved using the given STS client and MFA Device.
func CreateAwsSessionWithMfa(region string, stsClient *sts.STS, mfaDevice *iam.VirtualMFADevice) (*session.Session, error) {
tokenCode, err := GetTimeBasedOneTimePassword(mfaDevice)
if err != nil {
return nil, err
}
output, err := stsClient.GetSessionToken(&sts.GetSessionTokenInput{
SerialNumber: mfaDevice.SerialNumber,
TokenCode: aws.String(tokenCode),
})
if err != nil {
return nil, err
}
accessKeyID := *output.Credentials.AccessKeyId
secretAccessKey := *output.Credentials.SecretAccessKey
sessionToken := *output.Credentials.SessionToken
creds := CreateAwsCredentialsWithSessionToken(accessKeyID, secretAccessKey, sessionToken)
return session.NewSession(aws.NewConfig().WithRegion(region).WithCredentials(creds))
}
// CreateAwsCredentials creates an AWS Credentials configuration with specific AWS credentials.
func CreateAwsCredentials(accessKeyID string, secretAccessKey string) *credentials.Credentials {
creds := credentials.Value{AccessKeyID: accessKeyID, SecretAccessKey: secretAccessKey}
return credentials.NewStaticCredentialsFromCreds(creds)
}
// CreateAwsCredentialsWithSessionToken creates an AWS Credentials configuration with temporary AWS credentials by including a session token (used for
// authenticating with MFA).
func CreateAwsCredentialsWithSessionToken(accessKeyID, secretAccessKey, sessionToken string) *credentials.Credentials {
creds := credentials.Value{
AccessKeyID: accessKeyID,
SecretAccessKey: secretAccessKey,
SessionToken: sessionToken,
}
return credentials.NewStaticCredentialsFromCreds(creds)
}
// GetTimeBasedOneTimePassword gets a One-Time Password from the given mfaDevice. Per the RFC 6238 standard, this value will be different every 30 seconds.
func GetTimeBasedOneTimePassword(mfaDevice *iam.VirtualMFADevice) (string, error) {
base32StringSeed := string(mfaDevice.Base32StringSeed)
otp, err := totp.GenerateCode(base32StringSeed, time.Now())
if err != nil {
return "", err
}
return otp, nil
}
// ReadPasswordPolicyMinPasswordLength returns the minimal password length.
func ReadPasswordPolicyMinPasswordLength(iamClient *iam.IAM) (int, error) {
output, err := iamClient.GetAccountPasswordPolicy(&iam.GetAccountPasswordPolicyInput{})
if err != nil {
return -1, err
}
return int(*output.PasswordPolicy.MinimumPasswordLength), nil
}
// CredentialsError is an error that occurs because AWS credentials can't be found.
type CredentialsError struct {
UnderlyingErr error
}
func (err CredentialsError) Error() string {
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)
}