@@ -17,6 +17,7 @@ import (
1717 "sync"
1818 "time"
1919
20+ "v.io/v23/logging"
2021 "v.io/x/ref/lib/stats"
2122 "v.io/x/ref/runtime/internal/cloudvm/cloudpaths"
2223)
@@ -56,65 +57,88 @@ const (
5657)
5758
5859var (
59- onceAWS sync.Once
60- onAWS bool
60+ onceAWS sync.Once
61+ onAWS bool
62+ onIMDSv2 bool
6163)
6264
6365// OnAWS returns true if this process is running on Amazon Web Services.
6466// If true, the the stats variables AWSAccountIDStatName and GCPRegionStatName
6567// are set.
66- func OnAWS (ctx context.Context , timeout time.Duration ) bool {
68+ func OnAWS (ctx context.Context , logger logging. Logger , timeout time.Duration ) bool {
6769 onceAWS .Do (func () {
68- onAWS = awsInit (ctx , timeout )
70+ onAWS , onIMDSv2 = awsInit (ctx , logger , timeout )
71+ logger .VI (1 ).Infof ("OnAWS: onAWS: %v, onIMDSv2: %v" , onAWS , onIMDSv2 )
6972 })
7073 return onAWS
7174}
7275
7376// AWSPublicAddrs returns the current public IP of this AWS instance.
77+ // Must be called after OnAWS.
7478func AWSPublicAddrs (ctx context.Context , timeout time.Duration ) ([]net.Addr , error ) {
75- return awsGetAddr (ctx , awsExternalURL (), timeout )
79+ return awsGetAddr (ctx , onIMDSv2 , awsExternalURL (), timeout )
7680}
7781
7882// AWSPrivateAddrs returns the current private Addrs of this AWS instance.
83+ // Must be called after OnAWS.
7984func AWSPrivateAddrs (ctx context.Context , timeout time.Duration ) ([]net.Addr , error ) {
80- return awsGetAddr (ctx , awsInternalURL (), timeout )
85+ return awsGetAddr (ctx , onIMDSv2 , awsInternalURL (), timeout )
8186}
8287
83- func awsGet (ctx context.Context , url string , timeout time.Duration ) ([]byte , error ) {
88+ func awsGet (ctx context.Context , imdsv2 bool , url string , timeout time.Duration ) ([]byte , error ) {
8489 client := & http.Client {Timeout : timeout }
85- token , err := awsSetIMDSv2Token (ctx , awsTokenURL (), timeout )
86- if err != nil {
87- return nil , err
90+ var token string
91+ var err error
92+ if imdsv2 {
93+ token , err = awsSetIMDSv2Token (ctx , awsTokenURL (), timeout )
94+ if err != nil {
95+ return nil , err
96+ }
8897 }
8998 req , err := http .NewRequestWithContext (ctx , "GET" , url , nil )
90- req .Header .Add ("X-aws-ec2-metadata-token" , token )
9199 if err != nil {
92100 return nil , err
93101 }
102+ if len (token ) > 0 {
103+ req .Header .Add ("X-aws-ec2-metadata-token" , token )
104+ }
94105 resp , err := client .Do (req )
95106 if err != nil {
96107 return nil , err
97108 }
98109 defer resp .Body .Close ()
99110 if resp .StatusCode != 200 {
100- return nil , err
111+ return nil , fmt . Errorf ( "HTTP Error: %v %v" , url , resp . StatusCode )
101112 }
102113 if server := resp .Header ["Server" ]; len (server ) != 1 || server [0 ] != "EC2ws" {
103114 return nil , fmt .Errorf ("wrong headers" )
104115 }
105116 return ioutil .ReadAll (resp .Body )
106117}
107118
108- // awsInit returns true if it can access AWS project metadata. It also
119+ // awsInit returns true if it can access AWS project metadata and the version
120+ // of the metadata service it was able to access. It also
109121// creates two stats variables with the account ID and zone.
110- func awsInit (ctx context.Context , timeout time.Duration ) bool {
111- body , err := awsGet (ctx , awsIdentityDocURL (), timeout )
122+ func awsInit (ctx context.Context , logger logging.Logger , timeout time.Duration ) (bool , bool ) {
123+ v2 := false
124+ // Try the v1 service first since it should always work unless v2
125+ // is specifically configured (and hence v1 is disabled), in which
126+ // case the expectation is that it fails fast with a 4xx HTTP error.
127+ body , err := awsGet (ctx , false , awsIdentityDocURL (), timeout )
112128 if err != nil {
113- return false
129+ logger .VI (1 ).Infof ("failed to access v1 metadata service: %v" , err )
130+ // can't access v1, try v2.
131+ body , err = awsGet (ctx , true , awsIdentityDocURL (), timeout )
132+ if err != nil {
133+ logger .VI (1 ).Infof ("failed to access v2 metadata service: %v" , err )
134+ return false , false
135+ }
136+ v2 = true
114137 }
115138 doc := map [string ]interface {}{}
116139 if err := json .Unmarshal (body , & doc ); err != nil {
117- return false
140+ logger .VI (1 ).Infof ("failed to unmarshal metadata service response: %s: %v" , body , err )
141+ return false , false
118142 }
119143 found := 0
120144 for _ , v := range []struct {
@@ -130,11 +154,11 @@ func awsInit(ctx context.Context, timeout time.Duration) bool {
130154 }
131155 }
132156 }
133- return found == 2
157+ return found == 2 , v2
134158}
135159
136- func awsGetAddr (ctx context.Context , url string , timeout time.Duration ) ([]net.Addr , error ) {
137- body , err := awsGet (ctx , url , timeout )
160+ func awsGetAddr (ctx context.Context , imdsv2 bool , url string , timeout time.Duration ) ([]net.Addr , error ) {
161+ body , err := awsGet (ctx , imdsv2 , url , timeout )
138162 if err != nil {
139163 return nil , err
140164 }
0 commit comments