mirror of
https://github.com/caddyserver/caddy.git
synced 2025-12-08 06:09:53 +00:00
acmeserver: Configurable resolvers, fix smallstep deprecations (#5500)
* acmeserver: Configurable `resolvers`, fix smallstep deprecations * Improve default net/port * Update proxy resolvers parsing to use the new function * Update listeners.go Co-authored-by: itsxaos <33079230+itsxaos@users.noreply.github.com> --------- Co-authored-by: itsxaos <33079230+itsxaos@users.noreply.github.com>
This commit is contained in:
parent
1af419e7ec
commit
3f20a7c9f3
5 changed files with 284 additions and 53 deletions
|
|
@ -15,7 +15,10 @@
|
|||
package acmeserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
weakrand "math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
|
@ -28,7 +31,7 @@ import (
|
|||
"github.com/caddyserver/caddy/v2/modules/caddypki"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/smallstep/certificates/acme"
|
||||
acmeAPI "github.com/smallstep/certificates/acme/api"
|
||||
"github.com/smallstep/certificates/acme/api"
|
||||
acmeNoSQL "github.com/smallstep/certificates/acme/db/nosql"
|
||||
"github.com/smallstep/certificates/authority"
|
||||
"github.com/smallstep/certificates/authority/provisioner"
|
||||
|
|
@ -76,8 +79,26 @@ type Handler struct {
|
|||
// changed or removed in the future.
|
||||
SignWithRoot bool `json:"sign_with_root,omitempty"`
|
||||
|
||||
// The addresses of DNS resolvers to use when looking up
|
||||
// the TXT records for solving DNS challenges.
|
||||
// It accepts [network addresses](/docs/conventions#network-addresses)
|
||||
// with port range of only 1. If the host is an IP address,
|
||||
// it will be dialed directly to resolve the upstream server.
|
||||
// If the host is not an IP address, the addresses are resolved
|
||||
// using the [name resolution convention](https://golang.org/pkg/net/#hdr-Name_Resolution)
|
||||
// of the Go standard library. If the array contains more
|
||||
// than 1 resolver address, one is chosen at random.
|
||||
Resolvers []string `json:"resolvers,omitempty"`
|
||||
|
||||
logger *zap.Logger
|
||||
resolvers []caddy.NetworkAddress
|
||||
ctx caddy.Context
|
||||
|
||||
acmeDB acme.DB
|
||||
acmeAuth *authority.Authority
|
||||
acmeClient acme.Client
|
||||
acmeLinker acme.Linker
|
||||
acmeEndpoints http.Handler
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// CaddyModule returns the Caddy module information.
|
||||
|
|
@ -90,7 +111,9 @@ func (Handler) CaddyModule() caddy.ModuleInfo {
|
|||
|
||||
// Provision sets up the ACME server handler.
|
||||
func (ash *Handler) Provision(ctx caddy.Context) error {
|
||||
ash.ctx = ctx
|
||||
ash.logger = ctx.Logger()
|
||||
|
||||
// set some defaults
|
||||
if ash.CA == "" {
|
||||
ash.CA = caddypki.DefaultCAID
|
||||
|
|
@ -142,31 +165,30 @@ func (ash *Handler) Provision(ctx caddy.Context) error {
|
|||
DB: database,
|
||||
}
|
||||
|
||||
auth, err := ca.NewAuthority(authorityConfig)
|
||||
ash.acmeAuth, err = ca.NewAuthority(authorityConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var acmeDB acme.DB
|
||||
if authorityConfig.DB != nil {
|
||||
acmeDB, err = acmeNoSQL.New(auth.GetDatabase().(nosql.DB))
|
||||
if err != nil {
|
||||
return fmt.Errorf("configuring ACME DB: %v", err)
|
||||
}
|
||||
ash.acmeDB, err = acmeNoSQL.New(ash.acmeAuth.GetDatabase().(nosql.DB))
|
||||
if err != nil {
|
||||
return fmt.Errorf("configuring ACME DB: %v", err)
|
||||
}
|
||||
|
||||
// create the router for the ACME endpoints
|
||||
acmeRouterHandler := acmeAPI.NewHandler(acmeAPI.HandlerOptions{
|
||||
CA: auth,
|
||||
DB: acmeDB, // stores all the server state
|
||||
DNS: ash.Host, // used for directory links
|
||||
Prefix: strings.Trim(ash.PathPrefix, "/"), // used for directory links
|
||||
})
|
||||
ash.acmeClient, err = ash.makeClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ash.acmeLinker = acme.NewLinker(
|
||||
ash.Host,
|
||||
strings.Trim(ash.PathPrefix, "/"),
|
||||
)
|
||||
|
||||
// extract its http.Handler so we can use it directly
|
||||
r := chi.NewRouter()
|
||||
r.Route(ash.PathPrefix, func(r chi.Router) {
|
||||
acmeRouterHandler.Route(r)
|
||||
api.Route(r)
|
||||
})
|
||||
ash.acmeEndpoints = r
|
||||
|
||||
|
|
@ -175,6 +197,16 @@ func (ash *Handler) Provision(ctx caddy.Context) error {
|
|||
|
||||
func (ash Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
|
||||
if strings.HasPrefix(r.URL.Path, ash.PathPrefix) {
|
||||
acmeCtx := acme.NewContext(
|
||||
r.Context(),
|
||||
ash.acmeDB,
|
||||
ash.acmeClient,
|
||||
ash.acmeLinker,
|
||||
nil,
|
||||
)
|
||||
acmeCtx = authority.NewContext(acmeCtx, ash.acmeAuth)
|
||||
r = r.WithContext(acmeCtx)
|
||||
|
||||
ash.acmeEndpoints.ServeHTTP(w, r)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -227,6 +259,55 @@ func (ash Handler) openDatabase() (*db.AuthDB, error) {
|
|||
return database.(databaseCloser).DB, err
|
||||
}
|
||||
|
||||
// makeClient creates an ACME client which will use a custom
|
||||
// resolver instead of net.DefaultResolver.
|
||||
func (ash Handler) makeClient() (acme.Client, error) {
|
||||
for _, v := range ash.Resolvers {
|
||||
addr, err := caddy.ParseNetworkAddressWithDefaults(v, "udp", 53)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addr.PortRangeSize() != 1 {
|
||||
return nil, fmt.Errorf("resolver address must have exactly one address; cannot call %v", addr)
|
||||
}
|
||||
ash.resolvers = append(ash.resolvers, addr)
|
||||
}
|
||||
|
||||
var resolver *net.Resolver
|
||||
if len(ash.resolvers) != 0 {
|
||||
dialer := &net.Dialer{
|
||||
Timeout: 2 * time.Second,
|
||||
}
|
||||
resolver = &net.Resolver{
|
||||
PreferGo: true,
|
||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
//nolint:gosec
|
||||
addr := ash.resolvers[weakrand.Intn(len(ash.resolvers))]
|
||||
return dialer.DialContext(ctx, addr.Network, addr.JoinHostPort(0))
|
||||
},
|
||||
}
|
||||
} else {
|
||||
resolver = net.DefaultResolver
|
||||
}
|
||||
|
||||
return resolverClient{
|
||||
Client: acme.NewClient(),
|
||||
resolver: resolver,
|
||||
ctx: ash.ctx,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type resolverClient struct {
|
||||
acme.Client
|
||||
|
||||
resolver *net.Resolver
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (c resolverClient) LookupTxt(name string) ([]string, error) {
|
||||
return c.resolver.LookupTXT(c.ctx, name)
|
||||
}
|
||||
|
||||
const defaultPathPrefix = "/acme/"
|
||||
|
||||
var keyCleaner = regexp.MustCompile(`[^\w.-_]`)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue