github.com/Iqoqo/consul@v1.4.5/agent/agent_endpoint.go (about) 1 package agent 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "log" 8 "net" 9 "net/http" 10 "path/filepath" 11 "strconv" 12 "strings" 13 "time" 14 15 "github.com/mitchellh/mapstructure" 16 17 "github.com/hashicorp/go-memdb" 18 "github.com/mitchellh/hashstructure" 19 20 "github.com/hashicorp/consul/acl" 21 cachetype "github.com/hashicorp/consul/agent/cache-types" 22 "github.com/hashicorp/consul/agent/checks" 23 "github.com/hashicorp/consul/agent/config" 24 "github.com/hashicorp/consul/agent/debug" 25 "github.com/hashicorp/consul/agent/local" 26 "github.com/hashicorp/consul/agent/structs" 27 token_store "github.com/hashicorp/consul/agent/token" 28 "github.com/hashicorp/consul/api" 29 "github.com/hashicorp/consul/ipaddr" 30 "github.com/hashicorp/consul/lib" 31 "github.com/hashicorp/consul/lib/file" 32 "github.com/hashicorp/consul/logger" 33 "github.com/hashicorp/consul/types" 34 "github.com/hashicorp/logutils" 35 "github.com/hashicorp/serf/coordinate" 36 "github.com/hashicorp/serf/serf" 37 "github.com/prometheus/client_golang/prometheus" 38 "github.com/prometheus/client_golang/prometheus/promhttp" 39 ) 40 41 type Self struct { 42 Config interface{} 43 DebugConfig map[string]interface{} 44 Coord *coordinate.Coordinate 45 Member serf.Member 46 Stats map[string]map[string]string 47 Meta map[string]string 48 } 49 50 func (s *HTTPServer) AgentSelf(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 51 // Fetch the ACL token, if any, and enforce agent policy. 52 var token string 53 s.parseToken(req, &token) 54 rule, err := s.agent.resolveToken(token) 55 if err != nil { 56 return nil, err 57 } 58 if rule != nil && !rule.AgentRead(s.agent.config.NodeName) { 59 return nil, acl.ErrPermissionDenied 60 } 61 62 var cs lib.CoordinateSet 63 if !s.agent.config.DisableCoordinates { 64 var err error 65 if cs, err = s.agent.GetLANCoordinate(); err != nil { 66 return nil, err 67 } 68 } 69 70 config := struct { 71 Datacenter string 72 NodeName string 73 NodeID string 74 Revision string 75 Server bool 76 Version string 77 }{ 78 Datacenter: s.agent.config.Datacenter, 79 NodeName: s.agent.config.NodeName, 80 NodeID: string(s.agent.config.NodeID), 81 Revision: s.agent.config.Revision, 82 Server: s.agent.config.ServerMode, 83 Version: s.agent.config.Version, 84 } 85 return Self{ 86 Config: config, 87 DebugConfig: s.agent.config.Sanitized(), 88 Coord: cs[s.agent.config.SegmentName], 89 Member: s.agent.LocalMember(), 90 Stats: s.agent.Stats(), 91 Meta: s.agent.State.Metadata(), 92 }, nil 93 } 94 95 // enablePrometheusOutput will look for Prometheus mime-type or format Query parameter the same way as Nomad 96 func enablePrometheusOutput(req *http.Request) bool { 97 if format := req.URL.Query().Get("format"); format == "prometheus" { 98 return true 99 } 100 return false 101 } 102 103 func (s *HTTPServer) AgentMetrics(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 104 // Fetch the ACL token, if any, and enforce agent policy. 105 var token string 106 s.parseToken(req, &token) 107 rule, err := s.agent.resolveToken(token) 108 if err != nil { 109 return nil, err 110 } 111 if rule != nil && !rule.AgentRead(s.agent.config.NodeName) { 112 return nil, acl.ErrPermissionDenied 113 } 114 if enablePrometheusOutput(req) { 115 if s.agent.config.Telemetry.PrometheusRetentionTime < 1 { 116 resp.WriteHeader(http.StatusUnsupportedMediaType) 117 fmt.Fprint(resp, "Prometheus is not enabled since its retention time is not positive") 118 return nil, nil 119 } 120 handlerOptions := promhttp.HandlerOpts{ 121 ErrorLog: s.agent.logger, 122 ErrorHandling: promhttp.ContinueOnError, 123 } 124 125 handler := promhttp.HandlerFor(prometheus.DefaultGatherer, handlerOptions) 126 handler.ServeHTTP(resp, req) 127 return nil, nil 128 } 129 return s.agent.MemSink.DisplayMetrics(resp, req) 130 } 131 132 func (s *HTTPServer) AgentReload(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 133 // Fetch the ACL token, if any, and enforce agent policy. 134 var token string 135 s.parseToken(req, &token) 136 rule, err := s.agent.resolveToken(token) 137 if err != nil { 138 return nil, err 139 } 140 if rule != nil && !rule.AgentWrite(s.agent.config.NodeName) { 141 return nil, acl.ErrPermissionDenied 142 } 143 144 // Trigger the reload 145 errCh := make(chan error, 0) 146 select { 147 case <-s.agent.shutdownCh: 148 return nil, fmt.Errorf("Agent was shutdown before reload could be completed") 149 case s.agent.reloadCh <- errCh: 150 } 151 152 // Wait for the result of the reload, or for the agent to shutdown 153 select { 154 case <-s.agent.shutdownCh: 155 return nil, fmt.Errorf("Agent was shutdown before reload could be completed") 156 case err := <-errCh: 157 return nil, err 158 } 159 } 160 161 func buildAgentService(s *structs.NodeService, proxies map[string]*local.ManagedProxy) api.AgentService { 162 weights := api.AgentWeights{Passing: 1, Warning: 1} 163 if s.Weights != nil { 164 if s.Weights.Passing > 0 { 165 weights.Passing = s.Weights.Passing 166 } 167 weights.Warning = s.Weights.Warning 168 } 169 as := api.AgentService{ 170 Kind: api.ServiceKind(s.Kind), 171 ID: s.ID, 172 Service: s.Service, 173 Tags: s.Tags, 174 Meta: s.Meta, 175 Port: s.Port, 176 Address: s.Address, 177 EnableTagOverride: s.EnableTagOverride, 178 CreateIndex: s.CreateIndex, 179 ModifyIndex: s.ModifyIndex, 180 Weights: weights, 181 } 182 183 if as.Tags == nil { 184 as.Tags = []string{} 185 } 186 if as.Meta == nil { 187 as.Meta = map[string]string{} 188 } 189 // Attach Unmanaged Proxy config if exists 190 if s.Kind == structs.ServiceKindConnectProxy { 191 as.Proxy = s.Proxy.ToAPI() 192 // DEPRECATED (ProxyDestination) - remove this when removing ProxyDestination 193 // Also set the deprecated ProxyDestination 194 as.ProxyDestination = as.Proxy.DestinationServiceName 195 } 196 197 // Attach Connect configs if they exist. We use the actual proxy state since 198 // that may have had defaults filled in compared to the config that was 199 // provided with the service as stored in the NodeService here. 200 if proxy, ok := proxies[s.ID+"-proxy"]; ok { 201 as.Connect = &api.AgentServiceConnect{ 202 Proxy: &api.AgentServiceConnectProxy{ 203 ExecMode: api.ProxyExecMode(proxy.Proxy.ExecMode.String()), 204 Command: proxy.Proxy.Command, 205 Config: proxy.Proxy.Config, 206 Upstreams: proxy.Proxy.Upstreams.ToAPI(), 207 }, 208 } 209 } else if s.Connect.Native { 210 as.Connect = &api.AgentServiceConnect{ 211 Native: true, 212 } 213 } 214 return as 215 } 216 217 func (s *HTTPServer) AgentServices(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 218 // Fetch the ACL token, if any. 219 var token string 220 s.parseToken(req, &token) 221 222 services := s.agent.State.Services() 223 if err := s.agent.filterServices(token, &services); err != nil { 224 return nil, err 225 } 226 227 proxies := s.agent.State.Proxies() 228 229 // Convert into api.AgentService since that includes Connect config but so far 230 // NodeService doesn't need to internally. They are otherwise identical since 231 // that is the struct used in client for reading the one we output here 232 // anyway. 233 agentSvcs := make(map[string]*api.AgentService) 234 235 // Use empty list instead of nil 236 for id, s := range services { 237 agentService := buildAgentService(s, proxies) 238 agentSvcs[id] = &agentService 239 } 240 241 return agentSvcs, nil 242 } 243 244 // GET /v1/agent/service/:service_id 245 // 246 // Returns the service definition for a single local services and allows 247 // blocking watch using hash-based blocking. 248 func (s *HTTPServer) AgentService(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 249 // Get the proxy ID. Note that this is the ID of a proxy's service instance. 250 id := strings.TrimPrefix(req.URL.Path, "/v1/agent/service/") 251 252 // DEPRECATED(managed-proxies) - remove this whole hack. 253 // 254 // Support managed proxies until they are removed entirely. Since built-in 255 // proxy will now use this endpoint, in order to not break managed proxies in 256 // the interim until they are removed, we need to mirror the default-setting 257 // behavior they had. Rather than thread that through this whole method as 258 // special cases that need to be unwound later (and duplicate logic in the 259 // proxy config endpoint) just defer to that and then translate the response. 260 if managedProxy := s.agent.State.Proxy(id); managedProxy != nil { 261 // This is for a managed proxy, use the old endpoint's behavior 262 req.URL.Path = "/v1/agent/connect/proxy/" + id 263 obj, err := s.AgentConnectProxyConfig(resp, req) 264 if err != nil { 265 return obj, err 266 } 267 proxyCfg, ok := obj.(*api.ConnectProxyConfig) 268 if !ok { 269 return nil, errors.New("internal error") 270 } 271 // These are all set by defaults so type checks are just sanity checks that 272 // should never fail. 273 port, ok := proxyCfg.Config["bind_port"].(int) 274 if !ok || port < 1 { 275 return nil, errors.New("invalid proxy config") 276 } 277 addr, ok := proxyCfg.Config["bind_address"].(string) 278 if !ok || addr == "" { 279 return nil, errors.New("invalid proxy config") 280 } 281 localAddr, ok := proxyCfg.Config["local_service_address"].(string) 282 if !ok || localAddr == "" { 283 return nil, errors.New("invalid proxy config") 284 } 285 // Old local_service_address was a host:port 286 localAddress, localPortRaw, err := net.SplitHostPort(localAddr) 287 if err != nil { 288 return nil, err 289 } 290 localPort, err := strconv.Atoi(localPortRaw) 291 if err != nil { 292 return nil, err 293 } 294 295 reply := &api.AgentService{ 296 Kind: api.ServiceKindConnectProxy, 297 ID: proxyCfg.ProxyServiceID, 298 Service: managedProxy.Proxy.ProxyService.Service, 299 Port: port, 300 Address: addr, 301 ContentHash: proxyCfg.ContentHash, 302 Proxy: &api.AgentServiceConnectProxyConfig{ 303 DestinationServiceName: proxyCfg.TargetServiceName, 304 DestinationServiceID: proxyCfg.TargetServiceID, 305 LocalServiceAddress: localAddress, 306 LocalServicePort: localPort, 307 Config: proxyCfg.Config, 308 Upstreams: proxyCfg.Upstreams, 309 }, 310 } 311 return reply, nil 312 } 313 314 // Maybe block 315 var queryOpts structs.QueryOptions 316 if parseWait(resp, req, &queryOpts) { 317 // parseWait returns an error itself 318 return nil, nil 319 } 320 321 // Parse the token 322 var token string 323 s.parseToken(req, &token) 324 325 // Parse hash specially. Eventually this should happen in parseWait and end up 326 // in QueryOptions but I didn't want to make very general changes right away. 327 hash := req.URL.Query().Get("hash") 328 329 return s.agentLocalBlockingQuery(resp, hash, &queryOpts, 330 func(ws memdb.WatchSet) (string, interface{}, error) { 331 332 svcState := s.agent.State.ServiceState(id) 333 if svcState == nil { 334 resp.WriteHeader(http.StatusNotFound) 335 fmt.Fprintf(resp, "unknown proxy service ID: %s", id) 336 return "", nil, nil 337 } 338 339 svc := svcState.Service 340 341 // Setup watch on the service 342 ws.Add(svcState.WatchCh) 343 344 // Check ACLs. 345 rule, err := s.agent.resolveToken(token) 346 if err != nil { 347 return "", nil, err 348 } 349 if rule != nil && !rule.ServiceRead(svc.Service) { 350 return "", nil, acl.ErrPermissionDenied 351 } 352 353 var connect *api.AgentServiceConnect 354 var proxy *api.AgentServiceConnectProxyConfig 355 356 if svc.Connect.Native { 357 connect = &api.AgentServiceConnect{ 358 Native: svc.Connect.Native, 359 } 360 } 361 362 if svc.Kind == structs.ServiceKindConnectProxy { 363 proxy = svc.Proxy.ToAPI() 364 } 365 366 var weights api.AgentWeights 367 if svc.Weights != nil { 368 err := mapstructure.Decode(svc.Weights, &weights) 369 if err != nil { 370 return "", nil, err 371 } 372 } 373 374 // Calculate the content hash over the response, minus the hash field 375 reply := &api.AgentService{ 376 Kind: api.ServiceKind(svc.Kind), 377 ID: svc.ID, 378 Service: svc.Service, 379 Tags: svc.Tags, 380 Meta: svc.Meta, 381 Port: svc.Port, 382 Address: svc.Address, 383 EnableTagOverride: svc.EnableTagOverride, 384 Weights: weights, 385 Proxy: proxy, 386 Connect: connect, 387 } 388 389 rawHash, err := hashstructure.Hash(reply, nil) 390 if err != nil { 391 return "", nil, err 392 } 393 394 // Include the ContentHash in the response body 395 reply.ContentHash = fmt.Sprintf("%x", rawHash) 396 397 return reply.ContentHash, reply, nil 398 }) 399 } 400 401 func (s *HTTPServer) AgentChecks(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 402 // Fetch the ACL token, if any. 403 var token string 404 s.parseToken(req, &token) 405 406 checks := s.agent.State.Checks() 407 if err := s.agent.filterChecks(token, &checks); err != nil { 408 return nil, err 409 } 410 411 // Use empty list instead of nil 412 for id, c := range checks { 413 if c.ServiceTags == nil { 414 clone := *c 415 clone.ServiceTags = make([]string, 0) 416 checks[id] = &clone 417 } 418 } 419 420 return checks, nil 421 } 422 423 func (s *HTTPServer) AgentMembers(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 424 // Fetch the ACL token, if any. 425 var token string 426 s.parseToken(req, &token) 427 428 // Check if the WAN is being queried 429 wan := false 430 if other := req.URL.Query().Get("wan"); other != "" { 431 wan = true 432 } 433 434 segment := req.URL.Query().Get("segment") 435 if wan { 436 switch segment { 437 case "", api.AllSegments: 438 // The zero value and the special "give me all members" 439 // key are ok, otherwise the argument doesn't apply to 440 // the WAN. 441 default: 442 resp.WriteHeader(http.StatusBadRequest) 443 fmt.Fprint(resp, "Cannot provide a segment with wan=true") 444 return nil, nil 445 } 446 } 447 448 var members []serf.Member 449 if wan { 450 members = s.agent.WANMembers() 451 } else { 452 var err error 453 if segment == api.AllSegments { 454 members, err = s.agent.delegate.LANMembersAllSegments() 455 } else { 456 members, err = s.agent.delegate.LANSegmentMembers(segment) 457 } 458 if err != nil { 459 return nil, err 460 } 461 } 462 if err := s.agent.filterMembers(token, &members); err != nil { 463 return nil, err 464 } 465 return members, nil 466 } 467 468 func (s *HTTPServer) AgentJoin(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 469 // Fetch the ACL token, if any, and enforce agent policy. 470 var token string 471 s.parseToken(req, &token) 472 rule, err := s.agent.resolveToken(token) 473 if err != nil { 474 return nil, err 475 } 476 if rule != nil && !rule.AgentWrite(s.agent.config.NodeName) { 477 return nil, acl.ErrPermissionDenied 478 } 479 480 // Check if the WAN is being queried 481 wan := false 482 if other := req.URL.Query().Get("wan"); other != "" { 483 wan = true 484 } 485 486 // Get the address 487 addr := strings.TrimPrefix(req.URL.Path, "/v1/agent/join/") 488 if wan { 489 _, err = s.agent.JoinWAN([]string{addr}) 490 } else { 491 _, err = s.agent.JoinLAN([]string{addr}) 492 } 493 return nil, err 494 } 495 496 func (s *HTTPServer) AgentLeave(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 497 // Fetch the ACL token, if any, and enforce agent policy. 498 var token string 499 s.parseToken(req, &token) 500 rule, err := s.agent.resolveToken(token) 501 if err != nil { 502 return nil, err 503 } 504 if rule != nil && !rule.AgentWrite(s.agent.config.NodeName) { 505 return nil, acl.ErrPermissionDenied 506 } 507 508 if err := s.agent.Leave(); err != nil { 509 return nil, err 510 } 511 return nil, s.agent.ShutdownAgent() 512 } 513 514 func (s *HTTPServer) AgentForceLeave(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 515 // Fetch the ACL token, if any, and enforce agent policy. 516 var token string 517 s.parseToken(req, &token) 518 rule, err := s.agent.resolveToken(token) 519 if err != nil { 520 return nil, err 521 } 522 if rule != nil && !rule.AgentWrite(s.agent.config.NodeName) { 523 return nil, acl.ErrPermissionDenied 524 } 525 526 addr := strings.TrimPrefix(req.URL.Path, "/v1/agent/force-leave/") 527 return nil, s.agent.ForceLeave(addr) 528 } 529 530 // syncChanges is a helper function which wraps a blocking call to sync 531 // services and checks to the server. If the operation fails, we only 532 // only warn because the write did succeed and anti-entropy will sync later. 533 func (s *HTTPServer) syncChanges() { 534 if err := s.agent.State.SyncChanges(); err != nil { 535 s.agent.logger.Printf("[ERR] agent: failed to sync changes: %v", err) 536 } 537 } 538 539 func (s *HTTPServer) AgentRegisterCheck(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 540 var args structs.CheckDefinition 541 // Fixup the type decode of TTL or Interval. 542 decodeCB := func(raw interface{}) error { 543 return FixupCheckType(raw) 544 } 545 if err := decodeBody(req, &args, decodeCB); err != nil { 546 resp.WriteHeader(http.StatusBadRequest) 547 fmt.Fprintf(resp, "Request decode failed: %v", err) 548 return nil, nil 549 } 550 551 // Verify the check has a name. 552 if args.Name == "" { 553 resp.WriteHeader(http.StatusBadRequest) 554 fmt.Fprint(resp, "Missing check name") 555 return nil, nil 556 } 557 558 if args.Status != "" && !structs.ValidStatus(args.Status) { 559 resp.WriteHeader(http.StatusBadRequest) 560 fmt.Fprint(resp, "Bad check status") 561 return nil, nil 562 } 563 564 // Construct the health check. 565 health := args.HealthCheck(s.agent.config.NodeName) 566 567 // Verify the check type. 568 chkType := args.CheckType() 569 err := chkType.Validate() 570 if err != nil { 571 resp.WriteHeader(http.StatusBadRequest) 572 fmt.Fprint(resp, fmt.Errorf("Invalid check: %v", err)) 573 return nil, nil 574 } 575 576 // Get the provided token, if any, and vet against any ACL policies. 577 var token string 578 s.parseToken(req, &token) 579 if err := s.agent.vetCheckRegister(token, health); err != nil { 580 return nil, err 581 } 582 583 // Add the check. 584 if err := s.agent.AddCheck(health, chkType, true, token, ConfigSourceRemote); err != nil { 585 return nil, err 586 } 587 s.syncChanges() 588 return nil, nil 589 } 590 591 func (s *HTTPServer) AgentDeregisterCheck(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 592 checkID := types.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/deregister/")) 593 594 // Get the provided token, if any, and vet against any ACL policies. 595 var token string 596 s.parseToken(req, &token) 597 if err := s.agent.vetCheckUpdate(token, checkID); err != nil { 598 return nil, err 599 } 600 601 if err := s.agent.RemoveCheck(checkID, true); err != nil { 602 return nil, err 603 } 604 s.syncChanges() 605 return nil, nil 606 } 607 608 func (s *HTTPServer) AgentCheckPass(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 609 checkID := types.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/pass/")) 610 note := req.URL.Query().Get("note") 611 612 // Get the provided token, if any, and vet against any ACL policies. 613 var token string 614 s.parseToken(req, &token) 615 if err := s.agent.vetCheckUpdate(token, checkID); err != nil { 616 return nil, err 617 } 618 619 if err := s.agent.updateTTLCheck(checkID, api.HealthPassing, note); err != nil { 620 return nil, err 621 } 622 s.syncChanges() 623 return nil, nil 624 } 625 626 func (s *HTTPServer) AgentCheckWarn(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 627 checkID := types.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/warn/")) 628 note := req.URL.Query().Get("note") 629 630 // Get the provided token, if any, and vet against any ACL policies. 631 var token string 632 s.parseToken(req, &token) 633 if err := s.agent.vetCheckUpdate(token, checkID); err != nil { 634 return nil, err 635 } 636 637 if err := s.agent.updateTTLCheck(checkID, api.HealthWarning, note); err != nil { 638 return nil, err 639 } 640 s.syncChanges() 641 return nil, nil 642 } 643 644 func (s *HTTPServer) AgentCheckFail(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 645 checkID := types.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/fail/")) 646 note := req.URL.Query().Get("note") 647 648 // Get the provided token, if any, and vet against any ACL policies. 649 var token string 650 s.parseToken(req, &token) 651 if err := s.agent.vetCheckUpdate(token, checkID); err != nil { 652 return nil, err 653 } 654 655 if err := s.agent.updateTTLCheck(checkID, api.HealthCritical, note); err != nil { 656 return nil, err 657 } 658 s.syncChanges() 659 return nil, nil 660 } 661 662 // checkUpdate is the payload for a PUT to AgentCheckUpdate. 663 type checkUpdate struct { 664 // Status us one of the api.Health* states, "passing", "warning", or 665 // "critical". 666 Status string 667 668 // Output is the information to post to the UI for operators as the 669 // output of the process that decided to hit the TTL check. This is 670 // different from the note field that's associated with the check 671 // itself. 672 Output string 673 } 674 675 // AgentCheckUpdate is a PUT-based alternative to the GET-based Pass/Warn/Fail 676 // APIs. 677 func (s *HTTPServer) AgentCheckUpdate(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 678 var update checkUpdate 679 if err := decodeBody(req, &update, nil); err != nil { 680 resp.WriteHeader(http.StatusBadRequest) 681 fmt.Fprintf(resp, "Request decode failed: %v", err) 682 return nil, nil 683 } 684 685 switch update.Status { 686 case api.HealthPassing: 687 case api.HealthWarning: 688 case api.HealthCritical: 689 default: 690 resp.WriteHeader(http.StatusBadRequest) 691 fmt.Fprintf(resp, "Invalid check status: '%s'", update.Status) 692 return nil, nil 693 } 694 695 total := len(update.Output) 696 if total > checks.BufSize { 697 update.Output = fmt.Sprintf("%s ... (captured %d of %d bytes)", 698 update.Output[:checks.BufSize], checks.BufSize, total) 699 } 700 701 checkID := types.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/update/")) 702 703 // Get the provided token, if any, and vet against any ACL policies. 704 var token string 705 s.parseToken(req, &token) 706 if err := s.agent.vetCheckUpdate(token, checkID); err != nil { 707 return nil, err 708 } 709 710 if err := s.agent.updateTTLCheck(checkID, update.Status, update.Output); err != nil { 711 return nil, err 712 } 713 s.syncChanges() 714 return nil, nil 715 } 716 717 // agentHealthService Returns Health for a given service ID 718 func agentHealthService(serviceID string, s *HTTPServer) (int, string, api.HealthChecks) { 719 checks := s.agent.State.Checks() 720 serviceChecks := make(api.HealthChecks, 0) 721 for _, c := range checks { 722 if c.ServiceID == serviceID || c.ServiceID == "" { 723 // TODO: harmonize struct.HealthCheck and api.HealthCheck (or at least extract conversion function) 724 healthCheck := &api.HealthCheck{ 725 Node: c.Node, 726 CheckID: string(c.CheckID), 727 Name: c.Name, 728 Status: c.Status, 729 Notes: c.Notes, 730 Output: c.Output, 731 ServiceID: c.ServiceID, 732 ServiceName: c.ServiceName, 733 ServiceTags: c.ServiceTags, 734 } 735 serviceChecks = append(serviceChecks, healthCheck) 736 } 737 } 738 status := serviceChecks.AggregatedStatus() 739 switch status { 740 case api.HealthWarning: 741 return http.StatusTooManyRequests, status, serviceChecks 742 case api.HealthPassing: 743 return http.StatusOK, status, serviceChecks 744 default: 745 return http.StatusServiceUnavailable, status, serviceChecks 746 } 747 } 748 749 func returnTextPlain(req *http.Request) bool { 750 if contentType := req.Header.Get("Accept"); strings.HasPrefix(contentType, "text/plain") { 751 return true 752 } 753 if format := req.URL.Query().Get("format"); format != "" { 754 return format == "text" 755 } 756 return false 757 } 758 759 // AgentHealthServiceByID return the local Service Health given its ID 760 func (s *HTTPServer) AgentHealthServiceByID(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 761 // Pull out the service id (service id since there may be several instance of the same service on this host) 762 serviceID := strings.TrimPrefix(req.URL.Path, "/v1/agent/health/service/id/") 763 if serviceID == "" { 764 return nil, &BadRequestError{Reason: "Missing serviceID"} 765 } 766 services := s.agent.State.Services() 767 proxies := s.agent.State.Proxies() 768 for _, service := range services { 769 if service.ID == serviceID { 770 code, status, healthChecks := agentHealthService(serviceID, s) 771 if returnTextPlain(req) { 772 return status, CodeWithPayloadError{StatusCode: code, Reason: status, ContentType: "text/plain"} 773 } 774 serviceInfo := buildAgentService(service, proxies) 775 result := &api.AgentServiceChecksInfo{ 776 AggregatedStatus: status, 777 Checks: healthChecks, 778 Service: &serviceInfo, 779 } 780 return result, CodeWithPayloadError{StatusCode: code, Reason: status, ContentType: "application/json"} 781 } 782 } 783 notFoundReason := fmt.Sprintf("ServiceId %s not found", serviceID) 784 if returnTextPlain(req) { 785 return notFoundReason, CodeWithPayloadError{StatusCode: http.StatusNotFound, Reason: fmt.Sprintf("ServiceId %s not found", serviceID), ContentType: "application/json"} 786 } 787 return &api.AgentServiceChecksInfo{ 788 AggregatedStatus: api.HealthCritical, 789 Checks: nil, 790 Service: nil, 791 }, CodeWithPayloadError{StatusCode: http.StatusNotFound, Reason: notFoundReason, ContentType: "application/json"} 792 } 793 794 // AgentHealthServiceByName return the worse status of all the services with given name on an agent 795 func (s *HTTPServer) AgentHealthServiceByName(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 796 // Pull out the service name 797 serviceName := strings.TrimPrefix(req.URL.Path, "/v1/agent/health/service/name/") 798 if serviceName == "" { 799 return nil, &BadRequestError{Reason: "Missing service Name"} 800 } 801 code := http.StatusNotFound 802 status := fmt.Sprintf("ServiceName %s Not Found", serviceName) 803 services := s.agent.State.Services() 804 result := make([]api.AgentServiceChecksInfo, 0, 16) 805 proxies := s.agent.State.Proxies() 806 for _, service := range services { 807 if service.Service == serviceName { 808 scode, sstatus, healthChecks := agentHealthService(service.ID, s) 809 serviceInfo := buildAgentService(service, proxies) 810 res := api.AgentServiceChecksInfo{ 811 AggregatedStatus: sstatus, 812 Checks: healthChecks, 813 Service: &serviceInfo, 814 } 815 result = append(result, res) 816 // When service is not found, we ignore it and keep existing HTTP status 817 if code == http.StatusNotFound { 818 code = scode 819 status = sstatus 820 } 821 // We take the worst of all statuses, so we keep iterating 822 // passing: 200 < warning: 429 < critical: 503 823 if code < scode { 824 code = scode 825 status = sstatus 826 } 827 } 828 } 829 if returnTextPlain(req) { 830 return status, CodeWithPayloadError{StatusCode: code, Reason: status, ContentType: "text/plain"} 831 } 832 return result, CodeWithPayloadError{StatusCode: code, Reason: status, ContentType: "application/json"} 833 } 834 835 func (s *HTTPServer) AgentRegisterService(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 836 var args structs.ServiceDefinition 837 // Fixup the type decode of TTL or Interval if a check if provided. 838 decodeCB := func(raw interface{}) error { 839 rawMap, ok := raw.(map[string]interface{}) 840 if !ok { 841 return nil 842 } 843 844 // see https://github.com/hashicorp/consul/pull/3557 why we need this 845 // and why we should get rid of it. 846 config.TranslateKeys(rawMap, map[string]string{ 847 "enable_tag_override": "EnableTagOverride", 848 // Managed Proxy Config 849 "exec_mode": "ExecMode", 850 // Proxy Upstreams 851 "destination_name": "DestinationName", 852 "destination_type": "DestinationType", 853 "destination_namespace": "DestinationNamespace", 854 "local_bind_port": "LocalBindPort", 855 "local_bind_address": "LocalBindAddress", 856 // Proxy Config 857 "destination_service_name": "DestinationServiceName", 858 "destination_service_id": "DestinationServiceID", 859 "local_service_port": "LocalServicePort", 860 "local_service_address": "LocalServiceAddress", 861 // SidecarService 862 "sidecar_service": "SidecarService", 863 864 // DON'T Recurse into these opaque config maps or we might mangle user's 865 // keys. Note empty canonical is a special sentinel to prevent recursion. 866 "Meta": "", 867 // upstreams is an array but this prevents recursion into config field of 868 // any item in the array. 869 "Proxy.Config": "", 870 "Proxy.Upstreams.Config": "", 871 "Connect.Proxy.Config": "", 872 "Connect.Proxy.Upstreams.Config": "", 873 874 // Same exceptions as above, but for a nested sidecar_service note we use 875 // the canonical form SidecarService since that is translated by the time 876 // the lookup here happens. Note that sidecar service doesn't support 877 // managed proxies (connect.proxy). 878 "Connect.SidecarService.Meta": "", 879 "Connect.SidecarService.Proxy.Config": "", 880 "Connect.SidecarService.Proxy.Upstreams.config": "", 881 }) 882 883 for k, v := range rawMap { 884 switch strings.ToLower(k) { 885 case "check": 886 if err := FixupCheckType(v); err != nil { 887 return err 888 } 889 case "checks": 890 chkTypes, ok := v.([]interface{}) 891 if !ok { 892 continue 893 } 894 for _, chkType := range chkTypes { 895 if err := FixupCheckType(chkType); err != nil { 896 return err 897 } 898 } 899 } 900 } 901 return nil 902 } 903 if err := decodeBody(req, &args, decodeCB); err != nil { 904 resp.WriteHeader(http.StatusBadRequest) 905 fmt.Fprintf(resp, "Request decode failed: %v", err) 906 return nil, nil 907 } 908 909 // Verify the service has a name. 910 if args.Name == "" { 911 resp.WriteHeader(http.StatusBadRequest) 912 fmt.Fprint(resp, "Missing service name") 913 return nil, nil 914 } 915 916 // Check the service address here and in the catalog RPC endpoint 917 // since service registration isn't synchronous. 918 if ipaddr.IsAny(args.Address) { 919 resp.WriteHeader(http.StatusBadRequest) 920 fmt.Fprintf(resp, "Invalid service address") 921 return nil, nil 922 } 923 924 // Get the node service. 925 ns := args.NodeService() 926 if ns.Weights != nil { 927 if err := structs.ValidateWeights(ns.Weights); err != nil { 928 resp.WriteHeader(http.StatusBadRequest) 929 fmt.Fprint(resp, fmt.Errorf("Invalid Weights: %v", err)) 930 return nil, nil 931 } 932 } 933 if err := structs.ValidateMetadata(ns.Meta, false); err != nil { 934 resp.WriteHeader(http.StatusBadRequest) 935 fmt.Fprint(resp, fmt.Errorf("Invalid Service Meta: %v", err)) 936 return nil, nil 937 } 938 939 // Run validation. This is the same validation that would happen on 940 // the catalog endpoint so it helps ensure the sync will work properly. 941 if err := ns.Validate(); err != nil { 942 resp.WriteHeader(http.StatusBadRequest) 943 fmt.Fprintf(resp, err.Error()) 944 return nil, nil 945 } 946 947 // Verify the check type. 948 chkTypes, err := args.CheckTypes() 949 if err != nil { 950 resp.WriteHeader(http.StatusBadRequest) 951 fmt.Fprint(resp, fmt.Errorf("Invalid check: %v", err)) 952 return nil, nil 953 } 954 for _, check := range chkTypes { 955 if check.Status != "" && !structs.ValidStatus(check.Status) { 956 resp.WriteHeader(http.StatusBadRequest) 957 fmt.Fprint(resp, "Status for checks must 'passing', 'warning', 'critical'") 958 return nil, nil 959 } 960 } 961 962 // Verify the sidecar check types 963 if args.Connect != nil && args.Connect.SidecarService != nil { 964 chkTypes, err := args.Connect.SidecarService.CheckTypes() 965 if err != nil { 966 return nil, &BadRequestError{ 967 Reason: fmt.Sprintf("Invalid check in sidecar_service: %v", err), 968 } 969 } 970 for _, check := range chkTypes { 971 if check.Status != "" && !structs.ValidStatus(check.Status) { 972 return nil, &BadRequestError{ 973 Reason: "Status for checks must 'passing', 'warning', 'critical'", 974 } 975 } 976 } 977 } 978 979 // Get the provided token, if any, and vet against any ACL policies. 980 var token string 981 s.parseToken(req, &token) 982 if err := s.agent.vetServiceRegister(token, ns); err != nil { 983 return nil, err 984 } 985 986 // See if we have a sidecar to register too 987 sidecar, sidecarChecks, sidecarToken, err := s.agent.sidecarServiceFromNodeService(ns, token) 988 if err != nil { 989 return nil, &BadRequestError{ 990 Reason: fmt.Sprintf("Invalid SidecarService: %s", err)} 991 } 992 if sidecar != nil { 993 // Make sure we are allowed to register the sidecar using the token 994 // specified (might be specific to sidecar or the same one as the overall 995 // request). 996 if err := s.agent.vetServiceRegister(sidecarToken, sidecar); err != nil { 997 return nil, err 998 } 999 // We parsed the sidecar registration, now remove it from the NodeService 1000 // for the actual service since it's done it's job and we don't want to 1001 // persist it in the actual state/catalog. SidecarService is meant to be a 1002 // registration syntax sugar so don't propagate it any further. 1003 ns.Connect.SidecarService = nil 1004 } 1005 1006 // Get any proxy registrations 1007 proxy, err := args.ConnectManagedProxy() 1008 if err != nil { 1009 resp.WriteHeader(http.StatusBadRequest) 1010 fmt.Fprintf(resp, err.Error()) 1011 return nil, nil 1012 } 1013 1014 // If we have a proxy, verify that we're allowed to add a proxy via the API 1015 if proxy != nil && !s.agent.config.ConnectProxyAllowManagedAPIRegistration { 1016 return nil, &BadRequestError{ 1017 Reason: "Managed proxy registration via the API is disallowed."} 1018 } 1019 1020 // Add the service. 1021 if err := s.agent.AddService(ns, chkTypes, true, token, ConfigSourceRemote); err != nil { 1022 return nil, err 1023 } 1024 // Add proxy (which will add proxy service so do it before we trigger sync) 1025 if proxy != nil { 1026 if err := s.agent.AddProxy(proxy, true, false, "", ConfigSourceRemote); err != nil { 1027 return nil, err 1028 } 1029 } 1030 // Add sidecar. 1031 if sidecar != nil { 1032 if err := s.agent.AddService(sidecar, sidecarChecks, true, sidecarToken, ConfigSourceRemote); err != nil { 1033 return nil, err 1034 } 1035 } 1036 s.syncChanges() 1037 return nil, nil 1038 } 1039 1040 func (s *HTTPServer) AgentDeregisterService(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 1041 serviceID := strings.TrimPrefix(req.URL.Path, "/v1/agent/service/deregister/") 1042 1043 // Get the provided token, if any, and vet against any ACL policies. 1044 var token string 1045 s.parseToken(req, &token) 1046 if err := s.agent.vetServiceUpdate(token, serviceID); err != nil { 1047 return nil, err 1048 } 1049 1050 // Verify this isn't a proxy 1051 if s.agent.State.Proxy(serviceID) != nil { 1052 return nil, &BadRequestError{ 1053 Reason: "Managed proxy service cannot be deregistered directly. " + 1054 "Deregister the service that has a managed proxy to automatically " + 1055 "deregister the managed proxy itself."} 1056 } 1057 1058 if err := s.agent.RemoveService(serviceID, true); err != nil { 1059 return nil, err 1060 } 1061 1062 s.syncChanges() 1063 return nil, nil 1064 } 1065 1066 func (s *HTTPServer) AgentServiceMaintenance(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 1067 // Ensure we have a service ID 1068 serviceID := strings.TrimPrefix(req.URL.Path, "/v1/agent/service/maintenance/") 1069 if serviceID == "" { 1070 resp.WriteHeader(http.StatusBadRequest) 1071 fmt.Fprint(resp, "Missing service ID") 1072 return nil, nil 1073 } 1074 1075 // Ensure we have some action 1076 params := req.URL.Query() 1077 if _, ok := params["enable"]; !ok { 1078 resp.WriteHeader(http.StatusBadRequest) 1079 fmt.Fprint(resp, "Missing value for enable") 1080 return nil, nil 1081 } 1082 1083 raw := params.Get("enable") 1084 enable, err := strconv.ParseBool(raw) 1085 if err != nil { 1086 resp.WriteHeader(http.StatusBadRequest) 1087 fmt.Fprintf(resp, "Invalid value for enable: %q", raw) 1088 return nil, nil 1089 } 1090 1091 // Get the provided token, if any, and vet against any ACL policies. 1092 var token string 1093 s.parseToken(req, &token) 1094 if err := s.agent.vetServiceUpdate(token, serviceID); err != nil { 1095 return nil, err 1096 } 1097 1098 if enable { 1099 reason := params.Get("reason") 1100 if err = s.agent.EnableServiceMaintenance(serviceID, reason, token); err != nil { 1101 resp.WriteHeader(http.StatusNotFound) 1102 fmt.Fprint(resp, err.Error()) 1103 return nil, nil 1104 } 1105 } else { 1106 if err = s.agent.DisableServiceMaintenance(serviceID); err != nil { 1107 resp.WriteHeader(http.StatusNotFound) 1108 fmt.Fprint(resp, err.Error()) 1109 return nil, nil 1110 } 1111 } 1112 s.syncChanges() 1113 return nil, nil 1114 } 1115 1116 func (s *HTTPServer) AgentNodeMaintenance(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 1117 // Ensure we have some action 1118 params := req.URL.Query() 1119 if _, ok := params["enable"]; !ok { 1120 resp.WriteHeader(http.StatusBadRequest) 1121 fmt.Fprint(resp, "Missing value for enable") 1122 return nil, nil 1123 } 1124 1125 raw := params.Get("enable") 1126 enable, err := strconv.ParseBool(raw) 1127 if err != nil { 1128 resp.WriteHeader(http.StatusBadRequest) 1129 fmt.Fprintf(resp, "Invalid value for enable: %q", raw) 1130 return nil, nil 1131 } 1132 1133 // Get the provided token, if any, and vet against any ACL policies. 1134 var token string 1135 s.parseToken(req, &token) 1136 rule, err := s.agent.resolveToken(token) 1137 if err != nil { 1138 return nil, err 1139 } 1140 if rule != nil && !rule.NodeWrite(s.agent.config.NodeName, nil) { 1141 return nil, acl.ErrPermissionDenied 1142 } 1143 1144 if enable { 1145 s.agent.EnableNodeMaintenance(params.Get("reason"), token) 1146 } else { 1147 s.agent.DisableNodeMaintenance() 1148 } 1149 s.syncChanges() 1150 return nil, nil 1151 } 1152 1153 func (s *HTTPServer) AgentMonitor(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 1154 // Fetch the ACL token, if any, and enforce agent policy. 1155 var token string 1156 s.parseToken(req, &token) 1157 rule, err := s.agent.resolveToken(token) 1158 if err != nil { 1159 return nil, err 1160 } 1161 if rule != nil && !rule.AgentRead(s.agent.config.NodeName) { 1162 return nil, acl.ErrPermissionDenied 1163 } 1164 1165 // Get the provided loglevel. 1166 logLevel := req.URL.Query().Get("loglevel") 1167 if logLevel == "" { 1168 logLevel = "INFO" 1169 } 1170 1171 // Upper case the level since that's required by the filter. 1172 logLevel = strings.ToUpper(logLevel) 1173 1174 // Create a level filter and flusher. 1175 filter := logger.LevelFilter() 1176 filter.MinLevel = logutils.LogLevel(logLevel) 1177 if !logger.ValidateLevelFilter(filter.MinLevel, filter) { 1178 resp.WriteHeader(http.StatusBadRequest) 1179 fmt.Fprintf(resp, "Unknown log level: %s", filter.MinLevel) 1180 return nil, nil 1181 } 1182 flusher, ok := resp.(http.Flusher) 1183 if !ok { 1184 return nil, fmt.Errorf("Streaming not supported") 1185 } 1186 1187 // Set up a log handler. 1188 handler := &httpLogHandler{ 1189 filter: filter, 1190 logCh: make(chan string, 512), 1191 logger: s.agent.logger, 1192 } 1193 s.agent.LogWriter.RegisterHandler(handler) 1194 defer s.agent.LogWriter.DeregisterHandler(handler) 1195 notify := resp.(http.CloseNotifier).CloseNotify() 1196 1197 // Send header so client can start streaming body 1198 resp.WriteHeader(http.StatusOK) 1199 1200 // 0 byte write is needed before the Flush call so that if we are using 1201 // a gzip stream it will go ahead and write out the HTTP response header 1202 resp.Write([]byte("")) 1203 flusher.Flush() 1204 1205 // Stream logs until the connection is closed. 1206 for { 1207 select { 1208 case <-notify: 1209 s.agent.LogWriter.DeregisterHandler(handler) 1210 if handler.droppedCount > 0 { 1211 s.agent.logger.Printf("[WARN] agent: Dropped %d logs during monitor request", handler.droppedCount) 1212 } 1213 return nil, nil 1214 case log := <-handler.logCh: 1215 fmt.Fprintln(resp, log) 1216 flusher.Flush() 1217 } 1218 } 1219 } 1220 1221 type httpLogHandler struct { 1222 filter *logutils.LevelFilter 1223 logCh chan string 1224 logger *log.Logger 1225 droppedCount int 1226 } 1227 1228 func (h *httpLogHandler) HandleLog(log string) { 1229 // Check the log level 1230 if !h.filter.Check([]byte(log)) { 1231 return 1232 } 1233 1234 // Do a non-blocking send 1235 select { 1236 case h.logCh <- log: 1237 default: 1238 // Just increment a counter for dropped logs to this handler; we can't log now 1239 // because the lock is already held by the LogWriter invoking this 1240 h.droppedCount++ 1241 } 1242 } 1243 1244 func (s *HTTPServer) AgentToken(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 1245 if s.checkACLDisabled(resp, req) { 1246 return nil, nil 1247 } 1248 1249 // Fetch the ACL token, if any, and enforce agent policy. 1250 var token string 1251 s.parseToken(req, &token) 1252 rule, err := s.agent.resolveToken(token) 1253 if err != nil { 1254 return nil, err 1255 } 1256 if rule != nil && !rule.AgentWrite(s.agent.config.NodeName) { 1257 return nil, acl.ErrPermissionDenied 1258 } 1259 1260 // The body is just the token, but it's in a JSON object so we can add 1261 // fields to this later if needed. 1262 var args api.AgentToken 1263 if err := decodeBody(req, &args, nil); err != nil { 1264 resp.WriteHeader(http.StatusBadRequest) 1265 fmt.Fprintf(resp, "Request decode failed: %v", err) 1266 return nil, nil 1267 } 1268 1269 if s.agent.config.ACLEnableTokenPersistence { 1270 // we hold the lock around updating the internal token store 1271 // as well as persisting the tokens because we don't want to write 1272 // into the store to have something else wipe it out before we can 1273 // persist everything (like an agent config reload). The token store 1274 // lock is only held for those operations so other go routines that 1275 // just need to read some token out of the store will not be impacted 1276 // any more than they would be without token persistence. 1277 s.agent.persistedTokensLock.Lock() 1278 defer s.agent.persistedTokensLock.Unlock() 1279 } 1280 1281 // Figure out the target token. 1282 target := strings.TrimPrefix(req.URL.Path, "/v1/agent/token/") 1283 switch target { 1284 case "acl_token", "default": 1285 s.agent.tokens.UpdateUserToken(args.Token, token_store.TokenSourceAPI) 1286 1287 case "acl_agent_token", "agent": 1288 s.agent.tokens.UpdateAgentToken(args.Token, token_store.TokenSourceAPI) 1289 1290 case "acl_agent_master_token", "agent_master": 1291 s.agent.tokens.UpdateAgentMasterToken(args.Token, token_store.TokenSourceAPI) 1292 1293 case "acl_replication_token", "replication": 1294 s.agent.tokens.UpdateReplicationToken(args.Token, token_store.TokenSourceAPI) 1295 1296 default: 1297 resp.WriteHeader(http.StatusNotFound) 1298 fmt.Fprintf(resp, "Token %q is unknown", target) 1299 return nil, nil 1300 } 1301 1302 if s.agent.config.ACLEnableTokenPersistence { 1303 tokens := persistedTokens{} 1304 1305 if tok, source := s.agent.tokens.UserTokenAndSource(); tok != "" && source == token_store.TokenSourceAPI { 1306 tokens.Default = tok 1307 } 1308 1309 if tok, source := s.agent.tokens.AgentTokenAndSource(); tok != "" && source == token_store.TokenSourceAPI { 1310 tokens.Agent = tok 1311 } 1312 1313 if tok, source := s.agent.tokens.AgentMasterTokenAndSource(); tok != "" && source == token_store.TokenSourceAPI { 1314 tokens.AgentMaster = tok 1315 } 1316 1317 if tok, source := s.agent.tokens.ReplicationTokenAndSource(); tok != "" && source == token_store.TokenSourceAPI { 1318 tokens.Replication = tok 1319 } 1320 1321 data, err := json.Marshal(tokens) 1322 if err != nil { 1323 s.agent.logger.Printf("[WARN] agent: failed to persist tokens - %v", err) 1324 return nil, fmt.Errorf("Failed to marshal tokens for persistence: %v", err) 1325 } 1326 1327 if err := file.WriteAtomicWithPerms(filepath.Join(s.agent.config.DataDir, tokensPath), data, 0600); err != nil { 1328 s.agent.logger.Printf("[WARN] agent: failed to persist tokens - %v", err) 1329 return nil, fmt.Errorf("Failed to persist tokens - %v", err) 1330 } 1331 } 1332 1333 s.agent.logger.Printf("[INFO] agent: Updated agent's ACL token %q", target) 1334 return nil, nil 1335 } 1336 1337 // AgentConnectCARoots returns the trusted CA roots. 1338 func (s *HTTPServer) AgentConnectCARoots(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 1339 var args structs.DCSpecificRequest 1340 if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { 1341 return nil, nil 1342 } 1343 1344 raw, m, err := s.agent.cache.Get(cachetype.ConnectCARootName, &args) 1345 if err != nil { 1346 return nil, err 1347 } 1348 defer setCacheMeta(resp, &m) 1349 1350 // Add cache hit 1351 1352 reply, ok := raw.(*structs.IndexedCARoots) 1353 if !ok { 1354 // This should never happen, but we want to protect against panics 1355 return nil, fmt.Errorf("internal error: response type not correct") 1356 } 1357 defer setMeta(resp, &reply.QueryMeta) 1358 1359 return *reply, nil 1360 } 1361 1362 // AgentConnectCALeafCert returns the certificate bundle for a service 1363 // instance. This supports blocking queries to update the returned bundle. 1364 func (s *HTTPServer) AgentConnectCALeafCert(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 1365 // Get the service name. Note that this is the name of the service, 1366 // not the ID of the service instance. 1367 serviceName := strings.TrimPrefix(req.URL.Path, "/v1/agent/connect/ca/leaf/") 1368 1369 args := cachetype.ConnectCALeafRequest{ 1370 Service: serviceName, // Need name not ID 1371 } 1372 var qOpts structs.QueryOptions 1373 1374 // Store DC in the ConnectCALeafRequest but query opts separately 1375 // Don't resolve a proxy token to a real token that will be 1376 // done with a call to verifyProxyToken later along with 1377 // other security relevant checks. 1378 if done := s.parseWithoutResolvingProxyToken(resp, req, &args.Datacenter, &qOpts); done { 1379 return nil, nil 1380 } 1381 args.MinQueryIndex = qOpts.MinQueryIndex 1382 args.MaxQueryTime = qOpts.MaxQueryTime 1383 1384 // Verify the proxy token. This will check both the local proxy token 1385 // as well as the ACL if the token isn't local. The checks done in 1386 // verifyProxyToken are still relevant because a leaf cert can be cached 1387 // verifying the proxy token matches the service id or that a real 1388 // acl token still is valid and has ServiceWrite is necessary or 1389 // that cached cert is potentially unprotected. 1390 effectiveToken, _, err := s.agent.verifyProxyToken(qOpts.Token, serviceName, "") 1391 if err != nil { 1392 return nil, err 1393 } 1394 args.Token = effectiveToken 1395 1396 raw, m, err := s.agent.cache.Get(cachetype.ConnectCALeafName, &args) 1397 if err != nil { 1398 return nil, err 1399 } 1400 defer setCacheMeta(resp, &m) 1401 1402 reply, ok := raw.(*structs.IssuedCert) 1403 if !ok { 1404 // This should never happen, but we want to protect against panics 1405 return nil, fmt.Errorf("internal error: response type not correct") 1406 } 1407 setIndex(resp, reply.ModifyIndex) 1408 1409 return reply, nil 1410 } 1411 1412 // GET /v1/agent/connect/proxy/:proxy_service_id 1413 // 1414 // Returns the local proxy config for the identified proxy. Requires token= 1415 // param with the correct local ProxyToken (not ACL token). 1416 func (s *HTTPServer) AgentConnectProxyConfig(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 1417 // Get the proxy ID. Note that this is the ID of a proxy's service instance. 1418 id := strings.TrimPrefix(req.URL.Path, "/v1/agent/connect/proxy/") 1419 1420 // Maybe block 1421 var queryOpts structs.QueryOptions 1422 if parseWait(resp, req, &queryOpts) { 1423 // parseWait returns an error itself 1424 return nil, nil 1425 } 1426 1427 // Parse the token - don't resolve a proxy token to a real token 1428 // that will be done with a call to verifyProxyToken later along with 1429 // other security relevant checks. 1430 var token string 1431 s.parseTokenWithoutResolvingProxyToken(req, &token) 1432 1433 // Parse hash specially since it's only this endpoint that uses it currently. 1434 // Eventually this should happen in parseWait and end up in QueryOptions but I 1435 // didn't want to make very general changes right away. 1436 hash := req.URL.Query().Get("hash") 1437 1438 return s.agentLocalBlockingQuery(resp, hash, &queryOpts, 1439 func(ws memdb.WatchSet) (string, interface{}, error) { 1440 // Retrieve the proxy specified 1441 proxy := s.agent.State.Proxy(id) 1442 if proxy == nil { 1443 resp.WriteHeader(http.StatusNotFound) 1444 fmt.Fprintf(resp, "unknown proxy service ID: %s", id) 1445 return "", nil, nil 1446 } 1447 1448 // Lookup the target service as a convenience 1449 target := s.agent.State.Service(proxy.Proxy.TargetServiceID) 1450 if target == nil { 1451 // Not found since this endpoint is only useful for agent-managed proxies so 1452 // service missing means the service was deregistered racily with this call. 1453 resp.WriteHeader(http.StatusNotFound) 1454 fmt.Fprintf(resp, "unknown target service ID: %s", proxy.Proxy.TargetServiceID) 1455 return "", nil, nil 1456 } 1457 1458 // Validate the ACL token - because this endpoint uses data local to a single 1459 // agent, this function is responsible for all enforcement regarding 1460 // protection of the configuration. verifyProxyToken will match the proxies 1461 // token to the correct service or in the case of being provide a real ACL 1462 // token it will ensure that the requester has ServiceWrite privileges 1463 // for this service. 1464 _, isProxyToken, err := s.agent.verifyProxyToken(token, target.Service, id) 1465 if err != nil { 1466 return "", nil, err 1467 } 1468 1469 // Watch the proxy for changes 1470 ws.Add(proxy.WatchCh) 1471 1472 hash, err := hashstructure.Hash(proxy.Proxy, nil) 1473 if err != nil { 1474 return "", nil, err 1475 } 1476 contentHash := fmt.Sprintf("%x", hash) 1477 1478 // Set defaults 1479 config, err := s.agent.applyProxyConfigDefaults(proxy.Proxy) 1480 if err != nil { 1481 return "", nil, err 1482 } 1483 1484 // Only merge in telemetry config from agent if the requested is 1485 // authorized with a proxy token. This prevents us leaking potentially 1486 // sensitive config like Circonus API token via a public endpoint. Proxy 1487 // tokens are only ever generated in-memory and passed via ENV to a child 1488 // proxy process so potential for abuse here seems small. This endpoint in 1489 // general is only useful for managed proxies now so it should _always_ be 1490 // true that auth is via a proxy token but inconvenient for testing if we 1491 // lock it down so strictly. 1492 if isProxyToken { 1493 // Add telemetry config. Copy the global config so we can customize the 1494 // prefix. 1495 telemetryCfg := s.agent.config.Telemetry 1496 telemetryCfg.MetricsPrefix = telemetryCfg.MetricsPrefix + ".proxy." + target.ID 1497 1498 // First see if the user has specified telemetry 1499 if userRaw, ok := config["telemetry"]; ok { 1500 // User specified domething, see if it is compatible with agent 1501 // telemetry config: 1502 var uCfg lib.TelemetryConfig 1503 dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 1504 Result: &uCfg, 1505 // Make sure that if the user passes something that isn't just a 1506 // simple override of a valid TelemetryConfig that we fail so that we 1507 // don't clobber their custom config. 1508 ErrorUnused: true, 1509 }) 1510 if err == nil { 1511 if err = dec.Decode(userRaw); err == nil { 1512 // It did decode! Merge any unspecified fields from agent config. 1513 uCfg.MergeDefaults(&telemetryCfg) 1514 config["telemetry"] = uCfg 1515 } 1516 } 1517 // Failed to decode, just keep user's config["telemetry"] verbatim 1518 // with no agent merge. 1519 } else { 1520 // Add agent telemetry config. 1521 config["telemetry"] = telemetryCfg 1522 } 1523 } 1524 1525 reply := &api.ConnectProxyConfig{ 1526 ProxyServiceID: proxy.Proxy.ProxyService.ID, 1527 TargetServiceID: target.ID, 1528 TargetServiceName: target.Service, 1529 ContentHash: contentHash, 1530 ExecMode: api.ProxyExecMode(proxy.Proxy.ExecMode.String()), 1531 Command: proxy.Proxy.Command, 1532 Config: config, 1533 Upstreams: proxy.Proxy.Upstreams.ToAPI(), 1534 } 1535 return contentHash, reply, nil 1536 }) 1537 } 1538 1539 type agentLocalBlockingFunc func(ws memdb.WatchSet) (string, interface{}, error) 1540 1541 // agentLocalBlockingQuery performs a blocking query in a generic way against 1542 // local agent state that has no RPC or raft to back it. It uses `hash` parameter 1543 // instead of an `index`. The resp is needed to write the `X-Consul-ContentHash` 1544 // header back on return no Status nor body content is ever written to it. 1545 func (s *HTTPServer) agentLocalBlockingQuery(resp http.ResponseWriter, hash string, 1546 queryOpts *structs.QueryOptions, fn agentLocalBlockingFunc) (interface{}, error) { 1547 1548 // If we are not blocking we can skip tracking and allocating - nil WatchSet 1549 // is still valid to call Add on and will just be a no op. 1550 var ws memdb.WatchSet 1551 var timeout *time.Timer 1552 1553 if hash != "" { 1554 // TODO(banks) at least define these defaults somewhere in a const. Would be 1555 // nice not to duplicate the ones in consul/rpc.go too... 1556 wait := queryOpts.MaxQueryTime 1557 if wait == 0 { 1558 wait = 5 * time.Minute 1559 } 1560 if wait > 10*time.Minute { 1561 wait = 10 * time.Minute 1562 } 1563 // Apply a small amount of jitter to the request. 1564 wait += lib.RandomStagger(wait / 16) 1565 timeout = time.NewTimer(wait) 1566 } 1567 1568 for { 1569 // Must reset this every loop in case the Watch set is already closed but 1570 // hash remains same. In that case we'll need to re-block on ws.Watch() 1571 // again. 1572 ws = memdb.NewWatchSet() 1573 curHash, curResp, err := fn(ws) 1574 if err != nil { 1575 return curResp, err 1576 } 1577 // Return immediately if there is no timeout, the hash is different or the 1578 // Watch returns true (indicating timeout fired). Note that Watch on a nil 1579 // WatchSet immediately returns false which would incorrectly cause this to 1580 // loop and repeat again, however we rely on the invariant that ws == nil 1581 // IFF timeout == nil in which case the Watch call is never invoked. 1582 if timeout == nil || hash != curHash || ws.Watch(timeout.C) { 1583 resp.Header().Set("X-Consul-ContentHash", curHash) 1584 return curResp, err 1585 } 1586 // Watch returned false indicating a change was detected, loop and repeat 1587 // the callback to load the new value. If agent sync is paused it means 1588 // local state is currently being bulk-edited e.g. config reload. In this 1589 // case it's likely that local state just got unloaded and may or may not be 1590 // reloaded yet. Wait a short amount of time for Sync to resume to ride out 1591 // typical config reloads. 1592 if syncPauseCh := s.agent.syncPausedCh(); syncPauseCh != nil { 1593 select { 1594 case <-syncPauseCh: 1595 case <-timeout.C: 1596 } 1597 } 1598 } 1599 } 1600 1601 // AgentConnectAuthorize 1602 // 1603 // POST /v1/agent/connect/authorize 1604 // 1605 // Note: when this logic changes, consider if the Intention.Check RPC method 1606 // also needs to be updated. 1607 func (s *HTTPServer) AgentConnectAuthorize(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 1608 // Fetch the token 1609 var token string 1610 s.parseToken(req, &token) 1611 1612 // Decode the request from the request body 1613 var authReq structs.ConnectAuthorizeRequest 1614 if err := decodeBody(req, &authReq, nil); err != nil { 1615 return nil, BadRequestError{fmt.Sprintf("Request decode failed: %v", err)} 1616 } 1617 1618 authz, reason, cacheMeta, err := s.agent.ConnectAuthorize(token, &authReq) 1619 if err != nil { 1620 return nil, err 1621 } 1622 setCacheMeta(resp, cacheMeta) 1623 1624 return &connectAuthorizeResp{ 1625 Authorized: authz, 1626 Reason: reason, 1627 }, nil 1628 } 1629 1630 // connectAuthorizeResp is the response format/structure for the 1631 // /v1/agent/connect/authorize endpoint. 1632 type connectAuthorizeResp struct { 1633 Authorized bool // True if authorized, false if not 1634 Reason string // Reason for the Authorized value (whether true or false) 1635 } 1636 1637 // AgentHost 1638 // 1639 // GET /v1/agent/host 1640 // 1641 // Retrieves information about resources available and in-use for the 1642 // host the agent is running on such as CPU, memory, and disk usage. Requires 1643 // a operator:read ACL token. 1644 func (s *HTTPServer) AgentHost(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 1645 // Fetch the ACL token, if any, and enforce agent policy. 1646 var token string 1647 s.parseToken(req, &token) 1648 rule, err := s.agent.resolveToken(token) 1649 if err != nil { 1650 return nil, err 1651 } 1652 1653 if rule != nil && !rule.OperatorRead() { 1654 return nil, acl.ErrPermissionDenied 1655 } 1656 1657 return debug.CollectHostInfo(), nil 1658 }