diff --git a/cmd/tailscale/cli/web.go b/cmd/tailscale/cli/web.go index c2b2702e4..cb2088618 100644 --- a/cmd/tailscale/cli/web.go +++ b/cmd/tailscale/cli/web.go @@ -7,6 +7,7 @@ package cli import ( "bytes" "context" + "crypto/tls" _ "embed" "encoding/json" "encoding/xml" @@ -19,6 +20,7 @@ import ( "net/http" "net/http/cgi" "net/url" + "os" "os/exec" "runtime" "strings" @@ -83,6 +85,29 @@ var webArgs struct { cgi bool } +func tlsConfigFromEnvironment() *tls.Config { + crt := os.Getenv("TLS_CRT_PEM") + key := os.Getenv("TLS_KEY_PEM") + if crt == "" || key == "" { + return nil + } + + // We support passing in the complete certificate and key from environment + // variables because pfSense stores its cert+key in the PHP config. We populate + // TLS_CRT_PEM and TLS_KEY_PEM from PHP code before starting tailscale web. + // These are the PEM-encoded Certificate and Private Key. + + cert, err := tls.X509KeyPair([]byte(crt), []byte(key)) + if err != nil { + log.Printf("tlsConfigFromEnvironment: %v", err) + + // Fallback to unencrypted HTTP. + return nil + } + + return &tls.Config{Certificates: []tls.Certificate{cert}} +} + func runWeb(ctx context.Context, args []string) error { if len(args) > 0 { log.Fatalf("too many non-flag arguments: %q", args) @@ -96,8 +121,20 @@ func runWeb(ctx context.Context, args []string) error { return nil } - log.Printf("web server running on: %s", urlOfListenAddr(webArgs.listen)) - return http.ListenAndServe(webArgs.listen, http.HandlerFunc(webHandler)) + tlsConfig := tlsConfigFromEnvironment() + if tlsConfig != nil { + server := &http.Server{ + Addr: webArgs.listen, + TLSConfig: tlsConfig, + Handler: http.HandlerFunc(webHandler), + } + + log.Printf("web server runNIng on: https://%s", server.Addr) + return server.ListenAndServeTLS("", "") + } else { + log.Printf("web server running on: %s", urlOfListenAddr(webArgs.listen)) + return http.ListenAndServe(webArgs.listen, http.HandlerFunc(webHandler)) + } } // urlOfListenAddr parses a given listen address into a formatted URL