Skip to content

Commit ed150ba

Browse files
authored
Update S3 plugin to use IMDSv2 (#797)
1 parent 549507e commit ed150ba

File tree

1 file changed

+69
-25
lines changed

1 file changed

+69
-25
lines changed

plugin/s3/plugin.go

Lines changed: 69 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ package main
22

33
import (
44
"encoding/json"
5-
"errors"
65
"io"
7-
"io/ioutil"
86
"net/http"
97
"net/url"
108
"os"
@@ -27,6 +25,9 @@ const (
2725
DefaultSkipSSLValidation = false
2826
DefaultUseInstanceProfiles = false
2927
credentialsEndpoint = "http://169.254.169.254/latest/meta-data/iam/security-credentials"
28+
// IMDSv2 endpoints
29+
imdsTokenEndpoint = "http://169.254.169.254/latest/api/token"
30+
imdsTokenTTL = "21600" // 6 hours in seconds
3031
)
3132

3233
func validSigVersion(v string) bool {
@@ -636,37 +637,80 @@ func (e s3Endpoint) Connect() (*s3.Client, error) {
636637
}
637638

638639
func getInstanceProfileCredentials() (instanceProfileCredentials, error) {
639-
response, connectErr := http.Get(fmt.Sprintf("%s/", credentialsEndpoint))
640-
if connectErr != nil {
641-
return instanceProfileCredentials{}, connectErr
642-
} else if response.StatusCode != 200 {
643-
return instanceProfileCredentials{}, errors.New(fmt.Sprintf("Connection request to %s/ failed with code %d", credentialsEndpoint, response.StatusCode))
640+
var creds instanceProfileCredentials
641+
642+
// Step 1: Get IMDSv2 token
643+
tokenReq, err := http.NewRequest("PUT", imdsTokenEndpoint, nil)
644+
if err != nil {
645+
return creds, fmt.Errorf("failed to create token request: %v", err)
644646
}
647+
tokenReq.Header.Set("X-aws-ec2-metadata-token-ttl-seconds", imdsTokenTTL)
645648

646-
body, readErr := ioutil.ReadAll(response.Body)
647-
if readErr != nil {
648-
return instanceProfileCredentials{}, readErr
649+
client := &http.Client{Timeout: 10 * time.Second}
650+
tokenResp, err := client.Do(tokenReq)
651+
if err != nil {
652+
return creds, fmt.Errorf("failed to get IMDSv2 token: %v", err)
649653
}
650-
role := string(body)
651-
response.Body.Close()
654+
defer tokenResp.Body.Close()
652655

653-
var creds instanceProfileCredentials
654-
response, connectErr = http.Get(fmt.Sprintf("%s/%s", credentialsEndpoint, role))
655-
if connectErr != nil {
656-
return instanceProfileCredentials{}, connectErr
657-
} else if response.StatusCode != 200 {
658-
return instanceProfileCredentials{}, errors.New(fmt.Sprintf("Connection request to %s/%s failed with code %d", credentialsEndpoint, role, response.StatusCode))
656+
if tokenResp.StatusCode != 200 {
657+
return creds, fmt.Errorf("failed to get IMDSv2 token, status: %d", tokenResp.StatusCode)
659658
}
660-
defer response.Body.Close()
661659

662-
body, readErr = ioutil.ReadAll(response.Body)
663-
if readErr != nil {
664-
return instanceProfileCredentials{}, readErr
660+
tokenBytes, err := io.ReadAll(tokenResp.Body)
661+
if err != nil {
662+
return creds, fmt.Errorf("failed to read IMDSv2 token: %v", err)
665663
}
664+
token := strings.TrimSpace(string(tokenBytes))
666665

667-
unmarshallErr := json.Unmarshal(body, &creds)
668-
if unmarshallErr != nil {
669-
return instanceProfileCredentials{}, unmarshallErr
666+
// Step 2: Get IAM role name using IMDSv2 token
667+
roleReq, err := http.NewRequest("GET", credentialsEndpoint, nil)
668+
if err != nil {
669+
return creds, fmt.Errorf("failed to create role request: %v", err)
670+
}
671+
roleReq.Header.Set("X-aws-ec2-metadata-token", token)
672+
673+
roleResp, err := client.Do(roleReq)
674+
if err != nil {
675+
return creds, fmt.Errorf("failed to get IAM role: %v", err)
676+
}
677+
defer roleResp.Body.Close()
678+
679+
if roleResp.StatusCode != 200 {
680+
return creds, fmt.Errorf("failed to get IAM role, status: %d", roleResp.StatusCode)
681+
}
682+
683+
roleBytes, err := io.ReadAll(roleResp.Body)
684+
if err != nil {
685+
return creds, fmt.Errorf("failed to read IAM role: %v", err)
686+
}
687+
roleName := strings.TrimSpace(string(roleBytes))
688+
689+
// Step 3: Get credentials using the role name and IMDSv2 token
690+
credReq, err := http.NewRequest("GET", credentialsEndpoint+"/"+roleName, nil)
691+
if err != nil {
692+
return creds, fmt.Errorf("failed to create credentials request: %v", err)
693+
}
694+
credReq.Header.Set("X-aws-ec2-metadata-token", token)
695+
696+
credResp, err := client.Do(credReq)
697+
if err != nil {
698+
return creds, fmt.Errorf("failed to get credentials: %v", err)
699+
}
700+
defer credResp.Body.Close()
701+
702+
if credResp.StatusCode != 200 {
703+
return creds, fmt.Errorf("failed to get credentials, status: %d", credResp.StatusCode)
704+
}
705+
706+
credBytes, err := io.ReadAll(credResp.Body)
707+
if err != nil {
708+
return creds, fmt.Errorf("failed to read credentials: %v", err)
709+
}
710+
711+
err = json.Unmarshal(credBytes, &creds)
712+
if err != nil {
713+
return creds, fmt.Errorf("failed to parse credentials JSON: %v", err)
670714
}
671715

672716
return creds, nil

0 commit comments

Comments
 (0)