feat: deploy modes (--destroy) + HTTPS with Let's Encrypt

This commit is contained in:
Claus Lohmar 2026-07-06 15:45:23 +01:00
parent 29b9f3c159
commit 672653f731
2 changed files with 45 additions and 12 deletions

View file

@ -3,7 +3,7 @@ server:
host: "0.0.0.0"
tls:
enabled: false # Flip to true for Let's Encrypt HTTPS
email: "" # Required when enabled (e.g. admin@nextwks.eu)
enabled: true # ← was false, now true
email: "den@2-4-h.net" # ← Let's Encrypt account email
domain: "nextwks.eu" # Base domain for subdomain routing + certs

53
main.go
View file

@ -2,7 +2,6 @@ package main
import (
"context"
"crypto/tls"
"fmt"
"log"
"net/http"
@ -154,7 +153,7 @@ func main() {
boilerplateHandler(w, r)
})
addr := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port)
httpAddr := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port)
if cfg.TLS.Enabled {
log.Println("NextWorkspace starting with TLS (certmagic)...")
@ -165,27 +164,61 @@ func main() {
Path: filepath.Join(configDir, "certs"),
}
// Collect domains (from proxies.yaml + bare domain)
var domains []string
for subdomain := range proxies.Apps {
domains = append(domains, subdomain+"."+cfg.Domain)
}
domains = append(domains, cfg.Domain)
// Obtain and manage certificates
magic := certmagic.NewDefault()
if err := magic.ManageSync(context.Background(), domains); err != nil {
log.Fatalf("Failed to manage certificates: %v", err)
}
tlsConfig := magic.TLSConfig()
listener, err := tls.Listen("tcp", addr, tlsConfig)
if err != nil {
log.Fatalf("Failed to start TLS listener: %v", err)
// HTTP redirect handler (on :80)
redirectHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
target := "https://" + r.Host + r.URL.RequestURI()
http.Redirect(w, r, target, http.StatusMovedPermanently)
})
// Wrap with ACME challenge handler for Let's Encrypt http-01 validation
var httpHandler http.Handler = redirectHandler
if len(magic.Issuers) > 0 {
if ai, ok := magic.Issuers[0].(*certmagic.ACMEIssuer); ok {
httpHandler = ai.HTTPChallengeHandler(redirectHandler)
log.Println("ACME challenge handler enabled on :80")
}
}
log.Printf("NextWorkspace listening on %s (TLS)", addr)
log.Fatal(http.Serve(listener, router))
httpServer := &http.Server{
Addr: httpAddr,
Handler: httpHandler,
}
// HTTPS server with TLS
tlsConfig := magic.TLSConfig()
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2") // HTTP/2 support
httpsServer := &http.Server{
Addr: ":443",
Handler: router,
TLSConfig: tlsConfig,
}
// Start HTTP redirect server
go func() {
log.Printf("HTTP redirect listening on %s", httpAddr)
if err := httpServer.ListenAndServe(); err != nil {
log.Fatalf("HTTP server error: %v", err)
}
}()
log.Println("HTTPS listening on :443")
log.Fatal(httpsServer.ListenAndServeTLS("", "")) // certmagic provides certs via TLSConfig
} else {
log.Printf("NextWorkspace listening on %s (no TLS)", addr)
log.Fatal(http.ListenAndServe(addr, router))
log.Printf("NextWorkspace listening on %s (no TLS)", httpAddr)
log.Fatal(http.ListenAndServe(httpAddr, router))
}
}