@@ -2,9 +2,7 @@ package main
22
33import (
44"encoding/json"
5- "errors"
65"io"
7- "io/ioutil"
86"net/http"
97"net/url"
108"os"
@@ -27,6 +25,9 @@ const (
2725DefaultSkipSSLValidation = false
2826DefaultUseInstanceProfiles = false
2927credentialsEndpoint = "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
3233func validSigVersion (v string ) bool {
@@ -636,37 +637,80 @@ func (e s3Endpoint) Connect() (*s3.Client, error) {
636637}
637638
638639func 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
672716return creds , nil
0 commit comments