cert.go 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build !ios && !android && !js
  4. package localapi
  5. import (
  6. "fmt"
  7. "net/http"
  8. "strings"
  9. "tailscale.com/ipn/ipnlocal"
  10. )
  11. func (h *Handler) serveCert(w http.ResponseWriter, r *http.Request) {
  12. if !h.PermitWrite && !h.PermitCert {
  13. http.Error(w, "cert access denied", http.StatusForbidden)
  14. return
  15. }
  16. domain, ok := strings.CutPrefix(r.URL.Path, "/localapi/v0/cert/")
  17. if !ok {
  18. http.Error(w, "internal handler config wired wrong", 500)
  19. return
  20. }
  21. pair, err := h.b.GetCertPEM(r.Context(), domain)
  22. if err != nil {
  23. // TODO(bradfitz): 500 is a little lazy here. The errors returned from
  24. // GetCertPEM (and everywhere) should carry info info to get whether
  25. // they're 400 vs 403 vs 500 at minimum. And then we should have helpers
  26. // (in tsweb probably) to return an error that looks at the error value
  27. // to determine the HTTP status code.
  28. http.Error(w, fmt.Sprint(err), 500)
  29. return
  30. }
  31. serveKeyPair(w, r, pair)
  32. }
  33. func serveKeyPair(w http.ResponseWriter, r *http.Request, p *ipnlocal.TLSCertKeyPair) {
  34. w.Header().Set("Content-Type", "text/plain")
  35. switch r.URL.Query().Get("type") {
  36. case "", "crt", "cert":
  37. w.Write(p.CertPEM)
  38. case "key":
  39. w.Write(p.KeyPEM)
  40. case "pair":
  41. w.Write(p.KeyPEM)
  42. w.Write(p.CertPEM)
  43. default:
  44. http.Error(w, `invalid type; want "cert" (default), "key", or "pair"`, 400)
  45. }
  46. }