about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAlan Pearce2024-06-26 23:10:51 +0200
committerAlan Pearce2024-06-26 23:10:51 +0200
commitd5b95136d5f162645a6bfaa76833cbf5520f7e45 (patch)
tree41ceb9a1011418a77d6f9472e0fafe84c3eb27ff
parent98e63f34bd0ffa9087f7e2640d60d6d1c30ecc13 (diff)
downloadwebsite-d5b95136d5f162645a6bfaa76833cbf5520f7e45.tar.lz
website-d5b95136d5f162645a6bfaa76833cbf5520f7e45.tar.zst
website-d5b95136d5f162645a6bfaa76833cbf5520f7e45.zip
enable TLS for local development (using caddy as acme server)
-rw-r--r--internal/server/server.go21
-rw-r--r--internal/server/tls.go66
-rw-r--r--modd.conf3
3 files changed, 64 insertions, 26 deletions
diff --git a/internal/server/server.go b/internal/server/server.go
index 3110ec0..717320d 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -9,6 +9,7 @@ import (
 	"os"
 	"slices"
 	"strconv"
+	"strings"
 	"time"
 
 	"website/internal/builder"
@@ -31,13 +32,16 @@ var (
 )
 
 type Config struct {
-	Development   bool   `conf:"default:false,flag:dev"`
 	Root          string `conf:"default:website"`
 	Redirect      bool   `conf:"default:true"`
 	ListenAddress string `conf:"default:localhost"`
-	Port          int    `conf:"default:3000,short:p"`
-	TLSPort       int    `conf:"default:443"`
+	Port          int    `conf:"default:8080,short:p"`
+	TLSPort       int    `conf:"default:8443"`
 	TLS           bool   `conf:"default:false"`
+
+	Development bool   `conf:"default:false,flag:dev"`
+	ACMECACert  string `conf:"env:ACME_CA_CERT"`
+	Domains     string
 }
 
 type Server struct {
@@ -46,13 +50,18 @@ type Server struct {
 	config        *cfg.Config
 }
 
-func applyDevModeOverrides(config *cfg.Config, listenAddress string) {
+func applyDevModeOverrides(config *cfg.Config, runtimeConfig *Config) {
 	config.CSP.ScriptSrc = slices.Insert(config.CSP.ScriptSrc, 0, "'unsafe-inline'")
 	config.CSP.ConnectSrc = slices.Insert(config.CSP.ConnectSrc, 0, "'self'")
+	if runtimeConfig.Domains != "" {
+		config.Domains = strings.Split(runtimeConfig.Domains, ",")
+	} else {
+		config.Domains = []string{runtimeConfig.ListenAddress}
+	}
 	config.BaseURL = cfg.URL{
 		URL: &url.URL{
 			Scheme: "http",
-			Host:   listenAddress,
+			Host:   runtimeConfig.ListenAddress,
 		},
 	}
 }
@@ -110,7 +119,7 @@ func New(runtimeConfig *Config) (*Server, error) {
 		return nil, errors.WithMessage(err, "error parsing configuration file")
 	}
 	if runtimeConfig.Development {
-		applyDevModeOverrides(config, runtimeConfig.ListenAddress)
+		applyDevModeOverrides(config, runtimeConfig)
 	}
 
 	listenAddress := net.JoinHostPort(runtimeConfig.ListenAddress, strconv.Itoa(runtimeConfig.Port))
diff --git a/internal/server/tls.go b/internal/server/tls.go
index 370134c..84dae74 100644
--- a/internal/server/tls.go
+++ b/internal/server/tls.go
@@ -2,6 +2,8 @@ package server
 
 import (
 	"context"
+	"crypto/x509"
+	"website/internal/log"
 
 	"github.com/ardanlabs/conf/v3"
 	"github.com/caddyserver/caddy/v2"
@@ -19,25 +21,45 @@ type redisConfig struct {
 }
 
 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")
-	}
+	if s.runtimeConfig.Development {
+		ca := s.runtimeConfig.ACMECACert
+		if ca == "" {
+			return errors.New("Need ACME_CA_CERT to enable TLS in development")
+		}
+
+		cp := x509.NewCertPool()
+		cp.AppendCertsFromPEM([]byte(ca))
+
+		cfg := certmagic.NewDefault()
+		issuer := certmagic.NewACMEIssuer(cfg, certmagic.ACMEIssuer{
+			CA:                      "https://localhost/acme/local/directory",
+			TrustedRoots:            cp,
+			DisableTLSALPNChallenge: true,
+			AltHTTPPort:             s.runtimeConfig.Port,
+		})
 
-	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 = *issuer
+	} else {
+		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
@@ -46,5 +68,13 @@ func (s *Server) serveTLS() (err error) {
 	certmagic.HTTPPort = s.runtimeConfig.Port
 	certmagic.HTTPSPort = s.runtimeConfig.TLSPort
 
+	log.Debug(
+		"starting certmagic",
+		"http_port",
+		certmagic.HTTPPort,
+		"https_port",
+		certmagic.HTTPSPort,
+	)
+
 	return certmagic.HTTPS(s.config.Domains, s.Server.Handler)
 }
diff --git a/modd.conf b/modd.conf
index f956fea..0c73ea6 100644
--- a/modd.conf
+++ b/modd.conf
@@ -1,5 +1,4 @@
 **/*.go !**/*_templ.go {
-  daemon +sigint: systemfd -s http::3000 -- \
-    templ generate --watch \
+  daemon +sigint: templ generate --watch \
       --cmd="go run ./cmd/server --dev"
 }