diff --git a/directives.go b/directives.go index f9308c4..83730d1 100644 --- a/directives.go +++ b/directives.go @@ -1,6 +1,7 @@ package tlstunnel import ( + "crypto/tls" "fmt" "net" "net/url" @@ -40,13 +41,23 @@ func parseFrontend(srv *Server, d *scfg.Directive) error { return err } + unmanaged := false + tlsDirective := d.Children.Get("tls") + if tlsDirective != nil { + var err error + unmanaged, err = parseFrontendTLS(srv, tlsDirective) + if err != nil { + return err + } + } + for _, addr := range d.Params { host, port, err := net.SplitHostPort(addr) if err != nil { return fmt.Errorf("failed to parse frontend address %q: %v", addr, err) } - if host != "" { + if host != "" && !unmanaged { srv.ManagedNames = append(srv.ManagedNames, host) } @@ -96,6 +107,29 @@ func parseBackend(backend *Backend, d *scfg.Directive) error { return nil } +func parseFrontendTLS(srv *Server, d *scfg.Directive) (unmanaged bool, err error) { + for _, child := range d.Children { + switch child.Name { + case "load": + var certPath, keyPath string + if err := child.ParseParams(&certPath, &keyPath); err != nil { + return false, err + } + + cert, err := tls.LoadX509KeyPair(certPath, keyPath) + if err != nil { + return false, fmt.Errorf("directive \"load\": %v", err) + } + + srv.UnmanagedCerts = append(srv.UnmanagedCerts, cert) + unmanaged = true + default: + return false, fmt.Errorf("unknown %q directive", child.Name) + } + } + return unmanaged, nil +} + func parseTLS(srv *Server, d *scfg.Directive) error { for _, child := range d.Children { switch child.Name { diff --git a/server.go b/server.go index b9c3a0d..21154a7 100644 --- a/server.go +++ b/server.go @@ -16,11 +16,14 @@ import ( ) type Server struct { - Listeners map[string]*Listener // indexed by listening address - Frontends []*Frontend - ManagedNames []string - ACMEManager *certmagic.ACMEManager - ACMEConfig *certmagic.Config + Listeners map[string]*Listener // indexed by listening address + Frontends []*Frontend + + ManagedNames []string + UnmanagedCerts []tls.Certificate + + ACMEManager *certmagic.ACMEManager + ACMEConfig *certmagic.Config } func NewServer() *Server { @@ -55,6 +58,12 @@ func (srv *Server) RegisterListener(addr string) *Listener { } func (srv *Server) Start() error { + for _, cert := range srv.UnmanagedCerts { + if err := srv.ACMEConfig.CacheUnmanagedTLSCertificate(cert, nil); err != nil { + return err + } + } + if err := srv.ACMEConfig.ManageAsync(context.Background(), srv.ManagedNames); err != nil { return fmt.Errorf("failed to manage TLS certificates: %v", err) } diff --git a/tlstunnel.1.scd b/tlstunnel.1.scd index ae781ec..0107577 100644 --- a/tlstunnel.1.scd +++ b/tlstunnel.1.scd @@ -55,9 +55,18 @@ The following directives are supported: The _+proxy_ suffix can be added to the URI scheme to forward connection metadata via the PROXY protocol. + *tls* { ... } + Customise frontend-specific TLS configuration. + + The tls directive supports the following sub-directives: + + *load* + Load certificates and private keys from PEM files. + + This disables automatic TLS. *tls* { ... } - Customise TLS configuration. + Customise global TLS configuration. The tls directive supports the following sub-directives: