@@ -9,53 +9,34 @@ import (
9
9
"net/http"
10
10
"time"
11
11
12
+ "github.com/pkg/errors"
12
13
"github.com/rclone/rclone/fs"
13
14
)
14
15
15
16
// awsRegionFromMetadataAPI uses instance metadata API v2 to fetch region of the
16
17
// running instance see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html
17
18
// Returns empty string if region can't be obtained for whatever reason.
19
+ // Fallbacks to IMDSv1 if the session token cannot be obtained.
18
20
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"
23
22
24
23
// 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
26
26
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 = ""
50
29
}
51
30
52
31
// 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 )
54
33
if err != nil {
55
34
fs .Errorf (nil , "create metadata request: %+v" , err )
56
35
return ""
57
36
}
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
+ }
59
40
60
41
metadataClient := http.Client {
61
42
Timeout : 2 * time .Second ,
@@ -77,3 +58,30 @@ func awsRegionFromMetadataAPI() string {
77
58
78
59
return metadata .Region
79
60
}
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