github.com/sl1pm4t/consul@v1.4.5-0.20190325224627-74c31c540f9c/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  }