api.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "net/http/httputil"
  8. "github.com/cloudflare/cfssl/api"
  9. "github.com/cloudflare/cfssl/auth"
  10. "github.com/cloudflare/cfssl/helpers"
  11. "github.com/cloudflare/cfssl/log"
  12. "github.com/cloudflare/cfssl/signer"
  13. "github.com/cloudflare/cfssl/whitelist"
  14. "github.com/prometheus/client_golang/prometheus"
  15. "github.com/prometheus/client_golang/prometheus/promauto"
  16. )
  17. // A SignatureResponse contains only a certificate, as there is no other
  18. // useful data for the CA to return at this time.
  19. type SignatureResponse struct {
  20. Certificate string `json:"certificate"`
  21. }
  22. type filter func(string, *signer.SignRequest) bool
  23. var filters = map[string][]filter{}
  24. var (
  25. requests = promauto.NewCounterVec(
  26. prometheus.CounterOpts{
  27. Name: "requests_total",
  28. Help: "How many requests for each operation type and signer were succesfully processed.",
  29. },
  30. []string{"operation", "signer"},
  31. )
  32. erroredRequests = promauto.NewCounterVec(
  33. prometheus.CounterOpts{
  34. Name: "requests_errored_total",
  35. Help: "How many requests for each operation type resulted in an error.",
  36. },
  37. []string{"operation", "signer"},
  38. )
  39. badInputs = promauto.NewCounterVec(
  40. prometheus.CounterOpts{
  41. Name: "bad_inputs_total",
  42. Help: "How many times the input was malformed or not allowed.",
  43. },
  44. []string{"operation"},
  45. )
  46. )
  47. const (
  48. signOperation = "sign"
  49. )
  50. func fail(w http.ResponseWriter, req *http.Request, status, code int, msg, ad string) {
  51. badInputs.WithLabelValues(signOperation).Inc()
  52. if ad != "" {
  53. ad = " (" + ad + ")"
  54. }
  55. log.Errorf("[HTTP %d] %d - %s%s", status, code, msg, ad)
  56. dumpReq, err := httputil.DumpRequest(req, true)
  57. if err != nil {
  58. fmt.Printf("%v#v\n", req)
  59. } else {
  60. fmt.Printf("%s\n", dumpReq)
  61. }
  62. res := api.NewErrorResponse(msg, code)
  63. w.WriteHeader(status)
  64. jenc := json.NewEncoder(w)
  65. jenc.Encode(res)
  66. }
  67. func dispatchRequest(w http.ResponseWriter, req *http.Request) {
  68. if req.Method != "POST" {
  69. fail(w, req, http.StatusMethodNotAllowed, 1, "only POST is permitted", "")
  70. return
  71. }
  72. defer req.Body.Close()
  73. body, err := io.ReadAll(req.Body)
  74. if err != nil {
  75. fail(w, req, http.StatusInternalServerError, 1, err.Error(), "while reading request body")
  76. return
  77. }
  78. var authReq auth.AuthenticatedRequest
  79. err = json.Unmarshal(body, &authReq)
  80. if err != nil {
  81. fail(w, req, http.StatusBadRequest, 1, err.Error(), "while unmarshaling request body")
  82. return
  83. }
  84. var sigRequest signer.SignRequest
  85. err = json.Unmarshal(authReq.Request, &sigRequest)
  86. if err != nil {
  87. fail(w, req, http.StatusBadRequest, 1, err.Error(), "while unmarshalling authenticated request")
  88. return
  89. }
  90. if sigRequest.Label == "" {
  91. sigRequest.Label = defaultLabel
  92. }
  93. acl := whitelists[sigRequest.Label]
  94. if acl != nil {
  95. ip, err := whitelist.HTTPRequestLookup(req)
  96. if err != nil {
  97. fail(w, req, http.StatusInternalServerError, 1, err.Error(), "while getting request IP")
  98. return
  99. }
  100. if !acl.Permitted(ip) {
  101. fail(w, req, http.StatusForbidden, 1, "not authorised", "because IP is not whitelisted")
  102. return
  103. }
  104. }
  105. s, ok := signers[sigRequest.Label]
  106. if !ok {
  107. fail(w, req, http.StatusBadRequest, 1, "bad request", "request is for non-existent label "+sigRequest.Label)
  108. return
  109. }
  110. requests.WithLabelValues(signOperation, sigRequest.Label).Inc()
  111. // Sanity checks to ensure that we have a valid policy. This
  112. // should have been checked in NewAuthSignHandler.
  113. policy := s.Policy()
  114. if policy == nil {
  115. fail(w, req, http.StatusInternalServerError, 1, "invalid policy", "signer was initialised without a signing policy")
  116. return
  117. }
  118. profile := policy.Default
  119. if policy.Profiles != nil && sigRequest.Profile != "" {
  120. profile = policy.Profiles[sigRequest.Profile]
  121. if profile == nil {
  122. fail(w, req, http.StatusBadRequest, 1, "invalid profile", "failed to look up profile with name: "+sigRequest.Profile)
  123. return
  124. }
  125. }
  126. if profile == nil {
  127. fail(w, req, http.StatusInternalServerError, 1, "invalid profile", "signer was initialised without any valid profiles")
  128. return
  129. }
  130. if profile.Provider == nil {
  131. fail(w, req, http.StatusUnauthorized, 1, "authorisation required", "received unauthenticated request")
  132. return
  133. }
  134. validAuth := false
  135. if profile.Provider.Verify(&authReq) {
  136. validAuth = true
  137. } else if profile.PrevProvider != nil && profile.PrevProvider.Verify(&authReq) {
  138. validAuth = true
  139. }
  140. if !validAuth {
  141. fail(w, req, http.StatusBadRequest, 1, "invalid token", "received authenticated request with invalid token")
  142. return
  143. }
  144. if sigRequest.Request == "" {
  145. fail(w, req, http.StatusBadRequest, 1, "invalid request", "empty request")
  146. return
  147. }
  148. cert, err := s.Sign(sigRequest)
  149. if err != nil {
  150. erroredRequests.WithLabelValues(signOperation, sigRequest.Label).Inc()
  151. fail(w, req, http.StatusBadRequest, 1, "bad request", "signature failed: "+err.Error())
  152. return
  153. }
  154. x509Cert, err := helpers.ParseCertificatePEM(cert)
  155. if err != nil {
  156. erroredRequests.WithLabelValues(signOperation, sigRequest.Label).Inc()
  157. fail(w, req, http.StatusInternalServerError, 1, "bad certificate", err.Error())
  158. }
  159. log.Infof("signature: requester=%s, label=%s, profile=%s, serialno=%s",
  160. req.RemoteAddr, sigRequest.Label, sigRequest.Profile, x509Cert.SerialNumber)
  161. res := api.NewSuccessResponse(&SignatureResponse{Certificate: string(cert)})
  162. jenc := json.NewEncoder(w)
  163. err = jenc.Encode(res)
  164. if err != nil {
  165. log.Errorf("error writing response: %v", err)
  166. }
  167. }
  168. func metricsDisallowed(w http.ResponseWriter, req *http.Request) {
  169. log.Warning("attempt to access metrics endpoint from external address ", req.RemoteAddr)
  170. http.NotFound(w, req)
  171. }