mirror of
https://github.com/go-acme/lego
synced 2026-03-14 14:35:48 +01:00
feat: new HTTP-01 and TLS-ALPN-01 servers constructors
This commit is contained in:
parent
218ec2c138
commit
09908bab4d
7 changed files with 134 additions and 40 deletions
|
|
@ -52,10 +52,6 @@ func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Prov
|
|||
return chlg
|
||||
}
|
||||
|
||||
func (c *Challenge) SetProvider(provider challenge.Provider) {
|
||||
c.provider = provider
|
||||
}
|
||||
|
||||
func (c *Challenge) Solve(ctx context.Context, authz acme.Authorization) error {
|
||||
domain := challenge.GetTargetedDomain(authz)
|
||||
log.Info("acme: Trying to solve HTTP-01.", "domain", domain)
|
||||
|
|
|
|||
|
|
@ -9,15 +9,25 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/go-acme/lego/v5/challenge"
|
||||
"github.com/go-acme/lego/v5/log"
|
||||
)
|
||||
|
||||
var _ challenge.Provider = (*ProviderServer)(nil)
|
||||
|
||||
type Options struct {
|
||||
Network string
|
||||
NetworkStack challenge.NetworkStack
|
||||
Address string
|
||||
SocketMode fs.FileMode
|
||||
}
|
||||
|
||||
// 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 {
|
||||
address string
|
||||
network string // must be valid argument to net.Listen
|
||||
address string
|
||||
|
||||
socketMode fs.FileMode
|
||||
|
||||
|
|
@ -26,19 +36,42 @@ type ProviderServer struct {
|
|||
listener net.Listener
|
||||
}
|
||||
|
||||
// NewProviderServerWithOptions creates a new ProviderServer.
|
||||
func NewProviderServerWithOptions(opts Options) *ProviderServer {
|
||||
if opts.Network == "" {
|
||||
opts.Network = "tcp"
|
||||
}
|
||||
|
||||
return &ProviderServer{
|
||||
network: opts.NetworkStack.Network(opts.Network),
|
||||
address: opts.Address,
|
||||
socketMode: opts.SocketMode,
|
||||
matcher: &hostMatcher{},
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// Setting host 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 {
|
||||
func NewProviderServer(host, port string) *ProviderServer {
|
||||
if port == "" {
|
||||
// Fallback to port 80 if the port was not provided.
|
||||
port = "80"
|
||||
}
|
||||
|
||||
return &ProviderServer{network: "tcp", address: net.JoinHostPort(iface, port), matcher: &hostMatcher{}}
|
||||
return NewProviderServerWithOptions(Options{
|
||||
Network: "tcp",
|
||||
Address: net.JoinHostPort(host, port),
|
||||
})
|
||||
}
|
||||
|
||||
func NewUnixProviderServer(socketPath string, mode fs.FileMode) *ProviderServer {
|
||||
return &ProviderServer{network: "unix", address: socketPath, socketMode: mode, matcher: &hostMatcher{}}
|
||||
// NewUnixProviderServer creates a new ProviderServer.
|
||||
func NewUnixProviderServer(socketPath string, socketMode fs.FileMode) *ProviderServer {
|
||||
return NewProviderServerWithOptions(Options{
|
||||
Network: "unix",
|
||||
Address: socketPath,
|
||||
SocketMode: socketMode,
|
||||
})
|
||||
}
|
||||
|
||||
// Present starts a web server and makes the token available at `ChallengePath(token)` for web requests.
|
||||
|
|
@ -63,10 +96,6 @@ func (s *ProviderServer) Present(domain, token, keyAuth string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *ProviderServer) GetAddress() string {
|
||||
return s.address
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
|
@ -80,6 +109,10 @@ func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *ProviderServer) GetAddress() string {
|
||||
return s.address
|
||||
}
|
||||
|
||||
// SetProxyHeader changes the validation of incoming requests.
|
||||
// By default, s matches the "Host" header value to the domain name.
|
||||
//
|
||||
|
|
|
|||
20
challenge/network.go
Normal file
20
challenge/network.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package challenge
|
||||
|
||||
type NetworkStack int
|
||||
|
||||
const (
|
||||
dualStack NetworkStack = iota
|
||||
ipv4only
|
||||
ipv6only
|
||||
)
|
||||
|
||||
func (s NetworkStack) Network(proto string) string {
|
||||
switch s {
|
||||
case ipv4only:
|
||||
return proto + "4"
|
||||
case ipv6only:
|
||||
return proto + "6"
|
||||
default:
|
||||
return proto
|
||||
}
|
||||
}
|
||||
|
|
@ -57,10 +57,6 @@ func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Prov
|
|||
return chlg
|
||||
}
|
||||
|
||||
func (c *Challenge) SetProvider(provider challenge.Provider) {
|
||||
c.provider = provider
|
||||
}
|
||||
|
||||
// Solve manages the provider to validate and solve the challenge.
|
||||
func (c *Challenge) Solve(ctx context.Context, authz acme.Authorization) error {
|
||||
domain := authz.Identifier.Value
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/go-acme/lego/v5/challenge"
|
||||
"github.com/go-acme/lego/v5/log"
|
||||
)
|
||||
|
||||
|
|
@ -20,34 +21,52 @@ const (
|
|||
defaultTLSPort = "443"
|
||||
)
|
||||
|
||||
var _ challenge.Provider = (*ProviderServer)(nil)
|
||||
|
||||
type Options struct {
|
||||
Network string
|
||||
NetworkStack challenge.NetworkStack
|
||||
Host string
|
||||
Port string
|
||||
}
|
||||
|
||||
// ProviderServer implements ChallengeProvider for `TLS-ALPN-01` challenge.
|
||||
// It may be instantiated without using the NewProviderServer
|
||||
// if you want only to use the default values.
|
||||
type ProviderServer struct {
|
||||
iface string
|
||||
port string
|
||||
network string
|
||||
address string
|
||||
|
||||
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 443 respectively.
|
||||
func NewProviderServer(iface, port string) *ProviderServer {
|
||||
return &ProviderServer{iface: iface, port: port}
|
||||
// NewProviderServerWithOptions creates a new ProviderServer.
|
||||
func NewProviderServerWithOptions(opts Options) *ProviderServer {
|
||||
if opts.Port == "" {
|
||||
// Fallback to port 443 if the port was not provided.
|
||||
opts.Port = defaultTLSPort
|
||||
}
|
||||
|
||||
if opts.Network == "" {
|
||||
opts.Network = "tcp"
|
||||
}
|
||||
|
||||
return &ProviderServer{
|
||||
network: opts.NetworkStack.Network(opts.Network),
|
||||
address: net.JoinHostPort(opts.Host, opts.Port),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ProviderServer) GetAddress() string {
|
||||
return net.JoinHostPort(s.iface, s.port)
|
||||
// NewProviderServer creates a new ProviderServer on the selected interface and port.
|
||||
// Setting host and / or port to an empty string will make the server fall back to
|
||||
// the "any" interface and port 443 respectively.
|
||||
func NewProviderServer(host, port string) *ProviderServer {
|
||||
return NewProviderServerWithOptions(Options{Host: host, Port: port})
|
||||
}
|
||||
|
||||
// Present generates a certificate with an SHA-256 digest of the keyAuth provided
|
||||
// as the acmeValidation-v1 extension value to conform to the ACME-TLS-ALPN spec.
|
||||
func (s *ProviderServer) Present(domain, token, keyAuth string) error {
|
||||
if s.port == "" {
|
||||
// Fallback to port 443 if the port was not provided.
|
||||
s.port = defaultTLSPort
|
||||
}
|
||||
|
||||
// Generate the challenge certificate using the provided keyAuth and domain.
|
||||
cert, err := ChallengeCert(domain, keyAuth)
|
||||
if err != nil {
|
||||
|
|
@ -65,7 +84,7 @@ func (s *ProviderServer) Present(domain, token, keyAuth string) error {
|
|||
tlsConf.NextProtos = []string{ACMETLS1Protocol}
|
||||
|
||||
// Create the listener with the created tls.Config.
|
||||
s.listener, err = tls.Listen("tcp", s.GetAddress(), tlsConf)
|
||||
s.listener, err = tls.Listen(s.network, s.GetAddress(), tlsConf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not start HTTPS server for challenge: %w", err)
|
||||
}
|
||||
|
|
@ -94,3 +113,7 @@ func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ProviderServer) GetAddress() string {
|
||||
return s.address
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ func TestChallenge(t *testing.T) {
|
|||
solver := NewChallenge(
|
||||
core,
|
||||
mockValidate,
|
||||
&ProviderServer{port: port},
|
||||
NewProviderServerWithOptions(Options{Host: domain, Port: port}),
|
||||
)
|
||||
|
||||
authz := acme.Authorization{
|
||||
|
|
@ -105,7 +105,7 @@ func TestChallengeInvalidPort(t *testing.T) {
|
|||
solver := NewChallenge(
|
||||
core,
|
||||
func(_ context.Context, _ *api.Core, _ string, _ acme.Challenge) error { return nil },
|
||||
&ProviderServer{port: "123456"},
|
||||
NewProviderServerWithOptions(Options{Host: "127.0.0.1", Port: "123456"}),
|
||||
)
|
||||
|
||||
authz := acme.Authorization{
|
||||
|
|
@ -183,7 +183,7 @@ func TestChallengeIPaddress(t *testing.T) {
|
|||
solver := NewChallenge(
|
||||
core,
|
||||
mockValidate,
|
||||
&ProviderServer{port: port},
|
||||
NewProviderServerWithOptions(Options{Host: domain, Port: port}),
|
||||
)
|
||||
|
||||
authz := acme.Authorization{
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ func setupHTTPProvider(ctx *cli.Context) challenge.Provider {
|
|||
}
|
||||
|
||||
return ps
|
||||
|
||||
case ctx.IsSet(flgHTTPMemcachedHost):
|
||||
ps, err := memcached.NewMemcachedProvider(ctx.StringSlice(flgHTTPMemcachedHost))
|
||||
if err != nil {
|
||||
|
|
@ -65,6 +66,7 @@ func setupHTTPProvider(ctx *cli.Context) challenge.Provider {
|
|||
}
|
||||
|
||||
return ps
|
||||
|
||||
case ctx.IsSet(flgHTTPS3Bucket):
|
||||
ps, err := s3.NewHTTPProvider(ctx.String(flgHTTPS3Bucket))
|
||||
if err != nil {
|
||||
|
|
@ -73,8 +75,10 @@ func setupHTTPProvider(ctx *cli.Context) challenge.Provider {
|
|||
}
|
||||
|
||||
return ps
|
||||
|
||||
case ctx.IsSet(flgHTTPPort):
|
||||
iface := ctx.String(flgHTTPPort)
|
||||
|
||||
if !strings.Contains(iface, ":") {
|
||||
log.Fatal(
|
||||
fmt.Sprintf("The --%s switch only accepts interface:port or :port for its argument.", flgHTTPPort),
|
||||
|
|
@ -87,19 +91,31 @@ func setupHTTPProvider(ctx *cli.Context) challenge.Provider {
|
|||
log.Fatal("Could not split host and port.", "iface", iface, "error", err)
|
||||
}
|
||||
|
||||
srv := http01.NewProviderServer(host, port)
|
||||
srv := http01.NewProviderServerWithOptions(http01.Options{
|
||||
// TODO(ldez): set network stack
|
||||
Network: "tcp",
|
||||
Address: net.JoinHostPort(host, port),
|
||||
})
|
||||
|
||||
if header := ctx.String(flgHTTPProxyHeader); header != "" {
|
||||
srv.SetProxyHeader(header)
|
||||
}
|
||||
|
||||
return srv
|
||||
|
||||
case ctx.Bool(flgHTTP):
|
||||
srv := http01.NewProviderServer("", "")
|
||||
srv := http01.NewProviderServerWithOptions(http01.Options{
|
||||
// TODO(ldez): set network stack
|
||||
Network: "tcp",
|
||||
Address: net.JoinHostPort("", ":80"),
|
||||
})
|
||||
|
||||
if header := ctx.String(flgHTTPProxyHeader); header != "" {
|
||||
srv.SetProxyHeader(header)
|
||||
}
|
||||
|
||||
return srv
|
||||
|
||||
default:
|
||||
log.Fatal("Invalid HTTP challenge options.")
|
||||
return nil
|
||||
|
|
@ -119,9 +135,19 @@ func setupTLSProvider(ctx *cli.Context) challenge.Provider {
|
|||
log.Fatal("Could not split host and port.", "iface", iface, "error", err)
|
||||
}
|
||||
|
||||
return tlsalpn01.NewProviderServer(host, port)
|
||||
return tlsalpn01.NewProviderServerWithOptions(tlsalpn01.Options{
|
||||
// TODO(ldez): set network stack
|
||||
Network: "tcp",
|
||||
Host: host,
|
||||
Port: port,
|
||||
})
|
||||
|
||||
case ctx.Bool(flgTLS):
|
||||
return tlsalpn01.NewProviderServer("", "")
|
||||
return tlsalpn01.NewProviderServerWithOptions(tlsalpn01.Options{
|
||||
// TODO(ldez): set network stack
|
||||
Network: "tcp",
|
||||
})
|
||||
|
||||
default:
|
||||
log.Fatal("Invalid HTTP challenge options.")
|
||||
return nil
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue