@@ -5,16 +5,20 @@ package aws
55
66import (
77 "context"
8+ "crypto/sha256"
89 "encoding/base64"
10+ "encoding/hex"
11+ "encoding/json"
912 "fmt"
10- "os"
13+ "io"
14+ "net/http"
1115 "strings"
16+ "time"
1217
13- "github.com/aws/aws-sdk-go/aws/credentials"
14- "github.com/aws/aws-sdk-go/aws/ec2metadata"
15- "github.com/aws/aws-sdk-go/aws/session"
16- "github.com/hashicorp/go-hclog"
17- "github.com/hashicorp/go-secure-stdlib/awsutil"
18+ "github.com/aws/aws-sdk-go-v2/aws"
19+ v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
20+ "github.com/aws/aws-sdk-go-v2/config"
21+ "github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
1822 "github.com/hashicorp/go-uuid"
1923 "github.com/hashicorp/vault/api"
2024)
@@ -32,7 +36,7 @@ type AWSAuth struct {
3236 signatureType string
3337 region string
3438 iamServerIDHeaderValue string
35- creds * credentials. Credentials
39+ creds aws. CredentialsProvider
3640 nonce string
3741}
3842
@@ -95,102 +99,82 @@ func (a *AWSAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, e
9599 loginData := make (map [string ]interface {})
96100 switch a .authType {
97101 case ec2Type :
98- sess , err := session . NewSession ( )
102+ cfg , err := config . LoadDefaultConfig ( ctx , config . WithRegion ( a . region ) )
99103 if err != nil {
100- return nil , fmt .Errorf ("error creating session to probe EC2 metadata : %w" , err )
104+ return nil , fmt .Errorf ("error loading AWS config : %w" , err )
101105 }
102- metadataSvc := ec2metadata .New (sess )
103- if ! metadataSvc .Available () {
104- return nil , fmt .Errorf ("metadata service not available" )
106+ metadataSvc := imds .NewFromConfig (cfg )
107+
108+ var path string
109+ switch a .signatureType {
110+ case pkcs7Type :
111+ path = "/instance-identity/pkcs7"
112+ case identityType :
113+ path = "/instance-identity/document"
114+ case rsa2048Type :
115+ path = "/instance-identity/rsa2048"
116+ default :
117+ return nil , fmt .Errorf ("unknown signature type: %s" , a .signatureType )
105118 }
106119
107- if a .signatureType == pkcs7Type {
108- // fetch PKCS #7 signature
109- resp , err := metadataSvc .GetDynamicData ("/instance-identity/pkcs7" )
110- if err != nil {
111- return nil , fmt .Errorf ("unable to get PKCS 7 data from metadata service: %w" , err )
112- }
113- pkcs7 := strings .TrimSpace (resp )
114- loginData ["pkcs7" ] = pkcs7
115- } else if a .signatureType == identityType {
116- // fetch signature from identity document
117- doc , err := metadataSvc .GetDynamicData ("/instance-identity/document" )
118- if err != nil {
119- return nil , fmt .Errorf ("error requesting instance identity doc: %w" , err )
120- }
121- loginData ["identity" ] = base64 .StdEncoding .EncodeToString ([]byte (doc ))
122-
123- signature , err := metadataSvc .GetDynamicData ("/instance-identity/signature" )
124- if err != nil {
125- return nil , fmt .Errorf ("error requesting signature: %w" , err )
126- }
127- loginData ["signature" ] = signature
128- } else if a .signatureType == rsa2048Type {
129- // fetch RSA 2048 signature, which is also a PKCS#7 signature
130- resp , err := metadataSvc .GetDynamicData ("/instance-identity/rsa2048" )
131- if err != nil {
132- return nil , fmt .Errorf ("unable to get PKCS 7 data from metadata service: %w" , err )
133- }
134- pkcs7 := strings .TrimSpace (resp )
135- loginData ["pkcs7" ] = pkcs7
136- } else {
137- return nil , fmt .Errorf ("unknown signature type: %s" , a .signatureType )
120+ resp , err := metadataSvc .GetDynamicData (ctx , & imds.GetDynamicDataInput {Path : path })
121+ if err != nil {
122+ return nil , fmt .Errorf ("unable to get identity data: %w" , err )
123+ }
124+ defer resp .Content .Close ()
125+ body , err := io .ReadAll (resp .Content )
126+ if err != nil {
127+ return nil , fmt .Errorf ("error reading identity data: %w" , err )
138128 }
129+ pkcs7 := strings .TrimSpace (string (body ))
130+ loginData ["pkcs7" ] = pkcs7
139131
140- // Add the reauthentication value, if we have one
141132 if a .nonce == "" {
142- uid , err := uuid .GenerateUUID ()
133+ uuid , err := uuid .GenerateUUID ()
143134 if err != nil {
144- return nil , fmt .Errorf ("error generating uuid for reauthentication value : %w" , err )
135+ return nil , fmt .Errorf ("error generating uuid: %w" , err )
145136 }
146- a .nonce = uid
137+ a .nonce = uuid
147138 }
148139 loginData ["nonce" ] = a .nonce
149140 case iamType :
150- logger := hclog .Default ()
151141 if a .creds == nil {
152- credsConfig := awsutil.CredentialsConfig {
153- AccessKey : os .Getenv ("AWS_ACCESS_KEY_ID" ),
154- SecretKey : os .Getenv ("AWS_SECRET_ACCESS_KEY" ),
155- SessionToken : os .Getenv ("AWS_SESSION_TOKEN" ),
156- Logger : logger ,
157- }
158-
159- // the env vars above will take precedence if they are set, as
160- // they will be added to the ChainProvider stack first
161- var hasCredsFile bool
162- credsFilePath := os .Getenv ("AWS_SHARED_CREDENTIALS_FILE" )
163- if credsFilePath != "" {
164- hasCredsFile = true
165- credsConfig .Filename = credsFilePath
166- }
167-
168- creds , err := credsConfig .GenerateCredentialChain (awsutil .WithSharedCredentials (hasCredsFile ))
142+ cfg , err := config .LoadDefaultConfig (ctx , config .WithRegion (a .region ))
169143 if err != nil {
170- return nil , err
171- }
172- if creds == nil {
173- return nil , fmt .Errorf ("could not compile valid credential providers from static config, environment, shared, or instance metadata" )
144+ return nil , fmt .Errorf ("unable to load AWS config: %w" , err )
174145 }
146+ a .creds = cfg .Credentials
147+ }
175148
176- _ , err = creds .Get ( )
177- if err != nil {
178- return nil , fmt .Errorf ("failed to retrieve credentials from credential chain : %w" , err )
179- }
149+ credsVal , err := a . creds .Retrieve ( ctx )
150+ if err != nil {
151+ return nil , fmt .Errorf ("failed to retrieve credentials: %w" , err )
152+ }
180153
181- a .creds = creds
154+ const iamBody = "Action=GetCallerIdentity&Version=2011-06-15"
155+ req , err := http .NewRequest ("POST" , "https://sts.amazonaws.com/" , strings .NewReader (iamBody ))
156+ if err != nil {
157+ return nil , fmt .Errorf ("failed to construct STS request: %w" , err )
182158 }
159+ req .Header .Set ("Content-Type" , "application/x-www-form-urlencoded" )
183160
184- data , err := awsutil .GenerateLoginData (a .creds , a .iamServerIDHeaderValue , a .region , logger )
161+ hash := sha256 .Sum256 ([]byte (iamBody ))
162+ payloadHash := hex .EncodeToString (hash [:])
163+
164+ signer := v4 .NewSigner ()
165+ err = signer .SignHTTP (ctx , credsVal , req , payloadHash , "sts" , a .region , time .Now ().UTC ())
185166 if err != nil {
186- return nil , fmt .Errorf ("unable to generate login data for AWS auth endpoint : %w" , err )
167+ return nil , fmt .Errorf ("failed to sign STS request : %w" , err )
187168 }
188- loginData = data
169+
170+ headersData , _ := json .Marshal (req .Header )
171+
172+ loginData ["iam_http_request_method" ] = "POST"
173+ loginData ["iam_request_url" ] = base64 .StdEncoding .EncodeToString ([]byte (req .URL .String ()))
174+ loginData ["iam_request_body" ] = base64 .StdEncoding .EncodeToString ([]byte (iamBody ))
175+ loginData ["iam_request_headers" ] = base64 .StdEncoding .EncodeToString (headersData )
189176 }
190177
191- // Add role if we have one. If not, Vault will infer the role name based
192- // on the IAM friendly name (iam auth type) or EC2 instance's
193- // AMI ID (ec2 auth type).
194178 if a .roleName != "" {
195179 loginData ["role" ] = a .roleName
196180 }
0 commit comments