Skip to content

Commit e14b28a

Browse files
feat(aws): fallback to IMDSv1 if session token cannot be retrieved
1 parent 9d2ad98 commit e14b28a

File tree

1 file changed

+38
-30
lines changed

1 file changed

+38
-30
lines changed

pkg/rclone/aws.go

+38-30
Original file line numberDiff line numberDiff line change
@@ -9,53 +9,34 @@ import (
99
"net/http"
1010
"time"
1111

12+
"github.com/pkg/errors"
1213
"github.com/rclone/rclone/fs"
1314
)
1415

1516
// awsRegionFromMetadataAPI uses instance metadata API v2 to fetch region of the
1617
// running instance see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html
1718
// Returns empty string if region can't be obtained for whatever reason.
19+
// Fallbacks to IMDSv1 if the session token cannot be obtained.
1820
func awsRegionFromMetadataAPI() string {
19-
const (
20-
tokenUrl = "http://169.254.169.254/latest/api/token"
21-
docURL = "http://169.254.169.254/latest/dynamic/instance-identity/document"
22-
)
21+
const docURL = "http://169.254.169.254/latest/dynamic/instance-identity/document"
2322

2423
// Step 1: Request an IMDSv2 session token
25-
reqToken, err := http.NewRequestWithContext(context.Background(), http.MethodPut, tokenUrl, nil)
24+
token, err := awsAPIToken()
25+
// fallback to IMDSv1 when error on token retrieval
2626
if err != nil {
27-
fs.Errorf(nil, "create token request: %+v", err)
28-
return ""
29-
}
30-
reqToken.Header.Set("X-aws-ec2-metadata-token-ttl-seconds", "21600")
31-
tokenClient := http.Client{
32-
Timeout: 2 * time.Second,
33-
}
34-
resToken, err := tokenClient.Do(reqToken)
35-
if err != nil {
36-
fs.Errorf(nil, "IMDSv2 failed to fetch session token: %+v", err)
37-
return ""
38-
}
39-
defer resToken.Body.Close()
40-
41-
if resToken.StatusCode != http.StatusOK {
42-
fs.Errorf(nil, "failed to retrieve session token: %s", resToken.Status)
43-
return ""
44-
}
45-
46-
token, err := io.ReadAll(resToken.Body)
47-
if err != nil {
48-
fs.Errorf(nil, "Failed to read session token: %+v", err)
49-
return ""
27+
fs.Errorf(nil, "%+v", err)
28+
token = ""
5029
}
5130

5231
// Step 2: Use the session token to retrieve instance metadata
53-
reqMetadata, err := http.NewRequestWithContext(context.Background(), http.MethodGet, docURL, nil)
32+
reqMetadata, err := http.NewRequestWithContext(context.Background(), http.MethodGet, docURL, http.NoBody)
5433
if err != nil {
5534
fs.Errorf(nil, "create metadata request: %+v", err)
5635
return ""
5736
}
58-
reqMetadata.Header.Set("X-aws-ec2-metadata-token", string(token))
37+
if token != "" {
38+
reqMetadata.Header.Set("X-aws-ec2-metadata-token", token)
39+
}
5940

6041
metadataClient := http.Client{
6142
Timeout: 2 * time.Second,
@@ -77,3 +58,30 @@ func awsRegionFromMetadataAPI() string {
7758

7859
return metadata.Region
7960
}
61+
62+
func awsAPIToken() (string, error) {
63+
const tokenURL = "http://169.254.169.254/latest/api/token"
64+
65+
reqToken, err := http.NewRequestWithContext(context.Background(), http.MethodPut, tokenURL, http.NoBody)
66+
if err != nil {
67+
return "", errors.Wrap(err, "create token request")
68+
}
69+
reqToken.Header.Set("X-aws-ec2-metadata-token-ttl-seconds", "21600")
70+
tokenClient := http.Client{
71+
Timeout: 2 * time.Second,
72+
}
73+
resToken, err := tokenClient.Do(reqToken)
74+
if err != nil {
75+
return "", errors.Wrap(err, "IMDSv2 failed to fetch session token")
76+
}
77+
defer resToken.Body.Close()
78+
79+
if resToken.StatusCode != http.StatusOK {
80+
return "", errors.Wrap(err, "failed to retrieve session token")
81+
}
82+
token, err := io.ReadAll(resToken.Body)
83+
if err != nil {
84+
return "", errors.Wrap(err, "failed to read session token")
85+
}
86+
return string(token), nil
87+
}

0 commit comments

Comments
 (0)