Update server dependencies
This commit is contained in:
parent
c704ebb042
commit
1794e2680a
369 changed files with 23554 additions and 6306 deletions
184
vendor/github.com/go-acme/lego/v3/challenge/http01/domain_matcher.go
generated
vendored
Normal file
184
vendor/github.com/go-acme/lego/v3/challenge/http01/domain_matcher.go
generated
vendored
Normal file
|
@ -0,0 +1,184 @@
|
|||
package http01
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A domainMatcher tries to match a domain (the one we're requesting a certificate for)
|
||||
// in the HTTP request coming from the ACME validation servers.
|
||||
// This step is part of DNS rebind attack prevention,
|
||||
// where the webserver matches incoming requests to a list of domain the server acts authoritative for.
|
||||
//
|
||||
// The most simple check involves finding the domain in the HTTP Host header;
|
||||
// this is what hostMatcher does.
|
||||
// Use it, when the http01.ProviderServer is directly reachable from the internet,
|
||||
// or when it operates behind a transparent proxy.
|
||||
//
|
||||
// In many (reverse) proxy setups, Apache and NGINX traditionally move the Host header to a new header named X-Forwarded-Host.
|
||||
// Use arbitraryMatcher("X-Forwarded-Host") in this case,
|
||||
// or the appropriate header name for other proxy servers.
|
||||
//
|
||||
// RFC7239 has standardized the different forwarding headers into a single header named Forwarded.
|
||||
// The header value has a different format, so you should use forwardedMatcher
|
||||
// when the http01.ProviderServer operates behind a RFC7239 compatible proxy.
|
||||
// https://tools.ietf.org/html/rfc7239
|
||||
//
|
||||
// Note: RFC7239 also reminds us, "that an HTTP list [...] may be split over multiple header fields" (section 7.1),
|
||||
// meaning that
|
||||
// X-Header: a
|
||||
// X-Header: b
|
||||
// is equal to
|
||||
// X-Header: a, b
|
||||
//
|
||||
// All matcher implementations (explicitly not excluding arbitraryMatcher!)
|
||||
// have in common that they only match against the first value in such lists.
|
||||
type domainMatcher interface {
|
||||
// matches checks whether the request is valid for the given domain.
|
||||
matches(request *http.Request, domain string) bool
|
||||
|
||||
// name returns the header name used in the check.
|
||||
// This is primarily used to create meaningful error messages.
|
||||
name() string
|
||||
}
|
||||
|
||||
// hostMatcher checks whether (*net/http).Request.Host starts with a domain name.
|
||||
type hostMatcher struct{}
|
||||
|
||||
func (m *hostMatcher) name() string {
|
||||
return "Host"
|
||||
}
|
||||
|
||||
func (m *hostMatcher) matches(r *http.Request, domain string) bool {
|
||||
return strings.HasPrefix(r.Host, domain)
|
||||
}
|
||||
|
||||
// hostMatcher checks whether the specified (*net/http.Request).Header value starts with a domain name.
|
||||
type arbitraryMatcher string
|
||||
|
||||
func (m arbitraryMatcher) name() string {
|
||||
return string(m)
|
||||
}
|
||||
|
||||
func (m arbitraryMatcher) matches(r *http.Request, domain string) bool {
|
||||
return strings.HasPrefix(r.Header.Get(m.name()), domain)
|
||||
}
|
||||
|
||||
// forwardedMatcher checks whether the Forwarded header contains a "host" element starting with a domain name.
|
||||
// See https://tools.ietf.org/html/rfc7239 for details.
|
||||
type forwardedMatcher struct{}
|
||||
|
||||
func (m *forwardedMatcher) name() string {
|
||||
return "Forwarded"
|
||||
}
|
||||
|
||||
func (m *forwardedMatcher) matches(r *http.Request, domain string) bool {
|
||||
fwds, err := parseForwardedHeader(r.Header.Get(m.name()))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(fwds) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
host := fwds[0]["host"]
|
||||
return strings.HasPrefix(host, domain)
|
||||
}
|
||||
|
||||
// parsing requires some form of state machine
|
||||
func parseForwardedHeader(s string) (elements []map[string]string, err error) {
|
||||
cur := make(map[string]string)
|
||||
key := ""
|
||||
val := ""
|
||||
inquote := false
|
||||
|
||||
pos := 0
|
||||
l := len(s)
|
||||
for i := 0; i < l; i++ {
|
||||
r := rune(s[i])
|
||||
|
||||
if inquote {
|
||||
if r == '"' {
|
||||
cur[key] = s[pos:i]
|
||||
key = ""
|
||||
pos = i
|
||||
inquote = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case r == '"': // start of quoted-string
|
||||
if key == "" {
|
||||
return nil, fmt.Errorf("unexpected quoted string as pos %d", i)
|
||||
}
|
||||
inquote = true
|
||||
pos = i + 1
|
||||
|
||||
case r == ';': // end of forwarded-pair
|
||||
cur[key] = s[pos:i]
|
||||
key = ""
|
||||
i = skipWS(s, i)
|
||||
pos = i + 1
|
||||
|
||||
case r == '=': // end of token
|
||||
key = strings.ToLower(strings.TrimFunc(s[pos:i], isWS))
|
||||
i = skipWS(s, i)
|
||||
pos = i + 1
|
||||
|
||||
case r == ',': // end of forwarded-element
|
||||
if key != "" {
|
||||
if val == "" {
|
||||
val = s[pos:i]
|
||||
}
|
||||
cur[key] = val
|
||||
}
|
||||
elements = append(elements, cur)
|
||||
cur = make(map[string]string)
|
||||
key = ""
|
||||
val = ""
|
||||
|
||||
i = skipWS(s, i)
|
||||
pos = i + 1
|
||||
case tchar(r) || isWS(r): // valid token character or whitespace
|
||||
continue
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid token character at pos %d: %c", i, r)
|
||||
}
|
||||
}
|
||||
|
||||
if inquote {
|
||||
return nil, fmt.Errorf("unterminated quoted-string at pos %d", len(s))
|
||||
}
|
||||
|
||||
if key != "" {
|
||||
if pos < len(s) {
|
||||
val = s[pos:]
|
||||
}
|
||||
cur[key] = val
|
||||
}
|
||||
if len(cur) > 0 {
|
||||
elements = append(elements, cur)
|
||||
}
|
||||
return elements, nil
|
||||
}
|
||||
|
||||
func tchar(r rune) bool {
|
||||
return strings.ContainsRune("!#$%&'*+-.^_`|~", r) ||
|
||||
'0' <= r && r <= '9' ||
|
||||
'a' <= r && r <= 'z' ||
|
||||
'A' <= r && r <= 'Z'
|
||||
}
|
||||
|
||||
func skipWS(s string, i int) int {
|
||||
for isWS(rune(s[i+1])) {
|
||||
i++
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func isWS(r rune) bool {
|
||||
return strings.ContainsRune(" \t\v\r\n", r)
|
||||
}
|
65
vendor/github.com/go-acme/lego/v3/challenge/http01/http_challenge.go
generated
vendored
Normal file
65
vendor/github.com/go-acme/lego/v3/challenge/http01/http_challenge.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
package http01
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-acme/lego/v3/acme"
|
||||
"github.com/go-acme/lego/v3/acme/api"
|
||||
"github.com/go-acme/lego/v3/challenge"
|
||||
"github.com/go-acme/lego/v3/log"
|
||||
)
|
||||
|
||||
type ValidateFunc func(core *api.Core, domain string, chlng acme.Challenge) error
|
||||
|
||||
// ChallengePath returns the URL path for the `http-01` challenge
|
||||
func ChallengePath(token string) string {
|
||||
return "/.well-known/acme-challenge/" + token
|
||||
}
|
||||
|
||||
type Challenge struct {
|
||||
core *api.Core
|
||||
validate ValidateFunc
|
||||
provider challenge.Provider
|
||||
}
|
||||
|
||||
func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Provider) *Challenge {
|
||||
return &Challenge{
|
||||
core: core,
|
||||
validate: validate,
|
||||
provider: provider,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Challenge) SetProvider(provider challenge.Provider) {
|
||||
c.provider = provider
|
||||
}
|
||||
|
||||
func (c *Challenge) Solve(authz acme.Authorization) error {
|
||||
domain := challenge.GetTargetedDomain(authz)
|
||||
log.Infof("[%s] acme: Trying to solve HTTP-01", domain)
|
||||
|
||||
chlng, err := challenge.FindChallenge(challenge.HTTP01, authz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate the Key Authorization for the challenge
|
||||
keyAuth, err := c.core.GetKeyAuthorization(chlng.Token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.provider.Present(authz.Identifier.Value, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("[%s] acme: error presenting token: %w", domain, err)
|
||||
}
|
||||
defer func() {
|
||||
err := c.provider.CleanUp(authz.Identifier.Value, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
log.Warnf("[%s] acme: cleaning up failed: %v", domain, err)
|
||||
}
|
||||
}()
|
||||
|
||||
chlng.KeyAuthorization = keyAuth
|
||||
return c.validate(c.core, domain, chlng)
|
||||
}
|
122
vendor/github.com/go-acme/lego/v3/challenge/http01/http_challenge_server.go
generated
vendored
Normal file
122
vendor/github.com/go-acme/lego/v3/challenge/http01/http_challenge_server.go
generated
vendored
Normal file
|
@ -0,0 +1,122 @@
|
|||
package http01
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"strings"
|
||||
|
||||
"github.com/go-acme/lego/v3/log"
|
||||
)
|
||||
|
||||
// ProviderServer implements ChallengeProvider for `http-01` challenge
|
||||
// It may be instantiated without using the NewProviderServer function if
|
||||
// you want only to use the default values.
|
||||
type ProviderServer struct {
|
||||
iface string
|
||||
port string
|
||||
matcher domainMatcher
|
||||
done chan bool
|
||||
listener net.Listener
|
||||
}
|
||||
|
||||
// NewProviderServer creates a new ProviderServer on the selected interface and port.
|
||||
// Setting iface and / or port to an empty string will make the server fall back to
|
||||
// the "any" interface and port 80 respectively.
|
||||
func NewProviderServer(iface, port string) *ProviderServer {
|
||||
if port == "" {
|
||||
port = "80"
|
||||
}
|
||||
|
||||
return &ProviderServer{iface: iface, port: port, matcher: &hostMatcher{}}
|
||||
}
|
||||
|
||||
// Present starts a web server and makes the token available at `ChallengePath(token)` for web requests.
|
||||
func (s *ProviderServer) Present(domain, token, keyAuth string) error {
|
||||
var err error
|
||||
s.listener, err = net.Listen("tcp", s.GetAddress())
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not start HTTP server for challenge: %w", err)
|
||||
}
|
||||
|
||||
s.done = make(chan bool)
|
||||
go s.serve(domain, token, keyAuth)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ProviderServer) GetAddress() string {
|
||||
return net.JoinHostPort(s.iface, s.port)
|
||||
}
|
||||
|
||||
// CleanUp closes the HTTP server and removes the token from `ChallengePath(token)`
|
||||
func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error {
|
||||
if s.listener == nil {
|
||||
return nil
|
||||
}
|
||||
s.listener.Close()
|
||||
<-s.done
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetProxyHeader changes the validation of incoming requests.
|
||||
// By default, s matches the "Host" header value to the domain name.
|
||||
//
|
||||
// When the server runs behind a proxy server, this is not the correct place to look at;
|
||||
// Apache and NGINX have traditionally moved the original Host header into a new header named "X-Forwarded-Host".
|
||||
// Other webservers might use different names;
|
||||
// and RFC7239 has standadized a new header named "Forwarded" (with slightly different semantics).
|
||||
//
|
||||
// The exact behavior depends on the value of headerName:
|
||||
// - "" (the empty string) and "Host" will restore the default and only check the Host header
|
||||
// - "Forwarded" will look for a Forwarded header, and inspect it according to https://tools.ietf.org/html/rfc7239
|
||||
// - any other value will check the header value with the same name
|
||||
func (s *ProviderServer) SetProxyHeader(headerName string) {
|
||||
switch h := textproto.CanonicalMIMEHeaderKey(headerName); h {
|
||||
case "", "Host":
|
||||
s.matcher = &hostMatcher{}
|
||||
case "Forwarded":
|
||||
s.matcher = &forwardedMatcher{}
|
||||
default:
|
||||
s.matcher = arbitraryMatcher(h)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ProviderServer) serve(domain, token, keyAuth string) {
|
||||
path := ChallengePath(token)
|
||||
|
||||
// The incoming request must will be validated to prevent DNS rebind attacks.
|
||||
// We only respond with the keyAuth, when we're receiving a GET requests with
|
||||
// the "Host" header matching the domain (the latter is configurable though SetProxyHeader).
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodGet && s.matcher.matches(r, domain) {
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
_, err := w.Write([]byte(keyAuth))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
log.Infof("[%s] Served key authentication", domain)
|
||||
} else {
|
||||
log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the %s header properly.", r.Host, r.Method, s.matcher.name())
|
||||
_, err := w.Write([]byte("TEST"))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
httpServer := &http.Server{Handler: mux}
|
||||
|
||||
// Once httpServer is shut down
|
||||
// we don't want any lingering connections, so disable KeepAlives.
|
||||
httpServer.SetKeepAlivesEnabled(false)
|
||||
|
||||
err := httpServer.Serve(s.listener)
|
||||
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
|
||||
log.Println(err)
|
||||
}
|
||||
s.done <- true
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue