// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package agent import ( "fmt" "net/http" "strconv" "github.com/hashicorp/consul/agent/consul" "github.com/hashicorp/consul/agent/structs" ) // GET /v1/connect/ca/roots func (s *HTTPHandlers) ConnectCARoots(resp http.ResponseWriter, req *http.Request) (interface{}, error) { var args structs.DCSpecificRequest if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { return nil, nil } pemResponse := false if pemParam := req.URL.Query().Get("pem"); pemParam != "" { val, err := strconv.ParseBool(pemParam) if err != nil { return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: "The 'pem' query parameter must be a boolean value"} } pemResponse = val } var reply structs.IndexedCARoots defer setMeta(resp, &reply.QueryMeta) if err := s.agent.RPC(req.Context(), "ConnectCA.Roots", &args, &reply); err != nil { return nil, err } if !pemResponse { return reply, nil } // defined in RFC 8555 and registered with the IANA resp.Header().Set("Content-Type", "application/pem-certificate-chain") for _, root := range reply.Roots { if _, err := resp.Write([]byte(root.RootCert)); err != nil { return nil, err } for _, intermediate := range root.IntermediateCerts { if _, err := resp.Write([]byte(intermediate)); err != nil { return nil, err } } } return nil, nil } // /v1/connect/ca/configuration func (s *HTTPHandlers) ConnectCAConfiguration(resp http.ResponseWriter, req *http.Request) (interface{}, error) { switch req.Method { case "GET": return s.ConnectCAConfigurationGet(resp, req) case "PUT": return s.ConnectCAConfigurationSet(req) default: return nil, MethodNotAllowedError{req.Method, []string{"GET", "POST"}} } } // GET /v1/connect/ca/configuration func (s *HTTPHandlers) ConnectCAConfigurationGet(resp http.ResponseWriter, req *http.Request) (interface{}, error) { // Method is tested in ConnectCAConfiguration var args structs.DCSpecificRequest if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { return nil, nil } var reply structs.CAConfiguration err := s.agent.RPC(req.Context(), "ConnectCA.ConfigurationGet", &args, &reply) if err != nil { return nil, err } return reply, nil } // PUT /v1/connect/ca/configuration func (s *HTTPHandlers) ConnectCAConfigurationSet(req *http.Request) (interface{}, error) { // Method is tested in ConnectCAConfiguration var args structs.CARequest s.parseDC(req, &args.Datacenter) s.parseToken(req, &args.Token) if err := decodeBody(req.Body, &args.Config); err != nil { return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: fmt.Sprintf("Request decode failed: %v", err)} } var reply interface{} err := s.agent.RPC(req.Context(), "ConnectCA.ConfigurationSet", &args, &reply) if err != nil && err.Error() == consul.ErrStateReadOnly.Error() { return nil, HTTPError{ StatusCode: http.StatusBadRequest, Reason: "Provider State is read-only. It must be omitted" + " or identical to the current value", } } return nil, err }