diff --git a/docs/self-hosting.md b/docs/self-hosting.md index ca22f13d8..61270b38a 100644 --- a/docs/self-hosting.md +++ b/docs/self-hosting.md @@ -90,4 +90,3 @@ For this tutorial we will be using domain ```test.wiretrustee.com``` which point docker-compose logs management docker-compose logs coturn docker-compose logs dashboard - ``` diff --git a/management/README.md b/management/README.md index ce632fda1..c883b7831 100644 --- a/management/README.md +++ b/management/README.md @@ -14,7 +14,8 @@ Flags: -h, --help help for management --letsencrypt-domain string a domain to issue Let's Encrypt certificate for. Enables TLS using Let's Encrypt. Will fetch and renew certificate, and run the server with TLS --port int server port to listen on (default 33073) - + --cert-file string Location of your SSL certificate. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect + --cert-key string Location of your SSL certificate private key. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect Global Flags: --config string Wiretrustee config file location to write new config to (default "/etc/wiretrustee/config.json") --log-level string (default "info") diff --git a/management/cmd/management.go b/management/cmd/management.go index a1e26fd44..77aa58ed8 100644 --- a/management/cmd/management.go +++ b/management/cmd/management.go @@ -2,6 +2,7 @@ package cmd import ( "context" + "crypto/tls" "flag" "fmt" "github.com/wiretrustee/wiretrustee/management/server" @@ -25,6 +26,8 @@ var ( mgmtDataDir string mgmtConfig string mgmtLetsencryptDomain string + certFile string + certKey string kaep = keepalive.EnforcementPolicy{ MinTime: 15 * time.Second, @@ -71,12 +74,23 @@ var ( var httpServer *http.Server if config.HttpConfig.LetsEncryptDomain != "" { + //automatically generate a new certificate with Let's Encrypt certManager := encryption.CreateCertManager(config.Datadir, config.HttpConfig.LetsEncryptDomain) transportCredentials := credentials.NewTLS(certManager.TLSConfig()) opts = append(opts, grpc.Creds(transportCredentials)) httpServer = http.NewHttpsServer(config.HttpConfig, certManager, accountManager) + } else if config.HttpConfig.CertFile != "" && config.HttpConfig.CertKey != "" { + //use provided certificate + tlsConfig, err := loadTLSConfig(config.HttpConfig.CertFile, config.HttpConfig.CertKey) + if err != nil { + log.Fatal("cannot load TLS credentials: ", err) + } + transportCredentials := credentials.NewTLS(tlsConfig) + opts = append(opts, grpc.Creds(transportCredentials)) + httpServer = http.NewHttpsServerWithTLSConfig(config.HttpConfig, tlsConfig, accountManager) } else { + //start server without SSL httpServer = http.NewHttpServer(config.HttpConfig, accountManager) } @@ -136,14 +150,37 @@ func loadConfig() (*server.Config, error) { config.Datadir = mgmtDataDir } + if certKey != "" && certFile != "" { + config.HttpConfig.CertFile = certFile + config.HttpConfig.CertKey = certKey + } + return config, err } +func loadTLSConfig(certFile string, certKey string) (*tls.Config, error) { + // Load server's certificate and private key + serverCert, err := tls.LoadX509KeyPair(certFile, certKey) + if err != nil { + return nil, err + } + + // Create the credentials and return it + config := &tls.Config{ + Certificates: []tls.Certificate{serverCert}, + ClientAuth: tls.NoClientCert, + } + + return config, nil +} + func init() { mgmtCmd.Flags().IntVar(&mgmtPort, "port", 33073, "server port to listen on") mgmtCmd.Flags().StringVar(&mgmtDataDir, "datadir", "/var/lib/wiretrustee/", "server data directory location") mgmtCmd.Flags().StringVar(&mgmtConfig, "config", "/etc/wiretrustee/management.json", "Wiretrustee config file location. Config params specified via command line (e.g. datadir) have a precedence over configuration from this file") mgmtCmd.Flags().StringVar(&mgmtLetsencryptDomain, "letsencrypt-domain", "", "a domain to issue Let's Encrypt certificate for. Enables TLS using Let's Encrypt. Will fetch and renew certificate, and run the server with TLS") + mgmtCmd.Flags().StringVar(&certFile, "cert-file", "", "Location of your SSL certificate. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect") + mgmtCmd.Flags().StringVar(&certKey, "cert-key", "", "Location of your SSL certificate private key. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect") rootCmd.MarkFlagRequired("config") //nolint diff --git a/management/server/config.go b/management/server/config.go index c8da0388c..dc20cdce8 100644 --- a/management/server/config.go +++ b/management/server/config.go @@ -36,7 +36,11 @@ type TURNConfig struct { // HttpServerConfig is a config of the HTTP Management service server type HttpServerConfig struct { LetsEncryptDomain string - Address string + //CertFile is the location of the certificate + CertFile string + //CertKey is the location of the certificate private key + CertKey string + Address string // AuthAudience identifies the recipients that the JWT is intended for (aud in JWT) AuthAudience string // AuthIssuer identifies principal that issued the JWT. diff --git a/management/server/http/server.go b/management/server/http/server.go index 828a819d5..c8b8ed807 100644 --- a/management/server/http/server.go +++ b/management/server/http/server.go @@ -2,6 +2,7 @@ package http import ( "context" + "crypto/tls" "github.com/gorilla/mux" "github.com/rs/cors" log "github.com/sirupsen/logrus" @@ -17,10 +18,11 @@ type Server struct { server *http.Server config *s.HttpServerConfig certManager *autocert.Manager + tlsConfig *tls.Config accountManager *s.AccountManager } -// NewHttpsServer creates a new HTTPs server (with HTTPS support) +// NewHttpsServer creates a new HTTPs server (with HTTPS support) and a certManager that is responsible for generating and renewing Let's Encrypt certificate // The listening address will be :443 no matter what was specified in s.HttpServerConfig.Address func NewHttpsServer(config *s.HttpServerConfig, certManager *autocert.Manager, accountManager *s.AccountManager) *Server { server := &http.Server{ @@ -32,6 +34,18 @@ func NewHttpsServer(config *s.HttpServerConfig, certManager *autocert.Manager, a return &Server{server: server, config: config, certManager: certManager, accountManager: accountManager} } +// NewHttpsServerWithTLSConfig creates a new HTTPs server with a provided tls.Config. +// Usually used when you already have a certificate +func NewHttpsServerWithTLSConfig(config *s.HttpServerConfig, tlsConfig *tls.Config, accountManager *s.AccountManager) *Server { + server := &http.Server{ + Addr: config.Address, + WriteTimeout: time.Second * 15, + ReadTimeout: time.Second * 15, + IdleTimeout: time.Second * 60, + } + return &Server{server: server, config: config, tlsConfig: tlsConfig, accountManager: accountManager} +} + // NewHttpServer creates a new HTTP server (without HTTPS) func NewHttpServer(config *s.HttpServerConfig, accountManager *s.AccountManager) *Server { return NewHttpsServer(config, nil, accountManager) @@ -71,13 +85,26 @@ func (s *Server) Start() error { if s.certManager != nil { // if HTTPS is enabled we reuse the listener from the cert manager listener := s.certManager.Listener() - log.Infof("http server listening on %s", listener.Addr()) + log.Infof("HTTPs server listening on %s with Let's Encrypt autocert configured", listener.Addr()) if err = http.Serve(listener, s.certManager.HTTPHandler(r)); err != nil { log.Errorf("failed to serve https server: %v", err) return err } + } else if s.tlsConfig != nil { + listener, err := tls.Listen("tcp", s.config.Address, s.tlsConfig) + if err != nil { + log.Errorf("failed to serve https server: %v", err) + return err + } + log.Infof("HTTPs server listening on %s", listener.Addr()) + + if err = http.Serve(listener, r); err != nil { + log.Errorf("failed to serve https server: %v", err) + return err + } + } else { - log.Infof("http server listening on %s", s.server.Addr) + log.Infof("HTTP server listening on %s", s.server.Addr) if err = s.server.ListenAndServe(); err != nil { log.Errorf("failed to serve http server: %v", err) return err