github.com/Aestek/consul@v1.2.4-0.20190309222502-b2c31e33971a/agent/http_test.go (about) 1 package agent 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "net" 11 "net/http" 12 "net/http/httptest" 13 "os" 14 "path/filepath" 15 "runtime" 16 "strconv" 17 "strings" 18 "testing" 19 "time" 20 21 "github.com/hashicorp/consul/agent/structs" 22 tokenStore "github.com/hashicorp/consul/agent/token" 23 "github.com/hashicorp/consul/api" 24 "github.com/hashicorp/consul/testrpc" 25 "github.com/hashicorp/consul/testutil" 26 cleanhttp "github.com/hashicorp/go-cleanhttp" 27 "github.com/stretchr/testify/assert" 28 "github.com/stretchr/testify/require" 29 "golang.org/x/net/http2" 30 ) 31 32 func TestHTTPServer_UnixSocket(t *testing.T) { 33 t.Parallel() 34 if runtime.GOOS == "windows" { 35 t.SkipNow() 36 } 37 38 tempDir := testutil.TempDir(t, "consul") 39 defer os.RemoveAll(tempDir) 40 socket := filepath.Join(tempDir, "test.sock") 41 42 // Only testing mode, since uid/gid might not be settable 43 // from test environment. 44 a := NewTestAgent(t, t.Name(), ` 45 addresses { 46 http = "unix://`+socket+`" 47 } 48 unix_sockets { 49 mode = "0777" 50 } 51 `) 52 defer a.Shutdown() 53 54 // Ensure the socket was created 55 if _, err := os.Stat(socket); err != nil { 56 t.Fatalf("err: %s", err) 57 } 58 59 // Ensure the mode was set properly 60 fi, err := os.Stat(socket) 61 if err != nil { 62 t.Fatalf("err: %s", err) 63 } 64 if fi.Mode().String() != "Srwxrwxrwx" { 65 t.Fatalf("bad permissions: %s", fi.Mode()) 66 } 67 68 // Ensure we can get a response from the socket. 69 trans := cleanhttp.DefaultTransport() 70 trans.DialContext = func(_ context.Context, _, _ string) (net.Conn, error) { 71 return net.Dial("unix", socket) 72 } 73 client := &http.Client{ 74 Transport: trans, 75 } 76 77 // This URL doesn't look like it makes sense, but the scheme (http://) and 78 // the host (127.0.0.1) are required by the HTTP client library. In reality 79 // this will just use the custom dialer and talk to the socket. 80 resp, err := client.Get("http://127.0.0.1/v1/agent/self") 81 if err != nil { 82 t.Fatalf("err: %s", err) 83 } 84 defer resp.Body.Close() 85 86 if body, err := ioutil.ReadAll(resp.Body); err != nil || len(body) == 0 { 87 t.Fatalf("bad: %s %v", body, err) 88 } 89 } 90 91 func TestHTTPServer_UnixSocket_FileExists(t *testing.T) { 92 t.Parallel() 93 if runtime.GOOS == "windows" { 94 t.SkipNow() 95 } 96 97 tempDir := testutil.TempDir(t, "consul") 98 defer os.RemoveAll(tempDir) 99 socket := filepath.Join(tempDir, "test.sock") 100 101 // Create a regular file at the socket path 102 if err := ioutil.WriteFile(socket, []byte("hello world"), 0644); err != nil { 103 t.Fatalf("err: %s", err) 104 } 105 fi, err := os.Stat(socket) 106 if err != nil { 107 t.Fatalf("err: %s", err) 108 } 109 if !fi.Mode().IsRegular() { 110 t.Fatalf("not a regular file: %s", socket) 111 } 112 113 a := NewTestAgent(t, t.Name(), ` 114 addresses { 115 http = "unix://`+socket+`" 116 } 117 `) 118 defer a.Shutdown() 119 120 // Ensure the file was replaced by the socket 121 fi, err = os.Stat(socket) 122 if err != nil { 123 t.Fatalf("err: %s", err) 124 } 125 if fi.Mode()&os.ModeSocket == 0 { 126 t.Fatalf("expected socket to replace file") 127 } 128 } 129 130 func TestHTTPServer_H2(t *testing.T) { 131 t.Parallel() 132 133 // Fire up an agent with TLS enabled. 134 a := &TestAgent{ 135 Name: t.Name(), 136 UseTLS: true, 137 HCL: ` 138 key_file = "../test/client_certs/server.key" 139 cert_file = "../test/client_certs/server.crt" 140 ca_file = "../test/client_certs/rootca.crt" 141 `, 142 } 143 a.Start(t) 144 defer a.Shutdown() 145 146 // Make an HTTP/2-enabled client, using the API helpers to set 147 // up TLS to be as normal as possible for Consul. 148 tlscfg := &api.TLSConfig{ 149 Address: "consul.test", 150 KeyFile: "../test/client_certs/client.key", 151 CertFile: "../test/client_certs/client.crt", 152 CAFile: "../test/client_certs/rootca.crt", 153 } 154 tlsccfg, err := api.SetupTLSConfig(tlscfg) 155 if err != nil { 156 t.Fatalf("err: %v", err) 157 } 158 transport := api.DefaultConfig().Transport 159 transport.TLSClientConfig = tlsccfg 160 if err := http2.ConfigureTransport(transport); err != nil { 161 t.Fatalf("err: %v", err) 162 } 163 hc := &http.Client{ 164 Transport: transport, 165 } 166 167 // Hook a handler that echoes back the protocol. 168 handler := func(resp http.ResponseWriter, req *http.Request) { 169 resp.WriteHeader(http.StatusOK) 170 fmt.Fprint(resp, req.Proto) 171 } 172 w, ok := a.srv.Handler.(*wrappedMux) 173 if !ok { 174 t.Fatalf("handler is not expected type") 175 } 176 w.mux.HandleFunc("/echo", handler) 177 178 // Call it and make sure we see HTTP/2. 179 url := fmt.Sprintf("https://%s/echo", a.srv.ln.Addr().String()) 180 resp, err := hc.Get(url) 181 if err != nil { 182 t.Fatalf("err: %v", err) 183 } 184 defer resp.Body.Close() 185 body, err := ioutil.ReadAll(resp.Body) 186 if err != nil { 187 t.Fatalf("err: %v", err) 188 } 189 if !bytes.Equal(body, []byte("HTTP/2.0")) { 190 t.Fatalf("bad: %v", body) 191 } 192 193 // This doesn't have a closed-loop way to verify HTTP/2 support for 194 // some other endpoint, but configure an API client and make a call 195 // just as a sanity check. 196 cfg := &api.Config{ 197 Address: a.srv.ln.Addr().String(), 198 Scheme: "https", 199 HttpClient: hc, 200 } 201 client, err := api.NewClient(cfg) 202 if err != nil { 203 t.Fatalf("err: %v", err) 204 } 205 if _, err := client.Agent().Self(); err != nil { 206 t.Fatalf("err: %v", err) 207 } 208 } 209 210 func TestSetIndex(t *testing.T) { 211 t.Parallel() 212 resp := httptest.NewRecorder() 213 setIndex(resp, 1000) 214 header := resp.Header().Get("X-Consul-Index") 215 if header != "1000" { 216 t.Fatalf("Bad: %v", header) 217 } 218 setIndex(resp, 2000) 219 if v := resp.Header()["X-Consul-Index"]; len(v) != 1 { 220 t.Fatalf("bad: %#v", v) 221 } 222 } 223 224 func TestSetKnownLeader(t *testing.T) { 225 t.Parallel() 226 resp := httptest.NewRecorder() 227 setKnownLeader(resp, true) 228 header := resp.Header().Get("X-Consul-KnownLeader") 229 if header != "true" { 230 t.Fatalf("Bad: %v", header) 231 } 232 resp = httptest.NewRecorder() 233 setKnownLeader(resp, false) 234 header = resp.Header().Get("X-Consul-KnownLeader") 235 if header != "false" { 236 t.Fatalf("Bad: %v", header) 237 } 238 } 239 240 func TestSetLastContact(t *testing.T) { 241 t.Parallel() 242 tests := []struct { 243 desc string 244 d time.Duration 245 h string 246 }{ 247 {"neg", -1, "0"}, 248 {"zero", 0, "0"}, 249 {"pos", 123 * time.Millisecond, "123"}, 250 {"pos ms only", 123456 * time.Microsecond, "123"}, 251 } 252 for _, tt := range tests { 253 t.Run(tt.desc, func(t *testing.T) { 254 resp := httptest.NewRecorder() 255 setLastContact(resp, tt.d) 256 header := resp.Header().Get("X-Consul-LastContact") 257 if got, want := header, tt.h; got != want { 258 t.Fatalf("got X-Consul-LastContact header %q want %q", got, want) 259 } 260 }) 261 } 262 } 263 264 func TestSetMeta(t *testing.T) { 265 t.Parallel() 266 meta := structs.QueryMeta{ 267 Index: 1000, 268 KnownLeader: true, 269 LastContact: 123456 * time.Microsecond, 270 } 271 resp := httptest.NewRecorder() 272 setMeta(resp, &meta) 273 header := resp.Header().Get("X-Consul-Index") 274 if header != "1000" { 275 t.Fatalf("Bad: %v", header) 276 } 277 header = resp.Header().Get("X-Consul-KnownLeader") 278 if header != "true" { 279 t.Fatalf("Bad: %v", header) 280 } 281 header = resp.Header().Get("X-Consul-LastContact") 282 if header != "123" { 283 t.Fatalf("Bad: %v", header) 284 } 285 } 286 287 func TestHTTPAPI_BlockEndpoints(t *testing.T) { 288 t.Parallel() 289 290 a := NewTestAgent(t, t.Name(), ` 291 http_config { 292 block_endpoints = ["/v1/agent/self"] 293 } 294 `) 295 defer a.Shutdown() 296 297 handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 298 return nil, nil 299 } 300 301 // Try a blocked endpoint, which should get a 403. 302 { 303 req, _ := http.NewRequest("GET", "/v1/agent/self", nil) 304 resp := httptest.NewRecorder() 305 a.srv.wrap(handler, []string{"GET"})(resp, req) 306 if got, want := resp.Code, http.StatusForbidden; got != want { 307 t.Fatalf("bad response code got %d want %d", got, want) 308 } 309 } 310 311 // Make sure some other endpoint still works. 312 { 313 req, _ := http.NewRequest("GET", "/v1/agent/checks", nil) 314 resp := httptest.NewRecorder() 315 a.srv.wrap(handler, []string{"GET"})(resp, req) 316 if got, want := resp.Code, http.StatusOK; got != want { 317 t.Fatalf("bad response code got %d want %d", got, want) 318 } 319 } 320 } 321 322 func TestHTTPAPI_Ban_Nonprintable_Characters(t *testing.T) { 323 a := NewTestAgent(t, t.Name(), "") 324 defer a.Shutdown() 325 326 req, _ := http.NewRequest("GET", "/v1/kv/bad\x00ness", nil) 327 resp := httptest.NewRecorder() 328 a.srv.Handler.ServeHTTP(resp, req) 329 if got, want := resp.Code, http.StatusBadRequest; got != want { 330 t.Fatalf("bad response code got %d want %d", got, want) 331 } 332 } 333 334 func TestHTTPAPI_Allow_Nonprintable_Characters_With_Flag(t *testing.T) { 335 a := NewTestAgent(t, t.Name(), "disable_http_unprintable_char_filter = true") 336 defer a.Shutdown() 337 338 req, _ := http.NewRequest("GET", "/v1/kv/bad\x00ness", nil) 339 resp := httptest.NewRecorder() 340 a.srv.Handler.ServeHTTP(resp, req) 341 // Key doesn't actually exist so we should get 404 342 if got, want := resp.Code, http.StatusNotFound; got != want { 343 t.Fatalf("bad response code got %d want %d", got, want) 344 } 345 } 346 347 func TestHTTPAPI_TranslateAddrHeader(t *testing.T) { 348 t.Parallel() 349 // Header should not be present if address translation is off. 350 { 351 a := NewTestAgent(t, t.Name(), "") 352 defer a.Shutdown() 353 354 resp := httptest.NewRecorder() 355 handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 356 return nil, nil 357 } 358 359 req, _ := http.NewRequest("GET", "/v1/agent/self", nil) 360 a.srv.wrap(handler, []string{"GET"})(resp, req) 361 362 translate := resp.Header().Get("X-Consul-Translate-Addresses") 363 if translate != "" { 364 t.Fatalf("bad: expected %q, got %q", "", translate) 365 } 366 } 367 368 // Header should be set to true if it's turned on. 369 { 370 a := NewTestAgent(t, t.Name(), ` 371 translate_wan_addrs = true 372 `) 373 defer a.Shutdown() 374 375 resp := httptest.NewRecorder() 376 handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 377 return nil, nil 378 } 379 380 req, _ := http.NewRequest("GET", "/v1/agent/self", nil) 381 a.srv.wrap(handler, []string{"GET"})(resp, req) 382 383 translate := resp.Header().Get("X-Consul-Translate-Addresses") 384 if translate != "true" { 385 t.Fatalf("bad: expected %q, got %q", "true", translate) 386 } 387 } 388 } 389 390 func TestHTTPAPIResponseHeaders(t *testing.T) { 391 t.Parallel() 392 a := NewTestAgent(t, t.Name(), ` 393 http_config { 394 response_headers = { 395 "Access-Control-Allow-Origin" = "*" 396 "X-XSS-Protection" = "1; mode=block" 397 } 398 } 399 `) 400 defer a.Shutdown() 401 402 resp := httptest.NewRecorder() 403 handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 404 return nil, nil 405 } 406 407 req, _ := http.NewRequest("GET", "/v1/agent/self", nil) 408 a.srv.wrap(handler, []string{"GET"})(resp, req) 409 410 origin := resp.Header().Get("Access-Control-Allow-Origin") 411 if origin != "*" { 412 t.Fatalf("bad Access-Control-Allow-Origin: expected %q, got %q", "*", origin) 413 } 414 415 xss := resp.Header().Get("X-XSS-Protection") 416 if xss != "1; mode=block" { 417 t.Fatalf("bad X-XSS-Protection header: expected %q, got %q", "1; mode=block", xss) 418 } 419 } 420 421 func TestContentTypeIsJSON(t *testing.T) { 422 t.Parallel() 423 a := NewTestAgent(t, t.Name(), "") 424 defer a.Shutdown() 425 426 resp := httptest.NewRecorder() 427 handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 428 // stub out a DirEntry so that it will be encoded as JSON 429 return &structs.DirEntry{Key: "key"}, nil 430 } 431 432 req, _ := http.NewRequest("GET", "/v1/kv/key", nil) 433 a.srv.wrap(handler, []string{"GET"})(resp, req) 434 435 contentType := resp.Header().Get("Content-Type") 436 437 if contentType != "application/json" { 438 t.Fatalf("Content-Type header was not 'application/json'") 439 } 440 } 441 442 func TestHTTP_wrap_obfuscateLog(t *testing.T) { 443 t.Parallel() 444 buf := new(bytes.Buffer) 445 a := &TestAgent{Name: t.Name(), LogOutput: buf} 446 a.Start(t) 447 defer a.Shutdown() 448 449 handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 450 return nil, nil 451 } 452 453 for _, pair := range [][]string{ 454 { 455 "/some/url?token=secret1&token=secret2", 456 "/some/url?token=<hidden>&token=<hidden>", 457 }, 458 { 459 "/v1/acl/clone/secret1", 460 "/v1/acl/clone/<hidden>", 461 }, 462 { 463 "/v1/acl/clone/secret1?token=secret2", 464 "/v1/acl/clone/<hidden>?token=<hidden>", 465 }, 466 { 467 "/v1/acl/destroy/secret1", 468 "/v1/acl/destroy/<hidden>", 469 }, 470 { 471 "/v1/acl/destroy/secret1?token=secret2", 472 "/v1/acl/destroy/<hidden>?token=<hidden>", 473 }, 474 { 475 "/v1/acl/info/secret1", 476 "/v1/acl/info/<hidden>", 477 }, 478 { 479 "/v1/acl/info/secret1?token=secret2", 480 "/v1/acl/info/<hidden>?token=<hidden>", 481 }, 482 } { 483 url, want := pair[0], pair[1] 484 t.Run(url, func(t *testing.T) { 485 resp := httptest.NewRecorder() 486 req, _ := http.NewRequest("GET", url, nil) 487 a.srv.wrap(handler, []string{"GET"})(resp, req) 488 489 if got := buf.String(); !strings.Contains(got, want) { 490 t.Fatalf("got %s want %s", got, want) 491 } 492 }) 493 } 494 } 495 496 func TestPrettyPrint(t *testing.T) { 497 t.Parallel() 498 testPrettyPrint("pretty=1", t) 499 } 500 501 func TestPrettyPrintBare(t *testing.T) { 502 t.Parallel() 503 testPrettyPrint("pretty", t) 504 } 505 506 func testPrettyPrint(pretty string, t *testing.T) { 507 a := NewTestAgent(t, t.Name(), "") 508 defer a.Shutdown() 509 510 r := &structs.DirEntry{Key: "key"} 511 512 resp := httptest.NewRecorder() 513 handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 514 return r, nil 515 } 516 517 urlStr := "/v1/kv/key?" + pretty 518 req, _ := http.NewRequest("GET", urlStr, nil) 519 a.srv.wrap(handler, []string{"GET"})(resp, req) 520 521 expected, _ := json.MarshalIndent(r, "", " ") 522 expected = append(expected, "\n"...) 523 actual, err := ioutil.ReadAll(resp.Body) 524 if err != nil { 525 t.Fatalf("err: %s", err) 526 } 527 528 if !bytes.Equal(expected, actual) { 529 t.Fatalf("bad: %q", string(actual)) 530 } 531 } 532 533 func TestParseSource(t *testing.T) { 534 t.Parallel() 535 a := NewTestAgent(t, t.Name(), "") 536 defer a.Shutdown() 537 538 // Default is agent's DC and no node (since the user didn't care, then 539 // just give them the cheapest possible query). 540 req, _ := http.NewRequest("GET", "/v1/catalog/nodes", nil) 541 source := structs.QuerySource{} 542 a.srv.parseSource(req, &source) 543 if source.Datacenter != "dc1" || source.Node != "" { 544 t.Fatalf("bad: %v", source) 545 } 546 547 // Adding the source parameter should set that node. 548 req, _ = http.NewRequest("GET", "/v1/catalog/nodes?near=bob", nil) 549 source = structs.QuerySource{} 550 a.srv.parseSource(req, &source) 551 if source.Datacenter != "dc1" || source.Node != "bob" { 552 t.Fatalf("bad: %v", source) 553 } 554 555 // We should follow whatever dc parameter was given so that the node is 556 // looked up correctly on the receiving end. 557 req, _ = http.NewRequest("GET", "/v1/catalog/nodes?near=bob&dc=foo", nil) 558 source = structs.QuerySource{} 559 a.srv.parseSource(req, &source) 560 if source.Datacenter != "foo" || source.Node != "bob" { 561 t.Fatalf("bad: %v", source) 562 } 563 564 // The magic "_agent" node name will use the agent's local node name. 565 req, _ = http.NewRequest("GET", "/v1/catalog/nodes?near=_agent", nil) 566 source = structs.QuerySource{} 567 a.srv.parseSource(req, &source) 568 if source.Datacenter != "dc1" || source.Node != a.Config.NodeName { 569 t.Fatalf("bad: %v", source) 570 } 571 } 572 573 func TestParseCacheControl(t *testing.T) { 574 575 tests := []struct { 576 name string 577 headerVal string 578 want structs.QueryOptions 579 wantErr bool 580 }{ 581 { 582 name: "empty header", 583 headerVal: "", 584 want: structs.QueryOptions{}, 585 wantErr: false, 586 }, 587 { 588 name: "simple max-age", 589 headerVal: "max-age=30", 590 want: structs.QueryOptions{ 591 MaxAge: 30 * time.Second, 592 }, 593 wantErr: false, 594 }, 595 { 596 name: "zero max-age", 597 headerVal: "max-age=0", 598 want: structs.QueryOptions{ 599 MustRevalidate: true, 600 }, 601 wantErr: false, 602 }, 603 { 604 name: "must-revalidate", 605 headerVal: "must-revalidate", 606 want: structs.QueryOptions{ 607 MustRevalidate: true, 608 }, 609 wantErr: false, 610 }, 611 { 612 name: "mixes age, must-revalidate", 613 headerVal: "max-age=123, must-revalidate", 614 want: structs.QueryOptions{ 615 MaxAge: 123 * time.Second, 616 MustRevalidate: true, 617 }, 618 wantErr: false, 619 }, 620 { 621 name: "quoted max-age", 622 headerVal: "max-age=\"30\"", 623 want: structs.QueryOptions{}, 624 wantErr: true, 625 }, 626 { 627 name: "mixed case max-age", 628 headerVal: "Max-Age=30", 629 want: structs.QueryOptions{ 630 MaxAge: 30 * time.Second, 631 }, 632 wantErr: false, 633 }, 634 { 635 name: "simple stale-if-error", 636 headerVal: "stale-if-error=300", 637 want: structs.QueryOptions{ 638 StaleIfError: 300 * time.Second, 639 }, 640 wantErr: false, 641 }, 642 { 643 name: "combined with space", 644 headerVal: "max-age=30, stale-if-error=300", 645 want: structs.QueryOptions{ 646 MaxAge: 30 * time.Second, 647 StaleIfError: 300 * time.Second, 648 }, 649 wantErr: false, 650 }, 651 { 652 name: "combined no space", 653 headerVal: "stale-IF-error=300,max-age=30", 654 want: structs.QueryOptions{ 655 MaxAge: 30 * time.Second, 656 StaleIfError: 300 * time.Second, 657 }, 658 wantErr: false, 659 }, 660 { 661 name: "unsupported directive", 662 headerVal: "no-cache", 663 want: structs.QueryOptions{}, 664 wantErr: false, 665 }, 666 { 667 name: "mixed unsupported directive", 668 headerVal: "no-cache, max-age=120", 669 want: structs.QueryOptions{ 670 MaxAge: 120 * time.Second, 671 }, 672 wantErr: false, 673 }, 674 { 675 name: "garbage value", 676 headerVal: "max-age=\"I'm not, an int\"", 677 want: structs.QueryOptions{}, 678 wantErr: true, 679 }, 680 { 681 name: "garbage value with quotes", 682 headerVal: "max-age=\"I'm \\\"not an int\"", 683 want: structs.QueryOptions{}, 684 wantErr: true, 685 }, 686 } 687 688 for _, tt := range tests { 689 t.Run(tt.name, func(t *testing.T) { 690 require := require.New(t) 691 692 r, _ := http.NewRequest("GET", "/foo/bar", nil) 693 if tt.headerVal != "" { 694 r.Header.Set("Cache-Control", tt.headerVal) 695 } 696 697 rr := httptest.NewRecorder() 698 var got structs.QueryOptions 699 700 failed := parseCacheControl(rr, r, &got) 701 if tt.wantErr { 702 require.True(failed) 703 require.Equal(http.StatusBadRequest, rr.Code) 704 } else { 705 require.False(failed) 706 } 707 708 require.Equal(tt.want, got) 709 }) 710 } 711 } 712 713 func TestParseWait(t *testing.T) { 714 t.Parallel() 715 resp := httptest.NewRecorder() 716 var b structs.QueryOptions 717 718 req, _ := http.NewRequest("GET", "/v1/catalog/nodes?wait=60s&index=1000", nil) 719 if d := parseWait(resp, req, &b); d { 720 t.Fatalf("unexpected done") 721 } 722 723 if b.MinQueryIndex != 1000 { 724 t.Fatalf("Bad: %v", b) 725 } 726 if b.MaxQueryTime != 60*time.Second { 727 t.Fatalf("Bad: %v", b) 728 } 729 } 730 func TestPProfHandlers_EnableDebug(t *testing.T) { 731 t.Parallel() 732 require := require.New(t) 733 a := NewTestAgent(t, t.Name(), "enable_debug = true") 734 defer a.Shutdown() 735 736 resp := httptest.NewRecorder() 737 req, _ := http.NewRequest("GET", "/debug/pprof/profile", nil) 738 739 a.srv.Handler.ServeHTTP(resp, req) 740 741 require.Equal(http.StatusOK, resp.Code) 742 } 743 func TestPProfHandlers_DisableDebugNoACLs(t *testing.T) { 744 t.Parallel() 745 require := require.New(t) 746 a := NewTestAgent(t, t.Name(), "enable_debug = false") 747 defer a.Shutdown() 748 749 resp := httptest.NewRecorder() 750 req, _ := http.NewRequest("GET", "/debug/pprof/profile", nil) 751 752 a.srv.Handler.ServeHTTP(resp, req) 753 754 require.Equal(http.StatusUnauthorized, resp.Code) 755 } 756 757 func TestPProfHandlers_ACLs(t *testing.T) { 758 t.Parallel() 759 assert := assert.New(t) 760 dc1 := "dc1" 761 762 a := NewTestAgent(t, t.Name(), ` 763 acl_datacenter = "`+dc1+`" 764 acl_default_policy = "deny" 765 acl_master_token = "master" 766 acl_agent_token = "agent" 767 acl_agent_master_token = "towel" 768 acl_enforce_version_8 = true 769 enable_debug = false 770 `) 771 772 cases := []struct { 773 code int 774 token string 775 endpoint string 776 nilResponse bool 777 }{ 778 { 779 code: http.StatusOK, 780 token: "master", 781 endpoint: "/debug/pprof/heap", 782 nilResponse: false, 783 }, 784 { 785 code: http.StatusForbidden, 786 token: "agent", 787 endpoint: "/debug/pprof/heap", 788 nilResponse: true, 789 }, 790 { 791 code: http.StatusForbidden, 792 token: "agent", 793 endpoint: "/debug/pprof/", 794 nilResponse: true, 795 }, 796 { 797 code: http.StatusForbidden, 798 token: "", 799 endpoint: "/debug/pprof/", 800 nilResponse: true, 801 }, 802 { 803 code: http.StatusOK, 804 token: "master", 805 endpoint: "/debug/pprof/heap", 806 nilResponse: false, 807 }, 808 { 809 code: http.StatusForbidden, 810 token: "towel", 811 endpoint: "/debug/pprof/heap", 812 nilResponse: true, 813 }, 814 } 815 816 defer a.Shutdown() 817 testrpc.WaitForLeader(t, a.RPC, "dc1") 818 819 for i, c := range cases { 820 t.Run(fmt.Sprintf("case %d (%#v)", i, c), func(t *testing.T) { 821 req, _ := http.NewRequest("GET", fmt.Sprintf("%s?token=%s", c.endpoint, c.token), nil) 822 resp := httptest.NewRecorder() 823 a.srv.Handler.ServeHTTP(resp, req) 824 assert.Equal(c.code, resp.Code) 825 }) 826 } 827 } 828 829 func TestParseWait_InvalidTime(t *testing.T) { 830 t.Parallel() 831 resp := httptest.NewRecorder() 832 var b structs.QueryOptions 833 834 req, _ := http.NewRequest("GET", "/v1/catalog/nodes?wait=60foo&index=1000", nil) 835 if d := parseWait(resp, req, &b); !d { 836 t.Fatalf("expected done") 837 } 838 839 if resp.Code != 400 { 840 t.Fatalf("bad code: %v", resp.Code) 841 } 842 } 843 844 func TestParseWait_InvalidIndex(t *testing.T) { 845 t.Parallel() 846 resp := httptest.NewRecorder() 847 var b structs.QueryOptions 848 849 req, _ := http.NewRequest("GET", "/v1/catalog/nodes?wait=60s&index=foo", nil) 850 if d := parseWait(resp, req, &b); !d { 851 t.Fatalf("expected done") 852 } 853 854 if resp.Code != 400 { 855 t.Fatalf("bad code: %v", resp.Code) 856 } 857 } 858 859 func TestParseConsistency(t *testing.T) { 860 t.Parallel() 861 resp := httptest.NewRecorder() 862 var b structs.QueryOptions 863 864 req, _ := http.NewRequest("GET", "/v1/catalog/nodes?stale", nil) 865 a := NewTestAgent(t, t.Name(), "") 866 defer a.Shutdown() 867 if d := a.srv.parseConsistency(resp, req, &b); d { 868 t.Fatalf("unexpected done") 869 } 870 871 if !b.AllowStale { 872 t.Fatalf("Bad: %v", b) 873 } 874 if b.RequireConsistent { 875 t.Fatalf("Bad: %v", b) 876 } 877 878 b = structs.QueryOptions{} 879 req, _ = http.NewRequest("GET", "/v1/catalog/nodes?consistent", nil) 880 if d := a.srv.parseConsistency(resp, req, &b); d { 881 t.Fatalf("unexpected done") 882 } 883 884 if b.AllowStale { 885 t.Fatalf("Bad: %v", b) 886 } 887 if !b.RequireConsistent { 888 t.Fatalf("Bad: %v", b) 889 } 890 } 891 892 // ensureConsistency check if consistency modes are correctly applied 893 // if maxStale < 0 => stale, without MaxStaleDuration 894 // if maxStale == 0 => no stale 895 // if maxStale > 0 => stale + check duration 896 func ensureConsistency(t *testing.T, a *TestAgent, path string, maxStale time.Duration, requireConsistent bool) { 897 t.Helper() 898 req, _ := http.NewRequest("GET", path, nil) 899 var b structs.QueryOptions 900 resp := httptest.NewRecorder() 901 if d := a.srv.parseConsistency(resp, req, &b); d { 902 t.Fatalf("unexpected done") 903 } 904 allowStale := maxStale.Nanoseconds() != 0 905 if b.AllowStale != allowStale { 906 t.Fatalf("Bad Allow Stale") 907 } 908 if maxStale > 0 && b.MaxStaleDuration != maxStale { 909 t.Fatalf("Bad MaxStaleDuration: %d VS expected %d", b.MaxStaleDuration, maxStale) 910 } 911 if b.RequireConsistent != requireConsistent { 912 t.Fatal("Bad Consistent") 913 } 914 } 915 916 func TestParseConsistencyAndMaxStale(t *testing.T) { 917 a := NewTestAgent(t, t.Name(), "") 918 defer a.Shutdown() 919 920 // Default => Consistent 921 a.config.DiscoveryMaxStale = time.Duration(0) 922 ensureConsistency(t, a, "/v1/catalog/nodes", 0, false) 923 // Stale, without MaxStale 924 ensureConsistency(t, a, "/v1/catalog/nodes?stale", -1, false) 925 // Override explicitly 926 ensureConsistency(t, a, "/v1/catalog/nodes?max_stale=3s", 3*time.Second, false) 927 ensureConsistency(t, a, "/v1/catalog/nodes?stale&max_stale=3s", 3*time.Second, false) 928 929 // stale by defaul on discovery 930 a.config.DiscoveryMaxStale = time.Duration(7 * time.Second) 931 ensureConsistency(t, a, "/v1/catalog/nodes", a.config.DiscoveryMaxStale, false) 932 // Not in KV 933 ensureConsistency(t, a, "/v1/kv/my/path", 0, false) 934 935 // DiscoveryConsistencyLevel should apply 936 ensureConsistency(t, a, "/v1/health/service/one", a.config.DiscoveryMaxStale, false) 937 ensureConsistency(t, a, "/v1/catalog/service/one", a.config.DiscoveryMaxStale, false) 938 ensureConsistency(t, a, "/v1/catalog/services", a.config.DiscoveryMaxStale, false) 939 940 // Query path should be taken into account 941 ensureConsistency(t, a, "/v1/catalog/services?consistent", 0, true) 942 // Since stale is added, no MaxStale should be applied 943 ensureConsistency(t, a, "/v1/catalog/services?stale", -1, false) 944 ensureConsistency(t, a, "/v1/catalog/services?leader", 0, false) 945 } 946 947 func TestParseConsistency_Invalid(t *testing.T) { 948 t.Parallel() 949 resp := httptest.NewRecorder() 950 var b structs.QueryOptions 951 952 req, _ := http.NewRequest("GET", "/v1/catalog/nodes?stale&consistent", nil) 953 a := NewTestAgent(t, t.Name(), "") 954 defer a.Shutdown() 955 if d := a.srv.parseConsistency(resp, req, &b); !d { 956 t.Fatalf("expected done") 957 } 958 959 if resp.Code != 400 { 960 t.Fatalf("bad code: %v", resp.Code) 961 } 962 } 963 964 // Test ACL token is resolved in correct order 965 func TestACLResolution(t *testing.T) { 966 t.Parallel() 967 var token string 968 // Request without token 969 req, _ := http.NewRequest("GET", "/v1/catalog/nodes", nil) 970 // Request with explicit token 971 reqToken, _ := http.NewRequest("GET", "/v1/catalog/nodes?token=foo", nil) 972 // Request with header token only 973 reqHeaderToken, _ := http.NewRequest("GET", "/v1/catalog/nodes", nil) 974 reqHeaderToken.Header.Add("X-Consul-Token", "bar") 975 976 // Request with header and querystring tokens 977 reqBothTokens, _ := http.NewRequest("GET", "/v1/catalog/nodes?token=baz", nil) 978 reqBothTokens.Header.Add("X-Consul-Token", "zap") 979 980 // Request with Authorization Bearer token 981 reqAuthBearerToken, _ := http.NewRequest("GET", "/v1/catalog/nodes", nil) 982 reqAuthBearerToken.Header.Add("Authorization", "Bearer bearer-token") 983 984 // Request with invalid Authorization scheme 985 reqAuthBearerInvalidScheme, _ := http.NewRequest("GET", "/v1/catalog/nodes", nil) 986 reqAuthBearerInvalidScheme.Header.Add("Authorization", "Beer") 987 988 // Request with empty Authorization Bearer token 989 reqAuthBearerTokenEmpty, _ := http.NewRequest("GET", "/v1/catalog/nodes", nil) 990 reqAuthBearerTokenEmpty.Header.Add("Authorization", "Bearer") 991 992 // Request with empty Authorization Bearer token 993 reqAuthBearerTokenInvalid, _ := http.NewRequest("GET", "/v1/catalog/nodes", nil) 994 reqAuthBearerTokenInvalid.Header.Add("Authorization", "Bearertoken") 995 996 // Request with more than one space between Bearer and token 997 reqAuthBearerTokenMultiSpaces, _ := http.NewRequest("GET", "/v1/catalog/nodes", nil) 998 reqAuthBearerTokenMultiSpaces.Header.Add("Authorization", "Bearer bearer-token") 999 1000 // Request with Authorization Bearer token containing spaces 1001 reqAuthBearerTokenSpaces, _ := http.NewRequest("GET", "/v1/catalog/nodes", nil) 1002 reqAuthBearerTokenSpaces.Header.Add("Authorization", "Bearer bearer-token "+ 1003 " the rest is discarded ") 1004 1005 // Request with Authorization Bearer and querystring token 1006 reqAuthBearerAndQsToken, _ := http.NewRequest("GET", "/v1/catalog/nodes?token=qstoken", nil) 1007 reqAuthBearerAndQsToken.Header.Add("Authorization", "Bearer bearer-token") 1008 1009 // Request with Authorization Bearer and X-Consul-Token header token 1010 reqAuthBearerAndXToken, _ := http.NewRequest("GET", "/v1/catalog/nodes", nil) 1011 reqAuthBearerAndXToken.Header.Add("X-Consul-Token", "xtoken") 1012 reqAuthBearerAndXToken.Header.Add("Authorization", "Bearer bearer-token") 1013 1014 a := NewTestAgent(t, t.Name(), "") 1015 defer a.Shutdown() 1016 1017 // Check when no token is set 1018 a.tokens.UpdateUserToken("", tokenStore.TokenSourceConfig) 1019 a.srv.parseToken(req, &token) 1020 if token != "" { 1021 t.Fatalf("bad: %s", token) 1022 } 1023 1024 // Check when ACLToken set 1025 a.tokens.UpdateUserToken("agent", tokenStore.TokenSourceAPI) 1026 a.srv.parseToken(req, &token) 1027 if token != "agent" { 1028 t.Fatalf("bad: %s", token) 1029 } 1030 1031 // Explicit token has highest precedence 1032 a.srv.parseToken(reqToken, &token) 1033 if token != "foo" { 1034 t.Fatalf("bad: %s", token) 1035 } 1036 1037 // Header token has precedence over agent token 1038 a.srv.parseToken(reqHeaderToken, &token) 1039 if token != "bar" { 1040 t.Fatalf("bad: %s", token) 1041 } 1042 1043 // Querystring token has precedence over header and agent tokens 1044 a.srv.parseToken(reqBothTokens, &token) 1045 if token != "baz" { 1046 t.Fatalf("bad: %s", token) 1047 } 1048 1049 // 1050 // Authorization Bearer token tests 1051 // 1052 1053 // Check if Authorization bearer token header is parsed correctly 1054 a.srv.parseToken(reqAuthBearerToken, &token) 1055 if token != "bearer-token" { 1056 t.Fatalf("bad: %s", token) 1057 } 1058 1059 // Check Authorization Bearer scheme invalid 1060 a.srv.parseToken(reqAuthBearerInvalidScheme, &token) 1061 if token != "agent" { 1062 t.Fatalf("bad: %s", token) 1063 } 1064 1065 // Check if Authorization Bearer token is empty 1066 a.srv.parseToken(reqAuthBearerTokenEmpty, &token) 1067 if token != "agent" { 1068 t.Fatalf("bad: %s", token) 1069 } 1070 1071 // Check if the Authorization Bearer token is invalid 1072 a.srv.parseToken(reqAuthBearerTokenInvalid, &token) 1073 if token != "agent" { 1074 t.Fatalf("bad: %s", token) 1075 } 1076 1077 // Check multi spaces between Authorization Bearer and token value 1078 a.srv.parseToken(reqAuthBearerTokenMultiSpaces, &token) 1079 if token != "bearer-token" { 1080 t.Fatalf("bad: %s", token) 1081 } 1082 1083 // Check if Authorization Bearer token with spaces is parsed correctly 1084 a.srv.parseToken(reqAuthBearerTokenSpaces, &token) 1085 if token != "bearer-token" { 1086 t.Fatalf("bad: %s", token) 1087 } 1088 1089 // Check if explicit token has precedence over Authorization bearer token 1090 a.srv.parseToken(reqAuthBearerAndQsToken, &token) 1091 if token != "qstoken" { 1092 t.Fatalf("bad: %s", token) 1093 } 1094 1095 // Check if X-Consul-Token has precedence over Authorization bearer token 1096 a.srv.parseToken(reqAuthBearerAndXToken, &token) 1097 if token != "xtoken" { 1098 t.Fatalf("bad: %s", token) 1099 } 1100 } 1101 1102 func TestEnableWebUI(t *testing.T) { 1103 t.Parallel() 1104 a := NewTestAgent(t, t.Name(), ` 1105 ui = true 1106 `) 1107 defer a.Shutdown() 1108 1109 req, _ := http.NewRequest("GET", "/ui/", nil) 1110 resp := httptest.NewRecorder() 1111 a.srv.Handler.ServeHTTP(resp, req) 1112 if resp.Code != 200 { 1113 t.Fatalf("should handle ui") 1114 } 1115 } 1116 1117 func TestParseToken_ProxyTokenResolve(t *testing.T) { 1118 t.Parallel() 1119 1120 type endpointCheck struct { 1121 endpoint string 1122 handler func(s *HTTPServer, resp http.ResponseWriter, req *http.Request) (interface{}, error) 1123 } 1124 1125 // This is not an exhaustive list of all of our endpoints and is only testing GET endpoints 1126 // right now. However it provides decent coverage that the proxy token resolution 1127 // is happening properly 1128 tests := []endpointCheck{ 1129 {"/v1/acl/info/root", (*HTTPServer).ACLGet}, 1130 {"/v1/agent/self", (*HTTPServer).AgentSelf}, 1131 {"/v1/agent/metrics", (*HTTPServer).AgentMetrics}, 1132 {"/v1/agent/services", (*HTTPServer).AgentServices}, 1133 {"/v1/agent/checks", (*HTTPServer).AgentChecks}, 1134 {"/v1/agent/members", (*HTTPServer).AgentMembers}, 1135 {"/v1/agent/connect/ca/roots", (*HTTPServer).AgentConnectCARoots}, 1136 {"/v1/agent/connect/ca/leaf/test", (*HTTPServer).AgentConnectCALeafCert}, 1137 {"/v1/agent/connect/ca/proxy/test", (*HTTPServer).AgentConnectProxyConfig}, 1138 {"/v1/catalog/connect", (*HTTPServer).CatalogConnectServiceNodes}, 1139 {"/v1/catalog/datacenters", (*HTTPServer).CatalogDatacenters}, 1140 {"/v1/catalog/nodes", (*HTTPServer).CatalogNodes}, 1141 {"/v1/catalog/node/" + t.Name(), (*HTTPServer).CatalogNodeServices}, 1142 {"/v1/catalog/services", (*HTTPServer).CatalogServices}, 1143 {"/v1/catalog/service/test", (*HTTPServer).CatalogServiceNodes}, 1144 {"/v1/connect/ca/configuration", (*HTTPServer).ConnectCAConfiguration}, 1145 {"/v1/connect/ca/roots", (*HTTPServer).ConnectCARoots}, 1146 {"/v1/connect/intentions", (*HTTPServer).IntentionEndpoint}, 1147 {"/v1/coordinate/datacenters", (*HTTPServer).CoordinateDatacenters}, 1148 {"/v1/coordinate/nodes", (*HTTPServer).CoordinateNodes}, 1149 {"/v1/coordinate/node/" + t.Name(), (*HTTPServer).CoordinateNode}, 1150 {"/v1/event/list", (*HTTPServer).EventList}, 1151 {"/v1/health/node/" + t.Name(), (*HTTPServer).HealthNodeChecks}, 1152 {"/v1/health/checks/test", (*HTTPServer).HealthNodeChecks}, 1153 {"/v1/health/state/passing", (*HTTPServer).HealthChecksInState}, 1154 {"/v1/health/service/test", (*HTTPServer).HealthServiceNodes}, 1155 {"/v1/health/connect/test", (*HTTPServer).HealthConnectServiceNodes}, 1156 {"/v1/operator/raft/configuration", (*HTTPServer).OperatorRaftConfiguration}, 1157 // keyring endpoint has issues with returning errors if you haven't enabled encryption 1158 // {"/v1/operator/keyring", (*HTTPServer).OperatorKeyringEndpoint}, 1159 {"/v1/operator/autopilot/configuration", (*HTTPServer).OperatorAutopilotConfiguration}, 1160 {"/v1/operator/autopilot/health", (*HTTPServer).OperatorServerHealth}, 1161 {"/v1/query", (*HTTPServer).PreparedQueryGeneral}, 1162 {"/v1/session/list", (*HTTPServer).SessionList}, 1163 {"/v1/status/leader", (*HTTPServer).StatusLeader}, 1164 {"/v1/status/peers", (*HTTPServer).StatusPeers}, 1165 } 1166 1167 a := NewTestAgent(t, t.Name(), TestACLConfig()+testAllowProxyConfig()) 1168 defer a.Shutdown() 1169 1170 // Register a service with a managed proxy 1171 { 1172 reg := &structs.ServiceDefinition{ 1173 ID: "test-id", 1174 Name: "test", 1175 Address: "127.0.0.1", 1176 Port: 8000, 1177 Check: structs.CheckType{ 1178 TTL: 15 * time.Second, 1179 }, 1180 Connect: &structs.ServiceConnect{ 1181 Proxy: &structs.ServiceDefinitionConnectProxy{}, 1182 }, 1183 } 1184 1185 req, _ := http.NewRequest("PUT", "/v1/agent/service/register?token=root", jsonReader(reg)) 1186 resp := httptest.NewRecorder() 1187 _, err := a.srv.AgentRegisterService(resp, req) 1188 require.NoError(t, err) 1189 require.Equal(t, 200, resp.Code, "body: %s", resp.Body.String()) 1190 } 1191 1192 // Get the proxy token from the agent directly, since there is no API. 1193 proxy := a.State.Proxy("test-id-proxy") 1194 require.NotNil(t, proxy) 1195 token := proxy.ProxyToken 1196 require.NotEmpty(t, token) 1197 1198 for _, check := range tests { 1199 t.Run(fmt.Sprintf("GET(%s)", check.endpoint), func(t *testing.T) { 1200 req, _ := http.NewRequest("GET", fmt.Sprintf("%s?token=%s", check.endpoint, token), nil) 1201 resp := httptest.NewRecorder() 1202 _, err := check.handler(a.srv, resp, req) 1203 require.NoError(t, err) 1204 }) 1205 } 1206 } 1207 1208 func TestAllowedNets(t *testing.T) { 1209 type testVal struct { 1210 nets []string 1211 ip string 1212 expected bool 1213 } 1214 1215 for _, v := range []testVal{ 1216 { 1217 ip: "156.124.222.351", 1218 expected: true, 1219 }, 1220 { 1221 ip: "[::2]", 1222 expected: true, 1223 }, 1224 { 1225 nets: []string{"0.0.0.0/0"}, 1226 ip: "115.124.32.64", 1227 expected: true, 1228 }, 1229 { 1230 nets: []string{"::0/0"}, 1231 ip: "[::3]", 1232 expected: true, 1233 }, 1234 { 1235 nets: []string{"127.0.0.1/8"}, 1236 ip: "127.0.0.1", 1237 expected: true, 1238 }, 1239 { 1240 nets: []string{"127.0.0.1/8"}, 1241 ip: "128.0.0.1", 1242 expected: false, 1243 }, 1244 { 1245 nets: []string{"::1/8"}, 1246 ip: "[::1]", 1247 expected: true, 1248 }, 1249 { 1250 nets: []string{"255.255.255.255/32"}, 1251 ip: "127.0.0.1", 1252 expected: false, 1253 }, 1254 { 1255 nets: []string{"255.255.255.255/32", "127.0.0.1/8"}, 1256 ip: "127.0.0.1", 1257 expected: true, 1258 }, 1259 } { 1260 var nets []*net.IPNet 1261 for _, n := range v.nets { 1262 _, in, err := net.ParseCIDR(n) 1263 if err != nil { 1264 t.Fatal(err) 1265 } 1266 nets = append(nets, in) 1267 } 1268 1269 a := &TestAgent{ 1270 Name: t.Name(), 1271 } 1272 a.Start(t) 1273 defer a.Shutdown() 1274 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 1275 1276 a.config.AllowWriteHTTPFrom = nets 1277 1278 err := a.srv.checkWriteAccess(&http.Request{ 1279 Method: http.MethodPost, 1280 RemoteAddr: fmt.Sprintf("%s:16544", v.ip), 1281 }) 1282 actual := err == nil 1283 1284 if actual != v.expected { 1285 t.Fatalf("bad checkWriteAccess for values %+v, got %v", v, err) 1286 } 1287 1288 _, isForbiddenErr := err.(ForbiddenError) 1289 if err != nil && !isForbiddenErr { 1290 t.Fatalf("expected ForbiddenError but got: %s", err) 1291 } 1292 } 1293 } 1294 1295 // assertIndex tests that X-Consul-Index is set and non-zero 1296 func assertIndex(t *testing.T, resp *httptest.ResponseRecorder) { 1297 header := resp.Header().Get("X-Consul-Index") 1298 if header == "" || header == "0" { 1299 t.Fatalf("Bad: %v", header) 1300 } 1301 } 1302 1303 // checkIndex is like assertIndex but returns an error 1304 func checkIndex(resp *httptest.ResponseRecorder) error { 1305 header := resp.Header().Get("X-Consul-Index") 1306 if header == "" || header == "0" { 1307 return fmt.Errorf("Bad: %v", header) 1308 } 1309 return nil 1310 } 1311 1312 // getIndex parses X-Consul-Index 1313 func getIndex(t *testing.T, resp *httptest.ResponseRecorder) uint64 { 1314 header := resp.Header().Get("X-Consul-Index") 1315 if header == "" { 1316 t.Fatalf("Bad: %v", header) 1317 } 1318 val, err := strconv.Atoi(header) 1319 if err != nil { 1320 t.Fatalf("Bad: %v", header) 1321 } 1322 return uint64(val) 1323 } 1324 1325 func jsonReader(v interface{}) io.Reader { 1326 if v == nil { 1327 return nil 1328 } 1329 b := new(bytes.Buffer) 1330 if err := json.NewEncoder(b).Encode(v); err != nil { 1331 panic(err) 1332 } 1333 return b 1334 }