golang.org/x/oauth2@v0.18.0/google/externalaccount/aws_test.go (about) 1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package externalaccount 6 7 import ( 8 "context" 9 "encoding/json" 10 "errors" 11 "fmt" 12 "net/http" 13 "net/http/httptest" 14 neturl "net/url" 15 "reflect" 16 "strings" 17 "testing" 18 "time" 19 ) 20 21 var defaultTime = time.Date(2011, 9, 9, 23, 36, 0, 0, time.UTC) 22 var secondDefaultTime = time.Date(2020, 8, 11, 6, 55, 22, 0, time.UTC) 23 24 type validateHeaders func(r *http.Request) 25 26 func setTime(testTime time.Time) func() time.Time { 27 return func() time.Time { 28 return testTime 29 } 30 } 31 32 func setEnvironment(env map[string]string) func(string) string { 33 return func(key string) string { 34 return env[key] 35 } 36 } 37 38 var defaultRequestSigner = &awsRequestSigner{ 39 RegionName: "us-east-1", 40 AwsSecurityCredentials: &AwsSecurityCredentials{ 41 AccessKeyID: "AKIDEXAMPLE", 42 SecretAccessKey: "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", 43 }, 44 } 45 46 const ( 47 accessKeyID = "ASIARD4OQDT6A77FR3CL" 48 secretAccessKey = "Y8AfSaucF37G4PpvfguKZ3/l7Id4uocLXxX0+VTx" 49 securityToken = "IQoJb3JpZ2luX2VjEIz//////////wEaCXVzLWVhc3QtMiJGMEQCIH7MHX/Oy/OB8OlLQa9GrqU1B914+iMikqWQW7vPCKlgAiA/Lsv8Jcafn14owfxXn95FURZNKaaphj0ykpmS+Ki+CSq0AwhlEAAaDDA3NzA3MTM5MTk5NiIMx9sAeP1ovlMTMKLjKpEDwuJQg41/QUKx0laTZYjPlQvjwSqS3OB9P1KAXPWSLkliVMMqaHqelvMF/WO/glv3KwuTfQsavRNs3v5pcSEm4SPO3l7mCs7KrQUHwGP0neZhIKxEXy+Ls//1C/Bqt53NL+LSbaGv6RPHaX82laz2qElphg95aVLdYgIFY6JWV5fzyjgnhz0DQmy62/Vi8pNcM2/VnxeCQ8CC8dRDSt52ry2v+nc77vstuI9xV5k8mPtnaPoJDRANh0bjwY5Sdwkbp+mGRUJBAQRlNgHUJusefXQgVKBCiyJY4w3Csd8Bgj9IyDV+Azuy1jQqfFZWgP68LSz5bURyIjlWDQunO82stZ0BgplKKAa/KJHBPCp8Qi6i99uy7qh76FQAqgVTsnDuU6fGpHDcsDSGoCls2HgZjZFPeOj8mmRhFk1Xqvkbjuz8V1cJk54d3gIJvQt8gD2D6yJQZecnuGWd5K2e2HohvCc8Fc9kBl1300nUJPV+k4tr/A5R/0QfEKOZL1/k5lf1g9CREnrM8LVkGxCgdYMxLQow1uTL+QU67AHRRSp5PhhGX4Rek+01vdYSnJCMaPhSEgcLqDlQkhk6MPsyT91QMXcWmyO+cAZwUPwnRamFepuP4K8k2KVXs/LIJHLELwAZ0ekyaS7CptgOqS7uaSTFG3U+vzFZLEnGvWQ7y9IPNQZ+Dffgh4p3vF4J68y9049sI6Sr5d5wbKkcbm8hdCDHZcv4lnqohquPirLiFQ3q7B17V9krMPu3mz1cg4Ekgcrn/E09NTsxAqD8NcZ7C7ECom9r+X3zkDOxaajW6hu3Az8hGlyylDaMiFfRbBJpTIlxp7jfa7CxikNgNtEKLH9iCzvuSg2vhA==" 50 ) 51 52 var requestSignerWithToken = &awsRequestSigner{ 53 RegionName: "us-east-2", 54 AwsSecurityCredentials: &AwsSecurityCredentials{ 55 AccessKeyID: accessKeyID, 56 SecretAccessKey: secretAccessKey, 57 SessionToken: securityToken, 58 }, 59 } 60 61 func setDefaultTime(req *http.Request) { 62 // Don't use time.Format for this 63 // Our output signature expects this to be a Monday, even though Sept 9, 2011 is a Friday 64 req.Header.Add("date", "Mon, 09 Sep 2011 23:36:00 GMT") 65 } 66 67 func testRequestSigner(t *testing.T, rs *awsRequestSigner, input, expectedOutput *http.Request) { 68 t.Helper() 69 70 err := rs.SignRequest(input) 71 if err != nil { 72 t.Errorf("unexpected error: %q", err.Error()) 73 } 74 75 if got, want := input.URL.String(), expectedOutput.URL.String(); !reflect.DeepEqual(got, want) { 76 t.Errorf("url = %q, want %q", got, want) 77 } 78 if got, want := input.Method, expectedOutput.Method; !reflect.DeepEqual(got, want) { 79 t.Errorf("method = %q, want %q", got, want) 80 } 81 for header := range expectedOutput.Header { 82 if got, want := input.Header[header], expectedOutput.Header[header]; !reflect.DeepEqual(got, want) { 83 t.Errorf("header[%q] = %q, want %q", header, got, want) 84 } 85 } 86 } 87 88 func TestAWSv4Signature_GetRequest(t *testing.T) { 89 input, _ := http.NewRequest("GET", "https://host.foo.com", nil) 90 setDefaultTime(input) 91 92 output, _ := http.NewRequest("GET", "https://host.foo.com", nil) 93 output.Header = http.Header{ 94 "Host": []string{"host.foo.com"}, 95 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"}, 96 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470"}, 97 } 98 99 oldNow := now 100 defer func() { now = oldNow }() 101 now = setTime(defaultTime) 102 103 testRequestSigner(t, defaultRequestSigner, input, output) 104 } 105 106 func TestAWSv4Signature_GetRequestWithRelativePath(t *testing.T) { 107 input, _ := http.NewRequest("GET", "https://host.foo.com/foo/bar/../..", nil) 108 setDefaultTime(input) 109 110 output, _ := http.NewRequest("GET", "https://host.foo.com/foo/bar/../..", nil) 111 output.Header = http.Header{ 112 "Host": []string{"host.foo.com"}, 113 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"}, 114 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470"}, 115 } 116 117 oldNow := now 118 defer func() { now = oldNow }() 119 now = setTime(defaultTime) 120 121 testRequestSigner(t, defaultRequestSigner, input, output) 122 } 123 124 func TestAWSv4Signature_GetRequestWithDotPath(t *testing.T) { 125 input, _ := http.NewRequest("GET", "https://host.foo.com/./", nil) 126 setDefaultTime(input) 127 128 output, _ := http.NewRequest("GET", "https://host.foo.com/./", nil) 129 output.Header = http.Header{ 130 "Host": []string{"host.foo.com"}, 131 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"}, 132 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470"}, 133 } 134 135 oldNow := now 136 defer func() { now = oldNow }() 137 now = setTime(defaultTime) 138 139 testRequestSigner(t, defaultRequestSigner, input, output) 140 } 141 142 func TestAWSv4Signature_GetRequestWithPointlessDotPath(t *testing.T) { 143 input, _ := http.NewRequest("GET", "https://host.foo.com/./foo", nil) 144 setDefaultTime(input) 145 146 output, _ := http.NewRequest("GET", "https://host.foo.com/./foo", nil) 147 output.Header = http.Header{ 148 "Host": []string{"host.foo.com"}, 149 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"}, 150 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=910e4d6c9abafaf87898e1eb4c929135782ea25bb0279703146455745391e63a"}, 151 } 152 153 oldNow := now 154 defer func() { now = oldNow }() 155 now = setTime(defaultTime) 156 157 testRequestSigner(t, defaultRequestSigner, input, output) 158 } 159 160 func TestAWSv4Signature_GetRequestWithUtf8Path(t *testing.T) { 161 input, _ := http.NewRequest("GET", "https://host.foo.com/%E1%88%B4", nil) 162 setDefaultTime(input) 163 164 output, _ := http.NewRequest("GET", "https://host.foo.com/%E1%88%B4", nil) 165 output.Header = http.Header{ 166 "Host": []string{"host.foo.com"}, 167 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"}, 168 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=8d6634c189aa8c75c2e51e106b6b5121bed103fdb351f7d7d4381c738823af74"}, 169 } 170 171 oldNow := now 172 defer func() { now = oldNow }() 173 now = setTime(defaultTime) 174 175 testRequestSigner(t, defaultRequestSigner, input, output) 176 } 177 178 func TestAWSv4Signature_GetRequestWithDuplicateQuery(t *testing.T) { 179 input, _ := http.NewRequest("GET", "https://host.foo.com/?foo=Zoo&foo=aha", nil) 180 setDefaultTime(input) 181 182 output, _ := http.NewRequest("GET", "https://host.foo.com/?foo=Zoo&foo=aha", nil) 183 output.Header = http.Header{ 184 "Host": []string{"host.foo.com"}, 185 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"}, 186 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=be7148d34ebccdc6423b19085378aa0bee970bdc61d144bd1a8c48c33079ab09"}, 187 } 188 189 oldNow := now 190 defer func() { now = oldNow }() 191 now = setTime(defaultTime) 192 193 testRequestSigner(t, defaultRequestSigner, input, output) 194 } 195 196 func TestAWSv4Signature_GetRequestWithMisorderedQuery(t *testing.T) { 197 input, _ := http.NewRequest("GET", "https://host.foo.com/?foo=b&foo=a", nil) 198 setDefaultTime(input) 199 200 output, _ := http.NewRequest("GET", "https://host.foo.com/?foo=b&foo=a", nil) 201 output.Header = http.Header{ 202 "Host": []string{"host.foo.com"}, 203 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"}, 204 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=feb926e49e382bec75c9d7dcb2a1b6dc8aa50ca43c25d2bc51143768c0875acc"}, 205 } 206 207 oldNow := now 208 defer func() { now = oldNow }() 209 now = setTime(defaultTime) 210 211 testRequestSigner(t, defaultRequestSigner, input, output) 212 } 213 214 func TestAWSv4Signature_GetRequestWithUtf8Query(t *testing.T) { 215 input, _ := http.NewRequest("GET", "https://host.foo.com/?ሴ=bar", nil) 216 setDefaultTime(input) 217 218 output, _ := http.NewRequest("GET", "https://host.foo.com/?ሴ=bar", nil) 219 output.Header = http.Header{ 220 "Host": []string{"host.foo.com"}, 221 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"}, 222 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=6fb359e9a05394cc7074e0feb42573a2601abc0c869a953e8c5c12e4e01f1a8c"}, 223 } 224 225 oldNow := now 226 defer func() { now = oldNow }() 227 now = setTime(defaultTime) 228 229 testRequestSigner(t, defaultRequestSigner, input, output) 230 } 231 232 func TestAWSv4Signature_PostRequest(t *testing.T) { 233 input, _ := http.NewRequest("POST", "https://host.foo.com/", nil) 234 setDefaultTime(input) 235 input.Header.Add("ZOO", "zoobar") 236 237 output, _ := http.NewRequest("POST", "https://host.foo.com/", nil) 238 output.Header = http.Header{ 239 "Host": []string{"host.foo.com"}, 240 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"}, 241 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;zoo, Signature=b7a95a52518abbca0964a999a880429ab734f35ebbf1235bd79a5de87756dc4a"}, 242 "Zoo": []string{"zoobar"}, 243 } 244 245 oldNow := now 246 defer func() { now = oldNow }() 247 now = setTime(defaultTime) 248 249 testRequestSigner(t, defaultRequestSigner, input, output) 250 } 251 252 func TestAWSv4Signature_PostRequestWithCapitalizedHeaderValue(t *testing.T) { 253 input, _ := http.NewRequest("POST", "https://host.foo.com/", nil) 254 setDefaultTime(input) 255 input.Header.Add("zoo", "ZOOBAR") 256 257 output, _ := http.NewRequest("POST", "https://host.foo.com/", nil) 258 output.Header = http.Header{ 259 "Host": []string{"host.foo.com"}, 260 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"}, 261 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;zoo, Signature=273313af9d0c265c531e11db70bbd653f3ba074c1009239e8559d3987039cad7"}, 262 "Zoo": []string{"ZOOBAR"}, 263 } 264 265 oldNow := now 266 defer func() { now = oldNow }() 267 now = setTime(defaultTime) 268 269 testRequestSigner(t, defaultRequestSigner, input, output) 270 } 271 272 func TestAWSv4Signature_PostRequestPhfft(t *testing.T) { 273 input, _ := http.NewRequest("POST", "https://host.foo.com/", nil) 274 setDefaultTime(input) 275 input.Header.Add("p", "phfft") 276 277 output, _ := http.NewRequest("POST", "https://host.foo.com/", nil) 278 output.Header = http.Header{ 279 "Host": []string{"host.foo.com"}, 280 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"}, 281 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;p, Signature=debf546796015d6f6ded8626f5ce98597c33b47b9164cf6b17b4642036fcb592"}, 282 "P": []string{"phfft"}, 283 } 284 285 oldNow := now 286 defer func() { now = oldNow }() 287 now = setTime(defaultTime) 288 289 testRequestSigner(t, defaultRequestSigner, input, output) 290 } 291 292 func TestAWSv4Signature_PostRequestWithBody(t *testing.T) { 293 input, _ := http.NewRequest("POST", "https://host.foo.com/", strings.NewReader("foo=bar")) 294 setDefaultTime(input) 295 input.Header.Add("Content-Type", "application/x-www-form-urlencoded") 296 297 output, _ := http.NewRequest("POST", "https://host.foo.com/", nil) 298 output.Header = http.Header{ 299 "Host": []string{"host.foo.com"}, 300 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"}, 301 "Content-Type": []string{"application/x-www-form-urlencoded"}, 302 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=content-type;date;host, Signature=5a15b22cf462f047318703b92e6f4f38884e4a7ab7b1d6426ca46a8bd1c26cbc"}, 303 } 304 305 oldNow := now 306 defer func() { now = oldNow }() 307 now = setTime(defaultTime) 308 309 testRequestSigner(t, defaultRequestSigner, input, output) 310 } 311 312 func TestAWSv4Signature_PostRequestWithQueryString(t *testing.T) { 313 input, _ := http.NewRequest("POST", "https://host.foo.com/?foo=bar", nil) 314 setDefaultTime(input) 315 316 output, _ := http.NewRequest("POST", "https://host.foo.com/?foo=bar", nil) 317 output.Header = http.Header{ 318 "Host": []string{"host.foo.com"}, 319 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"}, 320 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b6e3b79003ce0743a491606ba1035a804593b0efb1e20a11cba83f8c25a57a92"}, 321 } 322 323 oldNow := now 324 defer func() { now = oldNow }() 325 now = setTime(defaultTime) 326 327 testRequestSigner(t, defaultRequestSigner, input, output) 328 } 329 330 func TestAWSv4Signature_GetRequestWithSecurityToken(t *testing.T) { 331 input, _ := http.NewRequest("GET", "https://ec2.us-east-2.amazonaws.com?Action=DescribeRegions&Version=2013-10-15", nil) 332 333 output, _ := http.NewRequest("GET", "https://ec2.us-east-2.amazonaws.com?Action=DescribeRegions&Version=2013-10-15", nil) 334 output.Header = http.Header{ 335 "Host": []string{"ec2.us-east-2.amazonaws.com"}, 336 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=" + accessKeyID + "/20200811/us-east-2/ec2/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=631ea80cddfaa545fdadb120dc92c9f18166e38a5c47b50fab9fce476e022855"}, 337 "X-Amz-Date": []string{"20200811T065522Z"}, 338 "X-Amz-Security-Token": []string{securityToken}, 339 } 340 341 oldNow := now 342 defer func() { now = oldNow }() 343 now = setTime(secondDefaultTime) 344 345 testRequestSigner(t, requestSignerWithToken, input, output) 346 } 347 348 func TestAWSv4Signature_PostRequestWithSecurityToken(t *testing.T) { 349 input, _ := http.NewRequest("POST", "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", nil) 350 351 output, _ := http.NewRequest("POST", "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", nil) 352 output.Header = http.Header{ 353 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=" + accessKeyID + "/20200811/us-east-2/sts/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=73452984e4a880ffdc5c392355733ec3f5ba310d5e0609a89244440cadfe7a7a"}, 354 "Host": []string{"sts.us-east-2.amazonaws.com"}, 355 "X-Amz-Date": []string{"20200811T065522Z"}, 356 "X-Amz-Security-Token": []string{securityToken}, 357 } 358 359 oldNow := now 360 defer func() { now = oldNow }() 361 now = setTime(secondDefaultTime) 362 363 testRequestSigner(t, requestSignerWithToken, input, output) 364 } 365 366 func TestAWSv4Signature_PostRequestWithSecurityTokenAndAdditionalHeaders(t *testing.T) { 367 requestParams := "{\"KeySchema\":[{\"KeyType\":\"HASH\",\"AttributeName\":\"Id\"}],\"TableName\":\"TestTable\",\"AttributeDefinitions\":[{\"AttributeName\":\"Id\",\"AttributeType\":\"S\"}],\"ProvisionedThroughput\":{\"WriteCapacityUnits\":5,\"ReadCapacityUnits\":5}}" 368 input, _ := http.NewRequest("POST", "https://dynamodb.us-east-2.amazonaws.com/", strings.NewReader(requestParams)) 369 input.Header.Add("Content-Type", "application/x-amz-json-1.0") 370 input.Header.Add("x-amz-target", "DynamoDB_20120810.CreateTable") 371 372 output, _ := http.NewRequest("POST", "https://dynamodb.us-east-2.amazonaws.com/", strings.NewReader(requestParams)) 373 output.Header = http.Header{ 374 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=" + accessKeyID + "/20200811/us-east-2/dynamodb/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-amz-security-token;x-amz-target, Signature=fdaa5b9cc9c86b80fe61eaf504141c0b3523780349120f2bd8145448456e0385"}, 375 "Host": []string{"dynamodb.us-east-2.amazonaws.com"}, 376 "X-Amz-Date": []string{"20200811T065522Z"}, 377 "Content-Type": []string{"application/x-amz-json-1.0"}, 378 "X-Amz-Target": []string{"DynamoDB_20120810.CreateTable"}, 379 "X-Amz-Security-Token": []string{securityToken}, 380 } 381 382 oldNow := now 383 defer func() { now = oldNow }() 384 now = setTime(secondDefaultTime) 385 386 testRequestSigner(t, requestSignerWithToken, input, output) 387 } 388 389 func TestAWSv4Signature_PostRequestWithAmzDateButNoSecurityToken(t *testing.T) { 390 var requestSigner = &awsRequestSigner{ 391 RegionName: "us-east-2", 392 AwsSecurityCredentials: &AwsSecurityCredentials{ 393 AccessKeyID: accessKeyID, 394 SecretAccessKey: secretAccessKey, 395 }, 396 } 397 398 input, _ := http.NewRequest("POST", "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", nil) 399 400 output, _ := http.NewRequest("POST", "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", nil) 401 output.Header = http.Header{ 402 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=" + accessKeyID + "/20200811/us-east-2/sts/aws4_request, SignedHeaders=host;x-amz-date, Signature=d095ba304919cd0d5570ba8a3787884ee78b860f268ed040ba23831d55536d56"}, 403 "Host": []string{"sts.us-east-2.amazonaws.com"}, 404 "X-Amz-Date": []string{"20200811T065522Z"}, 405 } 406 407 oldNow := now 408 defer func() { now = oldNow }() 409 now = setTime(secondDefaultTime) 410 411 testRequestSigner(t, requestSigner, input, output) 412 } 413 414 type testAwsServer struct { 415 url string 416 securityCredentialURL string 417 regionURL string 418 regionalCredVerificationURL string 419 imdsv2SessionTokenUrl string 420 421 Credentials map[string]string 422 423 WriteRolename func(http.ResponseWriter, *http.Request) 424 WriteSecurityCredentials func(http.ResponseWriter, *http.Request) 425 WriteRegion func(http.ResponseWriter, *http.Request) 426 WriteIMDSv2SessionToken func(http.ResponseWriter, *http.Request) 427 } 428 429 func createAwsTestServer(url, regionURL, regionalCredVerificationURL, imdsv2SessionTokenUrl string, rolename, region string, credentials map[string]string, imdsv2SessionToken string, validateHeaders validateHeaders) *testAwsServer { 430 server := &testAwsServer{ 431 url: url, 432 securityCredentialURL: fmt.Sprintf("%s/%s", url, rolename), 433 regionURL: regionURL, 434 regionalCredVerificationURL: regionalCredVerificationURL, 435 imdsv2SessionTokenUrl: imdsv2SessionTokenUrl, 436 Credentials: credentials, 437 WriteRolename: func(w http.ResponseWriter, r *http.Request) { 438 validateHeaders(r) 439 w.Write([]byte(rolename)) 440 }, 441 WriteRegion: func(w http.ResponseWriter, r *http.Request) { 442 validateHeaders(r) 443 w.Write([]byte(region)) 444 }, 445 WriteIMDSv2SessionToken: func(w http.ResponseWriter, r *http.Request) { 446 validateHeaders(r) 447 w.Write([]byte(imdsv2SessionToken)) 448 }, 449 } 450 451 server.WriteSecurityCredentials = func(w http.ResponseWriter, r *http.Request) { 452 validateHeaders(r) 453 jsonCredentials, _ := json.Marshal(server.Credentials) 454 w.Write(jsonCredentials) 455 } 456 457 return server 458 } 459 460 func createDefaultAwsTestServer() *testAwsServer { 461 return createAwsTestServer( 462 "/latest/meta-data/iam/security-credentials", 463 "/latest/meta-data/placement/availability-zone", 464 "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", 465 "", 466 "gcp-aws-role", 467 "us-east-2b", 468 map[string]string{ 469 "SecretAccessKey": secretAccessKey, 470 "AccessKeyId": accessKeyID, 471 "Token": securityToken, 472 }, 473 "", 474 noHeaderValidation, 475 ) 476 } 477 478 func createDefaultAwsTestServerWithImdsv2(t *testing.T) *testAwsServer { 479 validateSessionTokenHeaders := func(r *http.Request) { 480 if r.URL.Path == "/latest/api/token" { 481 headerValue := r.Header.Get(awsIMDSv2SessionTtlHeader) 482 if headerValue != awsIMDSv2SessionTtl { 483 t.Errorf("%q = \n%q\n want \n%q", awsIMDSv2SessionTtlHeader, headerValue, awsIMDSv2SessionTtl) 484 } 485 } else { 486 headerValue := r.Header.Get(awsIMDSv2SessionTokenHeader) 487 if headerValue != "sessiontoken" { 488 t.Errorf("%q = \n%q\n want \n%q", awsIMDSv2SessionTokenHeader, headerValue, "sessiontoken") 489 } 490 } 491 } 492 493 return createAwsTestServer( 494 "/latest/meta-data/iam/security-credentials", 495 "/latest/meta-data/placement/availability-zone", 496 "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", 497 "/latest/api/token", 498 "gcp-aws-role", 499 "us-east-2b", 500 map[string]string{ 501 "SecretAccessKey": secretAccessKey, 502 "AccessKeyId": accessKeyID, 503 "Token": securityToken, 504 }, 505 "sessiontoken", 506 validateSessionTokenHeaders, 507 ) 508 } 509 510 func (server *testAwsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { 511 switch p := r.URL.Path; p { 512 case server.url: 513 server.WriteRolename(w, r) 514 case server.securityCredentialURL: 515 server.WriteSecurityCredentials(w, r) 516 case server.regionURL: 517 server.WriteRegion(w, r) 518 case server.imdsv2SessionTokenUrl: 519 server.WriteIMDSv2SessionToken(w, r) 520 } 521 } 522 523 func notFound(w http.ResponseWriter, r *http.Request) { 524 w.WriteHeader(404) 525 w.Write([]byte("Not Found")) 526 } 527 528 func noHeaderValidation(r *http.Request) {} 529 530 func (server *testAwsServer) getCredentialSource(url string) *CredentialSource { 531 return &CredentialSource{ 532 EnvironmentID: "aws1", 533 URL: url + server.url, 534 RegionURL: url + server.regionURL, 535 RegionalCredVerificationURL: server.regionalCredVerificationURL, 536 IMDSv2SessionTokenURL: url + server.imdsv2SessionTokenUrl, 537 } 538 } 539 540 func getExpectedSubjectToken(url, region, accessKeyID, secretAccessKey, securityToken string) string { 541 req, _ := http.NewRequest("POST", url, nil) 542 req.Header.Add("x-goog-cloud-target-resource", testFileConfig.Audience) 543 signer := &awsRequestSigner{ 544 RegionName: region, 545 AwsSecurityCredentials: &AwsSecurityCredentials{ 546 AccessKeyID: accessKeyID, 547 SecretAccessKey: secretAccessKey, 548 SessionToken: securityToken, 549 }, 550 } 551 signer.SignRequest(req) 552 553 result := awsRequest{ 554 URL: url, 555 Method: "POST", 556 Headers: []awsRequestHeader{ 557 { 558 Key: "Authorization", 559 Value: req.Header.Get("Authorization"), 560 }, { 561 Key: "Host", 562 Value: req.Header.Get("Host"), 563 }, { 564 Key: "X-Amz-Date", 565 Value: req.Header.Get("X-Amz-Date"), 566 }, 567 }, 568 } 569 570 if securityToken != "" { 571 result.Headers = append(result.Headers, awsRequestHeader{ 572 Key: "X-Amz-Security-Token", 573 Value: securityToken, 574 }) 575 } 576 577 result.Headers = append(result.Headers, awsRequestHeader{ 578 Key: "X-Goog-Cloud-Target-Resource", 579 Value: testFileConfig.Audience, 580 }) 581 582 str, _ := json.Marshal(result) 583 return neturl.QueryEscape(string(str)) 584 } 585 586 func TestAWSCredential_BasicRequest(t *testing.T) { 587 server := createDefaultAwsTestServer() 588 ts := httptest.NewServer(server) 589 590 tfc := testFileConfig 591 tfc.CredentialSource = server.getCredentialSource(ts.URL) 592 oldGetenv := getenv 593 oldNow := now 594 defer func() { 595 getenv = oldGetenv 596 now = oldNow 597 }() 598 getenv = setEnvironment(map[string]string{}) 599 now = setTime(defaultTime) 600 601 base, err := tfc.parse(context.Background()) 602 if err != nil { 603 t.Fatalf("parse() failed %v", err) 604 } 605 606 out, err := base.subjectToken() 607 if err != nil { 608 t.Fatalf("retrieveSubjectToken() failed: %v", err) 609 } 610 611 expected := getExpectedSubjectToken( 612 "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", 613 "us-east-2", 614 accessKeyID, 615 secretAccessKey, 616 securityToken, 617 ) 618 619 if got, want := out, expected; !reflect.DeepEqual(got, want) { 620 t.Errorf("subjectToken = \n%q\n want \n%q", got, want) 621 } 622 } 623 624 func TestAWSCredential_IMDSv2(t *testing.T) { 625 server := createDefaultAwsTestServerWithImdsv2(t) 626 ts := httptest.NewServer(server) 627 628 tfc := testFileConfig 629 tfc.CredentialSource = server.getCredentialSource(ts.URL) 630 631 oldGetenv := getenv 632 oldNow := now 633 defer func() { 634 getenv = oldGetenv 635 now = oldNow 636 }() 637 getenv = setEnvironment(map[string]string{}) 638 now = setTime(defaultTime) 639 640 base, err := tfc.parse(context.Background()) 641 if err != nil { 642 t.Fatalf("parse() failed %v", err) 643 } 644 645 out, err := base.subjectToken() 646 if err != nil { 647 t.Fatalf("retrieveSubjectToken() failed: %v", err) 648 } 649 650 expected := getExpectedSubjectToken( 651 "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", 652 "us-east-2", 653 accessKeyID, 654 secretAccessKey, 655 securityToken, 656 ) 657 658 if got, want := out, expected; !reflect.DeepEqual(got, want) { 659 t.Errorf("subjectToken = \n%q\n want \n%q", got, want) 660 } 661 } 662 663 func TestAWSCredential_BasicRequestWithoutSecurityToken(t *testing.T) { 664 server := createDefaultAwsTestServer() 665 ts := httptest.NewServer(server) 666 delete(server.Credentials, "Token") 667 668 tfc := testFileConfig 669 tfc.CredentialSource = server.getCredentialSource(ts.URL) 670 671 oldGetenv := getenv 672 oldNow := now 673 defer func() { 674 getenv = oldGetenv 675 now = oldNow 676 }() 677 getenv = setEnvironment(map[string]string{}) 678 now = setTime(defaultTime) 679 680 base, err := tfc.parse(context.Background()) 681 if err != nil { 682 t.Fatalf("parse() failed %v", err) 683 } 684 685 out, err := base.subjectToken() 686 if err != nil { 687 t.Fatalf("retrieveSubjectToken() failed: %v", err) 688 } 689 690 expected := getExpectedSubjectToken( 691 "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", 692 "us-east-2", 693 accessKeyID, 694 secretAccessKey, 695 "", 696 ) 697 698 if got, want := out, expected; !reflect.DeepEqual(got, want) { 699 t.Errorf("subjectToken = \n%q\n want \n%q", got, want) 700 } 701 } 702 703 func TestAWSCredential_BasicRequestWithEnv(t *testing.T) { 704 server := createDefaultAwsTestServer() 705 ts := httptest.NewServer(server) 706 707 tfc := testFileConfig 708 tfc.CredentialSource = server.getCredentialSource(ts.URL) 709 710 oldGetenv := getenv 711 oldNow := now 712 defer func() { 713 getenv = oldGetenv 714 now = oldNow 715 }() 716 getenv = setEnvironment(map[string]string{ 717 "AWS_ACCESS_KEY_ID": "AKIDEXAMPLE", 718 "AWS_SECRET_ACCESS_KEY": "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", 719 "AWS_REGION": "us-west-1", 720 }) 721 now = setTime(defaultTime) 722 723 base, err := tfc.parse(context.Background()) 724 if err != nil { 725 t.Fatalf("parse() failed %v", err) 726 } 727 728 out, err := base.subjectToken() 729 if err != nil { 730 t.Fatalf("retrieveSubjectToken() failed: %v", err) 731 } 732 733 expected := getExpectedSubjectToken( 734 "https://sts.us-west-1.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", 735 "us-west-1", 736 "AKIDEXAMPLE", 737 "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", 738 "", 739 ) 740 741 if got, want := out, expected; !reflect.DeepEqual(got, want) { 742 t.Errorf("subjectToken = \n%q\n want \n%q", got, want) 743 } 744 } 745 746 func TestAWSCredential_BasicRequestWithDefaultEnv(t *testing.T) { 747 server := createDefaultAwsTestServer() 748 ts := httptest.NewServer(server) 749 750 tfc := testFileConfig 751 tfc.CredentialSource = server.getCredentialSource(ts.URL) 752 753 oldGetenv := getenv 754 oldNow := now 755 defer func() { 756 getenv = oldGetenv 757 now = oldNow 758 }() 759 getenv = setEnvironment(map[string]string{ 760 "AWS_ACCESS_KEY_ID": "AKIDEXAMPLE", 761 "AWS_SECRET_ACCESS_KEY": "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", 762 "AWS_REGION": "us-west-1", 763 }) 764 now = setTime(defaultTime) 765 766 base, err := tfc.parse(context.Background()) 767 if err != nil { 768 t.Fatalf("parse() failed %v", err) 769 } 770 771 out, err := base.subjectToken() 772 if err != nil { 773 t.Fatalf("retrieveSubjectToken() failed: %v", err) 774 } 775 expected := getExpectedSubjectToken( 776 "https://sts.us-west-1.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", 777 "us-west-1", 778 "AKIDEXAMPLE", 779 "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", 780 "", 781 ) 782 783 if got, want := out, expected; !reflect.DeepEqual(got, want) { 784 t.Errorf("subjectToken = \n%q\n want \n%q", got, want) 785 } 786 } 787 788 func TestAWSCredential_BasicRequestWithTwoRegions(t *testing.T) { 789 server := createDefaultAwsTestServer() 790 ts := httptest.NewServer(server) 791 792 tfc := testFileConfig 793 tfc.CredentialSource = server.getCredentialSource(ts.URL) 794 795 oldGetenv := getenv 796 oldNow := now 797 defer func() { 798 getenv = oldGetenv 799 now = oldNow 800 }() 801 getenv = setEnvironment(map[string]string{ 802 "AWS_ACCESS_KEY_ID": "AKIDEXAMPLE", 803 "AWS_SECRET_ACCESS_KEY": "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", 804 "AWS_REGION": "us-west-1", 805 "AWS_DEFAULT_REGION": "us-east-1", 806 }) 807 now = setTime(defaultTime) 808 809 base, err := tfc.parse(context.Background()) 810 if err != nil { 811 t.Fatalf("parse() failed %v", err) 812 } 813 814 out, err := base.subjectToken() 815 if err != nil { 816 t.Fatalf("retrieveSubjectToken() failed: %v", err) 817 } 818 expected := getExpectedSubjectToken( 819 "https://sts.us-west-1.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", 820 "us-west-1", 821 "AKIDEXAMPLE", 822 "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", 823 "", 824 ) 825 826 if got, want := out, expected; !reflect.DeepEqual(got, want) { 827 t.Errorf("subjectToken = \n%q\n want \n%q", got, want) 828 } 829 } 830 831 func TestAWSCredential_RequestWithBadVersion(t *testing.T) { 832 server := createDefaultAwsTestServer() 833 ts := httptest.NewServer(server) 834 835 tfc := testFileConfig 836 tfc.CredentialSource = server.getCredentialSource(ts.URL) 837 tfc.CredentialSource.EnvironmentID = "aws3" 838 839 oldGetenv := getenv 840 defer func() { 841 getenv = oldGetenv 842 }() 843 getenv = setEnvironment(map[string]string{}) 844 845 _, err := tfc.parse(context.Background()) 846 if err == nil { 847 t.Fatalf("parse() should have failed") 848 } 849 if got, want := err.Error(), "oauth2/google/externalaccount: aws version '3' is not supported in the current build"; !reflect.DeepEqual(got, want) { 850 t.Errorf("subjectToken = %q, want %q", got, want) 851 } 852 } 853 854 func TestAWSCredential_RequestWithNoRegionURL(t *testing.T) { 855 server := createDefaultAwsTestServer() 856 ts := httptest.NewServer(server) 857 858 tfc := testFileConfig 859 tfc.CredentialSource = server.getCredentialSource(ts.URL) 860 tfc.CredentialSource.RegionURL = "" 861 862 oldGetenv := getenv 863 defer func() { 864 getenv = oldGetenv 865 }() 866 getenv = setEnvironment(map[string]string{}) 867 868 base, err := tfc.parse(context.Background()) 869 if err != nil { 870 t.Fatalf("parse() failed %v", err) 871 } 872 873 _, err = base.subjectToken() 874 if err == nil { 875 t.Fatalf("retrieveSubjectToken() should have failed") 876 } 877 878 if got, want := err.Error(), "oauth2/google/externalaccount: unable to determine AWS region"; !reflect.DeepEqual(got, want) { 879 t.Errorf("subjectToken = %q, want %q", got, want) 880 } 881 } 882 883 func TestAWSCredential_RequestWithBadRegionURL(t *testing.T) { 884 server := createDefaultAwsTestServer() 885 ts := httptest.NewServer(server) 886 887 server.WriteRegion = notFound 888 889 tfc := testFileConfig 890 tfc.CredentialSource = server.getCredentialSource(ts.URL) 891 892 oldGetenv := getenv 893 defer func() { 894 getenv = oldGetenv 895 }() 896 getenv = setEnvironment(map[string]string{}) 897 898 base, err := tfc.parse(context.Background()) 899 if err != nil { 900 t.Fatalf("parse() failed %v", err) 901 } 902 903 _, err = base.subjectToken() 904 if err == nil { 905 t.Fatalf("retrieveSubjectToken() should have failed") 906 } 907 908 if got, want := err.Error(), "oauth2/google/externalaccount: unable to retrieve AWS region - Not Found"; !reflect.DeepEqual(got, want) { 909 t.Errorf("subjectToken = %q, want %q", got, want) 910 } 911 } 912 913 func TestAWSCredential_RequestWithMissingCredential(t *testing.T) { 914 server := createDefaultAwsTestServer() 915 ts := httptest.NewServer(server) 916 917 server.WriteSecurityCredentials = func(w http.ResponseWriter, r *http.Request) { 918 w.Write([]byte("{}")) 919 } 920 921 tfc := testFileConfig 922 tfc.CredentialSource = server.getCredentialSource(ts.URL) 923 924 oldGetenv := getenv 925 defer func() { 926 getenv = oldGetenv 927 }() 928 getenv = setEnvironment(map[string]string{}) 929 930 base, err := tfc.parse(context.Background()) 931 if err != nil { 932 t.Fatalf("parse() failed %v", err) 933 } 934 935 _, err = base.subjectToken() 936 if err == nil { 937 t.Fatalf("retrieveSubjectToken() should have failed") 938 } 939 940 if got, want := err.Error(), "oauth2/google/externalaccount: missing AccessKeyId credential"; !reflect.DeepEqual(got, want) { 941 t.Errorf("subjectToken = %q, want %q", got, want) 942 } 943 } 944 945 func TestAWSCredential_RequestWithIncompleteCredential(t *testing.T) { 946 server := createDefaultAwsTestServer() 947 ts := httptest.NewServer(server) 948 949 server.WriteSecurityCredentials = func(w http.ResponseWriter, r *http.Request) { 950 w.Write([]byte(`{"AccessKeyId":"FOOBARBAS"}`)) 951 } 952 953 tfc := testFileConfig 954 tfc.CredentialSource = server.getCredentialSource(ts.URL) 955 956 oldGetenv := getenv 957 defer func() { 958 getenv = oldGetenv 959 }() 960 getenv = setEnvironment(map[string]string{}) 961 962 base, err := tfc.parse(context.Background()) 963 if err != nil { 964 t.Fatalf("parse() failed %v", err) 965 } 966 967 _, err = base.subjectToken() 968 if err == nil { 969 t.Fatalf("retrieveSubjectToken() should have failed") 970 } 971 972 if got, want := err.Error(), "oauth2/google/externalaccount: missing SecretAccessKey credential"; !reflect.DeepEqual(got, want) { 973 t.Errorf("subjectToken = %q, want %q", got, want) 974 } 975 } 976 977 func TestAWSCredential_RequestWithNoCredentialURL(t *testing.T) { 978 server := createDefaultAwsTestServer() 979 ts := httptest.NewServer(server) 980 981 tfc := testFileConfig 982 tfc.CredentialSource = server.getCredentialSource(ts.URL) 983 tfc.CredentialSource.URL = "" 984 985 oldGetenv := getenv 986 defer func() { 987 getenv = oldGetenv 988 }() 989 getenv = setEnvironment(map[string]string{}) 990 991 base, err := tfc.parse(context.Background()) 992 if err != nil { 993 t.Fatalf("parse() failed %v", err) 994 } 995 996 _, err = base.subjectToken() 997 if err == nil { 998 t.Fatalf("retrieveSubjectToken() should have failed") 999 } 1000 1001 if got, want := err.Error(), "oauth2/google/externalaccount: unable to determine the AWS metadata server security credentials endpoint"; !reflect.DeepEqual(got, want) { 1002 t.Errorf("subjectToken = %q, want %q", got, want) 1003 } 1004 } 1005 1006 func TestAWSCredential_RequestWithBadCredentialURL(t *testing.T) { 1007 server := createDefaultAwsTestServer() 1008 ts := httptest.NewServer(server) 1009 server.WriteRolename = notFound 1010 1011 tfc := testFileConfig 1012 tfc.CredentialSource = server.getCredentialSource(ts.URL) 1013 1014 oldGetenv := getenv 1015 defer func() { 1016 getenv = oldGetenv 1017 }() 1018 getenv = setEnvironment(map[string]string{}) 1019 1020 base, err := tfc.parse(context.Background()) 1021 if err != nil { 1022 t.Fatalf("parse() failed %v", err) 1023 } 1024 1025 _, err = base.subjectToken() 1026 if err == nil { 1027 t.Fatalf("retrieveSubjectToken() should have failed") 1028 } 1029 1030 if got, want := err.Error(), "oauth2/google/externalaccount: unable to retrieve AWS role name - Not Found"; !reflect.DeepEqual(got, want) { 1031 t.Errorf("subjectToken = %q, want %q", got, want) 1032 } 1033 } 1034 1035 func TestAWSCredential_RequestWithBadFinalCredentialURL(t *testing.T) { 1036 server := createDefaultAwsTestServer() 1037 ts := httptest.NewServer(server) 1038 server.WriteSecurityCredentials = notFound 1039 1040 tfc := testFileConfig 1041 tfc.CredentialSource = server.getCredentialSource(ts.URL) 1042 1043 oldGetenv := getenv 1044 defer func() { 1045 getenv = oldGetenv 1046 }() 1047 getenv = setEnvironment(map[string]string{}) 1048 1049 base, err := tfc.parse(context.Background()) 1050 if err != nil { 1051 t.Fatalf("parse() failed %v", err) 1052 } 1053 1054 _, err = base.subjectToken() 1055 if err == nil { 1056 t.Fatalf("retrieveSubjectToken() should have failed") 1057 } 1058 1059 if got, want := err.Error(), "oauth2/google/externalaccount: unable to retrieve AWS security credentials - Not Found"; !reflect.DeepEqual(got, want) { 1060 t.Errorf("subjectToken = %q, want %q", got, want) 1061 } 1062 } 1063 1064 func TestAWSCredential_ShouldNotCallMetadataEndpointWhenCredsAreInEnv(t *testing.T) { 1065 server := createDefaultAwsTestServer() 1066 ts := httptest.NewServer(server) 1067 1068 metadataTs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1069 t.Error("Metadata server should not have been called.") 1070 })) 1071 1072 tfc := testFileConfig 1073 tfc.CredentialSource = server.getCredentialSource(ts.URL) 1074 tfc.CredentialSource.IMDSv2SessionTokenURL = metadataTs.URL 1075 1076 oldGetenv := getenv 1077 oldNow := now 1078 defer func() { 1079 getenv = oldGetenv 1080 now = oldNow 1081 }() 1082 getenv = setEnvironment(map[string]string{ 1083 "AWS_ACCESS_KEY_ID": "AKIDEXAMPLE", 1084 "AWS_SECRET_ACCESS_KEY": "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", 1085 "AWS_REGION": "us-west-1", 1086 }) 1087 now = setTime(defaultTime) 1088 1089 base, err := tfc.parse(context.Background()) 1090 if err != nil { 1091 t.Fatalf("parse() failed %v", err) 1092 } 1093 1094 out, err := base.subjectToken() 1095 if err != nil { 1096 t.Fatalf("retrieveSubjectToken() failed: %v", err) 1097 } 1098 1099 expected := getExpectedSubjectToken( 1100 "https://sts.us-west-1.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", 1101 "us-west-1", 1102 "AKIDEXAMPLE", 1103 "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", 1104 "", 1105 ) 1106 1107 if got, want := out, expected; !reflect.DeepEqual(got, want) { 1108 t.Errorf("subjectToken = \n%q\n want \n%q", got, want) 1109 } 1110 } 1111 1112 func TestAWSCredential_ShouldCallMetadataEndpointWhenNoRegion(t *testing.T) { 1113 server := createDefaultAwsTestServerWithImdsv2(t) 1114 ts := httptest.NewServer(server) 1115 1116 tfc := testFileConfig 1117 tfc.CredentialSource = server.getCredentialSource(ts.URL) 1118 1119 oldGetenv := getenv 1120 oldNow := now 1121 defer func() { 1122 getenv = oldGetenv 1123 now = oldNow 1124 }() 1125 getenv = setEnvironment(map[string]string{ 1126 "AWS_ACCESS_KEY_ID": accessKeyID, 1127 "AWS_SECRET_ACCESS_KEY": secretAccessKey, 1128 }) 1129 now = setTime(defaultTime) 1130 1131 base, err := tfc.parse(context.Background()) 1132 if err != nil { 1133 t.Fatalf("parse() failed %v", err) 1134 } 1135 1136 out, err := base.subjectToken() 1137 if err != nil { 1138 t.Fatalf("retrieveSubjectToken() failed: %v", err) 1139 } 1140 1141 expected := getExpectedSubjectToken( 1142 "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", 1143 "us-east-2", 1144 accessKeyID, 1145 secretAccessKey, 1146 "", 1147 ) 1148 1149 if got, want := out, expected; !reflect.DeepEqual(got, want) { 1150 t.Errorf("subjectToken = \n%q\n want \n%q", got, want) 1151 } 1152 } 1153 1154 func TestAWSCredential_ShouldCallMetadataEndpointWhenNoAccessKey(t *testing.T) { 1155 server := createDefaultAwsTestServerWithImdsv2(t) 1156 ts := httptest.NewServer(server) 1157 1158 tfc := testFileConfig 1159 tfc.CredentialSource = server.getCredentialSource(ts.URL) 1160 1161 oldGetenv := getenv 1162 oldNow := now 1163 defer func() { 1164 getenv = oldGetenv 1165 now = oldNow 1166 }() 1167 getenv = setEnvironment(map[string]string{ 1168 "AWS_SECRET_ACCESS_KEY": "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", 1169 "AWS_REGION": "us-west-1", 1170 }) 1171 now = setTime(defaultTime) 1172 1173 base, err := tfc.parse(context.Background()) 1174 if err != nil { 1175 t.Fatalf("parse() failed %v", err) 1176 } 1177 1178 out, err := base.subjectToken() 1179 if err != nil { 1180 t.Fatalf("retrieveSubjectToken() failed: %v", err) 1181 } 1182 1183 expected := getExpectedSubjectToken( 1184 "https://sts.us-west-1.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", 1185 "us-west-1", 1186 accessKeyID, 1187 secretAccessKey, 1188 securityToken, 1189 ) 1190 1191 if got, want := out, expected; !reflect.DeepEqual(got, want) { 1192 t.Errorf("subjectToken = \n%q\n want \n%q", got, want) 1193 } 1194 } 1195 1196 func TestAWSCredential_ShouldCallMetadataEndpointWhenNoSecretAccessKey(t *testing.T) { 1197 server := createDefaultAwsTestServerWithImdsv2(t) 1198 ts := httptest.NewServer(server) 1199 1200 tfc := testFileConfig 1201 tfc.CredentialSource = server.getCredentialSource(ts.URL) 1202 1203 oldGetenv := getenv 1204 oldNow := now 1205 defer func() { 1206 getenv = oldGetenv 1207 now = oldNow 1208 }() 1209 getenv = setEnvironment(map[string]string{ 1210 "AWS_ACCESS_KEY_ID": "AKIDEXAMPLE", 1211 "AWS_REGION": "us-west-1", 1212 }) 1213 now = setTime(defaultTime) 1214 1215 base, err := tfc.parse(context.Background()) 1216 if err != nil { 1217 t.Fatalf("parse() failed %v", err) 1218 } 1219 1220 out, err := base.subjectToken() 1221 if err != nil { 1222 t.Fatalf("retrieveSubjectToken() failed: %v", err) 1223 } 1224 1225 expected := getExpectedSubjectToken( 1226 "https://sts.us-west-1.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", 1227 "us-west-1", 1228 accessKeyID, 1229 secretAccessKey, 1230 securityToken, 1231 ) 1232 1233 if got, want := out, expected; !reflect.DeepEqual(got, want) { 1234 t.Errorf("subjectToken = \n%q\n want \n%q", got, want) 1235 } 1236 } 1237 1238 func TestAWSCredential_ProgrammaticAuth(t *testing.T) { 1239 tfc := testFileConfig 1240 securityCredentials := AwsSecurityCredentials{ 1241 AccessKeyID: accessKeyID, 1242 SecretAccessKey: secretAccessKey, 1243 SessionToken: securityToken, 1244 } 1245 1246 tfc.AwsSecurityCredentialsSupplier = testAwsSupplier{ 1247 awsRegion: "us-east-2", 1248 err: nil, 1249 credentials: &securityCredentials, 1250 } 1251 1252 oldNow := now 1253 defer func() { 1254 now = oldNow 1255 }() 1256 now = setTime(defaultTime) 1257 1258 base, err := tfc.parse(context.Background()) 1259 if err != nil { 1260 t.Fatalf("parse() failed %v", err) 1261 } 1262 1263 out, err := base.subjectToken() 1264 if err != nil { 1265 t.Fatalf("retrieveSubjectToken() failed: %v", err) 1266 } 1267 1268 expected := getExpectedSubjectToken( 1269 "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", 1270 "us-east-2", 1271 accessKeyID, 1272 secretAccessKey, 1273 securityToken, 1274 ) 1275 1276 if got, want := out, expected; !reflect.DeepEqual(got, want) { 1277 t.Errorf("subjectToken = \n%q\n want \n%q", got, want) 1278 } 1279 } 1280 1281 func TestAWSCredential_ProgrammaticAuthNoSessionToken(t *testing.T) { 1282 tfc := testFileConfig 1283 securityCredentials := AwsSecurityCredentials{ 1284 AccessKeyID: accessKeyID, 1285 SecretAccessKey: secretAccessKey, 1286 } 1287 1288 tfc.AwsSecurityCredentialsSupplier = testAwsSupplier{ 1289 awsRegion: "us-east-2", 1290 err: nil, 1291 credentials: &securityCredentials, 1292 } 1293 1294 oldNow := now 1295 defer func() { 1296 now = oldNow 1297 }() 1298 now = setTime(defaultTime) 1299 1300 base, err := tfc.parse(context.Background()) 1301 if err != nil { 1302 t.Fatalf("parse() failed %v", err) 1303 } 1304 1305 out, err := base.subjectToken() 1306 if err != nil { 1307 t.Fatalf("retrieveSubjectToken() failed: %v", err) 1308 } 1309 1310 expected := getExpectedSubjectToken( 1311 "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", 1312 "us-east-2", 1313 accessKeyID, 1314 secretAccessKey, 1315 "", 1316 ) 1317 1318 if got, want := out, expected; !reflect.DeepEqual(got, want) { 1319 t.Errorf("subjectToken = \n%q\n want \n%q", got, want) 1320 } 1321 } 1322 1323 func TestAWSCredential_ProgrammaticAuthError(t *testing.T) { 1324 tfc := testFileConfig 1325 testErr := errors.New("test error") 1326 tfc.AwsSecurityCredentialsSupplier = testAwsSupplier{ 1327 awsRegion: "us-east-2", 1328 err: testErr, 1329 credentials: nil, 1330 } 1331 1332 base, err := tfc.parse(context.Background()) 1333 if err != nil { 1334 t.Fatalf("parse() failed %v", err) 1335 } 1336 1337 _, err = base.subjectToken() 1338 if err == nil { 1339 t.Fatalf("subjectToken() should have failed") 1340 } 1341 if err != testErr { 1342 t.Errorf("error = %e, want %e", err, testErr) 1343 } 1344 } 1345 1346 func TestAWSCredential_ProgrammaticAuthRegionError(t *testing.T) { 1347 tfc := testFileConfig 1348 securityCredentials := AwsSecurityCredentials{ 1349 AccessKeyID: accessKeyID, 1350 SecretAccessKey: secretAccessKey, 1351 } 1352 1353 testErr := errors.New("test") 1354 tfc.AwsSecurityCredentialsSupplier = testAwsSupplier{ 1355 awsRegion: "", 1356 regionErr: testErr, 1357 credentials: &securityCredentials, 1358 } 1359 1360 base, err := tfc.parse(context.Background()) 1361 if err != nil { 1362 t.Fatalf("parse() failed %v", err) 1363 } 1364 1365 _, err = base.subjectToken() 1366 if err == nil { 1367 t.Fatalf("subjectToken() should have failed") 1368 } 1369 if err != testErr { 1370 t.Errorf("error = %e, want %e", err, testErr) 1371 } 1372 } 1373 1374 func TestAWSCredential_ProgrammaticAuthOptions(t *testing.T) { 1375 tfc := testFileConfig 1376 securityCredentials := AwsSecurityCredentials{ 1377 AccessKeyID: accessKeyID, 1378 SecretAccessKey: secretAccessKey, 1379 } 1380 expectedOptions := SupplierOptions{Audience: tfc.Audience, SubjectTokenType: tfc.SubjectTokenType} 1381 1382 tfc.AwsSecurityCredentialsSupplier = testAwsSupplier{ 1383 awsRegion: "us-east-2", 1384 credentials: &securityCredentials, 1385 expectedOptions: &expectedOptions, 1386 } 1387 1388 base, err := tfc.parse(context.Background()) 1389 if err != nil { 1390 t.Fatalf("parse() failed %v", err) 1391 } 1392 1393 _, err = base.subjectToken() 1394 if err != nil { 1395 t.Fatalf("subjectToken() failed %v", err) 1396 } 1397 } 1398 1399 func TestAWSCredential_ProgrammaticAuthContext(t *testing.T) { 1400 tfc := testFileConfig 1401 securityCredentials := AwsSecurityCredentials{ 1402 AccessKeyID: accessKeyID, 1403 SecretAccessKey: secretAccessKey, 1404 } 1405 ctx := context.Background() 1406 1407 tfc.AwsSecurityCredentialsSupplier = testAwsSupplier{ 1408 awsRegion: "us-east-2", 1409 credentials: &securityCredentials, 1410 expectedContext: ctx, 1411 } 1412 1413 base, err := tfc.parse(ctx) 1414 if err != nil { 1415 t.Fatalf("parse() failed %v", err) 1416 } 1417 1418 _, err = base.subjectToken() 1419 if err != nil { 1420 t.Fatalf("subjectToken() failed %v", err) 1421 } 1422 } 1423 1424 func TestAwsCredential_CredentialSourceType(t *testing.T) { 1425 server := createDefaultAwsTestServer() 1426 ts := httptest.NewServer(server) 1427 1428 tfc := testFileConfig 1429 tfc.CredentialSource = server.getCredentialSource(ts.URL) 1430 1431 base, err := tfc.parse(context.Background()) 1432 if err != nil { 1433 t.Fatalf("parse() failed %v", err) 1434 } 1435 1436 if got, want := base.credentialSourceType(), "aws"; got != want { 1437 t.Errorf("got %v but want %v", got, want) 1438 } 1439 } 1440 1441 type testAwsSupplier struct { 1442 err error 1443 regionErr error 1444 awsRegion string 1445 credentials *AwsSecurityCredentials 1446 expectedOptions *SupplierOptions 1447 expectedContext context.Context 1448 } 1449 1450 func (supp testAwsSupplier) AwsRegion(ctx context.Context, options SupplierOptions) (string, error) { 1451 if supp.regionErr != nil { 1452 return "", supp.regionErr 1453 } 1454 if supp.expectedOptions != nil { 1455 if supp.expectedOptions.Audience != options.Audience { 1456 return "", errors.New("Audience does not match") 1457 } 1458 if supp.expectedOptions.SubjectTokenType != options.SubjectTokenType { 1459 return "", errors.New("Audience does not match") 1460 } 1461 } 1462 if supp.expectedContext != nil { 1463 if supp.expectedContext != ctx { 1464 return "", errors.New("Context does not match") 1465 } 1466 } 1467 return supp.awsRegion, nil 1468 } 1469 1470 func (supp testAwsSupplier) AwsSecurityCredentials(ctx context.Context, options SupplierOptions) (*AwsSecurityCredentials, error) { 1471 if supp.err != nil { 1472 return nil, supp.err 1473 } 1474 if supp.expectedOptions != nil { 1475 if supp.expectedOptions.Audience != options.Audience { 1476 return nil, errors.New("Audience does not match") 1477 } 1478 if supp.expectedOptions.SubjectTokenType != options.SubjectTokenType { 1479 return nil, errors.New("Audience does not match") 1480 } 1481 } 1482 if supp.expectedContext != nil { 1483 if supp.expectedContext != ctx { 1484 return nil, errors.New("Context does not match") 1485 } 1486 } 1487 return supp.credentials, nil 1488 }