fix application bug
This commit is contained in:
13
vendor/github.com/mholt/caddy/caddy.go
generated
vendored
13
vendor/github.com/mholt/caddy/caddy.go
generated
vendored
@@ -193,9 +193,8 @@ func (i *Instance) Restart(newCaddyfile Input) (*Instance, error) {
|
||||
r := recover()
|
||||
if err != nil || r != nil {
|
||||
for _, fn := range i.OnRestartFailed {
|
||||
err2 := fn()
|
||||
if err2 != nil {
|
||||
log.Printf("[ERROR] Restart failed callback returned error: %v", err2)
|
||||
if err := fn(); err != nil {
|
||||
log.Printf("[ERROR] Restart failed callback returned error: %v", err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
@@ -781,6 +780,10 @@ func startServers(serverList []Server, inst *Instance, restartFds map[string]res
|
||||
}
|
||||
}
|
||||
|
||||
inst.servers = append(inst.servers, ServerListener{server: s, listener: ln, packet: pc})
|
||||
}
|
||||
|
||||
for _, s := range inst.servers {
|
||||
inst.wg.Add(2)
|
||||
stopWg.Add(2)
|
||||
func(s Server, ln net.Listener, pc net.PacketConn, inst *Instance) {
|
||||
@@ -799,9 +802,7 @@ func startServers(serverList []Server, inst *Instance, restartFds map[string]res
|
||||
}()
|
||||
errChan <- s.ServePacket(pc)
|
||||
}()
|
||||
}(s, ln, pc, inst)
|
||||
|
||||
inst.servers = append(inst.servers, ServerListener{server: s, listener: ln, packet: pc})
|
||||
}(s.server, s.listener, s.packet, inst)
|
||||
}
|
||||
|
||||
// Log errors that may be returned from Serve() calls,
|
||||
|
||||
88
vendor/github.com/mholt/caddy/caddy/caddymain/run.go
generated
vendored
88
vendor/github.com/mholt/caddy/caddy/caddymain/run.go
generated
vendored
@@ -25,6 +25,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -43,20 +44,20 @@ import (
|
||||
|
||||
func init() {
|
||||
caddy.TrapSignals()
|
||||
setVersion()
|
||||
|
||||
flag.BoolVar(&certmagic.Agreed, "agree", false, "Agree to the CA's Subscriber Agreement")
|
||||
flag.StringVar(&certmagic.CA, "ca", certmagic.CA, "URL to certificate authority's ACME server directory")
|
||||
flag.StringVar(&certmagic.DefaultServerName, "default-sni", certmagic.DefaultServerName, "If a ClientHello ServerName is empty, use this ServerName to choose a TLS certificate")
|
||||
flag.BoolVar(&certmagic.DisableHTTPChallenge, "disable-http-challenge", certmagic.DisableHTTPChallenge, "Disable the ACME HTTP challenge")
|
||||
flag.BoolVar(&certmagic.DisableTLSALPNChallenge, "disable-tls-alpn-challenge", certmagic.DisableTLSALPNChallenge, "Disable the ACME TLS-ALPN challenge")
|
||||
flag.BoolVar(&certmagic.Default.Agreed, "agree", false, "Agree to the CA's Subscriber Agreement")
|
||||
flag.StringVar(&certmagic.Default.CA, "ca", certmagic.Default.CA, "URL to certificate authority's ACME server directory")
|
||||
flag.StringVar(&certmagic.Default.DefaultServerName, "default-sni", certmagic.Default.DefaultServerName, "If a ClientHello ServerName is empty, use this ServerName to choose a TLS certificate")
|
||||
flag.BoolVar(&certmagic.Default.DisableHTTPChallenge, "disable-http-challenge", certmagic.Default.DisableHTTPChallenge, "Disable the ACME HTTP challenge")
|
||||
flag.BoolVar(&certmagic.Default.DisableTLSALPNChallenge, "disable-tls-alpn-challenge", certmagic.Default.DisableTLSALPNChallenge, "Disable the ACME TLS-ALPN challenge")
|
||||
flag.StringVar(&disabledMetrics, "disabled-metrics", "", "Comma-separated list of telemetry metrics to disable")
|
||||
flag.StringVar(&conf, "conf", "", "Caddyfile to load (default \""+caddy.DefaultConfigFile+"\")")
|
||||
flag.StringVar(&cpu, "cpu", "100%", "CPU cap")
|
||||
flag.StringVar(&envFile, "env", "", "Path to file with environment variables to load in KEY=VALUE format")
|
||||
flag.BoolVar(&printEnv, "env", false, "Enable to print environment variables")
|
||||
flag.StringVar(&envFile, "envfile", "", "Path to file with environment variables to load in KEY=VALUE format")
|
||||
flag.BoolVar(&fromJSON, "json-to-caddyfile", false, "From JSON stdin to Caddyfile stdout")
|
||||
flag.BoolVar(&plugins, "plugins", false, "List installed plugins")
|
||||
flag.StringVar(&certmagic.Email, "email", "", "Default ACME CA account email address")
|
||||
flag.StringVar(&certmagic.Default.Email, "email", "", "Default ACME CA account email address")
|
||||
flag.DurationVar(&certmagic.HTTPTimeout, "catimeout", certmagic.HTTPTimeout, "Default ACME CA HTTP timeout")
|
||||
flag.StringVar(&logfile, "log", "", "Process log file")
|
||||
flag.IntVar(&logRollMB, "log-roll-mb", 100, "Roll process log when it reaches this many megabytes (0 to disable rolling)")
|
||||
@@ -77,9 +78,12 @@ func init() {
|
||||
func Run() {
|
||||
flag.Parse()
|
||||
|
||||
module := getBuildModule()
|
||||
cleanModVersion := strings.TrimPrefix(module.Version, "v")
|
||||
|
||||
caddy.AppName = appName
|
||||
caddy.AppVersion = appVersion
|
||||
certmagic.UserAgent = appName + "/" + appVersion
|
||||
caddy.AppVersion = module.Version
|
||||
certmagic.UserAgent = appName + "/" + cleanModVersion
|
||||
|
||||
// Set up process log before anything bad happens
|
||||
switch logfile {
|
||||
@@ -117,6 +121,12 @@ func Run() {
|
||||
mustLogFatalf("%v", err)
|
||||
}
|
||||
|
||||
if printEnv {
|
||||
for _, v := range os.Environ() {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
// initialize telemetry client
|
||||
if EnableTelemetry {
|
||||
err := initTelemetry()
|
||||
@@ -137,9 +147,11 @@ func Run() {
|
||||
os.Exit(0)
|
||||
}
|
||||
if version {
|
||||
fmt.Printf("%s %s (unofficial)\n", appName, appVersion)
|
||||
if devBuild && gitShortStat != "" {
|
||||
fmt.Printf("%s\n%s\n", gitShortStat, gitFilesModified)
|
||||
if module.Sum != "" {
|
||||
// a build with a known version will also have a checksum
|
||||
fmt.Printf("Caddy %s (%s)\n", module.Version, module.Sum)
|
||||
} else {
|
||||
fmt.Println(module.Version)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
@@ -184,7 +196,7 @@ func Run() {
|
||||
}
|
||||
|
||||
// Begin telemetry (these are no-ops if telemetry disabled)
|
||||
telemetry.Set("caddy_version", appVersion)
|
||||
telemetry.Set("caddy_version", module.Version)
|
||||
telemetry.Set("num_listeners", len(instance.Servers()))
|
||||
telemetry.Set("server_type", serverType)
|
||||
telemetry.Set("os", runtime.GOOS)
|
||||
@@ -265,25 +277,25 @@ func defaultLoader(serverType string) (caddy.Input, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// setVersion figures out the version information
|
||||
// based on variables set by -ldflags.
|
||||
func setVersion() {
|
||||
// A development build is one that's not at a tag or has uncommitted changes
|
||||
devBuild = gitTag == "" || gitShortStat != ""
|
||||
|
||||
if buildDate != "" {
|
||||
buildDate = " " + buildDate
|
||||
}
|
||||
|
||||
// Only set the appVersion if -ldflags was used
|
||||
if gitNearestTag != "" || gitTag != "" {
|
||||
if devBuild && gitNearestTag != "" {
|
||||
appVersion = fmt.Sprintf("%s (+%s%s)",
|
||||
strings.TrimPrefix(gitNearestTag, "v"), gitCommit, buildDate)
|
||||
} else if gitTag != "" {
|
||||
appVersion = strings.TrimPrefix(gitTag, "v")
|
||||
// getBuildModule returns the build info of Caddy
|
||||
// from debug.BuildInfo (requires Go modules). If
|
||||
// no version information is available, a non-nil
|
||||
// value will still be returned, but with an
|
||||
// unknown version.
|
||||
func getBuildModule() *debug.Module {
|
||||
bi, ok := debug.ReadBuildInfo()
|
||||
if ok {
|
||||
// The recommended way to build Caddy involves
|
||||
// creating a separate main module, which
|
||||
// preserves caddy a read-only dependency
|
||||
// TODO: track related Go issue: https://github.com/golang/go/issues/29228
|
||||
for _, mod := range bi.Deps {
|
||||
if mod.Path == "github.com/mholt/caddy" {
|
||||
return mod
|
||||
}
|
||||
}
|
||||
}
|
||||
return &debug.Module{Version: "unknown"}
|
||||
}
|
||||
|
||||
func checkJSONCaddyfile() {
|
||||
@@ -580,22 +592,10 @@ var (
|
||||
toJSON bool
|
||||
version bool
|
||||
plugins bool
|
||||
printEnv bool
|
||||
validate bool
|
||||
disabledMetrics string
|
||||
)
|
||||
|
||||
// Build information obtained with the help of -ldflags
|
||||
var (
|
||||
appVersion = "(untracked dev build)" // inferred at startup
|
||||
devBuild = true // inferred at startup
|
||||
|
||||
buildDate string // date -u
|
||||
gitTag string // git describe --exact-match HEAD 2> /dev/null
|
||||
gitNearestTag string // git describe --abbrev=0 --tags HEAD
|
||||
gitCommit string // git rev-parse HEAD
|
||||
gitShortStat string // git diff-index --shortstat
|
||||
gitFilesModified string // git diff-index --name-only HEAD
|
||||
)
|
||||
|
||||
// EnableTelemetry defines whether telemetry is enabled in Run.
|
||||
var EnableTelemetry = true
|
||||
|
||||
9
vendor/github.com/mholt/caddy/caddyhttp/basicauth/basicauth.go
generated
vendored
9
vendor/github.com/mholt/caddy/caddyhttp/basicauth/basicauth.go
generated
vendored
@@ -26,6 +26,7 @@ import (
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -193,11 +194,15 @@ func PlainMatcher(passw string) PasswordMatcher {
|
||||
// compare hashes of equal length instead of actual password
|
||||
// to avoid leaking password length
|
||||
passwHash := sha1.New()
|
||||
passwHash.Write([]byte(passw))
|
||||
if _, err := passwHash.Write([]byte(passw)); err != nil {
|
||||
log.Printf("[ERROR] unable to write password hash: %v", err)
|
||||
}
|
||||
passwSum := passwHash.Sum(nil)
|
||||
return func(pw string) bool {
|
||||
pwHash := sha1.New()
|
||||
pwHash.Write([]byte(pw))
|
||||
if _, err := pwHash.Write([]byte(pw)); err != nil {
|
||||
log.Printf("[ERROR] unable to write password hash: %v", err)
|
||||
}
|
||||
pwSum := pwHash.Sum(nil)
|
||||
return subtle.ConstantTimeCompare([]byte(pwSum), []byte(passwSum)) == 1
|
||||
}
|
||||
|
||||
32
vendor/github.com/mholt/caddy/caddyhttp/browse/browse.go
generated
vendored
32
vendor/github.com/mholt/caddy/caddyhttp/browse/browse.go
generated
vendored
@@ -59,34 +59,34 @@ type Config struct {
|
||||
|
||||
// A Listing is the context used to fill out a template.
|
||||
type Listing struct {
|
||||
// The name of the directory (the last element of the path)
|
||||
// The name of the directory (the last element of the path).
|
||||
Name string
|
||||
|
||||
// The full path of the request
|
||||
// The full path of the request.
|
||||
Path string
|
||||
|
||||
// Whether the parent directory is browsable
|
||||
// Whether the parent directory is browse-able.
|
||||
CanGoUp bool
|
||||
|
||||
// The items (files and folders) in the path
|
||||
// The items (files and folders) in the path.
|
||||
Items []FileInfo
|
||||
|
||||
// The number of directories in the listing
|
||||
// The number of directories in the listing.
|
||||
NumDirs int
|
||||
|
||||
// The number of files (items that aren't directories) in the listing
|
||||
// The number of files (items that aren't directories) in the listing.
|
||||
NumFiles int
|
||||
|
||||
// Which sorting order is used
|
||||
// Which sorting order is used.
|
||||
Sort string
|
||||
|
||||
// And which order
|
||||
// And which order.
|
||||
Order string
|
||||
|
||||
// If ≠0 then Items have been limited to that many elements
|
||||
// If ≠0 then Items have been limited to that many elements.
|
||||
ItemsLimitedTo int
|
||||
|
||||
// Optional custom variables for use in browse templates
|
||||
// Optional custom variables for use in browse templates.
|
||||
User interface{}
|
||||
|
||||
httpserver.Context
|
||||
@@ -244,7 +244,7 @@ func (l Listing) applySort() {
|
||||
|
||||
func directoryListing(files []os.FileInfo, canGoUp bool, urlPath string, config *Config) (Listing, bool) {
|
||||
var (
|
||||
fileinfos []FileInfo
|
||||
fileInfos []FileInfo
|
||||
dirCount, fileCount int
|
||||
hasIndexFile bool
|
||||
)
|
||||
@@ -272,14 +272,14 @@ func directoryListing(files []os.FileInfo, canGoUp bool, urlPath string, config
|
||||
continue
|
||||
}
|
||||
|
||||
url := url.URL{Path: "./" + name} // prepend with "./" to fix paths with ':' in the name
|
||||
u := url.URL{Path: "./" + name} // prepend with "./" to fix paths with ':' in the name
|
||||
|
||||
fileinfos = append(fileinfos, FileInfo{
|
||||
fileInfos = append(fileInfos, FileInfo{
|
||||
IsDir: isDir,
|
||||
IsSymlink: isSymlink(f),
|
||||
Name: f.Name(),
|
||||
Size: f.Size(),
|
||||
URL: url.String(),
|
||||
URL: u.String(),
|
||||
ModTime: f.ModTime().UTC(),
|
||||
Mode: f.Mode(),
|
||||
})
|
||||
@@ -289,7 +289,7 @@ func directoryListing(files []os.FileInfo, canGoUp bool, urlPath string, config
|
||||
Name: path.Base(urlPath),
|
||||
Path: urlPath,
|
||||
CanGoUp: canGoUp,
|
||||
Items: fileinfos,
|
||||
Items: fileInfos,
|
||||
NumDirs: dirCount,
|
||||
NumFiles: fileCount,
|
||||
}, hasIndexFile
|
||||
@@ -504,7 +504,7 @@ func (b Browse) ServeListing(w http.ResponseWriter, r *http.Request, requestedFi
|
||||
|
||||
}
|
||||
|
||||
buf.WriteTo(w)
|
||||
_, _ = buf.WriteTo(w)
|
||||
|
||||
return http.StatusOK, nil
|
||||
}
|
||||
|
||||
18
vendor/github.com/mholt/caddy/caddyhttp/fastcgi/fcgiclient.go
generated
vendored
18
vendor/github.com/mholt/caddy/caddyhttp/fastcgi/fcgiclient.go
generated
vendored
@@ -175,15 +175,13 @@ func (rec *record) read(r io.Reader) (buf []byte, err error) {
|
||||
// FCGIClient implements a FastCGI client, which is a standard for
|
||||
// interfacing external applications with Web servers.
|
||||
type FCGIClient struct {
|
||||
mutex sync.Mutex
|
||||
rwc io.ReadWriteCloser
|
||||
h header
|
||||
buf bytes.Buffer
|
||||
stderr bytes.Buffer
|
||||
keepAlive bool
|
||||
reqID uint16
|
||||
readTimeout time.Duration
|
||||
sendTimeout time.Duration
|
||||
mutex sync.Mutex
|
||||
rwc io.ReadWriteCloser
|
||||
h header
|
||||
buf bytes.Buffer
|
||||
stderr bytes.Buffer
|
||||
keepAlive bool
|
||||
reqID uint16
|
||||
}
|
||||
|
||||
// DialWithDialerContext connects to the fcgi responder at the specified network address, using custom net.Dialer
|
||||
@@ -397,7 +395,7 @@ func (c *FCGIClient) Do(p map[string]string, req io.Reader) (r io.Reader, err er
|
||||
|
||||
body := newWriter(c, Stdin)
|
||||
if req != nil {
|
||||
io.Copy(body, req)
|
||||
_, _ = io.Copy(body, req)
|
||||
}
|
||||
body.Close()
|
||||
|
||||
|
||||
3
vendor/github.com/mholt/caddy/caddyhttp/httpserver/plugin.go
generated
vendored
3
vendor/github.com/mholt/caddy/caddyhttp/httpserver/plugin.go
generated
vendored
@@ -234,7 +234,7 @@ func (h *httpContext) MakeServers() ([]caddy.Server, error) {
|
||||
// trusted CA (obviously not a perfect heuristic)
|
||||
var looksLikeProductionCA bool
|
||||
for _, publicCAEndpoint := range caddytls.KnownACMECAs {
|
||||
if strings.Contains(certmagic.CA, publicCAEndpoint) {
|
||||
if strings.Contains(certmagic.Default.CA, publicCAEndpoint) {
|
||||
looksLikeProductionCA = true
|
||||
break
|
||||
}
|
||||
@@ -685,6 +685,7 @@ var directives = []string{
|
||||
"gopkg", // github.com/zikes/gopkg
|
||||
"restic", // github.com/restic/caddy
|
||||
"wkd", // github.com/emersion/caddy-wkd
|
||||
"dyndns", // github.com/linkonoid/caddy-dyndns
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
42
vendor/github.com/mholt/caddy/caddyhttp/httpserver/server.go
generated
vendored
42
vendor/github.com/mholt/caddy/caddyhttp/httpserver/server.go
generated
vendored
@@ -29,7 +29,6 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/h2quic"
|
||||
@@ -43,8 +42,6 @@ import (
|
||||
type Server struct {
|
||||
Server *http.Server
|
||||
quicServer *h2quic.Server
|
||||
listener net.Listener
|
||||
listenerMu sync.Mutex
|
||||
sites []*SiteConfig
|
||||
connTimeout time.Duration // max time to wait for a connection before force stop
|
||||
tlsGovChan chan struct{} // close to stop the TLS maintenance goroutine
|
||||
@@ -237,7 +234,9 @@ func makeHTTPServerWithTimeouts(addr string, group []*SiteConfig) *http.Server {
|
||||
|
||||
func (s *Server) wrapWithSvcHeaders(previousHandler http.Handler) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
s.quicServer.SetQuicHeaders(w.Header())
|
||||
if err := s.quicServer.SetQuicHeaders(w.Header()); err != nil {
|
||||
log.Println("[Error] failed to set proper headers for QUIC: ", err)
|
||||
}
|
||||
previousHandler.ServeHTTP(w, r)
|
||||
}
|
||||
}
|
||||
@@ -246,7 +245,7 @@ func (s *Server) wrapWithSvcHeaders(previousHandler http.Handler) http.HandlerFu
|
||||
// used to serve requests.
|
||||
func (s *Server) Listen() (net.Listener, error) {
|
||||
if s.Server == nil {
|
||||
return nil, fmt.Errorf("Server field is nil")
|
||||
return nil, fmt.Errorf("server field is nil")
|
||||
}
|
||||
|
||||
ln, err := net.Listen("tcp", s.Server.Addr)
|
||||
@@ -310,10 +309,6 @@ func (s *Server) ListenPacket() (net.PacketConn, error) {
|
||||
|
||||
// Serve serves requests on ln. It blocks until ln is closed.
|
||||
func (s *Server) Serve(ln net.Listener) error {
|
||||
s.listenerMu.Lock()
|
||||
s.listener = ln
|
||||
s.listenerMu.Unlock()
|
||||
|
||||
if s.Server.TLSConfig != nil {
|
||||
// Create TLS listener - note that we do not replace s.listener
|
||||
// with this TLS listener; tls.listener is unexported and does
|
||||
@@ -329,14 +324,19 @@ func (s *Server) Serve(ln net.Listener) error {
|
||||
s.tlsGovChan = caddytls.RotateSessionTicketKeys(s.Server.TLSConfig)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if s.quicServer != nil {
|
||||
if err := s.quicServer.Close(); err != nil {
|
||||
log.Println("[ERROR] failed to close QUIC server: ", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
err := s.Server.Serve(ln)
|
||||
if err == http.ErrServerClosed {
|
||||
err = nil // not an error worth reporting since closing a server is intentional
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
return err
|
||||
}
|
||||
if s.quicServer != nil {
|
||||
s.quicServer.Close()
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServePacket serves QUIC requests on pc until it is closed.
|
||||
@@ -559,8 +559,12 @@ func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tc.SetKeepAlive(true)
|
||||
tc.SetKeepAlivePeriod(3 * time.Minute)
|
||||
if err = tc.SetKeepAlive(true); err != nil {
|
||||
return
|
||||
}
|
||||
if err = tc.SetKeepAlivePeriod(3 * time.Minute); err != nil {
|
||||
return
|
||||
}
|
||||
return tc, nil
|
||||
}
|
||||
|
||||
@@ -598,7 +602,9 @@ func WriteTextResponse(w http.ResponseWriter, status int, body string) {
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
w.WriteHeader(status)
|
||||
w.Write([]byte(body))
|
||||
if _, err := w.Write([]byte(body)); err != nil {
|
||||
log.Println("[Error] failed to write body: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
// SafePath joins siteRoot and reqPath and converts it to a path that can
|
||||
|
||||
5
vendor/github.com/mholt/caddy/caddyhttp/httpserver/tplcontext.go
generated
vendored
5
vendor/github.com/mholt/caddy/caddyhttp/httpserver/tplcontext.go
generated
vendored
@@ -19,6 +19,7 @@ import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
mathrand "math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -420,7 +421,9 @@ func (c Context) RandomString(minLen, maxLen int) string {
|
||||
// secureRandomBytes returns a number of bytes using crypto/rand.
|
||||
secureRandomBytes := func(numBytes int) []byte {
|
||||
randomBytes := make([]byte, numBytes)
|
||||
rand.Read(randomBytes)
|
||||
if _, err := rand.Read(randomBytes); err != nil {
|
||||
log.Println("[ERROR] failed to read bytes: ", err)
|
||||
}
|
||||
return randomBytes
|
||||
}
|
||||
|
||||
|
||||
5
vendor/github.com/mholt/caddy/caddyhttp/markdown/markdown.go
generated
vendored
5
vendor/github.com/mholt/caddy/caddyhttp/markdown/markdown.go
generated
vendored
@@ -17,6 +17,7 @@
|
||||
package markdown
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
@@ -168,7 +169,9 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(html)))
|
||||
httpserver.SetLastModifiedHeader(w, lastModTime)
|
||||
if r.Method == http.MethodGet {
|
||||
w.Write(html)
|
||||
if _, err := w.Write(html); err != nil {
|
||||
log.Println("[ERROR] failed to write html response: ", err)
|
||||
}
|
||||
}
|
||||
return http.StatusOK, nil
|
||||
}
|
||||
|
||||
5
vendor/github.com/mholt/caddy/caddyhttp/proxy/policy.go
generated
vendored
5
vendor/github.com/mholt/caddy/caddyhttp/proxy/policy.go
generated
vendored
@@ -16,6 +16,7 @@ package proxy
|
||||
|
||||
import (
|
||||
"hash/fnv"
|
||||
"log"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
@@ -139,7 +140,9 @@ func hostByHashing(pool HostPool, s string) *UpstreamHost {
|
||||
// hash calculates a hash based on string s
|
||||
func hash(s string) uint32 {
|
||||
h := fnv.New32a()
|
||||
h.Write([]byte(s))
|
||||
if _, err := h.Write([]byte(s)); err != nil {
|
||||
log.Println("[ERROR] failed to write bytes: ", err)
|
||||
}
|
||||
return h.Sum32()
|
||||
}
|
||||
|
||||
|
||||
29
vendor/github.com/mholt/caddy/caddyhttp/proxy/proxy.go
generated
vendored
29
vendor/github.com/mholt/caddy/caddyhttp/proxy/proxy.go
generated
vendored
@@ -92,8 +92,10 @@ type UpstreamHost struct {
|
||||
// This is an int32 so that we can use atomic operations to do concurrent
|
||||
// reads & writes to this value. The default value of 0 indicates that it
|
||||
// is healthy and any non-zero value indicates unhealthy.
|
||||
Unhealthy int32
|
||||
HealthCheckResult atomic.Value
|
||||
Unhealthy int32
|
||||
HealthCheckResult atomic.Value
|
||||
UpstreamHeaderReplacements headerReplacements
|
||||
DownstreamHeaderReplacements headerReplacements
|
||||
}
|
||||
|
||||
// Down checks whether the upstream host is down or not.
|
||||
@@ -220,7 +222,7 @@ func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// set headers for request going upstream
|
||||
if host.UpstreamHeaders != nil {
|
||||
// modify headers for request that will be sent to the upstream host
|
||||
mutateHeadersByRules(outreq.Header, host.UpstreamHeaders, replacer)
|
||||
mutateHeadersByRules(outreq.Header, host.UpstreamHeaders, replacer, host.UpstreamHeaderReplacements)
|
||||
if hostHeaders, ok := outreq.Header["Host"]; ok && len(hostHeaders) > 0 {
|
||||
outreq.Host = hostHeaders[len(hostHeaders)-1]
|
||||
}
|
||||
@@ -230,7 +232,7 @@ func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// headers coming back downstream
|
||||
var downHeaderUpdateFn respUpdateFn
|
||||
if host.DownstreamHeaders != nil {
|
||||
downHeaderUpdateFn = createRespHeaderUpdateFn(host.DownstreamHeaders, replacer)
|
||||
downHeaderUpdateFn = createRespHeaderUpdateFn(host.DownstreamHeaders, replacer, host.DownstreamHeaderReplacements)
|
||||
}
|
||||
|
||||
// Before we retry the request we have to make sure
|
||||
@@ -376,13 +378,13 @@ func createUpstreamRequest(rw http.ResponseWriter, r *http.Request) (*http.Reque
|
||||
return outreq, cancel
|
||||
}
|
||||
|
||||
func createRespHeaderUpdateFn(rules http.Header, replacer httpserver.Replacer) respUpdateFn {
|
||||
func createRespHeaderUpdateFn(rules http.Header, replacer httpserver.Replacer, replacements headerReplacements) respUpdateFn {
|
||||
return func(resp *http.Response) {
|
||||
mutateHeadersByRules(resp.Header, rules, replacer)
|
||||
mutateHeadersByRules(resp.Header, rules, replacer, replacements)
|
||||
}
|
||||
}
|
||||
|
||||
func mutateHeadersByRules(headers, rules http.Header, repl httpserver.Replacer) {
|
||||
func mutateHeadersByRules(headers, rules http.Header, repl httpserver.Replacer, replacements headerReplacements) {
|
||||
for ruleField, ruleValues := range rules {
|
||||
if strings.HasPrefix(ruleField, "+") {
|
||||
for _, ruleValue := range ruleValues {
|
||||
@@ -400,6 +402,19 @@ func mutateHeadersByRules(headers, rules http.Header, repl httpserver.Replacer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ruleField, ruleValues := range replacements {
|
||||
for _, ruleValue := range ruleValues {
|
||||
// Replace variables in replacement string
|
||||
replacement := repl.Replace(ruleValue.to)
|
||||
original := headers.Get(ruleField)
|
||||
if len(replacement) > 0 && len(original) > 0 {
|
||||
// Replace matches in original string with replacement string
|
||||
replaced := ruleValue.regexp.ReplaceAllString(original, replacement)
|
||||
headers.Set(ruleField, replaced)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const CustomStatusContextCancelled = 499
|
||||
|
||||
35
vendor/github.com/mholt/caddy/caddyhttp/proxy/reverseproxy.go
generated
vendored
35
vendor/github.com/mholt/caddy/caddyhttp/proxy/reverseproxy.go
generated
vendored
@@ -31,6 +31,7 @@ import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -69,7 +70,9 @@ func pooledIoCopy(dst io.Writer, src io.Reader) {
|
||||
// Due to that we extend buf's length to its capacity here and
|
||||
// ensure it's always non-zero.
|
||||
bufCap := cap(buf)
|
||||
io.CopyBuffer(dst, src, buf[0:bufCap:bufCap])
|
||||
if _, err := io.CopyBuffer(dst, src, buf[0:bufCap:bufCap]); err != nil {
|
||||
log.Println("[ERROR] failed to copy buffer: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
// onExitFlushLoop is a callback set by tests to detect the state of the
|
||||
@@ -132,12 +135,12 @@ func (rp *ReverseProxy) srvDialerFunc(locator string, timeout time.Duration) fun
|
||||
}
|
||||
|
||||
func singleJoiningSlash(a, b string) string {
|
||||
aslash := strings.HasSuffix(a, "/")
|
||||
bslash := strings.HasPrefix(b, "/")
|
||||
aSlash := strings.HasSuffix(a, "/")
|
||||
bSlash := strings.HasPrefix(b, "/")
|
||||
switch {
|
||||
case aslash && bslash:
|
||||
case aSlash && bSlash:
|
||||
return a + b[1:]
|
||||
case !aslash && !bslash && b != "":
|
||||
case !aSlash && !bSlash && b != "":
|
||||
return a + "/" + b
|
||||
}
|
||||
return a + b
|
||||
@@ -275,7 +278,9 @@ func NewSingleHostReverseProxy(target *url.URL, without string, keepalive int, t
|
||||
transport.MaxIdleConnsPerHost = keepalive
|
||||
}
|
||||
if httpserver.HTTP2 {
|
||||
http2.ConfigureTransport(transport)
|
||||
if err := http2.ConfigureTransport(transport); err != nil {
|
||||
log.Println("[ERROR] failed to configure transport to use HTTP/2: ", err)
|
||||
}
|
||||
}
|
||||
rp.Transport = transport
|
||||
} else {
|
||||
@@ -284,7 +289,9 @@ func NewSingleHostReverseProxy(target *url.URL, without string, keepalive int, t
|
||||
Dial: rp.dialer.Dial,
|
||||
}
|
||||
if httpserver.HTTP2 {
|
||||
http2.ConfigureTransport(transport)
|
||||
if err := http2.ConfigureTransport(transport); err != nil {
|
||||
log.Println("[ERROR] failed to configure transport to use HTTP/2: ", err)
|
||||
}
|
||||
}
|
||||
rp.Transport = transport
|
||||
}
|
||||
@@ -394,7 +401,9 @@ func (rp *ReverseProxy) ServeHTTP(rw http.ResponseWriter, outreq *http.Request,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outreq.Write(backendConn)
|
||||
if err := outreq.Write(backendConn); err != nil {
|
||||
log.Println("[ERROR] failed to write: ", err)
|
||||
}
|
||||
}
|
||||
defer backendConn.Close()
|
||||
|
||||
@@ -416,7 +425,9 @@ func (rp *ReverseProxy) ServeHTTP(rw http.ResponseWriter, outreq *http.Request,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
backendConn.Write(rbuf)
|
||||
if _, err := backendConn.Write(rbuf); err != nil {
|
||||
log.Println("[ERROR] failed to write data to connection: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
go func() {
|
||||
@@ -434,7 +445,7 @@ func (rp *ReverseProxy) ServeHTTP(rw http.ResponseWriter, outreq *http.Request,
|
||||
bodyOpen := true
|
||||
closeBody := func() {
|
||||
if bodyOpen {
|
||||
res.Body.Close()
|
||||
_ = res.Body.Close()
|
||||
bodyOpen = false
|
||||
}
|
||||
}
|
||||
@@ -681,7 +692,7 @@ func getTransportDialTLS(t *http.Transport) func(network, addr string) (net.Conn
|
||||
errc <- err
|
||||
}()
|
||||
if err := <-errc; err != nil {
|
||||
plainConn.Close()
|
||||
_ = plainConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
if !tlsClientConfig.InsecureSkipVerify {
|
||||
@@ -690,7 +701,7 @@ func getTransportDialTLS(t *http.Transport) func(network, addr string) (net.Conn
|
||||
hostname = stripPort(addr)
|
||||
}
|
||||
if err := tlsConn.VerifyHostname(hostname); err != nil {
|
||||
plainConn.Close()
|
||||
_ = plainConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
118
vendor/github.com/mholt/caddy/caddyhttp/proxy/upstream.go
generated
vendored
118
vendor/github.com/mholt/caddy/caddyhttp/proxy/upstream.go
generated
vendored
@@ -21,10 +21,13 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -65,18 +68,39 @@ type staticUpstream struct {
|
||||
Port string
|
||||
ContentString string
|
||||
}
|
||||
WithoutPathPrefix string
|
||||
IgnoredSubPaths []string
|
||||
insecureSkipVerify bool
|
||||
MaxFails int32
|
||||
resolver srvResolver
|
||||
CaCertPool *x509.CertPool
|
||||
WithoutPathPrefix string
|
||||
IgnoredSubPaths []string
|
||||
insecureSkipVerify bool
|
||||
MaxFails int32
|
||||
resolver srvResolver
|
||||
CaCertPool *x509.CertPool
|
||||
upstreamHeaderReplacements headerReplacements
|
||||
downstreamHeaderReplacements headerReplacements
|
||||
}
|
||||
|
||||
type srvResolver interface {
|
||||
LookupSRV(context.Context, string, string, string) (string, []*net.SRV, error)
|
||||
}
|
||||
|
||||
// headerReplacement stores a compiled regex matcher and a string replacer, for replacement rules
|
||||
type headerReplacement struct {
|
||||
regexp *regexp.Regexp
|
||||
to string
|
||||
}
|
||||
|
||||
// headerReplacements stores a mapping of canonical MIME header to headerReplacement
|
||||
// Implements a subset of http.Header functions, to allow convenient addition and deletion of rules
|
||||
type headerReplacements map[string][]headerReplacement
|
||||
|
||||
func (h headerReplacements) Add(key string, value headerReplacement) {
|
||||
key = textproto.CanonicalMIMEHeaderKey(key)
|
||||
h[key] = append(h[key], value)
|
||||
}
|
||||
|
||||
func (h headerReplacements) Del(key string) {
|
||||
delete(h, textproto.CanonicalMIMEHeaderKey(key))
|
||||
}
|
||||
|
||||
// NewStaticUpstreams parses the configuration input and sets up
|
||||
// static upstreams for the proxy middleware. The host string parameter,
|
||||
// if not empty, is used for setting the upstream Host header for the
|
||||
@@ -86,18 +110,20 @@ func NewStaticUpstreams(c caddyfile.Dispenser, host string) ([]Upstream, error)
|
||||
for c.Next() {
|
||||
|
||||
upstream := &staticUpstream{
|
||||
from: "",
|
||||
stop: make(chan struct{}),
|
||||
upstreamHeaders: make(http.Header),
|
||||
downstreamHeaders: make(http.Header),
|
||||
Hosts: nil,
|
||||
Policy: &Random{},
|
||||
MaxFails: 1,
|
||||
TryInterval: 250 * time.Millisecond,
|
||||
MaxConns: 0,
|
||||
KeepAlive: http.DefaultMaxIdleConnsPerHost,
|
||||
Timeout: 30 * time.Second,
|
||||
resolver: net.DefaultResolver,
|
||||
from: "",
|
||||
stop: make(chan struct{}),
|
||||
upstreamHeaders: make(http.Header),
|
||||
downstreamHeaders: make(http.Header),
|
||||
Hosts: nil,
|
||||
Policy: &Random{},
|
||||
MaxFails: 1,
|
||||
TryInterval: 250 * time.Millisecond,
|
||||
MaxConns: 0,
|
||||
KeepAlive: http.DefaultMaxIdleConnsPerHost,
|
||||
Timeout: 30 * time.Second,
|
||||
resolver: net.DefaultResolver,
|
||||
upstreamHeaderReplacements: make(headerReplacements),
|
||||
downstreamHeaderReplacements: make(headerReplacements),
|
||||
}
|
||||
|
||||
if !c.Args(&upstream.from) {
|
||||
@@ -220,9 +246,11 @@ func (u *staticUpstream) NewHost(host string) (*UpstreamHost, error) {
|
||||
return false
|
||||
}
|
||||
}(u),
|
||||
WithoutPathPrefix: u.WithoutPathPrefix,
|
||||
MaxConns: u.MaxConns,
|
||||
HealthCheckResult: atomic.Value{},
|
||||
WithoutPathPrefix: u.WithoutPathPrefix,
|
||||
MaxConns: u.MaxConns,
|
||||
HealthCheckResult: atomic.Value{},
|
||||
UpstreamHeaderReplacements: u.upstreamHeaderReplacements,
|
||||
DownstreamHeaderReplacements: u.downstreamHeaderReplacements,
|
||||
}
|
||||
|
||||
baseURL, err := url.Parse(uh.Name)
|
||||
@@ -302,6 +330,8 @@ func parseUpstream(u string) ([]string, error) {
|
||||
}
|
||||
|
||||
func parseBlock(c *caddyfile.Dispenser, u *staticUpstream, hasSrv bool) error {
|
||||
var isUpstream bool
|
||||
|
||||
switch c.Val() {
|
||||
case "policy":
|
||||
if !c.NextArg() {
|
||||
@@ -431,23 +461,37 @@ func parseBlock(c *caddyfile.Dispenser, u *staticUpstream, hasSrv bool) error {
|
||||
}
|
||||
u.HealthCheck.ContentString = c.Val()
|
||||
case "header_upstream":
|
||||
var header, value string
|
||||
if !c.Args(&header, &value) {
|
||||
// When removing a header, the value can be optional.
|
||||
if !strings.HasPrefix(header, "-") {
|
||||
return c.ArgErr()
|
||||
}
|
||||
}
|
||||
u.upstreamHeaders.Add(header, value)
|
||||
isUpstream = true
|
||||
fallthrough
|
||||
case "header_downstream":
|
||||
var header, value string
|
||||
if !c.Args(&header, &value) {
|
||||
// When removing a header, the value can be optional.
|
||||
if !strings.HasPrefix(header, "-") {
|
||||
var header, value, replaced string
|
||||
if c.Args(&header, &value, &replaced) {
|
||||
// Don't allow - or + in replacements
|
||||
if strings.HasPrefix(header, "-") || strings.HasPrefix(header, "+") {
|
||||
return c.ArgErr()
|
||||
}
|
||||
r, err := regexp.Compile(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isUpstream {
|
||||
u.upstreamHeaderReplacements.Add(header, headerReplacement{r, replaced})
|
||||
} else {
|
||||
u.downstreamHeaderReplacements.Add(header, headerReplacement{r, replaced})
|
||||
}
|
||||
} else {
|
||||
if len(value) == 0 {
|
||||
// When removing a header, the value can be optional.
|
||||
if !strings.HasPrefix(header, "-") {
|
||||
return c.ArgErr()
|
||||
}
|
||||
}
|
||||
if isUpstream {
|
||||
u.upstreamHeaders.Add(header, value)
|
||||
} else {
|
||||
u.downstreamHeaders.Add(header, value)
|
||||
}
|
||||
}
|
||||
u.downstreamHeaders.Add(header, value)
|
||||
case "transparent":
|
||||
// Note: X-Forwarded-For header is always being appended for proxy connections
|
||||
// See implementation of createUpstreamRequest in proxy.go
|
||||
@@ -589,8 +633,10 @@ func (u *staticUpstream) healthCheck() {
|
||||
return true
|
||||
}
|
||||
defer func() {
|
||||
io.Copy(ioutil.Discard, r.Body)
|
||||
r.Body.Close()
|
||||
if _, err := io.Copy(ioutil.Discard, r.Body); err != nil {
|
||||
log.Println("[ERROR] failed to copy: ", err)
|
||||
}
|
||||
_ = r.Body.Close()
|
||||
}()
|
||||
if r.StatusCode < 200 || r.StatusCode >= 400 {
|
||||
return true
|
||||
|
||||
40
vendor/github.com/mholt/caddy/caddyhttp/websocket/websocket.go
generated
vendored
40
vendor/github.com/mholt/caddy/caddyhttp/websocket/websocket.go
generated
vendored
@@ -21,6 +21,7 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -80,9 +81,9 @@ type (
|
||||
|
||||
// ServeHTTP converts the HTTP request to a WebSocket connection and serves it up.
|
||||
func (ws WebSocket) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
for _, sockconfig := range ws.Sockets {
|
||||
if httpserver.Path(r.URL.Path).Matches(sockconfig.Path) {
|
||||
return serveWS(w, r, &sockconfig)
|
||||
for _, sockConfig := range ws.Sockets {
|
||||
if httpserver.Path(r.URL.Path).Matches(sockConfig.Path) {
|
||||
return serveWS(w, r, &sockConfig)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +136,7 @@ func serveWS(w http.ResponseWriter, r *http.Request, config *Config) (int, error
|
||||
go pumpStdout(conn, stdout, done)
|
||||
pumpStdin(conn, stdin)
|
||||
|
||||
stdin.Close() // close stdin to end the process
|
||||
_ = stdin.Close() // close stdin to end the process
|
||||
|
||||
if err := cmd.Process.Signal(os.Interrupt); err != nil { // signal an interrupt to kill the process
|
||||
return http.StatusInternalServerError, err
|
||||
@@ -155,7 +156,9 @@ func serveWS(w http.ResponseWriter, r *http.Request, config *Config) (int, error
|
||||
// status for an "exited" process is greater
|
||||
// than 0, but isn't really an error per se.
|
||||
// just going to ignore it for now.
|
||||
cmd.Wait()
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Println("[ERROR] failed to release resources: ", err)
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
@@ -221,8 +224,15 @@ func pumpStdin(conn *websocket.Conn, stdin io.WriteCloser) {
|
||||
// Setup our connection's websocket ping/pong handlers from our const values.
|
||||
defer conn.Close()
|
||||
conn.SetReadLimit(maxMessageSize)
|
||||
conn.SetReadDeadline(time.Now().Add(pongWait))
|
||||
conn.SetPongHandler(func(string) error { conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
|
||||
if err := conn.SetReadDeadline(time.Now().Add(pongWait)); err != nil {
|
||||
log.Println("[ERROR] failed to set read deadline: ", err)
|
||||
}
|
||||
conn.SetPongHandler(func(string) error {
|
||||
if err := conn.SetReadDeadline(time.Now().Add(pongWait)); err != nil {
|
||||
log.Println("[ERROR] failed to set read deadline: ", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
for {
|
||||
_, message, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
@@ -240,19 +250,24 @@ func pumpStdin(conn *websocket.Conn, stdin io.WriteCloser) {
|
||||
func pumpStdout(conn *websocket.Conn, stdout io.Reader, done chan struct{}) {
|
||||
go pinger(conn, done)
|
||||
defer func() {
|
||||
conn.Close()
|
||||
_ = conn.Close()
|
||||
close(done) // make sure to close the pinger when we are done.
|
||||
}()
|
||||
|
||||
s := bufio.NewScanner(stdout)
|
||||
for s.Scan() {
|
||||
conn.SetWriteDeadline(time.Now().Add(writeWait))
|
||||
if err := conn.SetWriteDeadline(time.Now().Add(writeWait)); err != nil {
|
||||
log.Println("[ERROR] failed to set write deadline: ", err)
|
||||
}
|
||||
if err := conn.WriteMessage(websocket.TextMessage, bytes.TrimSpace(s.Bytes())); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if s.Err() != nil {
|
||||
conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseGoingAway, s.Err().Error()), time.Time{})
|
||||
err := conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseGoingAway, s.Err().Error()), time.Time{})
|
||||
if err != nil {
|
||||
log.Println("[ERROR] WriteControl failed: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +280,10 @@ func pinger(conn *websocket.Conn, done chan struct{}) {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if err := conn.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)); err != nil {
|
||||
conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseGoingAway, err.Error()), time.Time{})
|
||||
err := conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseGoingAway, err.Error()), time.Time{})
|
||||
if err != nil {
|
||||
log.Println("[ERROR] WriteControl failed: ", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
case <-done:
|
||||
|
||||
38
vendor/github.com/mholt/caddy/caddytls/config.go
generated
vendored
38
vendor/github.com/mholt/caddy/caddytls/config.go
generated
vendored
@@ -21,13 +21,14 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/challenge/tlsalpn01"
|
||||
"github.com/go-acme/lego/challenge/tlsalpn01"
|
||||
|
||||
"github.com/go-acme/lego/certcrypto"
|
||||
"github.com/klauspost/cpuid"
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/mholt/certmagic"
|
||||
"github.com/xenolf/lego/certcrypto"
|
||||
)
|
||||
|
||||
// Config describes how TLS should be configured and used.
|
||||
@@ -117,22 +118,48 @@ func NewConfig(inst *caddy.Instance) (*Config, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("constructing cluster plugin %s: %v", clusterPluginName, err)
|
||||
}
|
||||
certmagic.DefaultStorage = storage
|
||||
certmagic.Default.Storage = storage
|
||||
} else {
|
||||
return nil, fmt.Errorf("unrecognized cluster plugin (was it included in the Caddy build?): %s", clusterPluginName)
|
||||
}
|
||||
}
|
||||
certCache = certmagic.NewCache(certmagic.DefaultStorage)
|
||||
certCache = certmagic.NewCache(certmagic.CacheOptions{
|
||||
GetConfigForCert: func(cert certmagic.Certificate) (certmagic.Config, error) {
|
||||
inst.StorageMu.Lock()
|
||||
cfgMap, ok := inst.Storage[configMapKey].(map[string]*Config)
|
||||
inst.StorageMu.Unlock()
|
||||
if ok {
|
||||
for hostname, cfg := range cfgMap {
|
||||
if cfg.Manager != nil && hostname == cert.Names[0] {
|
||||
return *cfg.Manager, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
// returning Default not strictly necessary, since Default is used as template
|
||||
// anyway; but this makes it clear that that's what we fall back to
|
||||
return certmagic.Default, nil
|
||||
},
|
||||
})
|
||||
storageCleaningTicker := time.NewTicker(12 * time.Hour)
|
||||
go func() {
|
||||
for range storageCleaningTicker.C {
|
||||
certmagic.CleanStorage(certmagic.Default.Storage, certmagic.CleanStorageOptions{
|
||||
OCSPStaples: true,
|
||||
})
|
||||
}
|
||||
}()
|
||||
inst.OnShutdown = append(inst.OnShutdown, func() error {
|
||||
certCache.Stop()
|
||||
storageCleaningTicker.Stop()
|
||||
return nil
|
||||
})
|
||||
|
||||
inst.StorageMu.Lock()
|
||||
inst.Storage[CertCacheInstStorageKey] = certCache
|
||||
inst.StorageMu.Unlock()
|
||||
}
|
||||
return &Config{
|
||||
Manager: certmagic.NewWithCache(certCache, certmagic.Config{}),
|
||||
Manager: certmagic.New(certCache, certmagic.Config{}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -418,7 +445,6 @@ func SetDefaultTLSParams(config *Config) {
|
||||
var supportedKeyTypes = map[string]certcrypto.KeyType{
|
||||
"P384": certcrypto.EC384,
|
||||
"P256": certcrypto.EC256,
|
||||
"RSA8192": certcrypto.RSA8192,
|
||||
"RSA4096": certcrypto.RSA4096,
|
||||
"RSA2048": certcrypto.RSA2048,
|
||||
}
|
||||
|
||||
2
vendor/github.com/mholt/caddy/caddytls/handshake.go
generated
vendored
2
vendor/github.com/mholt/caddy/caddytls/handshake.go
generated
vendored
@@ -42,7 +42,7 @@ type configGroup map[string]*Config
|
||||
func (cg configGroup) getConfig(hello *tls.ClientHelloInfo) *Config {
|
||||
name := certmagic.NormalizedName(hello.ServerName)
|
||||
if name == "" {
|
||||
name = certmagic.NormalizedName(certmagic.DefaultServerName)
|
||||
name = certmagic.NormalizedName(certmagic.Default.DefaultServerName)
|
||||
}
|
||||
|
||||
// if SNI is empty, prefer matching IP address (it is
|
||||
|
||||
2
vendor/github.com/mholt/caddy/caddytls/selfsigned.go
generated
vendored
2
vendor/github.com/mholt/caddy/caddytls/selfsigned.go
generated
vendored
@@ -14,7 +14,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/certcrypto"
|
||||
"github.com/go-acme/lego/certcrypto"
|
||||
)
|
||||
|
||||
// newSelfSignedCertificate returns a new self-signed certificate.
|
||||
|
||||
32
vendor/github.com/mholt/caddy/caddytls/setup.go
generated
vendored
32
vendor/github.com/mholt/caddy/caddytls/setup.go
generated
vendored
@@ -36,7 +36,9 @@ import (
|
||||
func init() {
|
||||
// opt-in TLS 1.3 for Go1.12
|
||||
// TODO: remove this line when Go1.13 is released.
|
||||
os.Setenv("GODEBUG", os.Getenv("GODEBUG")+",tls13=1")
|
||||
if err := os.Setenv("GODEBUG", os.Getenv("GODEBUG")+",tls13=1"); err != nil {
|
||||
log.Println("[ERROR] failed to set environment variable: ", err)
|
||||
}
|
||||
|
||||
caddy.RegisterPlugin("tls", caddy.Plugin{Action: setupTLS})
|
||||
|
||||
@@ -63,7 +65,7 @@ func setupTLS(c *caddy.Controller) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("constructing cluster plugin %s: %v", clusterPluginName, err)
|
||||
}
|
||||
certmagic.DefaultStorage = storage
|
||||
certmagic.Default.Storage = storage
|
||||
} else {
|
||||
return fmt.Errorf("unrecognized cluster plugin (was it included in the Caddy build?): %s", clusterPluginName)
|
||||
}
|
||||
@@ -363,6 +365,14 @@ func setupTLS(c *caddy.Controller) error {
|
||||
telemetry.Increment("tls_self_signed_count")
|
||||
}
|
||||
|
||||
// store this as a custom config
|
||||
cfgMap, ok := c.Get(configMapKey).(map[string]*Config)
|
||||
if !ok || cfgMap == nil {
|
||||
cfgMap = make(map[string]*Config)
|
||||
}
|
||||
cfgMap[config.Hostname] = config
|
||||
c.Set(configMapKey, cfgMap)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -401,26 +411,34 @@ func loadCertsInDir(cfg *Config, c *caddy.Controller, dir string) error {
|
||||
|
||||
if derBlock.Type == "CERTIFICATE" {
|
||||
// Re-encode certificate as PEM, appending to certificate chain
|
||||
pem.Encode(certBuilder, derBlock)
|
||||
if err := pem.Encode(certBuilder, derBlock); err != nil {
|
||||
log.Println("[ERROR] failed to write PEM encoding: ", err)
|
||||
}
|
||||
} else if derBlock.Type == "EC PARAMETERS" {
|
||||
// EC keys generated from openssl can be composed of two blocks:
|
||||
// parameters and key (parameter block should come first)
|
||||
if !foundKey {
|
||||
// Encode parameters
|
||||
pem.Encode(keyBuilder, derBlock)
|
||||
if err := pem.Encode(keyBuilder, derBlock); err != nil {
|
||||
log.Println("[ERROR] failed to write PEM encoding: ", err)
|
||||
}
|
||||
|
||||
// Key must immediately follow
|
||||
derBlock, bundle = pem.Decode(bundle)
|
||||
if derBlock == nil || derBlock.Type != "EC PRIVATE KEY" {
|
||||
return c.Errf("%s: expected elliptic private key to immediately follow EC parameters", path)
|
||||
}
|
||||
pem.Encode(keyBuilder, derBlock)
|
||||
if err := pem.Encode(keyBuilder, derBlock); err != nil {
|
||||
log.Println("[ERROR] failed to write PEM encoding: ", err)
|
||||
}
|
||||
foundKey = true
|
||||
}
|
||||
} else if derBlock.Type == "PRIVATE KEY" || strings.HasSuffix(derBlock.Type, " PRIVATE KEY") {
|
||||
// RSA key
|
||||
if !foundKey {
|
||||
pem.Encode(keyBuilder, derBlock)
|
||||
if err := pem.Encode(keyBuilder, derBlock); err != nil {
|
||||
log.Println("[ERROR] failed to write PEM encoding: ", err)
|
||||
}
|
||||
foundKey = true
|
||||
}
|
||||
} else {
|
||||
@@ -449,3 +467,5 @@ func loadCertsInDir(cfg *Config, c *caddy.Controller, dir string) error {
|
||||
func constructDefaultClusterPlugin() (certmagic.Storage, error) {
|
||||
return &certmagic.FileStorage{Path: caddy.AssetsPath()}, nil
|
||||
}
|
||||
|
||||
const configMapKey = "tls_custom_configs"
|
||||
|
||||
4
vendor/github.com/mholt/caddy/caddytls/tls.go
generated
vendored
4
vendor/github.com/mholt/caddy/caddytls/tls.go
generated
vendored
@@ -29,9 +29,9 @@
|
||||
package caddytls
|
||||
|
||||
import (
|
||||
"github.com/go-acme/lego/challenge"
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/mholt/certmagic"
|
||||
"github.com/xenolf/lego/challenge"
|
||||
)
|
||||
|
||||
// ConfigHolder is any type that has a Config; it presumably is
|
||||
@@ -93,7 +93,7 @@ var KnownACMECAs = []string{
|
||||
//
|
||||
// challenge.Provider is an interface that allows the implementation of custom
|
||||
// challenge providers. For more details, see:
|
||||
// https://godoc.org/github.com/xenolf/lego/acme#ChallengeProvider
|
||||
// https://godoc.org/github.com/go-acme/lego/acme#ChallengeProvider
|
||||
type ChallengeProvider challenge.Provider
|
||||
|
||||
// DNSProviderConstructor is a function that takes credentials and
|
||||
|
||||
5
vendor/github.com/mholt/caddy/onevent/on.go
generated
vendored
5
vendor/github.com/mholt/caddy/onevent/on.go
generated
vendored
@@ -20,12 +20,15 @@ func setup(c *caddy.Controller) error {
|
||||
}
|
||||
|
||||
// Register Event Hooks.
|
||||
c.OncePerServerBlock(func() error {
|
||||
err = c.OncePerServerBlock(func() error {
|
||||
for _, cfg := range config {
|
||||
caddy.RegisterEventHook("on-"+cfg.ID, cfg.Hook)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
5
vendor/github.com/mholt/caddy/telemetry/collection.go
generated
vendored
5
vendor/github.com/mholt/caddy/telemetry/collection.go
generated
vendored
@@ -283,7 +283,10 @@ func atomicAdd(key string, amount int) {
|
||||
// Do not use this for cryptographic purposes.
|
||||
func FastHash(input []byte) string {
|
||||
h := fnv.New32a()
|
||||
h.Write(input)
|
||||
if _, err := h.Write(input); err != nil {
|
||||
log.Println("[ERROR] failed to write bytes: ", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x", h.Sum32())
|
||||
}
|
||||
|
||||
|
||||
322
vendor/github.com/mholt/certmagic/cache.go
generated
vendored
322
vendor/github.com/mholt/certmagic/cache.go
generated
vendored
@@ -21,149 +21,251 @@ import (
|
||||
)
|
||||
|
||||
// Cache is a structure that stores certificates in memory.
|
||||
// Generally, there should only be one per process. However,
|
||||
// complex applications that virtualize the concept of a
|
||||
// "process" (such as Caddy, which virtualizes processes as
|
||||
// "instances" so it can do graceful, in-memory reloads of
|
||||
// its configuration) may use more of these per OS process.
|
||||
// A Cache indexes certificates by name for quick access
|
||||
// during TLS handshakes, and avoids duplicating certificates
|
||||
// in memory. Generally, there should only be one per process.
|
||||
// However, that is not a strict requirement; but using more
|
||||
// than one is a code smell, and may indicate an
|
||||
// over-engineered design.
|
||||
//
|
||||
// Using just one cache per process avoids duplication of
|
||||
// certificates across multiple configurations and makes
|
||||
// maintenance easier.
|
||||
// An empty cache is INVALID and must not be used. Be sure
|
||||
// to call NewCache to get a valid value.
|
||||
//
|
||||
// An empty cache is INVALID and must not be used.
|
||||
// Be sure to call NewCertificateCache to get one.
|
||||
//
|
||||
// These should be very long-lived values, and must not be
|
||||
// These should be very long-lived values and must not be
|
||||
// copied. Before all references leave scope to be garbage
|
||||
// collected, ensure you call Stop() to stop maintenance
|
||||
// maintenance on the certificates stored in this cache.
|
||||
// collected, ensure you call Stop() to stop maintenance on
|
||||
// the certificates stored in this cache and release locks.
|
||||
//
|
||||
// Caches are not usually manipulated directly; create a
|
||||
// Config value with a pointer to a Cache, and then use
|
||||
// the Config to interact with the cache. Caches are
|
||||
// agnostic of any particular storage or ACME config,
|
||||
// since each certificate may be managed and stored
|
||||
// differently.
|
||||
type Cache struct {
|
||||
// How often to check certificates for renewal
|
||||
RenewInterval time.Duration
|
||||
|
||||
// How often to check if OCSP stapling needs updating
|
||||
OCSPInterval time.Duration
|
||||
|
||||
// The storage implementation
|
||||
storage Storage
|
||||
// User configuration of the cache
|
||||
options CacheOptions
|
||||
|
||||
// The cache is keyed by certificate hash
|
||||
cache map[string]Certificate
|
||||
|
||||
// Protects the cache map
|
||||
// cacheIndex is a map of SAN to cache key (cert hash)
|
||||
cacheIndex map[string][]string
|
||||
|
||||
// Protects the cache and index maps
|
||||
mu sync.RWMutex
|
||||
|
||||
// Close this channel to cancel asset maintenance
|
||||
stopChan chan struct{}
|
||||
|
||||
// Used to signal when stopping is completed
|
||||
doneChan chan struct{}
|
||||
}
|
||||
|
||||
// NewCache returns a new, valid Cache backed by the
|
||||
// given storage implementation. It also begins a
|
||||
// maintenance goroutine for any managed certificates
|
||||
// stored in this cache.
|
||||
// NewCache returns a new, valid Cache for efficiently
|
||||
// accessing certificates in memory. It also begins a
|
||||
// maintenance goroutine to tend to the certificates
|
||||
// in the cache. Call Stop() when you are done with the
|
||||
// cache so it can clean up locks and stuff.
|
||||
//
|
||||
// See the godoc for Cache to use it properly.
|
||||
// Most users of this package will not need to call this
|
||||
// because a default certificate cache is created for you.
|
||||
// Only advanced use cases require creating a new cache.
|
||||
//
|
||||
// Note that all processes running in a cluster
|
||||
// configuration must use the same storage value
|
||||
// in order to share certificates. (A single storage
|
||||
// value may be shared by multiple clusters as well.)
|
||||
func NewCache(storage Storage) *Cache {
|
||||
c := &Cache{
|
||||
RenewInterval: DefaultRenewInterval,
|
||||
OCSPInterval: DefaultOCSPInterval,
|
||||
storage: storage,
|
||||
cache: make(map[string]Certificate),
|
||||
stopChan: make(chan struct{}),
|
||||
// This function panics if opts.GetConfigForCert is not
|
||||
// set. The reason is that a cache absolutely needs to
|
||||
// be able to get a Config with which to manage TLS
|
||||
// assets, and it is not safe to assume that the Default
|
||||
// config is always the correct one, since you have
|
||||
// created the cache yourself.
|
||||
//
|
||||
// See the godoc for Cache to use it properly. When
|
||||
// no longer needed, caches should be stopped with
|
||||
// Stop() to clean up resources even if the process
|
||||
// is being terminated, so that it can clean up
|
||||
// any locks for other processes to unblock!
|
||||
func NewCache(opts CacheOptions) *Cache {
|
||||
// assume default options if necessary
|
||||
if opts.OCSPCheckInterval <= 0 {
|
||||
opts.OCSPCheckInterval = DefaultOCSPCheckInterval
|
||||
}
|
||||
if opts.RenewCheckInterval <= 0 {
|
||||
opts.RenewCheckInterval = DefaultRenewCheckInterval
|
||||
}
|
||||
|
||||
// this must be set, because we cannot not
|
||||
// safely assume that the Default Config
|
||||
// is always the correct one to use
|
||||
if opts.GetConfigForCert == nil {
|
||||
panic("cache must be initialized with a GetConfigForCert callback")
|
||||
}
|
||||
|
||||
c := &Cache{
|
||||
options: opts,
|
||||
cache: make(map[string]Certificate),
|
||||
cacheIndex: make(map[string][]string),
|
||||
stopChan: make(chan struct{}),
|
||||
doneChan: make(chan struct{}),
|
||||
}
|
||||
|
||||
go c.maintainAssets()
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Stop stops the maintenance goroutine for
|
||||
// certificates in certCache.
|
||||
// certificates in certCache. It blocks until
|
||||
// stopping is complete. Once a cache is
|
||||
// stopped, it cannot be reused.
|
||||
func (certCache *Cache) Stop() {
|
||||
close(certCache.stopChan)
|
||||
close(certCache.stopChan) // signal to stop
|
||||
<-certCache.doneChan // wait for stop to complete
|
||||
}
|
||||
|
||||
// replaceCertificate replaces oldCert with newCert in the cache, and
|
||||
// updates all configs that are pointing to the old certificate to
|
||||
// point to the new one instead. newCert must already be loaded into
|
||||
// the cache (this method does NOT load it into the cache).
|
||||
// CacheOptions is used to configure certificate caches.
|
||||
// Once a cache has been created with certain options,
|
||||
// those settings cannot be changed.
|
||||
type CacheOptions struct {
|
||||
// REQUIRED. A function that returns a configuration
|
||||
// used for managing a certificate, or for accessing
|
||||
// that certificate's asset storage (e.g. for
|
||||
// OCSP staples, etc). The returned Config MUST
|
||||
// be associated with the same Cache as the caller.
|
||||
//
|
||||
// The reason this is a callback function, dynamically
|
||||
// returning a Config (instead of attaching a static
|
||||
// pointer to a Config on each certificate) is because
|
||||
// the config for how to manage a domain's certificate
|
||||
// might change from maintenance to maintenance. The
|
||||
// cache is so long-lived, we cannot assume that the
|
||||
// host's situation will always be the same; e.g. the
|
||||
// certificate might switch DNS providers, so the DNS
|
||||
// challenge (if used) would need to be adjusted from
|
||||
// the last time it was run ~8 weeks ago.
|
||||
GetConfigForCert ConfigGetter
|
||||
|
||||
// How often to check certificates for renewal;
|
||||
// if unset, DefaultOCSPCheckInterval will be used.
|
||||
OCSPCheckInterval time.Duration
|
||||
|
||||
// How often to check certificates for renewal;
|
||||
// if unset, DefaultRenewCheckInterval will be used.
|
||||
RenewCheckInterval time.Duration
|
||||
}
|
||||
|
||||
// ConfigGetter is a function that returns a config that
|
||||
// should be used when managing the given certificate
|
||||
// or its assets.
|
||||
type ConfigGetter func(Certificate) (Config, error)
|
||||
|
||||
// cacheCertificate calls unsyncedCacheCertificate with a write lock.
|
||||
//
|
||||
// Note that all the names on the old certificate will be deleted
|
||||
// from the name lookup maps of each config, then all the names on
|
||||
// the new certificate will be added to the lookup maps as long as
|
||||
// they do not overwrite any entries.
|
||||
// This function is safe for concurrent use.
|
||||
func (certCache *Cache) cacheCertificate(cert Certificate) {
|
||||
certCache.mu.Lock()
|
||||
certCache.unsyncedCacheCertificate(cert)
|
||||
certCache.mu.Unlock()
|
||||
}
|
||||
|
||||
// unsyncedCacheCertificate adds cert to the in-memory cache unless
|
||||
// it already exists in the cache (according to cert.Hash). It
|
||||
// updates the name index.
|
||||
//
|
||||
// The newCert may be modified and its cache entry updated.
|
||||
// This function is NOT safe for concurrent use. Callers MUST acquire
|
||||
// a write lock on certCache.mu first.
|
||||
func (certCache *Cache) unsyncedCacheCertificate(cert Certificate) {
|
||||
// no-op if this certificate already exists in the cache
|
||||
if _, ok := certCache.cache[cert.Hash]; ok {
|
||||
return
|
||||
}
|
||||
|
||||
// store the certificate
|
||||
certCache.cache[cert.Hash] = cert
|
||||
|
||||
// update the index so we can access it by name
|
||||
for _, name := range cert.Names {
|
||||
certCache.cacheIndex[name] = append(certCache.cacheIndex[name], cert.Hash)
|
||||
}
|
||||
}
|
||||
|
||||
// removeCertificate removes cert from the cache.
|
||||
//
|
||||
// This function is NOT safe for concurrent use; callers
|
||||
// MUST first acquire a write lock on certCache.mu.
|
||||
func (certCache *Cache) removeCertificate(cert Certificate) {
|
||||
// delete all mentions of this cert from the name index
|
||||
for _, name := range cert.Names {
|
||||
keyList := certCache.cacheIndex[name]
|
||||
for i, cacheKey := range keyList {
|
||||
if cacheKey == cert.Hash {
|
||||
keyList = append(keyList[:i], keyList[i+1:]...)
|
||||
}
|
||||
}
|
||||
if len(keyList) == 0 {
|
||||
delete(certCache.cacheIndex, name)
|
||||
} else {
|
||||
certCache.cacheIndex[name] = keyList
|
||||
}
|
||||
}
|
||||
|
||||
// delete the actual cert from the cache
|
||||
delete(certCache.cache, cert.Hash)
|
||||
}
|
||||
|
||||
// replaceCertificate atomically replaces oldCert with newCert in
|
||||
// the cache.
|
||||
//
|
||||
// This method is safe for concurrent use.
|
||||
func (certCache *Cache) replaceCertificate(oldCert, newCert Certificate) error {
|
||||
func (certCache *Cache) replaceCertificate(oldCert, newCert Certificate) {
|
||||
certCache.mu.Lock()
|
||||
defer certCache.mu.Unlock()
|
||||
|
||||
// have all the configs that are pointing to the old
|
||||
// certificate point to the new certificate instead
|
||||
for _, cfg := range oldCert.configs {
|
||||
// first delete all the name lookup entries that
|
||||
// pointed to the old certificate
|
||||
for name, certKey := range cfg.certificates {
|
||||
if certKey == oldCert.Hash {
|
||||
delete(cfg.certificates, name)
|
||||
}
|
||||
}
|
||||
|
||||
// then add name lookup entries for the names
|
||||
// on the new certificate, but don't overwrite
|
||||
// entries that may already exist, not only as
|
||||
// a courtesy, but importantly: because if we
|
||||
// overwrote a value here, and this config no
|
||||
// longer pointed to a certain certificate in
|
||||
// the cache, that certificate's list of configs
|
||||
// referring to it would be incorrect; so just
|
||||
// insert entries, don't overwrite any
|
||||
for _, name := range newCert.Names {
|
||||
if _, ok := cfg.certificates[name]; !ok {
|
||||
cfg.certificates[name] = newCert.Hash
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// since caching a new certificate attaches only the config
|
||||
// that loaded it, the new certificate needs to be given the
|
||||
// list of all the configs that use it, so copy the list
|
||||
// over from the old certificate to the new certificate
|
||||
// in the cache
|
||||
newCert.configs = oldCert.configs
|
||||
certCache.cache[newCert.Hash] = newCert
|
||||
|
||||
// finally, delete the old certificate from the cache
|
||||
delete(certCache.cache, oldCert.Hash)
|
||||
|
||||
return nil
|
||||
certCache.removeCertificate(oldCert)
|
||||
certCache.unsyncedCacheCertificate(newCert)
|
||||
certCache.mu.Unlock()
|
||||
}
|
||||
|
||||
// reloadManagedCertificate reloads the certificate corresponding to the name(s)
|
||||
// on oldCert into the cache, from storage. This also replaces the old certificate
|
||||
// with the new one, so that all configurations that used the old cert now point
|
||||
// to the new cert.
|
||||
func (certCache *Cache) reloadManagedCertificate(oldCert Certificate) error {
|
||||
// get the certificate from storage and cache it
|
||||
newCert, err := oldCert.configs[0].CacheManagedCertificate(oldCert.Names[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to reload certificate for %v into cache: %v", oldCert.Names, err)
|
||||
func (certCache *Cache) getFirstMatchingCert(name string) (Certificate, bool) {
|
||||
certCache.mu.RLock()
|
||||
defer certCache.mu.RUnlock()
|
||||
|
||||
allCertKeys := certCache.cacheIndex[name]
|
||||
if len(allCertKeys) == 0 {
|
||||
return Certificate{}, false
|
||||
}
|
||||
|
||||
// and replace the old certificate with the new one
|
||||
err = certCache.replaceCertificate(oldCert, newCert)
|
||||
if err != nil {
|
||||
return fmt.Errorf("replacing certificate %v: %v", oldCert.Names, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
cert, ok := certCache.cache[allCertKeys[0]]
|
||||
return cert, ok
|
||||
}
|
||||
|
||||
var defaultCache *Cache
|
||||
var defaultCacheMu sync.Mutex
|
||||
// TODO: This seems unused (but could be useful if TLS
|
||||
// handshakes serve up different certs for a single
|
||||
// name depending on other properties such as key type)
|
||||
func (certCache *Cache) getAllMatchingCerts(name string) []Certificate {
|
||||
certCache.mu.RLock()
|
||||
defer certCache.mu.RUnlock()
|
||||
|
||||
allCertKeys := certCache.cacheIndex[name]
|
||||
|
||||
certs := make([]Certificate, len(allCertKeys))
|
||||
for i := range allCertKeys {
|
||||
certs[i] = certCache.cache[allCertKeys[i]]
|
||||
}
|
||||
|
||||
return certs
|
||||
}
|
||||
|
||||
func (certCache *Cache) getConfig(cert Certificate) (*Config, error) {
|
||||
cfg, err := certCache.options.GetConfigForCert(cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cfg.certCache != nil && cfg.certCache != certCache {
|
||||
return nil, fmt.Errorf("config returned for certificate %v is not nil and points to different cache; got %p, expected %p (this one)",
|
||||
cert.Names, cfg.certCache, certCache)
|
||||
}
|
||||
return New(certCache, cfg), nil
|
||||
}
|
||||
|
||||
var (
|
||||
defaultCache *Cache
|
||||
defaultCacheMu sync.Mutex
|
||||
)
|
||||
|
||||
152
vendor/github.com/mholt/certmagic/certificates.go
generated
vendored
152
vendor/github.com/mholt/certmagic/certificates.go
generated
vendored
@@ -46,29 +46,21 @@ type Certificate struct {
|
||||
// The hex-encoded hash of this cert's chain's bytes.
|
||||
Hash string
|
||||
|
||||
// configs is the list of configs that use or refer to
|
||||
// The first one is assumed to be the config that is
|
||||
// "in charge" of this certificate (i.e. determines
|
||||
// whether it is managed, how it is managed, etc).
|
||||
// This field will be populated by cacheCertificate.
|
||||
// Only meddle with it if you know what you're doing!
|
||||
configs []*Config
|
||||
|
||||
// whether this certificate is under our management
|
||||
// Whether this certificate is under our management
|
||||
managed bool
|
||||
}
|
||||
|
||||
// NeedsRenewal returns true if the certificate is
|
||||
// expiring soon or has expired.
|
||||
func (c Certificate) NeedsRenewal() bool {
|
||||
if c.NotAfter.IsZero() {
|
||||
// expiring soon (according to cfg) or has expired.
|
||||
func (cert Certificate) NeedsRenewal(cfg *Config) bool {
|
||||
if cert.NotAfter.IsZero() {
|
||||
return false
|
||||
}
|
||||
renewDurationBefore := DefaultRenewDurationBefore
|
||||
if len(c.configs) > 0 && c.configs[0].RenewDurationBefore > 0 {
|
||||
renewDurationBefore = c.configs[0].RenewDurationBefore
|
||||
if cfg.RenewDurationBefore > 0 {
|
||||
renewDurationBefore = cfg.RenewDurationBefore
|
||||
}
|
||||
return time.Until(c.NotAfter) < renewDurationBefore
|
||||
return time.Until(cert.NotAfter) < renewDurationBefore
|
||||
}
|
||||
|
||||
// CacheManagedCertificate loads the certificate for domain into the
|
||||
@@ -79,19 +71,30 @@ func (c Certificate) NeedsRenewal() bool {
|
||||
//
|
||||
// This method is safe for concurrent use.
|
||||
func (cfg *Config) CacheManagedCertificate(domain string) (Certificate, error) {
|
||||
cert, err := cfg.loadManagedCertificate(domain)
|
||||
if err != nil {
|
||||
return cert, err
|
||||
}
|
||||
cfg.certCache.cacheCertificate(cert)
|
||||
if cfg.OnEvent != nil {
|
||||
cfg.OnEvent("cached_managed_cert", cert.Names)
|
||||
}
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// loadManagedCertificate loads the managed certificate for domain,
|
||||
// but it does not add it to the cache. It just loads from storage.
|
||||
func (cfg *Config) loadManagedCertificate(domain string) (Certificate, error) {
|
||||
certRes, err := cfg.loadCertResource(domain)
|
||||
if err != nil {
|
||||
return Certificate{}, err
|
||||
}
|
||||
cert, err := cfg.makeCertificateWithOCSP(certRes.Certificate, certRes.PrivateKey)
|
||||
cert, err := makeCertificateWithOCSP(cfg.Storage, certRes.Certificate, certRes.PrivateKey)
|
||||
if err != nil {
|
||||
return cert, err
|
||||
}
|
||||
cert.managed = true
|
||||
if cfg.OnEvent != nil {
|
||||
cfg.OnEvent("cached_managed_cert", cert.Names)
|
||||
}
|
||||
return cfg.cacheCertificate(cert), nil
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// CacheUnmanagedCertificatePEMFile loads a certificate for host using certFile
|
||||
@@ -100,11 +103,11 @@ func (cfg *Config) CacheManagedCertificate(domain string) (Certificate, error) {
|
||||
//
|
||||
// This method is safe for concurrent use.
|
||||
func (cfg *Config) CacheUnmanagedCertificatePEMFile(certFile, keyFile string) error {
|
||||
cert, err := cfg.makeCertificateFromDiskWithOCSP(certFile, keyFile)
|
||||
cert, err := makeCertificateFromDiskWithOCSP(cfg.Storage, certFile, keyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.cacheCertificate(cert)
|
||||
cfg.certCache.cacheCertificate(cert)
|
||||
if cfg.OnEvent != nil {
|
||||
cfg.OnEvent("cached_unmanaged_cert", cert.Names)
|
||||
}
|
||||
@@ -121,14 +124,14 @@ func (cfg *Config) CacheUnmanagedTLSCertificate(tlsCert tls.Certificate) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = cfg.certCache.stapleOCSP(&cert, nil)
|
||||
err = stapleOCSP(cfg.Storage, &cert, nil)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Stapling OCSP: %v", err)
|
||||
}
|
||||
if cfg.OnEvent != nil {
|
||||
cfg.OnEvent("cached_unmanaged_cert", cert.Names)
|
||||
}
|
||||
cfg.cacheCertificate(cert)
|
||||
cfg.certCache.cacheCertificate(cert)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -137,11 +140,11 @@ func (cfg *Config) CacheUnmanagedTLSCertificate(tlsCert tls.Certificate) error {
|
||||
//
|
||||
// This method is safe for concurrent use.
|
||||
func (cfg *Config) CacheUnmanagedCertificatePEMBytes(certBytes, keyBytes []byte) error {
|
||||
cert, err := cfg.makeCertificateWithOCSP(certBytes, keyBytes)
|
||||
cert, err := makeCertificateWithOCSP(cfg.Storage, certBytes, keyBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.cacheCertificate(cert)
|
||||
cfg.certCache.cacheCertificate(cert)
|
||||
if cfg.OnEvent != nil {
|
||||
cfg.OnEvent("cached_unmanaged_cert", cert.Names)
|
||||
}
|
||||
@@ -152,7 +155,7 @@ func (cfg *Config) CacheUnmanagedCertificatePEMBytes(certBytes, keyBytes []byte)
|
||||
// certificate and key files. It fills out all the fields in
|
||||
// the certificate except for the Managed and OnDemand flags.
|
||||
// (It is up to the caller to set those.) It staples OCSP.
|
||||
func (cfg *Config) makeCertificateFromDiskWithOCSP(certFile, keyFile string) (Certificate, error) {
|
||||
func makeCertificateFromDiskWithOCSP(storage Storage, certFile, keyFile string) (Certificate, error) {
|
||||
certPEMBlock, err := ioutil.ReadFile(certFile)
|
||||
if err != nil {
|
||||
return Certificate{}, err
|
||||
@@ -161,7 +164,21 @@ func (cfg *Config) makeCertificateFromDiskWithOCSP(certFile, keyFile string) (Ce
|
||||
if err != nil {
|
||||
return Certificate{}, err
|
||||
}
|
||||
return cfg.makeCertificateWithOCSP(certPEMBlock, keyPEMBlock)
|
||||
return makeCertificateWithOCSP(storage, certPEMBlock, keyPEMBlock)
|
||||
}
|
||||
|
||||
// makeCertificateWithOCSP is the same as makeCertificate except that it also
|
||||
// staples OCSP to the certificate.
|
||||
func makeCertificateWithOCSP(storage Storage, certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
|
||||
cert, err := makeCertificate(certPEMBlock, keyPEMBlock)
|
||||
if err != nil {
|
||||
return cert, err
|
||||
}
|
||||
err = stapleOCSP(storage, &cert, certPEMBlock)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Stapling OCSP: %v", err)
|
||||
}
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// makeCertificate turns a certificate PEM bundle and a key PEM block into
|
||||
@@ -169,7 +186,7 @@ func (cfg *Config) makeCertificateFromDiskWithOCSP(certFile, keyFile string) (Ce
|
||||
// its struct fields for convenience (except for the OnDemand and Managed
|
||||
// flags; it is up to the caller to set those properties!). This function
|
||||
// does NOT staple OCSP.
|
||||
func (*Config) makeCertificate(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
|
||||
func makeCertificate(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
|
||||
var cert Certificate
|
||||
|
||||
// Convert to a tls.Certificate
|
||||
@@ -187,20 +204,6 @@ func (*Config) makeCertificate(certPEMBlock, keyPEMBlock []byte) (Certificate, e
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// makeCertificateWithOCSP is the same as makeCertificate except that it also
|
||||
// staples OCSP to the certificate.
|
||||
func (cfg *Config) makeCertificateWithOCSP(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
|
||||
cert, err := cfg.makeCertificate(certPEMBlock, keyPEMBlock)
|
||||
if err != nil {
|
||||
return cert, err
|
||||
}
|
||||
err = cfg.certCache.stapleOCSP(&cert, certPEMBlock)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Stapling OCSP: %v", err)
|
||||
}
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// fillCertFromLeaf populates metadata fields on cert from tlsCert.
|
||||
func fillCertFromLeaf(cert *Certificate, tlsCert tls.Certificate) error {
|
||||
if len(tlsCert.Certificate) == 0 {
|
||||
@@ -253,11 +256,7 @@ func fillCertFromLeaf(cert *Certificate, tlsCert tls.Certificate) error {
|
||||
// means that another instance renewed the certificate in the
|
||||
// meantime, and it would be a good idea to simply load the cert
|
||||
// into our cache rather than repeating the renewal process again.
|
||||
func managedCertInStorageExpiresSoon(cert Certificate) (bool, error) {
|
||||
if len(cert.configs) == 0 {
|
||||
return false, fmt.Errorf("no configs for certificate")
|
||||
}
|
||||
cfg := cert.configs[0]
|
||||
func (cfg *Config) managedCertInStorageExpiresSoon(cert Certificate) (bool, error) {
|
||||
certRes, err := cfg.loadCertResource(cert.Names[0])
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -274,54 +273,17 @@ func managedCertInStorageExpiresSoon(cert Certificate) (bool, error) {
|
||||
return timeLeft < cfg.RenewDurationBefore, nil
|
||||
}
|
||||
|
||||
// cacheCertificate adds cert to the in-memory cache. If a certificate
|
||||
// with the same hash is already cached, it is NOT overwritten; instead,
|
||||
// cfg is added to the existing certificate's list of configs if not
|
||||
// already in the list. Then all the names on cert are used to add
|
||||
// entries to cfg.certificates (the config's name lookup map).
|
||||
// Then the certificate is stored/updated in the cache. It returns
|
||||
// a copy of the certificate that ends up being stored in the cache.
|
||||
//
|
||||
// It is VERY important, even for some test cases, that the Hash field
|
||||
// of the cert be set properly.
|
||||
//
|
||||
// This function is safe for concurrent use.
|
||||
func (cfg *Config) cacheCertificate(cert Certificate) Certificate {
|
||||
cfg.certCache.mu.Lock()
|
||||
defer cfg.certCache.mu.Unlock()
|
||||
|
||||
// if this certificate already exists in the cache,
|
||||
// use it instead of overwriting it -- very important!
|
||||
if existingCert, ok := cfg.certCache.cache[cert.Hash]; ok {
|
||||
cert = existingCert
|
||||
// reloadManagedCertificate reloads the certificate corresponding to the name(s)
|
||||
// on oldCert into the cache, from storage. This also replaces the old certificate
|
||||
// with the new one, so that all configurations that used the old cert now point
|
||||
// to the new cert.
|
||||
func (cfg *Config) reloadManagedCertificate(oldCert Certificate) error {
|
||||
newCert, err := cfg.loadManagedCertificate(oldCert.Names[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading managed certificate for %v from storage: %v", oldCert.Names, err)
|
||||
}
|
||||
|
||||
// attach this config to the certificate so we know which
|
||||
// configs are referencing/using the certificate, but don't
|
||||
// duplicate entries
|
||||
var found bool
|
||||
for _, c := range cert.configs {
|
||||
if c == cfg {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
cert.configs = append(cert.configs, cfg)
|
||||
}
|
||||
|
||||
// key the certificate by all its names for this config only,
|
||||
// this is how we find the certificate during handshakes
|
||||
// (yes, if certs overlap in the names they serve, one will
|
||||
// overwrite another here, but that's just how it goes)
|
||||
for _, name := range cert.Names {
|
||||
cfg.certificates[NormalizedName(name)] = cert.Hash
|
||||
}
|
||||
|
||||
// store the certificate
|
||||
cfg.certCache.cache[cert.Hash] = cert
|
||||
|
||||
return cert
|
||||
cfg.certCache.replaceCertificate(oldCert, newCert)
|
||||
return nil
|
||||
}
|
||||
|
||||
// HostQualifies returns true if the hostname alone
|
||||
|
||||
207
vendor/github.com/mholt/certmagic/certmagic.go
generated
vendored
207
vendor/github.com/mholt/certmagic/certmagic.go
generated
vendored
@@ -23,8 +23,9 @@
|
||||
// a ready-to-use tls.Config -- whatever layer you need TLS for, CertMagic
|
||||
// makes it easy. See the HTTPS, Listen, and TLS functions for that.
|
||||
//
|
||||
// If you need more control, create a Config using New() and then call
|
||||
// Manage() on the config; but you'll have to be sure to solve the HTTP
|
||||
// If you need more control, create a Cache using NewCache() and then make
|
||||
// a Config using New(). You can then call Manage() on the config. But if
|
||||
// you use this lower-level API, you'll have to be sure to solve the HTTP
|
||||
// and TLS-ALPN challenges yourself (unless you disabled them or use the
|
||||
// DNS challenge) by using the provided Config.GetCertificate function
|
||||
// in your tls.Config and/or Config.HTTPChallangeHandler in your HTTP
|
||||
@@ -45,22 +46,22 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/certcrypto"
|
||||
"github.com/xenolf/lego/challenge"
|
||||
"github.com/go-acme/lego/certcrypto"
|
||||
)
|
||||
|
||||
// HTTPS serves mux for all domainNames using the HTTP
|
||||
// and HTTPS ports, redirecting all HTTP requests to HTTPS.
|
||||
// It uses the Default config.
|
||||
//
|
||||
// This high-level convenience function is opinionated and
|
||||
// applies sane defaults for production use, including
|
||||
// timeouts for HTTP requests and responses. To allow very
|
||||
// long-lived requests or connections, you should make your
|
||||
// own http.Server values and use this package's Listen(),
|
||||
// TLS(), or Config.TLSConfig() functions to customize to
|
||||
// your needs. For example, servers which need to support
|
||||
// large uploads or downloads with slow clients may need to
|
||||
// use longer timeouts, thus this function is not suitable.
|
||||
// long-lived connections, you should make your own
|
||||
// http.Server values and use this package's Listen(), TLS(),
|
||||
// or Config.TLSConfig() functions to customize to your needs.
|
||||
// For example, servers which need to support large uploads or
|
||||
// downloads with slow clients may need to use longer timeouts,
|
||||
// thus this function is not suitable.
|
||||
//
|
||||
// Calling this function signifies your acceptance to
|
||||
// the CA's Subscriber Agreement and/or Terms of Service.
|
||||
@@ -69,7 +70,10 @@ func HTTPS(domainNames []string, mux http.Handler) error {
|
||||
mux = http.DefaultServeMux
|
||||
}
|
||||
|
||||
cfg, err := manageWithDefaultConfig(domainNames, false)
|
||||
Default.Agreed = true
|
||||
cfg := NewDefault()
|
||||
|
||||
err := cfg.Manage(domainNames)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -154,31 +158,40 @@ func httpRedirectHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// TLS enables management of certificates for domainNames
|
||||
// and returns a valid tls.Config.
|
||||
// and returns a valid tls.Config. It uses the Default
|
||||
// config.
|
||||
//
|
||||
// Because this is a convenience function that returns
|
||||
// only a tls.Config, it does not assume HTTP is being
|
||||
// served on the HTTP port, so the HTTP challenge is
|
||||
// disabled (no HTTPChallengeHandler is necessary).
|
||||
// disabled (no HTTPChallengeHandler is necessary). The
|
||||
// package variable Default is modified so that the
|
||||
// HTTP challenge is disabled.
|
||||
//
|
||||
// Calling this function signifies your acceptance to
|
||||
// the CA's Subscriber Agreement and/or Terms of Service.
|
||||
func TLS(domainNames []string) (*tls.Config, error) {
|
||||
cfg, err := manageWithDefaultConfig(domainNames, true)
|
||||
return cfg.TLSConfig(), err
|
||||
Default.Agreed = true
|
||||
Default.DisableHTTPChallenge = true
|
||||
cfg := NewDefault()
|
||||
return cfg.TLSConfig(), cfg.Manage(domainNames)
|
||||
}
|
||||
|
||||
// Listen manages certificates for domainName and returns a
|
||||
// TLS listener.
|
||||
// TLS listener. It uses the Default config.
|
||||
//
|
||||
// Because this convenience function returns only a TLS-enabled
|
||||
// listener and does not presume HTTP is also being served,
|
||||
// the HTTP challenge will be disabled.
|
||||
// the HTTP challenge will be disabled. The package variable
|
||||
// Default is modified so that the HTTP challenge is disabled.
|
||||
//
|
||||
// Calling this function signifies your acceptance to
|
||||
// the CA's Subscriber Agreement and/or Terms of Service.
|
||||
func Listen(domainNames []string) (net.Listener, error) {
|
||||
cfg, err := manageWithDefaultConfig(domainNames, true)
|
||||
Default.Agreed = true
|
||||
Default.DisableHTTPChallenge = true
|
||||
cfg := NewDefault()
|
||||
err := cfg.Manage(domainNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -186,36 +199,36 @@ func Listen(domainNames []string) (net.Listener, error) {
|
||||
}
|
||||
|
||||
// Manage obtains certificates for domainNames and keeps them
|
||||
// renewed using the returned Config.
|
||||
// renewed using the Default config.
|
||||
//
|
||||
// This is a slightly lower-level function; you will need to
|
||||
// wire up support for the ACME challenges yourself. You can
|
||||
// obtain a Config to help you do that by calling NewDefault().
|
||||
//
|
||||
// You will need to ensure that you use a TLS config that gets
|
||||
// certificates from this Config and that the HTTP and TLS-ALPN
|
||||
// challenges can be solved. The easiest way to do this is to
|
||||
// use cfg.TLSConfig() as your TLS config and to wrap your
|
||||
// HTTP handler with cfg.HTTPChallengeHandler(). If you don't
|
||||
// have an HTTP server, you will need to disable the HTTP
|
||||
// challenge.
|
||||
// use NewDefault().TLSConfig() as your TLS config and to wrap
|
||||
// your HTTP handler with NewDefault().HTTPChallengeHandler().
|
||||
// If you don't have an HTTP server, you will need to disable
|
||||
// the HTTP challenge.
|
||||
//
|
||||
// If you already have a TLS config you want to use, you can
|
||||
// simply set its GetCertificate field to cfg.GetCertificate.
|
||||
// simply set its GetCertificate field to
|
||||
// NewDefault().GetCertificate.
|
||||
//
|
||||
// Calling this function signifies your acceptance to
|
||||
// the CA's Subscriber Agreement and/or Terms of Service.
|
||||
func Manage(domainNames []string) (cfg *Config, err error) {
|
||||
return manageWithDefaultConfig(domainNames, false)
|
||||
}
|
||||
|
||||
// manageWithDefaultConfig returns a TLS configuration that
|
||||
// is fully managed for the given names, optionally
|
||||
// with the HTTP challenge disabled.
|
||||
func manageWithDefaultConfig(domainNames []string, disableHTTPChallenge bool) (*Config, error) {
|
||||
cfg := NewDefault()
|
||||
cfg.DisableHTTPChallenge = disableHTTPChallenge
|
||||
return cfg, cfg.Manage(domainNames)
|
||||
func Manage(domainNames []string) error {
|
||||
Default.Agreed = true
|
||||
return NewDefault().Manage(domainNames)
|
||||
}
|
||||
|
||||
// OnDemandConfig contains some state relevant for providing
|
||||
// on-demand TLS.
|
||||
// on-demand TLS. Important note: If you are using the
|
||||
// MaxObtain property to limit the maximum number of certs
|
||||
// to be issued, the count of how many certs were issued
|
||||
// will be reset if this struct gets garbage-collected.
|
||||
type OnDemandConfig struct {
|
||||
// If set, this function will be the absolute
|
||||
// authority on whether the hostname (according
|
||||
@@ -246,6 +259,8 @@ type OnDemandConfig struct {
|
||||
// The number of certificates that have been issued on-demand
|
||||
// by this config. It is only safe to modify this count atomically.
|
||||
// If it reaches MaxObtain, on-demand issuances must fail.
|
||||
// Note that this will necessarily be reset to 0 if the
|
||||
// struct leaves scope and/or gets garbage-collected.
|
||||
obtainedCount int32
|
||||
}
|
||||
|
||||
@@ -405,98 +420,28 @@ func isInternal(addr string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Package defaults
|
||||
var (
|
||||
// The endpoint of the directory for the ACME
|
||||
// CA we are to use
|
||||
CA = LetsEncryptProductionCA
|
||||
|
||||
// The email address to use when creating or
|
||||
// selecting an existing ACME server account
|
||||
Email string
|
||||
|
||||
// The synchronization implementation - all
|
||||
// instances of certmagic in a cluster must
|
||||
// use the same value here, otherwise some
|
||||
// cert operations will not be properly
|
||||
// coordinated
|
||||
Sync Locker
|
||||
|
||||
// Set to true if agreed to the CA's
|
||||
// subscriber agreement
|
||||
Agreed bool
|
||||
|
||||
// Disable all HTTP challenges
|
||||
DisableHTTPChallenge bool
|
||||
|
||||
// Disable all TLS-ALPN challenges
|
||||
DisableTLSALPNChallenge bool
|
||||
|
||||
// How long before expiration to renew certificates
|
||||
RenewDurationBefore = DefaultRenewDurationBefore
|
||||
|
||||
// How long before expiration to require a renewed
|
||||
// certificate when in interactive mode, like when
|
||||
// the program is first starting up (see
|
||||
// mholt/caddy#1680). A wider window between
|
||||
// RenewDurationBefore and this value will suppress
|
||||
// errors under duress (bad) but hopefully this duration
|
||||
// will give it enough time for the blockage to be
|
||||
// relieved.
|
||||
RenewDurationBeforeAtStartup = DefaultRenewDurationBeforeAtStartup
|
||||
|
||||
// An optional event callback clients can set
|
||||
// to subscribe to certain things happening
|
||||
// internally by this config; invocations are
|
||||
// synchronous, so make them return quickly!
|
||||
OnEvent func(event string, data interface{})
|
||||
|
||||
// The host (ONLY the host, not port) to listen
|
||||
// on if necessary to start a listener to solve
|
||||
// an ACME challenge
|
||||
ListenHost string
|
||||
|
||||
// The alternate port to use for the ACME HTTP
|
||||
// challenge; if non-empty, this port will be
|
||||
// used instead of HTTPChallengePort to spin up
|
||||
// a listener for the HTTP challenge
|
||||
AltHTTPPort int
|
||||
|
||||
// The alternate port to use for the ACME
|
||||
// TLS-ALPN challenge; the system must forward
|
||||
// TLSALPNChallengePort to this port for
|
||||
// challenge to succeed
|
||||
AltTLSALPNPort int
|
||||
|
||||
// The DNS provider to use when solving the
|
||||
// ACME DNS challenge
|
||||
DNSProvider challenge.Provider
|
||||
|
||||
// The type of key to use when generating
|
||||
// certificates
|
||||
KeyType = certcrypto.RSA2048
|
||||
|
||||
// The maximum amount of time to allow for
|
||||
// obtaining a certificate. If empty, the
|
||||
// default from the underlying lego lib is
|
||||
// used. If set, it must not be too low so
|
||||
// as to cancel orders too early, running
|
||||
// the risk of rate limiting.
|
||||
CertObtainTimeout time.Duration
|
||||
|
||||
// Set the default server name for clients
|
||||
// not indicating a server name using SNI.
|
||||
// In most cases this will be the primary
|
||||
// domain that is being served.
|
||||
DefaultServerName string
|
||||
|
||||
// The state needed to operate on-demand TLS
|
||||
OnDemand *OnDemandConfig
|
||||
|
||||
// Add the must staple TLS extension to the
|
||||
// CSR generated by lego/acme
|
||||
MustStaple bool
|
||||
)
|
||||
// Default contains the package defaults for the
|
||||
// various Config fields. This is used as a template
|
||||
// when creating your own Configs with New(), and it
|
||||
// is also used as the Config by all the high-level
|
||||
// functions in this package.
|
||||
//
|
||||
// The fields of this value will be used for Config
|
||||
// fields which are unset. Feel free to modify these
|
||||
// defaults, but do not use this Config by itself: it
|
||||
// is only a template. Valid configurations can be
|
||||
// obtained by calling New() (if you have your own
|
||||
// certificate cache) or NewDefault() (if you only
|
||||
// need a single config and want to use the default
|
||||
// cache). This is the only Config which can access
|
||||
// the default certificate cache.
|
||||
var Default = Config{
|
||||
CA: LetsEncryptProductionCA,
|
||||
RenewDurationBefore: DefaultRenewDurationBefore,
|
||||
RenewDurationBeforeAtStartup: DefaultRenewDurationBeforeAtStartup,
|
||||
KeyType: certcrypto.EC256,
|
||||
Storage: defaultFileStorage,
|
||||
}
|
||||
|
||||
const (
|
||||
// HTTPChallengePort is the officially-designated port for
|
||||
@@ -520,16 +465,16 @@ const (
|
||||
var (
|
||||
// HTTPPort is the port on which to serve HTTP
|
||||
// and, by extension, the HTTP challenge (unless
|
||||
// AltHTTPPort is set).
|
||||
// Default.AltHTTPPort is set).
|
||||
HTTPPort = 80
|
||||
|
||||
// HTTPSPort is the port on which to serve HTTPS
|
||||
// and, by extension, the TLS-ALPN challenge
|
||||
// (unless AltTLSALPNPort is set).
|
||||
// (unless Default.AltTLSALPNPort is set).
|
||||
HTTPSPort = 443
|
||||
)
|
||||
|
||||
// Variables for conveniently serving HTTPS
|
||||
// Variables for conveniently serving HTTPS.
|
||||
var (
|
||||
httpLn, httpsLn net.Listener
|
||||
lnMu sync.Mutex
|
||||
|
||||
45
vendor/github.com/mholt/certmagic/client.go
generated
vendored
45
vendor/github.com/mholt/certmagic/client.go
generated
vendored
@@ -23,12 +23,12 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/certificate"
|
||||
"github.com/xenolf/lego/challenge"
|
||||
"github.com/xenolf/lego/challenge/http01"
|
||||
"github.com/xenolf/lego/challenge/tlsalpn01"
|
||||
"github.com/xenolf/lego/lego"
|
||||
"github.com/xenolf/lego/registration"
|
||||
"github.com/go-acme/lego/certificate"
|
||||
"github.com/go-acme/lego/challenge"
|
||||
"github.com/go-acme/lego/challenge/http01"
|
||||
"github.com/go-acme/lego/challenge/tlsalpn01"
|
||||
"github.com/go-acme/lego/lego"
|
||||
"github.com/go-acme/lego/registration"
|
||||
)
|
||||
|
||||
// acmeMu ensures that only one ACME challenge occurs at a time.
|
||||
@@ -52,6 +52,13 @@ func listenerAddressInUse(addr string) bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (cfg *Config) newManager(interactive bool) (Manager, error) {
|
||||
if cfg.NewManager != nil {
|
||||
return cfg.NewManager(interactive)
|
||||
}
|
||||
return cfg.newACMEClient(interactive)
|
||||
}
|
||||
|
||||
func (cfg *Config) newACMEClient(interactive bool) (*acmeClient, error) {
|
||||
// look up or create the user account
|
||||
leUser, err := cfg.getUser(cfg.Email)
|
||||
@@ -62,15 +69,15 @@ func (cfg *Config) newACMEClient(interactive bool) (*acmeClient, error) {
|
||||
// ensure key type and timeout are set
|
||||
keyType := cfg.KeyType
|
||||
if keyType == "" {
|
||||
keyType = KeyType
|
||||
keyType = Default.KeyType
|
||||
}
|
||||
certObtainTimeout := cfg.CertObtainTimeout
|
||||
if certObtainTimeout == 0 {
|
||||
certObtainTimeout = CertObtainTimeout
|
||||
certObtainTimeout = Default.CertObtainTimeout
|
||||
}
|
||||
|
||||
// ensure CA URL (directory endpoint) is set
|
||||
caURL := CA
|
||||
caURL := Default.CA
|
||||
if cfg.CA != "" {
|
||||
caURL = cfg.CA
|
||||
}
|
||||
@@ -91,6 +98,7 @@ func (cfg *Config) newACMEClient(interactive bool) (*acmeClient, error) {
|
||||
clientKey := caURL + leUser.Email + string(keyType)
|
||||
|
||||
// if an underlying client with this configuration already exists, reuse it
|
||||
// TODO: Could this be a global cache instead, perhaps?
|
||||
cfg.acmeClientsMu.Lock()
|
||||
client, ok := cfg.acmeClients[clientKey]
|
||||
if !ok {
|
||||
@@ -232,12 +240,12 @@ func (cfg *Config) lockKey(op, domainName string) string {
|
||||
func (c *acmeClient) Obtain(name string) error {
|
||||
// ensure idempotency of the obtain operation for this name
|
||||
lockKey := c.config.lockKey("cert_acme", name)
|
||||
err := c.config.certCache.storage.Lock(lockKey)
|
||||
err := c.config.Storage.Lock(lockKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := c.config.certCache.storage.Unlock(lockKey); err != nil {
|
||||
if err := c.config.Storage.Unlock(lockKey); err != nil {
|
||||
log.Printf("[ERROR][%s] Obtain: Unable to unlock '%s': %v", name, lockKey, err)
|
||||
}
|
||||
}()
|
||||
@@ -292,12 +300,12 @@ func (c *acmeClient) Obtain(name string) error {
|
||||
func (c *acmeClient) Renew(name string) error {
|
||||
// ensure idempotency of the renew operation for this name
|
||||
lockKey := c.config.lockKey("cert_acme", name)
|
||||
err := c.config.certCache.storage.Lock(lockKey)
|
||||
err := c.config.Storage.Lock(lockKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := c.config.certCache.storage.Unlock(lockKey); err != nil {
|
||||
if err := c.config.Storage.Unlock(lockKey); err != nil {
|
||||
log.Printf("[ERROR][%s] Renew: Unable to unlock '%s': %v", name, lockKey, err)
|
||||
}
|
||||
}()
|
||||
@@ -351,7 +359,7 @@ func (c *acmeClient) Renew(name string) error {
|
||||
// Revoke revokes the certificate for name and deletes
|
||||
// it from storage.
|
||||
func (c *acmeClient) Revoke(name string) error {
|
||||
if !c.config.certCache.storage.Exists(StorageKeys.SitePrivateKey(c.config.CA, name)) {
|
||||
if !c.config.Storage.Exists(StorageKeys.SitePrivateKey(c.config.CA, name)) {
|
||||
return fmt.Errorf("private key not found for %s", name)
|
||||
}
|
||||
|
||||
@@ -369,15 +377,15 @@ func (c *acmeClient) Revoke(name string) error {
|
||||
c.config.OnEvent("acme_cert_revoked", name)
|
||||
}
|
||||
|
||||
err = c.config.certCache.storage.Delete(StorageKeys.SiteCert(c.config.CA, name))
|
||||
err = c.config.Storage.Delete(StorageKeys.SiteCert(c.config.CA, name))
|
||||
if err != nil {
|
||||
return fmt.Errorf("certificate revoked, but unable to delete certificate file: %v", err)
|
||||
}
|
||||
err = c.config.certCache.storage.Delete(StorageKeys.SitePrivateKey(c.config.CA, name))
|
||||
err = c.config.Storage.Delete(StorageKeys.SitePrivateKey(c.config.CA, name))
|
||||
if err != nil {
|
||||
return fmt.Errorf("certificate revoked, but unable to delete private key: %v", err)
|
||||
}
|
||||
err = c.config.certCache.storage.Delete(StorageKeys.SiteMeta(c.config.CA, name))
|
||||
err = c.config.Storage.Delete(StorageKeys.SiteMeta(c.config.CA, name))
|
||||
if err != nil {
|
||||
return fmt.Errorf("certificate revoked, but unable to delete certificate metadata: %v", err)
|
||||
}
|
||||
@@ -398,3 +406,6 @@ var (
|
||||
UserAgent string
|
||||
HTTPTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
// Interface guard
|
||||
var _ Manager = (*acmeClient)(nil)
|
||||
|
||||
210
vendor/github.com/mholt/certmagic/config.go
generated
vendored
210
vendor/github.com/mholt/certmagic/config.go
generated
vendored
@@ -20,11 +20,11 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/certcrypto"
|
||||
"github.com/xenolf/lego/certificate"
|
||||
"github.com/xenolf/lego/challenge"
|
||||
"github.com/xenolf/lego/challenge/tlsalpn01"
|
||||
"github.com/xenolf/lego/lego"
|
||||
"github.com/go-acme/lego/certcrypto"
|
||||
"github.com/go-acme/lego/certificate"
|
||||
"github.com/go-acme/lego/challenge"
|
||||
"github.com/go-acme/lego/challenge/tlsalpn01"
|
||||
"github.com/go-acme/lego/lego"
|
||||
)
|
||||
|
||||
// Config configures a certificate manager instance.
|
||||
@@ -74,7 +74,7 @@ type Config struct {
|
||||
ListenHost string
|
||||
|
||||
// The alternate port to use for the ACME HTTP
|
||||
// challenge; if non-empty, this port will be
|
||||
// challenge; if non-empty, this port will be
|
||||
// used instead of HTTPChallengePort to spin up
|
||||
// a listener for the HTTP challenge
|
||||
AltHTTPPort int
|
||||
@@ -113,117 +113,167 @@ type Config struct {
|
||||
// CSR generated by lego/acme
|
||||
MustStaple bool
|
||||
|
||||
// Map of hostname to certificate hash; used
|
||||
// to complete handshakes and serve the right
|
||||
// certificate given SNI
|
||||
certificates map[string]string
|
||||
// The storage to access when storing or
|
||||
// loading TLS assets
|
||||
Storage Storage
|
||||
|
||||
// Pointer to the certificate store to use
|
||||
// NewManager returns a new Manager. If nil,
|
||||
// an ACME client will be created and used.
|
||||
NewManager func(interactive bool) (Manager, error)
|
||||
|
||||
// Pointer to the in-memory certificate cache
|
||||
certCache *Cache
|
||||
|
||||
// Map of client config key to ACME clients
|
||||
// so they can be reused
|
||||
// TODO: It might be better if these were globally cached, rather than per-config, which are ephemeral... but maybe evict them after a certain time, like 1 day or something
|
||||
acmeClients map[string]*lego.Client
|
||||
acmeClientsMu *sync.Mutex
|
||||
}
|
||||
|
||||
// NewDefault returns a new, valid, default config.
|
||||
// NewDefault makes a valid config based on the package
|
||||
// Default config. Most users will call this function
|
||||
// instead of New() since most use cases require only a
|
||||
// single config for any and all certificates.
|
||||
//
|
||||
// Calling this function signifies your acceptance to
|
||||
// the CA's Subscriber Agreement and/or Terms of Service.
|
||||
// If your requirements are more advanced (for example,
|
||||
// multiple configs depending on the certificate), then use
|
||||
// New() instead. (You will need to make your own Cache
|
||||
// first.) If you only need a single Config to manage your
|
||||
// certs (even if that config changes, as long as it is the
|
||||
// only one), customize the Default package variable before
|
||||
// calling NewDefault().
|
||||
//
|
||||
// All calls to NewDefault() will return configs that use the
|
||||
// same, default certificate cache. All configs returned
|
||||
// by NewDefault() are based on the values of the fields of
|
||||
// Default at the time it is called.
|
||||
func NewDefault() *Config {
|
||||
return New(Config{Agreed: true})
|
||||
}
|
||||
|
||||
// New makes a valid config based on cfg and uses
|
||||
// a default certificate cache. All calls to
|
||||
// New() will use the same certificate cache.
|
||||
func New(cfg Config) *Config {
|
||||
return NewWithCache(nil, cfg)
|
||||
}
|
||||
|
||||
// NewWithCache makes a valid new config based on cfg
|
||||
// and uses the provided certificate cache. If certCache
|
||||
// is nil, a new, default one will be created using
|
||||
// DefaultStorage; or, if a default cache has already
|
||||
// been created, it will be reused.
|
||||
func NewWithCache(certCache *Cache, cfg Config) *Config {
|
||||
// avoid nil pointers with sensible defaults,
|
||||
// careful to initialize a default cache (which
|
||||
// begins its maintenance goroutine) only if
|
||||
// needed - and only once (we don't initialize
|
||||
// it at package init to give importers a chance
|
||||
// to set DefaultStorage if they so desire)
|
||||
if certCache == nil {
|
||||
defaultCacheMu.Lock()
|
||||
if defaultCache == nil {
|
||||
defaultCache = NewCache(DefaultStorage)
|
||||
}
|
||||
certCache = defaultCache
|
||||
defaultCacheMu.Unlock()
|
||||
defaultCacheMu.Lock()
|
||||
if defaultCache == nil {
|
||||
defaultCache = NewCache(CacheOptions{
|
||||
// the cache will likely need to renew certificates,
|
||||
// so it will need to know how to do that, which
|
||||
// depends on the certificate being managed and which
|
||||
// can change during the lifetime of the cache; this
|
||||
// callback makes it possible to get the latest and
|
||||
// correct config with which to manage the cert,
|
||||
// but if the user does not provide one, we can only
|
||||
// assume that we are to use the default config
|
||||
GetConfigForCert: func(Certificate) (Config, error) {
|
||||
return Default, nil
|
||||
},
|
||||
})
|
||||
}
|
||||
if certCache.storage == nil {
|
||||
certCache.storage = DefaultStorage
|
||||
certCache := defaultCache
|
||||
defaultCacheMu.Unlock()
|
||||
|
||||
return newWithCache(certCache, Default)
|
||||
}
|
||||
|
||||
// New makes a new, valid config based on cfg and
|
||||
// uses the provided certificate cache. certCache
|
||||
// MUST NOT be nil or this function will panic.
|
||||
//
|
||||
// Use this method when you have an advanced use case
|
||||
// that requires a custom certificate cache and config
|
||||
// that may differ from the Default. For example, if
|
||||
// not all certificates are managed/renewed the same
|
||||
// way, you need to make your own Cache value with a
|
||||
// GetConfigForCert callback that returns the correct
|
||||
// configuration for each certificate. However, for
|
||||
// the vast majority of cases, there will be only a
|
||||
// single Config, thus the default cache (which always
|
||||
// uses the default Config) and default config will
|
||||
// suffice, and you should use New() instead.
|
||||
func New(certCache *Cache, cfg Config) *Config {
|
||||
if certCache == nil {
|
||||
panic("a certificate cache is required")
|
||||
}
|
||||
if certCache.options.GetConfigForCert == nil {
|
||||
panic("cache must have GetConfigForCert set in its options")
|
||||
}
|
||||
return newWithCache(certCache, cfg)
|
||||
}
|
||||
|
||||
// newWithCache ensures that cfg is a valid config by populating
|
||||
// zero-value fields from the Default Config. If certCache is
|
||||
// nil, this function panics.
|
||||
func newWithCache(certCache *Cache, cfg Config) *Config {
|
||||
if certCache == nil {
|
||||
panic("cannot make a valid config without a pointer to a certificate cache")
|
||||
}
|
||||
|
||||
// fill in default values
|
||||
if cfg.CA == "" {
|
||||
cfg.CA = CA
|
||||
cfg.CA = Default.CA
|
||||
}
|
||||
if cfg.Email == "" {
|
||||
cfg.Email = Email
|
||||
cfg.Email = Default.Email
|
||||
}
|
||||
if cfg.OnDemand == nil {
|
||||
cfg.OnDemand = OnDemand
|
||||
cfg.OnDemand = Default.OnDemand
|
||||
}
|
||||
if !cfg.Agreed {
|
||||
cfg.Agreed = Agreed
|
||||
cfg.Agreed = Default.Agreed
|
||||
}
|
||||
if !cfg.DisableHTTPChallenge {
|
||||
cfg.DisableHTTPChallenge = DisableHTTPChallenge
|
||||
cfg.DisableHTTPChallenge = Default.DisableHTTPChallenge
|
||||
}
|
||||
if !cfg.DisableTLSALPNChallenge {
|
||||
cfg.DisableTLSALPNChallenge = DisableTLSALPNChallenge
|
||||
cfg.DisableTLSALPNChallenge = Default.DisableTLSALPNChallenge
|
||||
}
|
||||
if cfg.RenewDurationBefore == 0 {
|
||||
cfg.RenewDurationBefore = RenewDurationBefore
|
||||
cfg.RenewDurationBefore = Default.RenewDurationBefore
|
||||
}
|
||||
if cfg.RenewDurationBeforeAtStartup == 0 {
|
||||
cfg.RenewDurationBeforeAtStartup = RenewDurationBeforeAtStartup
|
||||
cfg.RenewDurationBeforeAtStartup = Default.RenewDurationBeforeAtStartup
|
||||
}
|
||||
if cfg.OnEvent == nil {
|
||||
cfg.OnEvent = OnEvent
|
||||
cfg.OnEvent = Default.OnEvent
|
||||
}
|
||||
if cfg.ListenHost == "" {
|
||||
cfg.ListenHost = ListenHost
|
||||
cfg.ListenHost = Default.ListenHost
|
||||
}
|
||||
if cfg.AltHTTPPort == 0 {
|
||||
cfg.AltHTTPPort = AltHTTPPort
|
||||
cfg.AltHTTPPort = Default.AltHTTPPort
|
||||
}
|
||||
if cfg.AltTLSALPNPort == 0 {
|
||||
cfg.AltTLSALPNPort = AltTLSALPNPort
|
||||
cfg.AltTLSALPNPort = Default.AltTLSALPNPort
|
||||
}
|
||||
if cfg.DNSProvider == nil {
|
||||
cfg.DNSProvider = DNSProvider
|
||||
cfg.DNSProvider = Default.DNSProvider
|
||||
}
|
||||
if cfg.KeyType == "" {
|
||||
cfg.KeyType = KeyType
|
||||
cfg.KeyType = Default.KeyType
|
||||
}
|
||||
if cfg.CertObtainTimeout == 0 {
|
||||
cfg.CertObtainTimeout = CertObtainTimeout
|
||||
cfg.CertObtainTimeout = Default.CertObtainTimeout
|
||||
}
|
||||
if cfg.DefaultServerName == "" {
|
||||
cfg.DefaultServerName = DefaultServerName
|
||||
cfg.DefaultServerName = Default.DefaultServerName
|
||||
}
|
||||
if cfg.OnDemand == nil {
|
||||
cfg.OnDemand = OnDemand
|
||||
cfg.OnDemand = Default.OnDemand
|
||||
}
|
||||
if !cfg.MustStaple {
|
||||
cfg.MustStaple = MustStaple
|
||||
cfg.MustStaple = Default.MustStaple
|
||||
}
|
||||
if cfg.Storage == nil {
|
||||
cfg.Storage = Default.Storage
|
||||
}
|
||||
if cfg.NewManager == nil {
|
||||
cfg.NewManager = Default.NewManager
|
||||
}
|
||||
|
||||
// absolutely don't allow a nil storage,
|
||||
// because that would make almost anything
|
||||
// a config can do pointless
|
||||
if cfg.Storage == nil {
|
||||
cfg.Storage = defaultFileStorage
|
||||
}
|
||||
|
||||
// ensure the unexported fields are valid
|
||||
cfg.certificates = make(map[string]string)
|
||||
cfg.certCache = certCache
|
||||
cfg.acmeClients = make(map[string]*lego.Client)
|
||||
cfg.acmeClientsMu = new(sync.Mutex)
|
||||
@@ -272,7 +322,7 @@ func (cfg *Config) Manage(domainNames []string) error {
|
||||
}
|
||||
|
||||
// for existing certificates, make sure it is renewed
|
||||
if cert.NeedsRenewal() {
|
||||
if cert.NeedsRenewal(cfg) {
|
||||
err := cfg.RenewCert(domainName, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: renewing certificate: %v", domainName, err)
|
||||
@@ -303,11 +353,11 @@ func (cfg *Config) ObtainCert(name string, interactive bool) error {
|
||||
if skip {
|
||||
return nil
|
||||
}
|
||||
client, err := cfg.newACMEClient(interactive)
|
||||
manager, err := cfg.newManager(interactive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return client.Obtain(name)
|
||||
return manager.Obtain(name)
|
||||
}
|
||||
|
||||
// RenewCert renews the certificate for name using cfg. It stows the
|
||||
@@ -320,20 +370,20 @@ func (cfg *Config) RenewCert(name string, interactive bool) error {
|
||||
if skip {
|
||||
return nil
|
||||
}
|
||||
client, err := cfg.newACMEClient(interactive)
|
||||
manager, err := cfg.newManager(interactive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return client.Renew(name)
|
||||
return manager.Renew(name)
|
||||
}
|
||||
|
||||
// RevokeCert revokes the certificate for domain via ACME protocol.
|
||||
func (cfg *Config) RevokeCert(domain string, interactive bool) error {
|
||||
client, err := cfg.newACMEClient(interactive)
|
||||
manager, err := cfg.newManager(interactive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return client.Revoke(domain)
|
||||
return manager.Revoke(domain)
|
||||
}
|
||||
|
||||
// TLSConfig is an opinionated method that returns a
|
||||
@@ -394,18 +444,26 @@ func (cfg *Config) storageHasCertResources(domain string) bool {
|
||||
certKey := StorageKeys.SiteCert(cfg.CA, domain)
|
||||
keyKey := StorageKeys.SitePrivateKey(cfg.CA, domain)
|
||||
metaKey := StorageKeys.SiteMeta(cfg.CA, domain)
|
||||
return cfg.certCache.storage.Exists(certKey) &&
|
||||
cfg.certCache.storage.Exists(keyKey) &&
|
||||
cfg.certCache.storage.Exists(metaKey)
|
||||
return cfg.Storage.Exists(certKey) &&
|
||||
cfg.Storage.Exists(keyKey) &&
|
||||
cfg.Storage.Exists(metaKey)
|
||||
}
|
||||
|
||||
// managedCertNeedsRenewal returns true if certRes is
|
||||
// expiring soon or already expired, or if the process
|
||||
// of checking the expiration returned an error.
|
||||
func (cfg *Config) managedCertNeedsRenewal(certRes certificate.Resource) bool {
|
||||
cert, err := cfg.makeCertificate(certRes.Certificate, certRes.PrivateKey)
|
||||
cert, err := makeCertificate(certRes.Certificate, certRes.PrivateKey)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
return cert.NeedsRenewal()
|
||||
return cert.NeedsRenewal(cfg)
|
||||
}
|
||||
|
||||
// Manager is a type that can manage a certificate.
|
||||
// They are usually very short-lived.
|
||||
type Manager interface {
|
||||
Obtain(name string) error
|
||||
Renew(name string) error
|
||||
Revoke(name string) error
|
||||
}
|
||||
|
||||
10
vendor/github.com/mholt/certmagic/crypto.go
generated
vendored
10
vendor/github.com/mholt/certmagic/crypto.go
generated
vendored
@@ -26,8 +26,8 @@ import (
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
|
||||
"github.com/go-acme/lego/certificate"
|
||||
"github.com/klauspost/cpuid"
|
||||
"github.com/xenolf/lego/certificate"
|
||||
)
|
||||
|
||||
// encodePrivateKey marshals a EC or RSA private key into a PEM-encoded array of bytes.
|
||||
@@ -119,20 +119,20 @@ func (cfg *Config) saveCertResource(cert *certificate.Resource) error {
|
||||
},
|
||||
}
|
||||
|
||||
return storeTx(cfg.certCache.storage, all)
|
||||
return storeTx(cfg.Storage, all)
|
||||
}
|
||||
|
||||
func (cfg *Config) loadCertResource(domain string) (certificate.Resource, error) {
|
||||
var certRes certificate.Resource
|
||||
certBytes, err := cfg.certCache.storage.Load(StorageKeys.SiteCert(cfg.CA, domain))
|
||||
certBytes, err := cfg.Storage.Load(StorageKeys.SiteCert(cfg.CA, domain))
|
||||
if err != nil {
|
||||
return certRes, err
|
||||
}
|
||||
keyBytes, err := cfg.certCache.storage.Load(StorageKeys.SitePrivateKey(cfg.CA, domain))
|
||||
keyBytes, err := cfg.Storage.Load(StorageKeys.SitePrivateKey(cfg.CA, domain))
|
||||
if err != nil {
|
||||
return certRes, err
|
||||
}
|
||||
metaBytes, err := cfg.certCache.storage.Load(StorageKeys.SiteMeta(cfg.CA, domain))
|
||||
metaBytes, err := cfg.Storage.Load(StorageKeys.SiteMeta(cfg.CA, domain))
|
||||
if err != nil {
|
||||
return certRes, err
|
||||
}
|
||||
|
||||
34
vendor/github.com/mholt/certmagic/handshake.go
generated
vendored
34
vendor/github.com/mholt/certmagic/handshake.go
generated
vendored
@@ -25,7 +25,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/challenge/tlsalpn01"
|
||||
"github.com/go-acme/lego/challenge/tlsalpn01"
|
||||
)
|
||||
|
||||
// GetCertificate gets a certificate to satisfy clientHello. In getting
|
||||
@@ -94,10 +94,6 @@ func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certif
|
||||
func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate, matched, defaulted bool) {
|
||||
name := NormalizedName(hello.ServerName)
|
||||
|
||||
cfg.certCache.mu.RLock()
|
||||
defer cfg.certCache.mu.RUnlock()
|
||||
|
||||
var certKey string
|
||||
var ok bool
|
||||
|
||||
if name == "" {
|
||||
@@ -108,8 +104,7 @@ func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate,
|
||||
if err == nil {
|
||||
addr = ip
|
||||
}
|
||||
if certKey, ok = cfg.certificates[addr]; ok {
|
||||
cert = cfg.certCache.cache[certKey]
|
||||
if cert, ok = cfg.certCache.getFirstMatchingCert(addr); ok {
|
||||
matched = true
|
||||
return
|
||||
}
|
||||
@@ -118,16 +113,14 @@ func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate,
|
||||
// fall back to a "default" certificate, if specified
|
||||
if cfg.DefaultServerName != "" {
|
||||
normDefault := NormalizedName(cfg.DefaultServerName)
|
||||
if certKey, ok := cfg.certificates[normDefault]; ok {
|
||||
cert = cfg.certCache.cache[certKey]
|
||||
if cert, ok = cfg.certCache.getFirstMatchingCert(normDefault); ok {
|
||||
defaulted = true
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if SNI is specified, try an exact match first
|
||||
if certKey, ok = cfg.certificates[name]; ok {
|
||||
cert = cfg.certCache.cache[certKey]
|
||||
if cert, ok = cfg.certCache.getFirstMatchingCert(name); ok {
|
||||
matched = true
|
||||
return
|
||||
}
|
||||
@@ -138,8 +131,7 @@ func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate,
|
||||
for i := range labels {
|
||||
labels[i] = "*"
|
||||
candidate := strings.Join(labels, ".")
|
||||
if certKey, ok = cfg.certificates[candidate]; ok {
|
||||
cert = cfg.certCache.cache[certKey]
|
||||
if cert, ok = cfg.certCache.getFirstMatchingCert(candidate); ok {
|
||||
matched = true
|
||||
return
|
||||
}
|
||||
@@ -153,7 +145,10 @@ func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate,
|
||||
// whether it complies with RFC 6066 about SNI, but I think
|
||||
// it does, soooo...)
|
||||
// (this is how we solved the former ACME TLS-SNI challenge)
|
||||
if directCert, ok := cfg.certCache.cache[name]; ok {
|
||||
cfg.certCache.mu.RLock()
|
||||
directCert, ok := cfg.certCache.cache[name]
|
||||
cfg.certCache.mu.RUnlock()
|
||||
if ok {
|
||||
cert = directCert
|
||||
matched = true
|
||||
return
|
||||
@@ -316,7 +311,7 @@ func (cfg *Config) handshakeMaintenance(hello *tls.ClientHelloInfo, cert Certifi
|
||||
if cert.OCSP != nil {
|
||||
refreshTime := cert.OCSP.ThisUpdate.Add(cert.OCSP.NextUpdate.Sub(cert.OCSP.ThisUpdate) / 2)
|
||||
if time.Now().After(refreshTime) {
|
||||
err := cfg.certCache.stapleOCSP(&cert, nil)
|
||||
err := stapleOCSP(cfg.Storage, &cert, nil)
|
||||
if err != nil {
|
||||
// An error with OCSP stapling is not the end of the world, and in fact, is
|
||||
// quite common considering not all certs have issuer URLs that support it.
|
||||
@@ -362,15 +357,12 @@ func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCe
|
||||
// even though the recursive nature of the dynamic cert loading
|
||||
// would just call this function anyway, we do it here to
|
||||
// make the replacement as atomic as possible.
|
||||
newCert, err := currentCert.configs[0].CacheManagedCertificate(name)
|
||||
newCert, err := cfg.CacheManagedCertificate(name)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] loading renewed certificate for %s: %v", name, err)
|
||||
} else {
|
||||
// replace the old certificate with the new one
|
||||
err = cfg.certCache.replaceCertificate(currentCert, newCert)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Replacing certificate for %s: %v", name, err)
|
||||
}
|
||||
cfg.certCache.replaceCertificate(currentCert, newCert)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,7 +388,7 @@ func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCe
|
||||
// A boolean true is returned if a valid certificate is returned.
|
||||
func (cfg *Config) tryDistributedChallengeSolver(clientHello *tls.ClientHelloInfo) (Certificate, bool, error) {
|
||||
tokenKey := distributedSolver{config: cfg}.challengeTokensKey(clientHello.ServerName)
|
||||
chalInfoBytes, err := cfg.certCache.storage.Load(tokenKey)
|
||||
chalInfoBytes, err := cfg.Storage.Load(tokenKey)
|
||||
if err != nil {
|
||||
if _, ok := err.(ErrNotExist); ok {
|
||||
return Certificate{}, false, nil
|
||||
|
||||
12
vendor/github.com/mholt/certmagic/httphandler.go
generated
vendored
12
vendor/github.com/mholt/certmagic/httphandler.go
generated
vendored
@@ -20,7 +20,7 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/xenolf/lego/challenge/http01"
|
||||
"github.com/go-acme/lego/challenge/http01"
|
||||
)
|
||||
|
||||
// HTTPChallengeHandler wraps h in a handler that can solve the ACME
|
||||
@@ -57,7 +57,7 @@ func (cfg *Config) HandleHTTPChallenge(w http.ResponseWriter, r *http.Request) b
|
||||
if cfg.DisableHTTPChallenge {
|
||||
return false
|
||||
}
|
||||
if !strings.HasPrefix(r.URL.Path, challengeBasePath) {
|
||||
if !LooksLikeHTTPChallenge(r) {
|
||||
return false
|
||||
}
|
||||
return cfg.distributedHTTPChallengeSolver(w, r)
|
||||
@@ -73,7 +73,7 @@ func (cfg *Config) distributedHTTPChallengeSolver(w http.ResponseWriter, r *http
|
||||
}
|
||||
|
||||
tokenKey := distributedSolver{config: cfg}.challengeTokensKey(r.Host)
|
||||
chalInfoBytes, err := cfg.certCache.storage.Load(tokenKey)
|
||||
chalInfoBytes, err := cfg.Storage.Load(tokenKey)
|
||||
if err != nil {
|
||||
if _, ok := err.(ErrNotExist); !ok {
|
||||
log.Printf("[ERROR][%s] Opening distributed HTTP challenge token file: %v", r.Host, err)
|
||||
@@ -108,4 +108,10 @@ func answerHTTPChallenge(w http.ResponseWriter, r *http.Request, chalInfo challe
|
||||
return false
|
||||
}
|
||||
|
||||
// LooksLikeHTTPChallenge returns true if r looks like an ACME
|
||||
// HTTP challenge request from an ACME server.
|
||||
func LooksLikeHTTPChallenge(r *http.Request) bool {
|
||||
return r.Method == "GET" && strings.HasPrefix(r.URL.Path, challengeBasePath)
|
||||
}
|
||||
|
||||
const challengeBasePath = "/.well-known/acme-challenge"
|
||||
|
||||
138
vendor/github.com/mholt/certmagic/maintain.go
generated
vendored
138
vendor/github.com/mholt/certmagic/maintain.go
generated
vendored
@@ -24,37 +24,34 @@ import (
|
||||
// maintainAssets is a permanently-blocking function
|
||||
// that loops indefinitely and, on a regular schedule, checks
|
||||
// certificates for expiration and initiates a renewal of certs
|
||||
// that are expiring soon. It also updates OCSP stapling and
|
||||
// performs other maintenance of assets. It should only be
|
||||
// called once per process.
|
||||
//
|
||||
// You must pass in the channel which you'll close when
|
||||
// maintenance should stop, to allow this goroutine to clean up
|
||||
// after itself and unblock. (Not that you HAVE to stop it...)
|
||||
// that are expiring soon. It also updates OCSP stapling. It
|
||||
// should only be called once per cache.
|
||||
func (certCache *Cache) maintainAssets() {
|
||||
renewalTicker := time.NewTicker(certCache.RenewInterval)
|
||||
ocspTicker := time.NewTicker(certCache.OCSPInterval)
|
||||
renewalTicker := time.NewTicker(certCache.options.RenewCheckInterval)
|
||||
ocspTicker := time.NewTicker(certCache.options.OCSPCheckInterval)
|
||||
|
||||
log.Printf("[INFO][%s] Started certificate maintenance routine", certCache.storage)
|
||||
log.Printf("[INFO][cache:%p] Started certificate maintenance routine", certCache)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-renewalTicker.C:
|
||||
log.Printf("[INFO][%s] Scanning for expiring certificates", certCache.storage)
|
||||
log.Printf("[INFO][cache:%p] Scanning for expiring certificates", certCache)
|
||||
err := certCache.RenewManagedCertificates(false)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR][%s] Renewing managed certificates: %v", certCache.storage, err)
|
||||
log.Printf("[ERROR][cache:%p] Renewing managed certificates: %v", certCache, err)
|
||||
}
|
||||
log.Printf("[INFO][%s] Done scanning certificates", certCache.storage)
|
||||
log.Printf("[INFO][cache:%p] Done scanning certificates", certCache)
|
||||
case <-ocspTicker.C:
|
||||
log.Printf("[INFO][%s] Scanning for stale OCSP staples", certCache.storage)
|
||||
log.Printf("[INFO][cache:%p] Scanning for stale OCSP staples", certCache)
|
||||
certCache.updateOCSPStaples()
|
||||
certCache.deleteOldStapleFiles()
|
||||
log.Printf("[INFO][%s] Done checking OCSP staples", certCache.storage)
|
||||
// certCache.deleteOldStapleFiles()
|
||||
log.Printf("[INFO][cache:%p] Done checking OCSP staples", certCache)
|
||||
case <-certCache.stopChan:
|
||||
renewalTicker.Stop()
|
||||
ocspTicker.Stop()
|
||||
log.Printf("[INFO][%s] Stopped certificate maintenance routine", certCache.storage)
|
||||
// TODO: stop any in-progress maintenance operations and clear locks we made
|
||||
log.Printf("[INFO][cache:%p] Stopped certificate maintenance routine", certCache)
|
||||
close(certCache.doneChan)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -65,6 +62,10 @@ func (certCache *Cache) maintainAssets() {
|
||||
// automatically on a regular basis; normally you will not
|
||||
// need to call this.
|
||||
func (certCache *Cache) RenewManagedCertificates(interactive bool) error {
|
||||
// configs will hold a map of certificate name to the config
|
||||
// to use when managing that certificate
|
||||
configs := make(map[string]*Config)
|
||||
|
||||
// we use the queues for a very important reason: to do any and all
|
||||
// operations that could require an exclusive write lock outside
|
||||
// of the read lock! otherwise we get a deadlock, yikes. in other
|
||||
@@ -75,11 +76,6 @@ func (certCache *Cache) RenewManagedCertificates(interactive bool) error {
|
||||
|
||||
certCache.mu.RLock()
|
||||
for certKey, cert := range certCache.cache {
|
||||
if len(cert.configs) == 0 {
|
||||
// this is bad if this happens, probably a programmer error (oops)
|
||||
log.Printf("[ERROR] No associated TLS config for certificate with names %v; unable to manage", cert.Names)
|
||||
continue
|
||||
}
|
||||
if !cert.managed {
|
||||
continue
|
||||
}
|
||||
@@ -91,13 +87,26 @@ func (certCache *Cache) RenewManagedCertificates(interactive bool) error {
|
||||
continue
|
||||
}
|
||||
|
||||
// get the config associated with this certificate
|
||||
cfg, err := certCache.getConfig(cert)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Getting configuration to manage certificate for names %v; unable to renew: %v", cert.Names, err)
|
||||
continue
|
||||
}
|
||||
if cfg == nil {
|
||||
// this is bad if this happens, probably a programmer error (oops)
|
||||
log.Printf("[ERROR] No configuration associated with certificate for names %v; unable to manage", cert.Names)
|
||||
continue
|
||||
}
|
||||
configs[cert.Names[0]] = cfg
|
||||
|
||||
// if time is up or expires soon, we need to try to renew it
|
||||
if cert.NeedsRenewal() {
|
||||
if cert.NeedsRenewal(cfg) {
|
||||
// see if the certificate in storage has already been renewed, possibly by another
|
||||
// instance that didn't coordinate with this one; if so, just load it (this
|
||||
// might happen if another instance already renewed it - kinda sloppy but checking disk
|
||||
// first is a simple way to possibly drastically reduce rate limit problems)
|
||||
storedCertExpiring, err := managedCertInStorageExpiresSoon(cert)
|
||||
storedCertExpiring, err := cfg.managedCertInStorageExpiresSoon(cert)
|
||||
if err != nil {
|
||||
// hmm, weird, but not a big deal, maybe it was deleted or something
|
||||
log.Printf("[NOTICE] Error while checking if certificate for %v in storage is also expiring soon: %v",
|
||||
@@ -125,7 +134,9 @@ func (certCache *Cache) RenewManagedCertificates(interactive bool) error {
|
||||
log.Printf("[INFO] Certificate for %v expires in %v, but is already renewed in storage; reloading stored certificate",
|
||||
oldCert.Names, timeLeft)
|
||||
|
||||
err := certCache.reloadManagedCertificate(oldCert)
|
||||
cfg := configs[oldCert.Names[0]]
|
||||
|
||||
err := cfg.reloadManagedCertificate(oldCert)
|
||||
if err != nil {
|
||||
if interactive {
|
||||
return err // operator is present, so report error immediately
|
||||
@@ -139,28 +150,30 @@ func (certCache *Cache) RenewManagedCertificates(interactive bool) error {
|
||||
timeLeft := oldCert.NotAfter.Sub(time.Now().UTC())
|
||||
log.Printf("[INFO] Certificate for %v expires in %v; attempting renewal", oldCert.Names, timeLeft)
|
||||
|
||||
cfg := configs[oldCert.Names[0]]
|
||||
|
||||
// Get the name which we should use to renew this certificate;
|
||||
// we only support managing certificates with one name per cert,
|
||||
// so this should be easy.
|
||||
renewName := oldCert.Names[0]
|
||||
|
||||
// perform renewal
|
||||
err := oldCert.configs[0].RenewCert(renewName, interactive)
|
||||
err := cfg.RenewCert(renewName, interactive)
|
||||
if err != nil {
|
||||
if interactive {
|
||||
// Certificate renewal failed and the operator is present. See a discussion about
|
||||
// this in issue mholt/caddy#642. For a while, we only stopped if the certificate
|
||||
// was expired, but in reality, there is no difference between reporting it now
|
||||
// versus later, except that there's somebody present to deal withit right now.
|
||||
// versus later, except that there's somebody present to deal with it right now.
|
||||
// Follow-up: See issue mholt/caddy#1680. Only fail in this case if the certificate
|
||||
// is dangerously close to expiration.
|
||||
timeLeft := oldCert.NotAfter.Sub(time.Now().UTC())
|
||||
if timeLeft < oldCert.configs[0].RenewDurationBeforeAtStartup {
|
||||
if timeLeft < cfg.RenewDurationBeforeAtStartup {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Printf("[ERROR] %v", err)
|
||||
if oldCert.configs[0].OnDemand != nil {
|
||||
if cfg.OnDemand != nil {
|
||||
// loaded dynamically, remove dynamically
|
||||
deleteQueue = append(deleteQueue, oldCert)
|
||||
}
|
||||
@@ -169,7 +182,7 @@ func (certCache *Cache) RenewManagedCertificates(interactive bool) error {
|
||||
|
||||
// successful renewal, so update in-memory cache by loading
|
||||
// renewed certificate so it will be used with handshakes
|
||||
err = certCache.reloadManagedCertificate(oldCert)
|
||||
err = cfg.reloadManagedCertificate(oldCert)
|
||||
if err != nil {
|
||||
if interactive {
|
||||
return err // operator is present, so report error immediately
|
||||
@@ -180,18 +193,7 @@ func (certCache *Cache) RenewManagedCertificates(interactive bool) error {
|
||||
|
||||
// Deletion queue
|
||||
for _, cert := range deleteQueue {
|
||||
certCache.mu.Lock()
|
||||
// remove any pointers to this certificate from Configs
|
||||
for _, cfg := range cert.configs {
|
||||
for name, certKey := range cfg.certificates {
|
||||
if certKey == cert.Hash {
|
||||
delete(cfg.certificates, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
// then delete the certificate from the cache
|
||||
delete(certCache.cache, cert.Hash)
|
||||
certCache.mu.Unlock()
|
||||
certCache.removeCertificate(cert)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -229,7 +231,18 @@ func (certCache *Cache) updateOCSPStaples() {
|
||||
}
|
||||
}
|
||||
|
||||
err := certCache.stapleOCSP(&cert, nil)
|
||||
cfg, err := certCache.getConfig(cert)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Getting configuration to manage OCSP for certificate with names %v; unable to refresh: %v", cert.Names, err)
|
||||
continue
|
||||
}
|
||||
if cfg == nil {
|
||||
// this is bad if this happens, probably a programmer error (oops)
|
||||
log.Printf("[ERROR] No configuration associated with certificate for names %v; unable to manage OCSP", cert.Names)
|
||||
continue
|
||||
}
|
||||
|
||||
err = stapleOCSP(cfg.Storage, &cert, nil)
|
||||
if err != nil {
|
||||
if cert.OCSP != nil {
|
||||
// if there was no staple before, that's fine; otherwise we should log the error
|
||||
@@ -260,16 +273,32 @@ func (certCache *Cache) updateOCSPStaples() {
|
||||
}
|
||||
}
|
||||
|
||||
// deleteOldStapleFiles deletes cached OCSP staples that have expired.
|
||||
// CleanStorageOptions specifies how to clean up a storage unit.
|
||||
type CleanStorageOptions struct {
|
||||
OCSPStaples bool
|
||||
// TODO: long-expired certificates
|
||||
}
|
||||
|
||||
// CleanStorage tidies up the given storage according to opts; this
|
||||
// generally involves deleting assets which are no longer required.
|
||||
// TODO: We should do this for long-expired certificates, too.
|
||||
func (certCache *Cache) deleteOldStapleFiles() {
|
||||
ocspKeys, err := certCache.storage.List(prefixOCSP, false)
|
||||
func CleanStorage(storage Storage, opts CleanStorageOptions) {
|
||||
if opts.OCSPStaples {
|
||||
err := deleteOldOCSPStaples(storage)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Deleting old OCSP staples: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func deleteOldOCSPStaples(storage Storage) error {
|
||||
ocspKeys, err := storage.List(prefixOCSP, false)
|
||||
if err != nil {
|
||||
// maybe just hasn't been created yet; no big deal
|
||||
return
|
||||
return nil
|
||||
}
|
||||
for _, key := range ocspKeys {
|
||||
ocspBytes, err := certCache.storage.Load(key)
|
||||
ocspBytes, err := storage.Load(key)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] While deleting old OCSP staples, unable to load staple file: %v", err)
|
||||
continue
|
||||
@@ -277,7 +306,7 @@ func (certCache *Cache) deleteOldStapleFiles() {
|
||||
resp, err := ocsp.ParseResponse(ocspBytes, nil)
|
||||
if err != nil {
|
||||
// contents are invalid; delete it
|
||||
err = certCache.storage.Delete(key)
|
||||
err = storage.Delete(key)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Purging corrupt staple file %s: %v", key, err)
|
||||
}
|
||||
@@ -285,17 +314,18 @@ func (certCache *Cache) deleteOldStapleFiles() {
|
||||
}
|
||||
if time.Now().After(resp.NextUpdate) {
|
||||
// response has expired; delete it
|
||||
err = certCache.storage.Delete(key)
|
||||
err = storage.Delete(key)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Purging expired staple file %s: %v", key, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
// DefaultRenewInterval is how often to check certificates for renewal.
|
||||
DefaultRenewInterval = 12 * time.Hour
|
||||
// DefaultRenewCheckInterval is how often to check certificates for renewal.
|
||||
DefaultRenewCheckInterval = 12 * time.Hour
|
||||
|
||||
// DefaultRenewDurationBefore is how long before expiration to renew certificates.
|
||||
DefaultRenewDurationBefore = (24 * time.Hour) * 30
|
||||
@@ -304,6 +334,6 @@ const (
|
||||
// a renewed certificate when the process is first starting up (see mholt/caddy#1680).
|
||||
DefaultRenewDurationBeforeAtStartup = (24 * time.Hour) * 7
|
||||
|
||||
// DefaultOCSPInterval is how often to check if OCSP stapling needs updating.
|
||||
DefaultOCSPInterval = 1 * time.Hour
|
||||
// DefaultOCSPCheckInterval is how often to check if OCSP stapling needs updating.
|
||||
DefaultOCSPCheckInterval = 1 * time.Hour
|
||||
)
|
||||
|
||||
10
vendor/github.com/mholt/certmagic/ocsp.go
generated
vendored
10
vendor/github.com/mholt/certmagic/ocsp.go
generated
vendored
@@ -35,7 +35,7 @@ import (
|
||||
//
|
||||
// Errors here are not necessarily fatal, it could just be that the
|
||||
// certificate doesn't have an issuer URL.
|
||||
func (certCache *Cache) stapleOCSP(cert *Certificate, pemBundle []byte) error {
|
||||
func stapleOCSP(storage Storage, cert *Certificate, pemBundle []byte) error {
|
||||
if pemBundle == nil {
|
||||
// we need a PEM encoding only for some function calls below
|
||||
bundle := new(bytes.Buffer)
|
||||
@@ -53,7 +53,7 @@ func (certCache *Cache) stapleOCSP(cert *Certificate, pemBundle []byte) error {
|
||||
// First try to load OCSP staple from storage and see if
|
||||
// we can still use it.
|
||||
ocspStapleKey := StorageKeys.OCSPStaple(cert, pemBundle)
|
||||
cachedOCSP, err := certCache.storage.Load(ocspStapleKey)
|
||||
cachedOCSP, err := storage.Load(ocspStapleKey)
|
||||
if err == nil {
|
||||
resp, err := ocsp.ParseResponse(cachedOCSP, nil)
|
||||
if err == nil {
|
||||
@@ -69,7 +69,7 @@ func (certCache *Cache) stapleOCSP(cert *Certificate, pemBundle []byte) error {
|
||||
// because we loaded it by name, whereas the maintenance routine
|
||||
// just iterates the list of files, even if somehow a non-staple
|
||||
// file gets in the folder. in this case we are sure it is corrupt.)
|
||||
err := certCache.storage.Delete(ocspStapleKey)
|
||||
err := storage.Delete(ocspStapleKey)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Unable to delete invalid OCSP staple file: %v", err)
|
||||
}
|
||||
@@ -104,7 +104,7 @@ func (certCache *Cache) stapleOCSP(cert *Certificate, pemBundle []byte) error {
|
||||
cert.Certificate.OCSPStaple = ocspBytes
|
||||
cert.OCSP = ocspResp
|
||||
if gotNewOCSP {
|
||||
err := certCache.storage.Store(ocspStapleKey, ocspBytes)
|
||||
err := storage.Store(ocspStapleKey, ocspBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to write OCSP staple file for %v: %v", cert.Names, err)
|
||||
}
|
||||
@@ -121,7 +121,7 @@ func (certCache *Cache) stapleOCSP(cert *Certificate, pemBundle []byte) error {
|
||||
// IssuingCertificateURL in the certificate. If the []byte and/or ocsp.Response return
|
||||
// values are nil, the OCSP status may be assumed OCSPUnknown.
|
||||
//
|
||||
// Borrowed from github.com/xenolf/lego
|
||||
// Borrowed from github.com/go-acme/lego
|
||||
func getOCSPForCert(bundle []byte) ([]byte, *ocsp.Response, error) {
|
||||
// TODO: Perhaps this should be synchronized too, with a Locker?
|
||||
|
||||
|
||||
8
vendor/github.com/mholt/certmagic/solvers.go
generated
vendored
8
vendor/github.com/mholt/certmagic/solvers.go
generated
vendored
@@ -20,8 +20,8 @@ import (
|
||||
"log"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/xenolf/lego/challenge"
|
||||
"github.com/xenolf/lego/challenge/tlsalpn01"
|
||||
"github.com/go-acme/lego/challenge"
|
||||
"github.com/go-acme/lego/challenge/tlsalpn01"
|
||||
)
|
||||
|
||||
// tlsALPNSolver is a type that can solve TLS-ALPN challenges using
|
||||
@@ -117,7 +117,7 @@ func (dhs distributedSolver) Present(domain, token, keyAuth string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return dhs.config.certCache.storage.Store(dhs.challengeTokensKey(domain), infoBytes)
|
||||
return dhs.config.Storage.Store(dhs.challengeTokensKey(domain), infoBytes)
|
||||
}
|
||||
|
||||
// CleanUp invokes the underlying solver's CleanUp method
|
||||
@@ -129,7 +129,7 @@ func (dhs distributedSolver) CleanUp(domain, token, keyAuth string) error {
|
||||
log.Printf("[ERROR] Cleaning up standard provider server: %v", err)
|
||||
}
|
||||
}
|
||||
return dhs.config.certCache.storage.Delete(dhs.challengeTokensKey(domain))
|
||||
return dhs.config.Storage.Delete(dhs.challengeTokensKey(domain))
|
||||
}
|
||||
|
||||
// challengeTokensPrefix returns the key prefix for challenge info.
|
||||
|
||||
3
vendor/github.com/mholt/certmagic/storage.go
generated
vendored
3
vendor/github.com/mholt/certmagic/storage.go
generated
vendored
@@ -267,6 +267,3 @@ type ErrNotExist interface {
|
||||
// defaultFileStorage is a convenient, default storage
|
||||
// implementation using the local file system.
|
||||
var defaultFileStorage = &FileStorage{Path: dataDir()}
|
||||
|
||||
// DefaultStorage is the default Storage implementation.
|
||||
var DefaultStorage Storage = defaultFileStorage
|
||||
|
||||
34
vendor/github.com/mholt/certmagic/user.go
generated
vendored
34
vendor/github.com/mholt/certmagic/user.go
generated
vendored
@@ -29,8 +29,8 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/registration"
|
||||
"github.com/go-acme/lego/acme"
|
||||
"github.com/go-acme/lego/registration"
|
||||
)
|
||||
|
||||
// user represents a Let's Encrypt user account.
|
||||
@@ -79,10 +79,12 @@ func (cfg *Config) newUser(email string) (user, error) {
|
||||
// will NOT be prompted and an empty email may be returned.
|
||||
func (cfg *Config) getEmail(allowPrompts bool) error {
|
||||
leEmail := cfg.Email
|
||||
|
||||
// First try package default email
|
||||
if leEmail == "" {
|
||||
leEmail = Email
|
||||
leEmail = Default.Email
|
||||
}
|
||||
|
||||
// Then try to get most recent user email from storage
|
||||
var gotRecentEmail bool
|
||||
if leEmail == "" {
|
||||
@@ -96,13 +98,15 @@ func (cfg *Config) getEmail(allowPrompts bool) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// User might have just signified their agreement
|
||||
cfg.Agreed = Default.Agreed
|
||||
}
|
||||
|
||||
// save the email for later and ensure it is consistent
|
||||
// for repeated use; then update cfg with our new defaults
|
||||
Email = strings.TrimSpace(strings.ToLower(leEmail))
|
||||
cfg.Email = Email
|
||||
cfg.Agreed = Agreed
|
||||
// for repeated use; then update cfg with the email
|
||||
Default.Email = strings.TrimSpace(strings.ToLower(leEmail))
|
||||
cfg.Email = Default.Email
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -111,7 +115,7 @@ func (cfg *Config) getAgreementURL() (string, error) {
|
||||
if agreementTestURL != "" {
|
||||
return agreementTestURL, nil
|
||||
}
|
||||
caURL := CA
|
||||
caURL := Default.CA
|
||||
if cfg.CA != "" {
|
||||
caURL = cfg.CA
|
||||
}
|
||||
@@ -149,7 +153,7 @@ func (cfg *Config) promptUserForEmail() (string, error) {
|
||||
return "", fmt.Errorf("reading email address: %v", err)
|
||||
}
|
||||
leEmail = strings.TrimSpace(leEmail)
|
||||
Agreed = true
|
||||
Default.Agreed = true
|
||||
return leEmail, nil
|
||||
}
|
||||
|
||||
@@ -161,7 +165,7 @@ func (cfg *Config) promptUserForEmail() (string, error) {
|
||||
func (cfg *Config) getUser(email string) (user, error) {
|
||||
var user user
|
||||
|
||||
regBytes, err := cfg.certCache.storage.Load(StorageKeys.UserReg(cfg.CA, email))
|
||||
regBytes, err := cfg.Storage.Load(StorageKeys.UserReg(cfg.CA, email))
|
||||
if err != nil {
|
||||
if _, ok := err.(ErrNotExist); ok {
|
||||
// create a new user
|
||||
@@ -169,7 +173,7 @@ func (cfg *Config) getUser(email string) (user, error) {
|
||||
}
|
||||
return user, err
|
||||
}
|
||||
keyBytes, err := cfg.certCache.storage.Load(StorageKeys.UserPrivateKey(cfg.CA, email))
|
||||
keyBytes, err := cfg.Storage.Load(StorageKeys.UserPrivateKey(cfg.CA, email))
|
||||
if err != nil {
|
||||
if _, ok := err.(ErrNotExist); ok {
|
||||
// create a new user
|
||||
@@ -212,7 +216,7 @@ func (cfg *Config) saveUser(user user) error {
|
||||
},
|
||||
}
|
||||
|
||||
return storeTx(cfg.certCache.storage, all)
|
||||
return storeTx(cfg.Storage, all)
|
||||
}
|
||||
|
||||
// promptUserAgreement simply outputs the standard user
|
||||
@@ -246,13 +250,13 @@ func (cfg *Config) askUserAgreement(agreementURL string) bool {
|
||||
// account, errors here are discarded to simplify code flow in
|
||||
// the caller, and errors are not important here anyway.
|
||||
func (cfg *Config) mostRecentUserEmail() (string, bool) {
|
||||
userList, err := cfg.certCache.storage.List(StorageKeys.UsersPrefix(cfg.CA), false)
|
||||
userList, err := cfg.Storage.List(StorageKeys.UsersPrefix(cfg.CA), false)
|
||||
if err != nil || len(userList) == 0 {
|
||||
return "", false
|
||||
}
|
||||
sort.Slice(userList, func(i, j int) bool {
|
||||
iInfo, _ := cfg.certCache.storage.Stat(userList[i])
|
||||
jInfo, _ := cfg.certCache.storage.Stat(userList[j])
|
||||
iInfo, _ := cfg.Storage.Stat(userList[i])
|
||||
jInfo, _ := cfg.Storage.Stat(userList[j])
|
||||
return jInfo.Modified.Before(iInfo.Modified)
|
||||
})
|
||||
user, err := cfg.getUser(path.Base(userList[0]))
|
||||
|
||||
Reference in New Issue
Block a user