mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-30 05:25:35 +00:00
e7bf6e716b
Some users run "tailscale cert" in a cron job to renew their certificates on disk. The time until the next cron job run may be long enough for the old cert to expire with our default heristics. Add a `--min-validity` flag which ensures that the returned cert is valid for at least the provided duration (unless it's longer than the cert lifetime set by Let's Encrypt). Updates #8725 Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
63 lines
1.7 KiB
Go
63 lines
1.7 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"
|
|
"time"
|
|
|
|
"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
|
|
}
|
|
var minValidity time.Duration
|
|
if minValidityStr := r.URL.Query().Get("min_validity"); minValidityStr != "" {
|
|
var err error
|
|
minValidity, err = time.ParseDuration(minValidityStr)
|
|
if err != nil {
|
|
http.Error(w, fmt.Sprintf("invalid validity parameter: %v", err), http.StatusBadRequest)
|
|
return
|
|
}
|
|
}
|
|
pair, err := h.b.GetCertPEMWithValidity(r.Context(), domain, minValidity)
|
|
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)
|
|
}
|
|
}
|