mirror of
https://github.com/caddyserver/caddy.git
synced 2024-11-29 12:16:16 +08:00
letsencrypt: Support for http-01, awkwardly straddling that and SimpleHTTP for now
This commit is contained in:
parent
659df6967e
commit
580b50ea20
|
@ -2,62 +2,73 @@ package letsencrypt
|
|||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"sync"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/mholt/caddy/middleware"
|
||||
)
|
||||
|
||||
// Handler is a Caddy middleware that can proxy ACME requests
|
||||
// to the real ACME endpoint. This is necessary to renew certificates
|
||||
// while the server is running. Obviously, a site served on port
|
||||
// 443 (HTTPS) binds to that port, so another listener created by
|
||||
// our acme client can't bind successfully and solve the challenge.
|
||||
// Thus, we chain this handler in so that it can, when activated,
|
||||
// proxy ACME requests to an ACME client listening on an alternate
|
||||
// port.
|
||||
const challengeBasePath = "/.well-known/acme-challenge"
|
||||
|
||||
// Handler is a Caddy middleware that can proxy ACME challenge
|
||||
// requests to the real ACME client endpoint. This is necessary
|
||||
// to renew certificates while the server is running.
|
||||
type Handler struct {
|
||||
sync.Mutex // protects the ChallengePath property
|
||||
Next middleware.Handler
|
||||
ChallengeActive int32 // use sync/atomic for speed to set/get this flag
|
||||
ChallengePath string // the exact request path to match before proxying
|
||||
ChallengeActive int32 // TODO: use sync/atomic to set/get this flag safely and efficiently
|
||||
}
|
||||
|
||||
// ServeHTTP is basically a no-op unless an ACME challenge is active on this host
|
||||
// and the request path matches the expected path exactly.
|
||||
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// Only if challenge is active
|
||||
if atomic.LoadInt32(&h.ChallengeActive) == 1 {
|
||||
h.Lock()
|
||||
path := h.ChallengePath
|
||||
h.Unlock()
|
||||
// TODO: this won't work until the global challenge hook in the acme package is ready
|
||||
//if atomic.LoadInt32(&h.ChallengeActive) == 1 {
|
||||
|
||||
// Request path must be correct; if so, proxy to ACME client
|
||||
if r.URL.Path == path {
|
||||
upstream, err := url.Parse("https://" + r.Host + ":" + alternatePort)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
proxy := httputil.NewSingleHostReverseProxy(upstream)
|
||||
proxy.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // client uses self-signed cert
|
||||
}
|
||||
proxy.ServeHTTP(w, r)
|
||||
return 0, nil
|
||||
// Proxy challenge requests to ACME client
|
||||
if strings.HasPrefix(r.URL.Path, challengeBasePath) {
|
||||
scheme := "http"
|
||||
if r.TLS != nil {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
hostname, _, err := net.SplitHostPort(r.URL.Host)
|
||||
if err != nil {
|
||||
hostname = r.URL.Host
|
||||
}
|
||||
|
||||
upstream, err := url.Parse(scheme + "://" + hostname + ":" + alternatePort)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
proxy := httputil.NewSingleHostReverseProxy(upstream)
|
||||
proxy.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // client uses self-signed cert
|
||||
}
|
||||
proxy.ServeHTTP(w, r)
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
//}
|
||||
|
||||
return h.Next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// TODO: SimpleHTTP deprecation imminent!! meaning these
|
||||
// challenge handlers will go away and be replaced with
|
||||
// something else.
|
||||
|
||||
// ChallengeOn enables h to proxy ACME requests.
|
||||
func (h *Handler) ChallengeOn(challengePath string) {
|
||||
h.Lock()
|
||||
h.ChallengePath = challengePath
|
||||
h.Unlock()
|
||||
// h.Lock()
|
||||
// h.ChallengePath = challengePath
|
||||
// h.Unlock()
|
||||
atomic.StoreInt32(&h.ChallengeActive, 1)
|
||||
}
|
||||
|
||||
|
|
|
@ -344,24 +344,34 @@ func autoConfigure(allConfigs []server.Config, cfgIndex int) []server.Config {
|
|||
cfg.Port = "https"
|
||||
}
|
||||
|
||||
// Chain in ACME middleware proxy if we use up the SSL port
|
||||
if cfg.Port == "https" || cfg.Port == "443" {
|
||||
handler := new(Handler)
|
||||
mid := func(next middleware.Handler) middleware.Handler {
|
||||
handler.Next = next
|
||||
return handler
|
||||
}
|
||||
cfg.Middleware["/"] = append(cfg.Middleware["/"], mid)
|
||||
acmeHandlers[cfg.Host] = handler
|
||||
}
|
||||
|
||||
// Set up http->https redirect as long as there isn't already a http counterpart
|
||||
// in the configs and this isn't, for some reason, already on port 80
|
||||
// in the configs and this isn't, for some reason, already on port 80.
|
||||
// Also, the port 80 variant of this config is necessary for proxying challenge requests.
|
||||
if !otherHostHasScheme(allConfigs, cfgIndex, "http") &&
|
||||
cfg.Port != "80" && cfg.Port != "http" { // (would not be http port with current program flow, but just in case)
|
||||
allConfigs = append(allConfigs, redirPlaintextHost(*cfg))
|
||||
}
|
||||
|
||||
// To support renewals, we need handlers at ports 80 and 443,
|
||||
// depending on the challenge type that is used to complete renewal.
|
||||
// Every proxy for this host can share the handler.
|
||||
handler := new(Handler)
|
||||
mid := func(next middleware.Handler) middleware.Handler {
|
||||
handler.Next = next
|
||||
return handler
|
||||
}
|
||||
acmeHandlers[cfg.Host] = handler
|
||||
|
||||
// Handler needs to go in 80 and 443
|
||||
for i, c := range allConfigs {
|
||||
if c.Address() == cfg.Host+":80" ||
|
||||
c.Address() == cfg.Host+":443" ||
|
||||
c.Address() == cfg.Host+":http" ||
|
||||
c.Address() == cfg.Host+":https" {
|
||||
allConfigs[i].Middleware["/"] = append(allConfigs[i].Middleware["/"], mid)
|
||||
}
|
||||
}
|
||||
|
||||
return allConfigs
|
||||
}
|
||||
|
||||
|
|
|
@ -144,9 +144,7 @@ func renewCertificates(configs []server.Config, useCustomPort bool) (int, []erro
|
|||
acme.OnSimpleHTTPStart = acmeHandlers[cfg.Host].ChallengeOn
|
||||
acme.OnSimpleHTTPEnd = acmeHandlers[cfg.Host].ChallengeOff
|
||||
|
||||
// Renew certificate.
|
||||
// TODO: revokeOld should be an option in the caddyfile
|
||||
// TODO: bundle should be an option in the caddyfile as well :)
|
||||
// Renew certificate
|
||||
Renew:
|
||||
newCertMeta, err := client.RenewCertificate(certMeta, true, true)
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in New Issue
Block a user