2019-03-11 18:56:48 +02:00
|
|
|
package tlsalpn01
|
2018-06-14 01:20:56 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/tls"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
2018-12-06 23:50:17 +02:00
|
|
|
"strings"
|
|
|
|
|
2019-07-30 21:19:32 +02:00
|
|
|
"github.com/go-acme/lego/v3/log"
|
2018-06-14 01:20:56 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2018-06-18 15:44:18 +02:00
|
|
|
// ACMETLS1Protocol is the ALPN Protocol ID for the ACME-TLS/1 Protocol.
|
|
|
|
ACMETLS1Protocol = "acme-tls/1"
|
2018-06-14 01:20:56 +02:00
|
|
|
|
2018-12-06 23:50:17 +02:00
|
|
|
// defaultTLSPort is the port that the ProviderServer will default to
|
2018-06-14 01:20:56 +02:00
|
|
|
// when no other port is provided.
|
|
|
|
defaultTLSPort = "443"
|
|
|
|
)
|
|
|
|
|
2018-12-06 23:50:17 +02:00
|
|
|
// ProviderServer implements ChallengeProvider for `TLS-ALPN-01` challenge.
|
|
|
|
// It may be instantiated without using the NewProviderServer
|
2018-06-14 01:20:56 +02:00
|
|
|
// if you want only to use the default values.
|
2018-12-06 23:50:17 +02:00
|
|
|
type ProviderServer struct {
|
2018-06-14 01:20:56 +02:00
|
|
|
iface string
|
|
|
|
port string
|
|
|
|
listener net.Listener
|
|
|
|
}
|
|
|
|
|
2018-12-06 23:50:17 +02:00
|
|
|
// 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}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *ProviderServer) GetAddress() string {
|
|
|
|
return net.JoinHostPort(s.iface, s.port)
|
2018-06-14 01:20:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Present generates a certificate with a SHA-256 digest of the keyAuth provided
|
2018-12-06 23:50:17 +02:00
|
|
|
// 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 == "" {
|
2018-06-14 01:20:56 +02:00
|
|
|
// Fallback to port 443 if the port was not provided.
|
2018-12-06 23:50:17 +02:00
|
|
|
s.port = defaultTLSPort
|
2018-06-14 01:20:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the challenge certificate using the provided keyAuth and domain.
|
2018-12-06 23:50:17 +02:00
|
|
|
cert, err := ChallengeCert(domain, keyAuth)
|
2018-06-14 01:20:56 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Place the generated certificate with the extension into the TLS config
|
|
|
|
// so that it can serve the correct details.
|
|
|
|
tlsConf := new(tls.Config)
|
|
|
|
tlsConf.Certificates = []tls.Certificate{*cert}
|
|
|
|
|
|
|
|
// We must set that the `acme-tls/1` application level protocol is supported
|
|
|
|
// so that the protocol negotiation can succeed. Reference:
|
2019-11-05 18:58:30 +02:00
|
|
|
// https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07#section-6.2
|
2018-06-18 15:44:18 +02:00
|
|
|
tlsConf.NextProtos = []string{ACMETLS1Protocol}
|
2018-06-14 01:20:56 +02:00
|
|
|
|
|
|
|
// Create the listener with the created tls.Config.
|
2018-12-06 23:50:17 +02:00
|
|
|
s.listener, err = tls.Listen("tcp", s.GetAddress(), tlsConf)
|
2018-06-14 01:20:56 +02:00
|
|
|
if err != nil {
|
2020-03-20 23:53:09 +02:00
|
|
|
return fmt.Errorf("could not start HTTPS server for challenge: %w", err)
|
2018-06-14 01:20:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Shut the server down when we're finished.
|
|
|
|
go func() {
|
2018-12-06 23:50:17 +02:00
|
|
|
err := http.Serve(s.listener, nil)
|
|
|
|
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
|
2018-09-24 21:07:20 +02:00
|
|
|
log.Println(err)
|
|
|
|
}
|
2018-06-14 01:20:56 +02:00
|
|
|
}()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CleanUp closes the HTTPS server.
|
2018-12-06 23:50:17 +02:00
|
|
|
func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error {
|
|
|
|
if s.listener == nil {
|
2018-06-14 01:20:56 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Server was created, close it.
|
2018-12-06 23:50:17 +02:00
|
|
|
if err := s.listener.Close(); err != nil && err != http.ErrServerClosed {
|
2018-06-14 01:20:56 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|