From 588ead0b00241cdada934c354f77010a436facbd Mon Sep 17 00:00:00 2001 From: Drew Edwards Date: Thu, 18 Jan 2024 02:03:59 +0000 Subject: [PATCH] feat: HTTP -> HTTPS redirects on all site configs (#26) --- .gitignore | 1 + caskethttp/httpserver/https.go | 10 ++++++++-- caskethttp/httpserver/https_test.go | 12 ++++++------ caskettls/config.go | 4 ++++ caskettls/setup.go | 2 ++ 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index b1f1c6a8..033753b9 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ access.log /*.conf Casketfile +Casketfile.* !casketfile/ casket/go.mod casket/go.sum diff --git a/caskethttp/httpserver/https.go b/caskethttp/httpserver/https.go index 1c80a825..5cf2ce3a 100644 --- a/caskethttp/httpserver/https.go +++ b/caskethttp/httpserver/https.go @@ -29,7 +29,7 @@ func activateHTTPS(cctx casket.Context) error { operatorPresent := !casket.Started() if !casket.Quiet && operatorPresent { - fmt.Print("Activating privacy features... ") + fmt.Println("Activating privacy features... ") } ctx := cctx.(*httpContext) @@ -147,7 +147,8 @@ func makePlaintextRedirects(allConfigs []*SiteConfig) []*SiteConfig { httpPort := strconv.Itoa(certmagic.HTTPPort) httpsPort := strconv.Itoa(certmagic.HTTPSPort) for i, cfg := range allConfigs { - if cfg.TLS.Managed && + if cfg.TLS.Enabled && + !cfg.TLS.NoRedirect && !hostHasOtherPort(allConfigs, i, httpPort) && (cfg.Addr.Port == httpsPort || !hostHasOtherPort(allConfigs, i, httpsPort)) { allConfigs = append(allConfigs, redirPlaintextHost(cfg)) @@ -193,6 +194,11 @@ func redirPlaintextHost(cfg *SiteConfig) *SiteConfig { redirPort = "" } + operatorPresent := !casket.Started() + if !casket.Quiet && operatorPresent { + fmt.Println("[INFO] Creating automatic HTTP->HTTPS redirect for", cfg.Addr.Host) + } + redirMiddleware := func(next Handler) Handler { return HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { // Construct the URL to which to redirect. Note that the Host in a diff --git a/caskethttp/httpserver/https_test.go b/caskethttp/httpserver/https_test.go index c5f763b4..28a2fd04 100644 --- a/caskethttp/httpserver/https_test.go +++ b/caskethttp/httpserver/https_test.go @@ -23,8 +23,8 @@ import ( "strconv" "testing" - "github.com/tmpim/certmagic" "github.com/tmpim/casket/caskettls" + "github.com/tmpim/certmagic" ) func TestRedirPlaintextHost(t *testing.T) { @@ -153,18 +153,18 @@ func TestHostHasOtherPort(t *testing.T) { func TestMakePlaintextRedirects(t *testing.T) { configs := []*SiteConfig{ // Happy path = standard redirect from 80 to 443 - {Addr: Address{Host: "example.com"}, TLS: &caskettls.Config{Managed: true}}, + {Addr: Address{Host: "example.com"}, TLS: &caskettls.Config{Managed: true, Enabled: true}}, // Host on port 80 already defined; don't change it (no redirect) {Addr: Address{Host: "sub1.example.com", Port: "80", Scheme: "http"}, TLS: new(caskettls.Config)}, - {Addr: Address{Host: "sub1.example.com"}, TLS: &caskettls.Config{Managed: true}}, + {Addr: Address{Host: "sub1.example.com"}, TLS: &caskettls.Config{Managed: true, Enabled: true}}, // Redirect from port 80 to port 5000 in this case - {Addr: Address{Host: "sub2.example.com", Port: "5000"}, TLS: &caskettls.Config{Managed: true}}, + {Addr: Address{Host: "sub2.example.com", Port: "5000"}, TLS: &caskettls.Config{Managed: true, Enabled: true}}, // Can redirect from 80 to either 443 or 5001, but choose 443 - {Addr: Address{Host: "sub3.example.com", Port: "443"}, TLS: &caskettls.Config{Managed: true}}, - {Addr: Address{Host: "sub3.example.com", Port: "5001", Scheme: "https"}, TLS: &caskettls.Config{Managed: true}}, + {Addr: Address{Host: "sub3.example.com", Port: "443"}, TLS: &caskettls.Config{Managed: true, Enabled: true}}, + {Addr: Address{Host: "sub3.example.com", Port: "5001", Scheme: "https"}, TLS: &caskettls.Config{Managed: true, Enabled: true}}, } result := makePlaintextRedirects(configs) diff --git a/caskettls/config.go b/caskettls/config.go index 1d44e5d9..b1d9f27f 100644 --- a/caskettls/config.go +++ b/caskettls/config.go @@ -84,6 +84,10 @@ type Config struct { // Manager is how certificates are managed Manager *certmagic.Config + // NoRedirect will disable the automatic HTTP->HTTPS redirect, regardless + // of whether the site is managed or not. + NoRedirect bool + // SelfSigned means that this hostname is // served with a self-signed certificate // that we generated in memory for convenience diff --git a/caskettls/setup.go b/caskettls/setup.go index dbf833f4..88100874 100644 --- a/caskettls/setup.go +++ b/caskettls/setup.go @@ -278,6 +278,8 @@ func setupTLS(c *casket.Controller) error { } parts[0] = "*" config.Hostname = strings.Join(parts, ".") + case "no_redirect": + config.NoRedirect = true default: return c.Errf("Unknown subdirective '%s'", c.Val()) }