mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
c1ecae13ab
While our `shouldStartDomainRenewal` check is correct, `getCertPEM` would always bail if the existing cert is not expired. Add the same `shouldStartDomainRenewal` check to `getCertPEM` to make it proceed with renewal when existing certs are still valid but should be renewed. The extra check is expensive (ARI request towards LetsEncrypt), so cache the last check result for 1hr to not degrade `tailscale serve` performance. Also, asynchronous renewal is great for `tailscale serve` but confusing for `tailscale cert`. Add an explicit flag to `GetCertPEM` to force a synchronous renewal for `tailscale cert`. Fixes #8725 Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
53 lines
1.4 KiB
Go
53 lines
1.4 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
//go:build !ios && !android && !js
|
|
|
|
package localapi
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"tailscale.com/ipn/ipnlocal"
|
|
)
|
|
|
|
func (h *Handler) serveCert(w http.ResponseWriter, r *http.Request) {
|
|
if !h.PermitWrite && !h.PermitCert {
|
|
http.Error(w, "cert access denied", http.StatusForbidden)
|
|
return
|
|
}
|
|
domain, ok := strings.CutPrefix(r.URL.Path, "/localapi/v0/cert/")
|
|
if !ok {
|
|
http.Error(w, "internal handler config wired wrong", 500)
|
|
return
|
|
}
|
|
pair, err := h.b.GetCertPEM(r.Context(), domain, true)
|
|
if err != nil {
|
|
// TODO(bradfitz): 500 is a little lazy here. The errors returned from
|
|
// GetCertPEM (and everywhere) should carry info info to get whether
|
|
// they're 400 vs 403 vs 500 at minimum. And then we should have helpers
|
|
// (in tsweb probably) to return an error that looks at the error value
|
|
// to determine the HTTP status code.
|
|
http.Error(w, fmt.Sprint(err), 500)
|
|
return
|
|
}
|
|
serveKeyPair(w, r, pair)
|
|
}
|
|
|
|
func serveKeyPair(w http.ResponseWriter, r *http.Request, p *ipnlocal.TLSCertKeyPair) {
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
switch r.URL.Query().Get("type") {
|
|
case "", "crt", "cert":
|
|
w.Write(p.CertPEM)
|
|
case "key":
|
|
w.Write(p.KeyPEM)
|
|
case "pair":
|
|
w.Write(p.KeyPEM)
|
|
w.Write(p.CertPEM)
|
|
default:
|
|
http.Error(w, `invalid type; want "cert" (default), "key", or "pair"`, 400)
|
|
}
|
|
}
|