From 50456c578497e9921558941eae59fa01bcf269bf Mon Sep 17 00:00:00 2001 From: Alan Pearce Date: Mon, 24 Jun 2024 17:18:27 +0200 Subject: handle TLS in server with ACME --- internal/config/config.go | 3 ++- internal/server/server.go | 29 ++++++++++++++--------------- internal/server/tcp.go | 27 +++++++++++++++++++++++++++ internal/server/tls.go | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 16 deletions(-) create mode 100644 internal/server/tcp.go create mode 100644 internal/server/tls.go (limited to 'internal') diff --git a/internal/config/config.go b/internal/config/config.go index 4477ad4..7d43462 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -38,7 +38,8 @@ type Config struct { Description string DomainStartDate string `toml:"domain_start_date"` OriginalDomain string `toml:"original_domain"` - OIDCHost URL `toml:"oidc_host"` + Domains []string + OIDCHost URL `toml:"oidc_host"` Taxonomies []Taxonomy CSP *CSP `toml:"content-security-policy"` Extra struct { diff --git a/internal/server/server.go b/internal/server/server.go index 26d1c1f..9edebe3 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -11,7 +11,6 @@ import ( "website/internal/builder" cfg "website/internal/config" - "website/internal/listenfd" "website/internal/log" "website/internal/website" @@ -32,10 +31,13 @@ type Config struct { Root string `conf:"default:website"` ListenAddress string `conf:"default:localhost"` Port string `conf:"default:3000,short:p"` + TLS bool `conf:"default:false"` } type Server struct { *http.Server + config *cfg.Config + tls bool } func applyDevModeOverrides(config *cfg.Config, listenAddress string) { @@ -150,7 +152,7 @@ func New(runtimeConfig *Config) (*Server, error) { }) return &Server{ - &http.Server{ + Server: &http.Server{ Addr: listenAddress, ReadHeaderTimeout: 1 * time.Minute, Handler: http.MaxBytesHandler(h2c.NewHandler( @@ -160,24 +162,21 @@ func New(runtimeConfig *Config) (*Server, error) { }, ), 0), }, + config: config, + tls: runtimeConfig.TLS, }, nil } -func (s *Server) Start() error { - l, err := listenfd.GetListener(0) - if err != nil { - log.Warn("could not create listener from listenfd", "error", err) - } - - log.Debug("listener from listenfd?", "passed", l != nil) - if l == nil { - l, err = net.Listen("tcp", s.Addr) - if err != nil { - return errors.Wrap(err, "could not create listener") - } +func (s *Server) serve(tls bool) error { + if tls { + return s.serveTLS() + } else { + return s.serveTCP() } +} - if err := http.Serve(l, s.Handler); err != http.ErrServerClosed { +func (s *Server) Start() error { + if err := s.serve(s.tls); err != http.ErrServerClosed { return errors.Wrap(err, "error creating/closing server") } diff --git a/internal/server/tcp.go b/internal/server/tcp.go new file mode 100644 index 0000000..4dc3314 --- /dev/null +++ b/internal/server/tcp.go @@ -0,0 +1,27 @@ +package server + +import ( + "net" + + "website/internal/listenfd" + "website/internal/log" + + "github.com/pkg/errors" +) + +func (s *Server) serveTCP() error { + l, err := listenfd.GetListener(0) + if err != nil { + log.Warn("could not create listener from listenfd", "error", err) + } + + log.Debug("listener from listenfd?", "passed", l != nil) + if l == nil { + l, err = net.Listen("tcp", s.Addr) + if err != nil { + return errors.Wrap(err, "could not create listener") + } + } + + return s.Serve(l) +} diff --git a/internal/server/tls.go b/internal/server/tls.go new file mode 100644 index 0000000..b60f474 --- /dev/null +++ b/internal/server/tls.go @@ -0,0 +1,47 @@ +package server + +import ( + "context" + + "github.com/ardanlabs/conf/v3" + "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/certmagic" + certmagic_redis "github.com/pberkel/caddy-storage-redis" + "github.com/pkg/errors" +) + +type redisConfig struct { + Address string `conf:"required"` + Username string `conf:"default:default"` + Password string `conf:"required"` + EncryptionKey string `conf:"required"` + KeyPrefix string `conf:"default:certmagic"` +} + +func (s *Server) serveTLS() (err error) { + rc := &redisConfig{} + _, err = conf.Parse("REDIS", rc) + if err != nil { + return errors.Wrap(err, "could not parse redis config") + } + + rs := certmagic_redis.New() + rs.Address = []string{rc.Address} + rs.Username = rc.Username + rs.Password = rc.Password + rs.EncryptionKey = rc.EncryptionKey + rs.KeyPrefix = rc.KeyPrefix + + certmagic.Default.Storage = rs + err = rs.Provision(caddy.Context{ + Context: context.Background(), + }) + if err != nil { + return errors.Wrap(err, "could not provision redis storage") + } + + certmagic.DefaultACME.Agreed = true + certmagic.DefaultACME.Email = s.config.Email + + return certmagic.HTTPS(s.config.Domains, s.Server.Handler) +} -- cgit 1.4.1