Skip to content

Commit

Permalink
m
Browse files Browse the repository at this point in the history
  • Loading branch information
komuw committed Jun 22, 2023
1 parent 3f06025 commit 9f8d319
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 50 deletions.
7 changes: 5 additions & 2 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ func Run(h http.Handler, o Opts, l *slog.Logger) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

tlsConf, errTc := getTlsConfig(ctx, h, o, l)
tlsConf, acmeH, errTc := getTlsConfig(ctx, h, o, l)
if errTc != nil {
return errTc
}
Expand All @@ -267,7 +267,10 @@ func Run(h http.Handler, o Opts, l *slog.Logger) error {
// 4. https://github.com/golang/go/issues/27375
Handler: http.TimeoutHandler(
http.MaxBytesHandler(
h,
acmeHandler(
h,
acmeH,
),
int64(o.maxBodyBytes), // limit in bytes.
),
o.handlerTimeout,
Expand Down
88 changes: 40 additions & 48 deletions server/tls_conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,10 @@ import (
"crypto/tls"
"errors"
"fmt"
"net"
"net/http"
"strings"
"time"

"github.com/komuw/ong/log"

"golang.org/x/crypto/acme"
"golang.org/x/crypto/acme/autocert"
"golang.org/x/exp/slog"
Expand All @@ -25,20 +22,52 @@ import (
// (d) https://github.com/caddyserver/certmagic/blob/master/handshake.go whose license(Apache 2.0) can be found here: https://github.com/caddyserver/certmagic/blob/v0.16.1/LICENSE.txt
//

// acmeHandler returns a Handler that will handle ACME [http-01] challenge requests using acmeH
// and handles normal requests using appHandler.
//
// ACME CA sends challenge requests to `/.well-known/acme-challenge/` uri.
// Note that this `http-01` challenge does not allow [wildcard] certificates.
//
// [http-01]: https://letsencrypt.org/docs/challenge-types/
// [wildcard]: https://letsencrypt.org/docs/faq/#does-let-s-encrypt-issue-wildcard-certificates
func acmeHandler(
appHandler http.Handler,
acmeH func(fallback http.Handler) http.Handler,
) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// This code is taken from; https://github.com/golang/crypto/blob/v0.10.0/acme/autocert/autocert.go#L398-L401
if strings.HasPrefix(r.URL.Path, "/.well-known/acme-challenge/") {
acmeH(appHandler).ServeHTTP(w, r)
return
}

appHandler.ServeHTTP(w, r)
}
}

// getTlsConfig returns a proper tls configuration given the options passed in.
// The tls config may either procure certifiates from ACME, from disk or be nil(for non-tls traffic)
//
// h is the fallback is the http handler that will be delegated to for non ACME requests.
func getTlsConfig(ctx context.Context, h http.Handler, o Opts, l *slog.Logger) (*tls.Config, error) {
func getTlsConfig(ctx context.Context, h http.Handler, o Opts, l *slog.Logger) (c *tls.Config, acmeH func(fallback http.Handler) http.Handler, e error) {
defer func() {
// see: https://go.dev/play/p/3orL3CyP9a8
if o.tls.email != "" { // This is ACME
if acmeH == nil && e == nil {
e = errors.New("ong/server: acme could not be setup properly")
}
}
}()

if err := validateDomain(o.tls.domain); err != nil {
return nil, err
return nil, nil, err
}

if o.tls.email != "" {
// 1. use ACME.
//
if o.tls.url == "" {
return nil, errors.New("ong/server: acmeURL cannot be empty if email is also specified")
return nil, nil, errors.New("ong/server: acmeURL cannot be empty if email is also specified")
}

m := &autocert.Manager{
Expand Down Expand Up @@ -85,54 +114,17 @@ func getTlsConfig(ctx context.Context, h http.Handler, o Opts, l *slog.Logger) (
},
}

go func() {
// This server will handle requests to the ACME `/.well-known/acme-challenge/` URI.
// Note that this `http-01` challenge does not allow wildcard certificates.
// see: https://letsencrypt.org/docs/challenge-types/
// https://letsencrypt.org/docs/faq/#does-let-s-encrypt-issue-wildcard-certificates
autocertHandler := m.HTTPHandler(h)
autocertServer := &http.Server{
// serve HTTP, which will redirect automatically to HTTPS
Addr: ":80",
Handler: autocertHandler,
ReadHeaderTimeout: 20 * time.Second,
ReadTimeout: 40 * time.Second,
WriteTimeout: 40 * time.Second,
IdleTimeout: 120 * time.Second,
ErrorLog: slog.NewLogLogger(l.Handler(), slog.LevelDebug),
BaseContext: func(net.Listener) context.Context { return ctx },
}

cfg := listenerConfig()
lstr, err := cfg.Listen(ctx, "tcp", autocertServer.Addr)
if err != nil {
l.Error("autocertServer, unable to create listener", "error", err)
return
}

slog.NewLogLogger(l.Handler(), log.LevelImmediate).
Printf("acme/autocert server listening at %s", autocertServer.Addr)

if errAutocertSrv := autocertServer.Serve(lstr); errAutocertSrv != nil {
l.Error("ong/server. acme/autocert unable to serve",
"func", "autocertServer.ListenAndServe",
"addr", autocertServer.Addr,
"error", errAutocertSrv,
)
}
}()

return tlsConf, nil
return tlsConf, m.HTTPHandler, nil
}
if o.tls.certFile != "" {
// 2. get from disk.
//
if len(o.tls.keyFile) < 1 {
return nil, errors.New("ong/server: keyFile cannot be empty if certFile is also specified")
return nil, nil, errors.New("ong/server: keyFile cannot be empty if certFile is also specified")
}
c, err := tls.LoadX509KeyPair(o.tls.certFile, o.tls.keyFile)
if err != nil {
return nil, err
return nil, nil, err
}

tlsConf := &tls.Config{
Expand All @@ -150,11 +142,11 @@ func getTlsConfig(ctx context.Context, h http.Handler, o Opts, l *slog.Logger) (
return &c, nil
},
}
return tlsConf, nil
return tlsConf, nil, nil
}

// 3. non-tls traffic.
return nil, errors.New("ong/server: ong only serves https")
return nil, nil, errors.New("ong/server: ong only serves https")
}

func validateDomain(domain string) error {
Expand Down

0 comments on commit 9f8d319

Please sign in to comment.