From e5b844d2c4baf000d9a3b0a79af55a00d88c7003 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Tue, 30 Jun 2020 21:34:47 -0700 Subject: [PATCH] Support automatically generating SSL certificates --- cmd/root.go | 44 ++++++++++++++++++++++++++++++++++++++------ go.mod | 5 +++-- go.sum | 4 ++++ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 71c7656..2f9bdf8 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -6,6 +6,7 @@ import ( "github.com/apex/log" "github.com/mitchellh/colorstring" "github.com/pterodactyl/wings/loggers/cli" + "golang.org/x/crypto/acme/autocert" "net/http" "os" "path" @@ -27,11 +28,19 @@ import ( var configPath = config.DefaultLocation var debug = false var shouldRunProfiler = false +var useAutomaticTls = false +var tlsHostname = "" var root = &cobra.Command{ Use: "wings", Short: "The wings of the pterodactyl game management panel", Long: ``, + PreRun: func(cmd *cobra.Command, args []string) { + if useAutomaticTls && len(tlsHostname) == 0 { + fmt.Println("A TLS hostname must be provided when running wings with automatic TLS, e.g.:\n\n ./wings --auto-tls --tls-hostname my.example.com") + os.Exit(1) + } + }, Run: rootCmdRun, } @@ -39,6 +48,8 @@ func init() { root.PersistentFlags().StringVar(&configPath, "config", config.DefaultLocation, "set the location for the configuration file") root.PersistentFlags().BoolVar(&debug, "debug", false, "pass in order to run wings in debug mode") root.PersistentFlags().BoolVar(&shouldRunProfiler, "profile", false, "pass in order to profile wings") + root.PersistentFlags().BoolVar(&useAutomaticTls, "auto-tls", false, "pass in order to have wings generate and manage it's own SSL certificates using Let's Encrypt") + root.PersistentFlags().StringVar(&tlsHostname, "tls-hostname", "", "required with --auto-tls, the FQDN for the generated SSL certificate") root.AddCommand(configureCmd) } @@ -218,17 +229,38 @@ func rootCmdRun(*cobra.Command, []string) { } log.WithFields(log.Fields{ - "ssl": c.Api.Ssl.Enabled, - "host": c.Api.Host, - "port": c.Api.Port, - }).Info("configuring webserver...") + "use_ssl": c.Api.Ssl.Enabled, + "use_auto_tls": useAutomaticTls && len(tlsHostname) > 0, + "host_address": c.Api.Host, + "host_port": c.Api.Port, + }).Info("configuring internal webserver") r := router.Configure() addr := fmt.Sprintf("%s:%d", c.Api.Host, c.Api.Port) - if c.Api.Ssl.Enabled { + if useAutomaticTls && len(tlsHostname) > 0 { + m := autocert.Manager{ + Prompt: autocert.AcceptTOS, + Cache: autocert.DirCache(path.Join(c.System.RootDirectory, "/.tls-cache")), + HostPolicy: autocert.HostWhitelist(tlsHostname), + } + + log.WithField("hostname", tlsHostname). + Info("webserver is now listening with auto-TLS enabled; certifcates will be automatically generated by Let's Encrypt") + + // We don't use the autotls runner here since we need to specify a port other than 443 + // to be using for SSL connections for Wings. + s := &http.Server{Addr: addr, TLSConfig: m.TLSConfig(), Handler: r} + + go http.ListenAndServe(":http", m.HTTPHandler(nil)) + if err := s.ListenAndServeTLS("", ""); err != nil { + log.WithFields(log.Fields{"auto_tls": true, "tls_hostname": tlsHostname, "error": err}). + Fatal("failed to configure HTTP server using auto-tls") + os.Exit(1) + } + } else if c.Api.Ssl.Enabled { if err := r.RunTLS(addr, c.Api.Ssl.CertificateFile, c.Api.Ssl.KeyFile); err != nil { - log.WithField("error", err).Fatal("failed to configure HTTPS server") + log.WithFields(log.Fields{"auto_tls": false, "error": err}).Fatal("failed to configure HTTPS server") os.Exit(1) } } else { diff --git a/go.mod b/go.mod index 2f5b4ca..885a0a8 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,8 @@ require ( github.com/gabriel-vasile/mimetype v0.1.4 github.com/gbrlsnchs/jwt/v3 v3.0.0-rc.0 github.com/ghodss/yaml v1.0.0 - github.com/gin-gonic/gin v1.6.2 + github.com/gin-gonic/autotls v0.0.0-20200518075542-45033372a9ad + github.com/gin-gonic/gin v1.6.3 github.com/golang/protobuf v1.3.5 // indirect github.com/google/uuid v1.1.1 github.com/gorilla/websocket v1.4.0 @@ -63,7 +64,7 @@ require ( github.com/stretchr/objx v0.2.0 // indirect github.com/yuin/goldmark v1.1.30 // indirect go.uber.org/zap v1.15.0 - golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 // indirect + golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 // indirect golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a diff --git a/go.sum b/go.sum index c63597c..f9ec8af 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,12 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/autotls v0.0.0-20200518075542-45033372a9ad h1:qXUH5CUVcICSzL8DedgF39LCsrVoMFbswxByQVtd5h8= +github.com/gin-gonic/autotls v0.0.0-20200518075542-45033372a9ad/go.mod h1:yLpIL/Gol/Y5/A36WyQNCRIAVuhiuHEtITA6UQ7EghY= github.com/gin-gonic/gin v1.6.2 h1:88crIK23zO6TqlQBt+f9FrPJNKm9ZEr7qjp9vl/d5TM= github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=