github.com/Aestek/consul@v1.2.4-0.20190309222502-b2c31e33971a/agent/http.go (about) 1 package agent 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "net" 8 "net/http" 9 "net/http/pprof" 10 "net/url" 11 "os" 12 "reflect" 13 "regexp" 14 "strconv" 15 "strings" 16 "time" 17 18 "github.com/NYTimes/gziphandler" 19 metrics "github.com/armon/go-metrics" 20 "github.com/hashicorp/consul/acl" 21 "github.com/hashicorp/consul/agent/cache" 22 "github.com/hashicorp/consul/agent/consul" 23 "github.com/hashicorp/consul/agent/structs" 24 "github.com/hashicorp/consul/api" 25 cleanhttp "github.com/hashicorp/go-cleanhttp" 26 "github.com/mitchellh/mapstructure" 27 "github.com/pkg/errors" 28 ) 29 30 // MethodNotAllowedError should be returned by a handler when the HTTP method is not allowed. 31 type MethodNotAllowedError struct { 32 Method string 33 Allow []string 34 } 35 36 func (e MethodNotAllowedError) Error() string { 37 return fmt.Sprintf("method %s not allowed", e.Method) 38 } 39 40 // BadRequestError should be returned by a handler when parameters or the payload are not valid 41 type BadRequestError struct { 42 Reason string 43 } 44 45 func (e BadRequestError) Error() string { 46 return fmt.Sprintf("Bad request: %s", e.Reason) 47 } 48 49 // CodeWithPayloadError allow returning non HTTP 200 50 // Error codes while not returning PlainText payload 51 type CodeWithPayloadError struct { 52 Reason string 53 StatusCode int 54 ContentType string 55 } 56 57 func (e CodeWithPayloadError) Error() string { 58 return e.Reason 59 } 60 61 type ForbiddenError struct { 62 } 63 64 func (e ForbiddenError) Error() string { 65 return "Access is restricted" 66 } 67 68 // HTTPServer provides an HTTP api for an agent. 69 type HTTPServer struct { 70 *http.Server 71 ln net.Listener 72 agent *Agent 73 blacklist *Blacklist 74 75 // proto is filled by the agent to "http" or "https". 76 proto string 77 } 78 79 type redirectFS struct { 80 fs http.FileSystem 81 } 82 83 func (fs *redirectFS) Open(name string) (http.File, error) { 84 file, err := fs.fs.Open(name) 85 if err != nil { 86 file, err = fs.fs.Open("/index.html") 87 } 88 return file, err 89 } 90 91 // endpoint is a Consul-specific HTTP handler that takes the usual arguments in 92 // but returns a response object and error, both of which are handled in a 93 // common manner by Consul's HTTP server. 94 type endpoint func(resp http.ResponseWriter, req *http.Request) (interface{}, error) 95 96 // unboundEndpoint is an endpoint method on a server. 97 type unboundEndpoint func(s *HTTPServer, resp http.ResponseWriter, req *http.Request) (interface{}, error) 98 99 // endpoints is a map from URL pattern to unbound endpoint. 100 var endpoints map[string]unboundEndpoint 101 102 // allowedMethods is a map from endpoint prefix to supported HTTP methods. 103 // An empty slice means an endpoint handles OPTIONS requests and MethodNotFound errors itself. 104 var allowedMethods map[string][]string 105 106 // registerEndpoint registers a new endpoint, which should be done at package 107 // init() time. 108 func registerEndpoint(pattern string, methods []string, fn unboundEndpoint) { 109 if endpoints == nil { 110 endpoints = make(map[string]unboundEndpoint) 111 } 112 if endpoints[pattern] != nil || allowedMethods[pattern] != nil { 113 panic(fmt.Errorf("Pattern %q is already registered", pattern)) 114 } 115 116 endpoints[pattern] = fn 117 allowedMethods[pattern] = methods 118 } 119 120 // wrappedMux hangs on to the underlying mux for unit tests. 121 type wrappedMux struct { 122 mux *http.ServeMux 123 handler http.Handler 124 } 125 126 // ServeHTTP implements the http.Handler interface. 127 func (w *wrappedMux) ServeHTTP(resp http.ResponseWriter, req *http.Request) { 128 w.handler.ServeHTTP(resp, req) 129 } 130 131 // handler is used to attach our handlers to the mux 132 func (s *HTTPServer) handler(enableDebug bool) http.Handler { 133 mux := http.NewServeMux() 134 135 // handleFuncMetrics takes the given pattern and handler and wraps to produce 136 // metrics based on the pattern and request. 137 handleFuncMetrics := func(pattern string, handler http.HandlerFunc) { 138 // Get the parts of the pattern. We omit any initial empty for the 139 // leading slash, and put an underscore as a "thing" placeholder if we 140 // see a trailing slash, which means the part after is parsed. This lets 141 // us distinguish from things like /v1/query and /v1/query/<query id>. 142 var parts []string 143 for i, part := range strings.Split(pattern, "/") { 144 if part == "" { 145 if i == 0 { 146 continue 147 } 148 part = "_" 149 } 150 parts = append(parts, part) 151 } 152 153 // Register the wrapper, which will close over the expensive-to-compute 154 // parts from above. 155 // TODO (kyhavlov): Convert this to utilize metric labels in a major release 156 wrapper := func(resp http.ResponseWriter, req *http.Request) { 157 start := time.Now() 158 handler(resp, req) 159 key := append([]string{"http", req.Method}, parts...) 160 metrics.MeasureSince(key, start) 161 } 162 163 gzipWrapper, _ := gziphandler.GzipHandlerWithOpts(gziphandler.MinSize(0)) 164 gzipHandler := gzipWrapper(http.HandlerFunc(wrapper)) 165 mux.Handle(pattern, gzipHandler) 166 } 167 168 // handlePProf takes the given pattern and pprof handler 169 // and wraps it to add authorization and metrics 170 handlePProf := func(pattern string, handler http.HandlerFunc) { 171 wrapper := func(resp http.ResponseWriter, req *http.Request) { 172 var token string 173 s.parseToken(req, &token) 174 175 rule, err := s.agent.resolveToken(token) 176 if err != nil { 177 resp.WriteHeader(http.StatusForbidden) 178 return 179 } 180 181 // If enableDebug is not set, and ACLs are disabled, write 182 // an unauthorized response 183 if !enableDebug { 184 if s.checkACLDisabled(resp, req) { 185 return 186 } 187 } 188 189 // If the token provided does not have the necessary permissions, 190 // write a forbidden response 191 if rule != nil && !rule.OperatorRead() { 192 resp.WriteHeader(http.StatusForbidden) 193 return 194 } 195 196 // Call the pprof handler 197 handler(resp, req) 198 } 199 200 handleFuncMetrics(pattern, http.HandlerFunc(wrapper)) 201 } 202 203 mux.HandleFunc("/", s.Index) 204 for pattern, fn := range endpoints { 205 thisFn := fn 206 methods, _ := allowedMethods[pattern] 207 bound := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 208 return thisFn(s, resp, req) 209 } 210 handleFuncMetrics(pattern, s.wrap(bound, methods)) 211 } 212 213 // Register wrapped pprof handlers 214 handlePProf("/debug/pprof/", pprof.Index) 215 handlePProf("/debug/pprof/cmdline", pprof.Cmdline) 216 handlePProf("/debug/pprof/profile", pprof.Profile) 217 handlePProf("/debug/pprof/symbol", pprof.Symbol) 218 handlePProf("/debug/pprof/trace", pprof.Trace) 219 220 if s.IsUIEnabled() { 221 legacy_ui, err := strconv.ParseBool(os.Getenv("CONSUL_UI_LEGACY")) 222 if err != nil { 223 legacy_ui = false 224 } 225 var uifs http.FileSystem 226 227 // Use the custom UI dir if provided. 228 if s.agent.config.UIDir != "" { 229 uifs = http.Dir(s.agent.config.UIDir) 230 } else { 231 fs := assetFS() 232 233 if legacy_ui { 234 fs.Prefix += "/v1/" 235 } else { 236 fs.Prefix += "/v2/" 237 } 238 uifs = fs 239 } 240 241 if !legacy_ui { 242 uifs = &redirectFS{fs: uifs} 243 } 244 245 mux.Handle("/robots.txt", http.FileServer(uifs)) 246 mux.Handle("/ui/", http.StripPrefix("/ui/", http.FileServer(uifs))) 247 } 248 249 // Wrap the whole mux with a handler that bans URLs with non-printable 250 // characters, unless disabled explicitly to deal with old keys that fail this 251 // check. 252 h := cleanhttp.PrintablePathCheckHandler(mux, nil) 253 if s.agent.config.DisableHTTPUnprintableCharFilter { 254 h = mux 255 } 256 return &wrappedMux{ 257 mux: mux, 258 handler: h, 259 } 260 } 261 262 // nodeName returns the node name of the agent 263 func (s *HTTPServer) nodeName() string { 264 return s.agent.config.NodeName 265 } 266 267 // aclEndpointRE is used to find old ACL endpoints that take tokens in the URL 268 // so that we can redact them. The ACL endpoints that take the token in the URL 269 // are all of the form /v1/acl/<verb>/<token>, and can optionally include query 270 // parameters which are indicated by a question mark. We capture the part before 271 // the token, the token, and any query parameters after, and then reassemble as 272 // $1<hidden>$3 (the token in $2 isn't used), which will give: 273 // 274 // /v1/acl/clone/foo -> /v1/acl/clone/<hidden> 275 // /v1/acl/clone/foo?token=bar -> /v1/acl/clone/<hidden>?token=<hidden> 276 // 277 // The query parameter in the example above is obfuscated like any other, after 278 // this regular expression is applied, so the regular expression substitution 279 // results in: 280 // 281 // /v1/acl/clone/foo?token=bar -> /v1/acl/clone/<hidden>?token=bar 282 // ^---- $1 ----^^- $2 -^^-- $3 --^ 283 // 284 // And then the loop that looks for parameters called "token" does the last 285 // step to get to the final redacted form. 286 var ( 287 aclEndpointRE = regexp.MustCompile("^(/v1/acl/(create|update|destroy|info|clone|list)/)([^?]+)([?]?.*)$") 288 ) 289 290 // wrap is used to wrap functions to make them more convenient 291 func (s *HTTPServer) wrap(handler endpoint, methods []string) http.HandlerFunc { 292 return func(resp http.ResponseWriter, req *http.Request) { 293 setHeaders(resp, s.agent.config.HTTPResponseHeaders) 294 setTranslateAddr(resp, s.agent.config.TranslateWANAddrs) 295 296 // Obfuscate any tokens from appearing in the logs 297 formVals, err := url.ParseQuery(req.URL.RawQuery) 298 if err != nil { 299 s.agent.logger.Printf("[ERR] http: Failed to decode query: %s from=%s", err, req.RemoteAddr) 300 resp.WriteHeader(http.StatusInternalServerError) 301 return 302 } 303 logURL := req.URL.String() 304 if tokens, ok := formVals["token"]; ok { 305 for _, token := range tokens { 306 if token == "" { 307 logURL += "<hidden>" 308 continue 309 } 310 logURL = strings.Replace(logURL, token, "<hidden>", -1) 311 } 312 } 313 logURL = aclEndpointRE.ReplaceAllString(logURL, "$1<hidden>$4") 314 315 if s.blacklist.Block(req.URL.Path) { 316 errMsg := "Endpoint is blocked by agent configuration" 317 s.agent.logger.Printf("[ERR] http: Request %s %v, error: %v from=%s", req.Method, logURL, err, req.RemoteAddr) 318 resp.WriteHeader(http.StatusForbidden) 319 fmt.Fprint(resp, errMsg) 320 return 321 } 322 323 isForbidden := func(err error) bool { 324 if acl.IsErrPermissionDenied(err) || acl.IsErrNotFound(err) { 325 return true 326 } 327 _, ok := err.(ForbiddenError) 328 return ok 329 } 330 331 isMethodNotAllowed := func(err error) bool { 332 _, ok := err.(MethodNotAllowedError) 333 return ok 334 } 335 336 isBadRequest := func(err error) bool { 337 _, ok := err.(BadRequestError) 338 return ok 339 } 340 341 isTooManyRequests := func(err error) bool { 342 // Sadness net/rpc can't do nice typed errors so this is all we got 343 return err.Error() == consul.ErrRateLimited.Error() 344 } 345 346 addAllowHeader := func(methods []string) { 347 resp.Header().Add("Allow", strings.Join(methods, ",")) 348 } 349 350 handleErr := func(err error) { 351 s.agent.logger.Printf("[ERR] http: Request %s %v, error: %v from=%s", req.Method, logURL, err, req.RemoteAddr) 352 switch { 353 case isForbidden(err): 354 resp.WriteHeader(http.StatusForbidden) 355 fmt.Fprint(resp, err.Error()) 356 case structs.IsErrRPCRateExceeded(err): 357 resp.WriteHeader(http.StatusTooManyRequests) 358 case isMethodNotAllowed(err): 359 // RFC2616 states that for 405 Method Not Allowed the response 360 // MUST include an Allow header containing the list of valid 361 // methods for the requested resource. 362 // https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 363 addAllowHeader(err.(MethodNotAllowedError).Allow) 364 resp.WriteHeader(http.StatusMethodNotAllowed) // 405 365 fmt.Fprint(resp, err.Error()) 366 case isBadRequest(err): 367 resp.WriteHeader(http.StatusBadRequest) 368 fmt.Fprint(resp, err.Error()) 369 case isTooManyRequests(err): 370 resp.WriteHeader(http.StatusTooManyRequests) 371 fmt.Fprint(resp, err.Error()) 372 default: 373 resp.WriteHeader(http.StatusInternalServerError) 374 fmt.Fprint(resp, err.Error()) 375 } 376 } 377 378 start := time.Now() 379 defer func() { 380 s.agent.logger.Printf("[DEBUG] http: Request %s %v (%v) from=%s", req.Method, logURL, time.Since(start), req.RemoteAddr) 381 }() 382 383 var obj interface{} 384 385 // if this endpoint has declared methods, respond appropriately to OPTIONS requests. Otherwise let the endpoint handle that. 386 if req.Method == "OPTIONS" && len(methods) > 0 { 387 addAllowHeader(append([]string{"OPTIONS"}, methods...)) 388 return 389 } 390 391 // if this endpoint has declared methods, check the request method. Otherwise let the endpoint handle that. 392 methodFound := len(methods) == 0 393 for _, method := range methods { 394 if method == req.Method { 395 methodFound = true 396 break 397 } 398 } 399 400 if !methodFound { 401 err = MethodNotAllowedError{req.Method, append([]string{"OPTIONS"}, methods...)} 402 } else { 403 err = s.checkWriteAccess(req) 404 405 if err == nil { 406 // Invoke the handler 407 obj, err = handler(resp, req) 408 } 409 } 410 contentType := "application/json" 411 httpCode := http.StatusOK 412 if err != nil { 413 if errPayload, ok := err.(CodeWithPayloadError); ok { 414 httpCode = errPayload.StatusCode 415 if errPayload.ContentType != "" { 416 contentType = errPayload.ContentType 417 } 418 if errPayload.Reason != "" { 419 resp.Header().Add("X-Consul-Reason", errPayload.Reason) 420 } 421 } else { 422 handleErr(err) 423 return 424 } 425 } 426 if obj == nil { 427 return 428 } 429 var buf []byte 430 if contentType == "application/json" { 431 buf, err = s.marshalJSON(req, obj) 432 if err != nil { 433 handleErr(err) 434 return 435 } 436 } else { 437 if strings.HasPrefix(contentType, "text/") { 438 if val, ok := obj.(string); ok { 439 buf = []byte(val) 440 } 441 } 442 } 443 resp.Header().Set("Content-Type", contentType) 444 resp.WriteHeader(httpCode) 445 resp.Write(buf) 446 } 447 } 448 449 // marshalJSON marshals the object into JSON, respecting the user's pretty-ness 450 // configuration. 451 func (s *HTTPServer) marshalJSON(req *http.Request, obj interface{}) ([]byte, error) { 452 if _, ok := req.URL.Query()["pretty"]; ok || s.agent.config.DevMode { 453 buf, err := json.MarshalIndent(obj, "", " ") 454 if err != nil { 455 return nil, err 456 } 457 buf = append(buf, "\n"...) 458 return buf, nil 459 } 460 461 buf, err := json.Marshal(obj) 462 if err != nil { 463 return nil, err 464 } 465 return buf, err 466 } 467 468 // Returns true if the UI is enabled. 469 func (s *HTTPServer) IsUIEnabled() bool { 470 return s.agent.config.UIDir != "" || s.agent.config.EnableUI 471 } 472 473 // Renders a simple index page 474 func (s *HTTPServer) Index(resp http.ResponseWriter, req *http.Request) { 475 // Check if this is a non-index path 476 if req.URL.Path != "/" { 477 resp.WriteHeader(http.StatusNotFound) 478 return 479 } 480 481 // Give them something helpful if there's no UI so they at least know 482 // what this server is. 483 if !s.IsUIEnabled() { 484 fmt.Fprint(resp, "Consul Agent") 485 return 486 } 487 488 // Redirect to the UI endpoint 489 http.Redirect(resp, req, "/ui/", http.StatusMovedPermanently) // 301 490 } 491 492 // decodeBody is used to decode a JSON request body 493 func decodeBody(req *http.Request, out interface{}, cb func(interface{}) error) error { 494 // This generally only happens in tests since real HTTP requests set 495 // a non-nil body with no content. We guard against it anyways to prevent 496 // a panic. The EOF response is the same behavior as an empty reader. 497 if req.Body == nil { 498 return io.EOF 499 } 500 501 var raw interface{} 502 dec := json.NewDecoder(req.Body) 503 if err := dec.Decode(&raw); err != nil { 504 return err 505 } 506 507 // Invoke the callback prior to decode 508 if cb != nil { 509 if err := cb(raw); err != nil { 510 return err 511 } 512 } 513 514 decodeConf := &mapstructure.DecoderConfig{ 515 DecodeHook: mapstructure.ComposeDecodeHookFunc( 516 mapstructure.StringToTimeDurationHookFunc(), 517 stringToReadableDurationFunc(), 518 ), 519 Result: &out, 520 } 521 522 decoder, err := mapstructure.NewDecoder(decodeConf) 523 if err != nil { 524 return err 525 } 526 527 return decoder.Decode(raw) 528 } 529 530 // stringToReadableDurationFunc is a mapstructure hook for decoding a string 531 // into an api.ReadableDuration for backwards compatibility. 532 func stringToReadableDurationFunc() mapstructure.DecodeHookFunc { 533 return func( 534 f reflect.Type, 535 t reflect.Type, 536 data interface{}) (interface{}, error) { 537 var v api.ReadableDuration 538 if t != reflect.TypeOf(v) { 539 return data, nil 540 } 541 542 switch { 543 case f.Kind() == reflect.String: 544 if dur, err := time.ParseDuration(data.(string)); err != nil { 545 return nil, err 546 } else { 547 v = api.ReadableDuration(dur) 548 } 549 return v, nil 550 default: 551 return data, nil 552 } 553 } 554 } 555 556 // setTranslateAddr is used to set the address translation header. This is only 557 // present if the feature is active. 558 func setTranslateAddr(resp http.ResponseWriter, active bool) { 559 if active { 560 resp.Header().Set("X-Consul-Translate-Addresses", "true") 561 } 562 } 563 564 // setIndex is used to set the index response header 565 func setIndex(resp http.ResponseWriter, index uint64) { 566 // If we ever return X-Consul-Index of 0 blocking clients will go into a busy 567 // loop and hammer us since ?index=0 will never block. It's always safe to 568 // return index=1 since the very first Raft write is always an internal one 569 // writing the raft config for the cluster so no user-facing blocking query 570 // will ever legitimately have an X-Consul-Index of 1. 571 if index == 0 { 572 index = 1 573 } 574 resp.Header().Set("X-Consul-Index", strconv.FormatUint(index, 10)) 575 } 576 577 // setKnownLeader is used to set the known leader header 578 func setKnownLeader(resp http.ResponseWriter, known bool) { 579 s := "true" 580 if !known { 581 s = "false" 582 } 583 resp.Header().Set("X-Consul-KnownLeader", s) 584 } 585 586 func setConsistency(resp http.ResponseWriter, consistency string) { 587 if consistency != "" { 588 resp.Header().Set("X-Consul-Effective-Consistency", consistency) 589 } 590 } 591 592 // setLastContact is used to set the last contact header 593 func setLastContact(resp http.ResponseWriter, last time.Duration) { 594 if last < 0 { 595 last = 0 596 } 597 lastMsec := uint64(last / time.Millisecond) 598 resp.Header().Set("X-Consul-LastContact", strconv.FormatUint(lastMsec, 10)) 599 } 600 601 // setMeta is used to set the query response meta data 602 func setMeta(resp http.ResponseWriter, m *structs.QueryMeta) { 603 setIndex(resp, m.Index) 604 setLastContact(resp, m.LastContact) 605 setKnownLeader(resp, m.KnownLeader) 606 setConsistency(resp, m.ConsistencyLevel) 607 } 608 609 // setCacheMeta sets http response headers to indicate cache status. 610 func setCacheMeta(resp http.ResponseWriter, m *cache.ResultMeta) { 611 if m == nil { 612 return 613 } 614 str := "MISS" 615 if m.Hit { 616 str = "HIT" 617 } 618 resp.Header().Set("X-Cache", str) 619 if m.Hit { 620 resp.Header().Set("Age", fmt.Sprintf("%.0f", m.Age.Seconds())) 621 } 622 } 623 624 // setHeaders is used to set canonical response header fields 625 func setHeaders(resp http.ResponseWriter, headers map[string]string) { 626 for field, value := range headers { 627 resp.Header().Set(http.CanonicalHeaderKey(field), value) 628 } 629 } 630 631 // parseWait is used to parse the ?wait and ?index query params 632 // Returns true on error 633 func parseWait(resp http.ResponseWriter, req *http.Request, b *structs.QueryOptions) bool { 634 query := req.URL.Query() 635 if wait := query.Get("wait"); wait != "" { 636 dur, err := time.ParseDuration(wait) 637 if err != nil { 638 resp.WriteHeader(http.StatusBadRequest) 639 fmt.Fprint(resp, "Invalid wait time") 640 return true 641 } 642 b.MaxQueryTime = dur 643 } 644 if idx := query.Get("index"); idx != "" { 645 index, err := strconv.ParseUint(idx, 10, 64) 646 if err != nil { 647 resp.WriteHeader(http.StatusBadRequest) 648 fmt.Fprint(resp, "Invalid index") 649 return true 650 } 651 b.MinQueryIndex = index 652 } 653 return false 654 } 655 656 // parseCacheControl parses the CacheControl HTTP header value. So far we only 657 // support maxage directive. 658 func parseCacheControl(resp http.ResponseWriter, req *http.Request, b *structs.QueryOptions) bool { 659 raw := strings.ToLower(req.Header.Get("Cache-Control")) 660 661 if raw == "" { 662 return false 663 } 664 665 // Didn't want to import a full parser for this. While quoted strings are 666 // allowed in some directives, max-age does not allow them per 667 // https://tools.ietf.org/html/rfc7234#section-5.2.2.8 so we assume all 668 // well-behaved clients use the exact token form of max-age=<delta-seconds> 669 // where delta-seconds is a non-negative decimal integer. 670 directives := strings.Split(raw, ",") 671 672 parseDurationOrFail := func(raw string) (time.Duration, bool) { 673 i, err := strconv.Atoi(raw) 674 if err != nil { 675 resp.WriteHeader(http.StatusBadRequest) 676 fmt.Fprint(resp, "Invalid Cache-Control header.") 677 return 0, true 678 } 679 return time.Duration(i) * time.Second, false 680 } 681 682 for _, d := range directives { 683 d = strings.ToLower(strings.TrimSpace(d)) 684 685 if d == "must-revalidate" { 686 b.MustRevalidate = true 687 } 688 689 if strings.HasPrefix(d, "max-age=") { 690 d, failed := parseDurationOrFail(d[8:]) 691 if failed { 692 return true 693 } 694 b.MaxAge = d 695 if d == 0 { 696 // max-age=0 specifically means that we need to consider the cache stale 697 // immediately however MaxAge = 0 is indistinguishable from the default 698 // where MaxAge is unset. 699 b.MustRevalidate = true 700 } 701 } 702 if strings.HasPrefix(d, "stale-if-error=") { 703 d, failed := parseDurationOrFail(d[15:]) 704 if failed { 705 return true 706 } 707 b.StaleIfError = d 708 } 709 } 710 711 return false 712 } 713 714 // parseConsistency is used to parse the ?stale and ?consistent query params. 715 // Returns true on error 716 func (s *HTTPServer) parseConsistency(resp http.ResponseWriter, req *http.Request, b *structs.QueryOptions) bool { 717 query := req.URL.Query() 718 defaults := true 719 if _, ok := query["stale"]; ok { 720 b.AllowStale = true 721 defaults = false 722 } 723 if _, ok := query["consistent"]; ok { 724 b.RequireConsistent = true 725 defaults = false 726 } 727 if _, ok := query["leader"]; ok { 728 defaults = false 729 } 730 if _, ok := query["cached"]; ok { 731 b.UseCache = true 732 defaults = false 733 } 734 if maxStale := query.Get("max_stale"); maxStale != "" { 735 dur, err := time.ParseDuration(maxStale) 736 if err != nil { 737 resp.WriteHeader(http.StatusBadRequest) 738 fmt.Fprintf(resp, "Invalid max_stale value %q", maxStale) 739 return true 740 } 741 b.MaxStaleDuration = dur 742 if dur.Nanoseconds() > 0 { 743 b.AllowStale = true 744 defaults = false 745 } 746 } 747 // No specific Consistency has been specified by caller 748 if defaults { 749 path := req.URL.Path 750 if strings.HasPrefix(path, "/v1/catalog") || strings.HasPrefix(path, "/v1/health") { 751 if s.agent.config.DiscoveryMaxStale.Nanoseconds() > 0 { 752 b.MaxStaleDuration = s.agent.config.DiscoveryMaxStale 753 b.AllowStale = true 754 } 755 } 756 } 757 if b.AllowStale && b.RequireConsistent { 758 resp.WriteHeader(http.StatusBadRequest) 759 fmt.Fprint(resp, "Cannot specify ?stale with ?consistent, conflicting semantics.") 760 return true 761 } 762 if b.UseCache && b.RequireConsistent { 763 resp.WriteHeader(http.StatusBadRequest) 764 fmt.Fprint(resp, "Cannot specify ?cached with ?consistent, conflicting semantics.") 765 return true 766 } 767 return false 768 } 769 770 // parseDC is used to parse the ?dc query param 771 func (s *HTTPServer) parseDC(req *http.Request, dc *string) { 772 if other := req.URL.Query().Get("dc"); other != "" { 773 *dc = other 774 } else if *dc == "" { 775 *dc = s.agent.config.Datacenter 776 } 777 } 778 779 // parseTokenInternal is used to parse the ?token query param or the X-Consul-Token header or 780 // Authorization Bearer token (RFC6750) and 781 // optionally resolve proxy tokens to real ACL tokens. If the token is invalid or not specified it will populate 782 // the token with the agents UserToken (acl_token in the consul configuration) 783 // Parsing has the following priority: ?token, X-Consul-Token and last "Authorization: Bearer " 784 func (s *HTTPServer) parseTokenInternal(req *http.Request, token *string, resolveProxyToken bool) { 785 tok := "" 786 if other := req.URL.Query().Get("token"); other != "" { 787 tok = other 788 } else if other := req.Header.Get("X-Consul-Token"); other != "" { 789 tok = other 790 } else if other := req.Header.Get("Authorization"); other != "" { 791 // HTTP Authorization headers are in the format: <Scheme>[SPACE]<Value> 792 // Ref. https://tools.ietf.org/html/rfc7236#section-3 793 parts := strings.Split(other, " ") 794 795 // Authorization Header is invalid if containing 1 or 0 parts, e.g.: 796 // "" || "<Scheme><Value>" || "<Scheme>" || "<Value>" 797 if len(parts) > 1 { 798 scheme := parts[0] 799 // Everything after "<Scheme>" is "<Value>", trimmed 800 value := strings.TrimSpace(strings.Join(parts[1:], " ")) 801 802 // <Scheme> must be "Bearer" 803 if scheme == "Bearer" { 804 // Since Bearer tokens shouldnt contain spaces (rfc6750#section-2.1) 805 // "value" is tokenized, only the first item is used 806 tok = strings.TrimSpace(strings.Split(value, " ")[0]) 807 } 808 } 809 } 810 811 if tok != "" { 812 if resolveProxyToken { 813 if p := s.agent.resolveProxyToken(tok); p != nil { 814 *token = s.agent.State.ServiceToken(p.Proxy.TargetServiceID) 815 return 816 } 817 } 818 819 *token = tok 820 return 821 } 822 823 *token = s.agent.tokens.UserToken() 824 } 825 826 // parseToken is used to parse the ?token query param or the X-Consul-Token header or 827 // Authorization Bearer token header (RFC6750) and resolve proxy tokens to real ACL tokens 828 func (s *HTTPServer) parseToken(req *http.Request, token *string) { 829 s.parseTokenInternal(req, token, true) 830 } 831 832 // parseTokenWithoutResolvingProxyToken is used to parse the ?token query param or the X-Consul-Token header 833 // or Authorization Bearer header token (RFC6750) and 834 func (s *HTTPServer) parseTokenWithoutResolvingProxyToken(req *http.Request, token *string) { 835 s.parseTokenInternal(req, token, false) 836 } 837 838 func sourceAddrFromRequest(req *http.Request) string { 839 xff := req.Header.Get("X-Forwarded-For") 840 forwardHosts := strings.Split(xff, ",") 841 if len(forwardHosts) > 0 { 842 forwardIp := net.ParseIP(strings.TrimSpace(forwardHosts[0])) 843 if forwardIp != nil { 844 return forwardIp.String() 845 } 846 } 847 848 host, _, err := net.SplitHostPort(req.RemoteAddr) 849 if err != nil { 850 return "" 851 } 852 853 ip := net.ParseIP(host) 854 if ip != nil { 855 return ip.String() 856 } else { 857 return "" 858 } 859 } 860 861 // parseSource is used to parse the ?near=<node> query parameter, used for 862 // sorting by RTT based on a source node. We set the source's DC to the target 863 // DC in the request, if given, or else the agent's DC. 864 func (s *HTTPServer) parseSource(req *http.Request, source *structs.QuerySource) { 865 s.parseDC(req, &source.Datacenter) 866 source.Ip = sourceAddrFromRequest(req) 867 if node := req.URL.Query().Get("near"); node != "" { 868 if node == "_agent" { 869 source.Node = s.agent.config.NodeName 870 } else { 871 source.Node = node 872 } 873 } 874 } 875 876 // parseMetaFilter is used to parse the ?node-meta=key:value query parameter, used for 877 // filtering results to nodes with the given metadata key/value 878 func (s *HTTPServer) parseMetaFilter(req *http.Request) map[string]string { 879 if filterList, ok := req.URL.Query()["node-meta"]; ok { 880 filters := make(map[string]string) 881 for _, filter := range filterList { 882 key, value := ParseMetaPair(filter) 883 filters[key] = value 884 } 885 return filters 886 } 887 return nil 888 } 889 890 // parseInternal is a convenience method for endpoints that need 891 // to use both parseWait and parseDC. 892 func (s *HTTPServer) parseInternal(resp http.ResponseWriter, req *http.Request, dc *string, b *structs.QueryOptions, resolveProxyToken bool) bool { 893 s.parseDC(req, dc) 894 s.parseTokenInternal(req, &b.Token, resolveProxyToken) 895 if s.parseConsistency(resp, req, b) { 896 return true 897 } 898 if parseCacheControl(resp, req, b) { 899 return true 900 } 901 return parseWait(resp, req, b) 902 } 903 904 // parse is a convenience method for endpoints that need 905 // to use both parseWait and parseDC. 906 func (s *HTTPServer) parse(resp http.ResponseWriter, req *http.Request, dc *string, b *structs.QueryOptions) bool { 907 return s.parseInternal(resp, req, dc, b, true) 908 } 909 910 // parseWithoutResolvingProxyToken is a convenience method similar to parse except that it disables resolving proxy tokens 911 func (s *HTTPServer) parseWithoutResolvingProxyToken(resp http.ResponseWriter, req *http.Request, dc *string, b *structs.QueryOptions) bool { 912 return s.parseInternal(resp, req, dc, b, false) 913 } 914 915 func (s *HTTPServer) checkWriteAccess(req *http.Request) error { 916 if req.Method == http.MethodGet || req.Method == http.MethodHead || req.Method == http.MethodOptions { 917 return nil 918 } 919 920 allowed := s.agent.config.AllowWriteHTTPFrom 921 if len(allowed) == 0 { 922 return nil 923 } 924 925 ipStr, _, err := net.SplitHostPort(req.RemoteAddr) 926 if err != nil { 927 return errors.Wrap(err, "unable to parse remote addr") 928 } 929 930 ip := net.ParseIP(ipStr) 931 932 for _, n := range allowed { 933 if n.Contains(ip) { 934 return nil 935 } 936 } 937 938 return ForbiddenError{} 939 }