From 09f5953431c4f541861fd06beb7437ef48a92cce Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Wed, 21 Nov 2018 18:26:19 -0500 Subject: [PATCH] Work as a standalone server, including TLS This supports running the server on port 443, serving secure pages, with automatic redirects from the insecure site. It also modifies the configuration process to better guide users through configuring for running behind a reverse proxy or as a standalone server. This closes T537 --- app.go | 23 +++++++++++--- config/config.go | 7 +++++ config/setup.go | 79 +++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 97 insertions(+), 12 deletions(-) diff --git a/app.go b/app.go index e061de4..d72ee49 100644 --- a/app.go +++ b/app.go @@ -401,11 +401,26 @@ func Serve() { os.Exit(0) }() - // Start web application server http.Handle("/", r) - log.Info("Serving on http://localhost:%d\n", app.cfg.Server.Port) - log.Info("---") - err = http.ListenAndServe(fmt.Sprintf(":%d", app.cfg.Server.Port), nil) + + // Start web application server + if app.cfg.IsSecureStandalone() { + log.Info("Serving redirects on http://localhost:80") + go func() { + err = http.ListenAndServe(":80", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, app.cfg.App.Host, http.StatusMovedPermanently) + })) + log.Error("Unable to start redirect server: %v", err) + }() + + log.Info("Serving on https://localhost:443") + log.Info("---") + err = http.ListenAndServeTLS(":443", app.cfg.Server.TLSCertPath, app.cfg.Server.TLSKeyPath, nil) + } else { + log.Info("Serving on http://localhost:%d\n", app.cfg.Server.Port) + log.Info("---") + err = http.ListenAndServe(fmt.Sprintf(":%d", app.cfg.Server.Port), nil) + } if err != nil { log.Error("Unable to start: %v", err) os.Exit(1) diff --git a/config/config.go b/config/config.go index c3c0628..56d8848 100644 --- a/config/config.go +++ b/config/config.go @@ -13,6 +13,9 @@ type ( HiddenHost string `ini:"hidden_host"` Port int `ini:"port"` + TLSCertPath string `ini:"tls_cert_path"` + TLSKeyPath string `ini:"tls_key_path"` + Dev bool `ini:"-"` } @@ -76,6 +79,10 @@ func New() *Config { } } +func (cfg *Config) IsSecureStandalone() bool { + return cfg.Server.Port == 443 && cfg.Server.TLSCertPath != "" && cfg.Server.TLSKeyPath != "" +} + func Load() (*Config, error) { cfg, err := ini.Load(FileName) if err != nil { diff --git a/config/setup.go b/config/setup.go index 54fa961..6a2f780 100644 --- a/config/setup.go +++ b/config/setup.go @@ -47,17 +47,80 @@ func Configure() (*SetupData, error) { Selected: fmt.Sprintf(`{{.Label}} {{ . | faint }}`), } - prompt := promptui.Prompt{ - Templates: tmpls, - Label: "Local port", - Validate: validatePort, - Default: fmt.Sprintf("%d", data.Config.Server.Port), + // Environment selection + selPrompt := promptui.Select{ + Templates: selTmpls, + Label: "Environment", + Items: []string{"Development", "Production, standalone", "Production, behind reverse proxy"}, } - port, err := prompt.Run() + _, envType, err := selPrompt.Run() if err != nil { return data, err } - data.Config.Server.Port, _ = strconv.Atoi(port) // Ignore error, as we've already validated number + isDevEnv := envType == "Development" + isStandalone := envType == "Production, standalone" + + data.Config.Server.Dev = isDevEnv + + var prompt promptui.Prompt + if isDevEnv || !isStandalone { + // Running in dev environment or behind reverse proxy; ask for port + prompt = promptui.Prompt{ + Templates: tmpls, + Label: "Local port", + Validate: validatePort, + Default: fmt.Sprintf("%d", data.Config.Server.Port), + } + port, err := prompt.Run() + if err != nil { + return data, err + } + data.Config.Server.Port, _ = strconv.Atoi(port) // Ignore error, as we've already validated number + } + + if isStandalone { + selPrompt = promptui.Select{ + Templates: selTmpls, + Label: "Web server mode", + Items: []string{"Insecure (port 80)", "Secure (port 443)"}, + } + sel, _, err := selPrompt.Run() + if err != nil { + return data, err + } + if sel == 0 { + data.Config.Server.Port = 80 + data.Config.Server.TLSCertPath = "" + data.Config.Server.TLSKeyPath = "" + } else if sel == 1 { + data.Config.Server.Port = 443 + + prompt = promptui.Prompt{ + Templates: tmpls, + Label: "Certificate path", + Validate: validateNonEmpty, + Default: data.Config.Server.TLSCertPath, + } + data.Config.Server.TLSCertPath, err = prompt.Run() + if err != nil { + return data, err + } + + prompt = promptui.Prompt{ + Templates: tmpls, + Label: "Key path", + Validate: validateNonEmpty, + Default: data.Config.Server.TLSKeyPath, + } + data.Config.Server.TLSKeyPath, err = prompt.Run() + if err != nil { + return data, err + } + } + } else { + data.Config.Server.TLSCertPath = "" + data.Config.Server.TLSKeyPath = "" + } fmt.Println() title(" Database setup ") @@ -124,7 +187,7 @@ func Configure() (*SetupData, error) { title(" App setup ") fmt.Println() - selPrompt := promptui.Select{ + selPrompt = promptui.Select{ Templates: selTmpls, Label: "Site type", Items: []string{"Single user blog", "Multi-user instance"},