Skip to content

Commit

Permalink
Add option to load TLS cert/key, and use HTTPS
Browse files Browse the repository at this point in the history
  • Loading branch information
deluan committed Mar 17, 2023
1 parent 7ea1113 commit 673880d
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 13 deletions.
16 changes: 11 additions & 5 deletions cmd/root.go
Expand Up @@ -103,7 +103,7 @@ func startServer(ctx context.Context) func() error {
if strings.HasPrefix(conf.Server.UILoginBackgroundURL, "/") {
a.MountRouter("Background images", consts.DefaultUILoginBackgroundURL, backgrounds.NewHandler())
}
return a.Run(ctx, fmt.Sprintf("%s:%d", conf.Server.Address, conf.Server.Port))
return a.Run(ctx, fmt.Sprintf("%s:%d", conf.Server.Address, conf.Server.Port), conf.Server.TLSCert, conf.Server.TLSKey)
}
}

Expand Down Expand Up @@ -162,11 +162,14 @@ func init() {
_ = viper.BindPFlag("datafolder", rootCmd.PersistentFlags().Lookup("datafolder"))
_ = viper.BindPFlag("loglevel", rootCmd.PersistentFlags().Lookup("loglevel"))

rootCmd.Flags().StringP("address", "a", viper.GetString("address"), "IP address to bind")
rootCmd.Flags().IntP("port", "p", viper.GetInt("port"), "HTTP port Navidrome will use")
rootCmd.Flags().StringP("address", "a", viper.GetString("address"), "IP address to bind to")
rootCmd.Flags().IntP("port", "p", viper.GetInt("port"), "HTTP port Navidrome will listen to")
rootCmd.Flags().String("baseurl", viper.GetString("baseurl"), "base URL to configure Navidrome behind a proxy (ex: /music or http://my.server.com)")
rootCmd.Flags().String("tlscert", viper.GetString("tlscert"), "optional path to a TLS cert file (enables HTTPS listening)")
rootCmd.Flags().String("tlskey", viper.GetString("tlskey"), "optional path to a TLS key file (enables HTTPS listening)")

rootCmd.Flags().Duration("sessiontimeout", viper.GetDuration("sessiontimeout"), "how long Navidrome will wait before closing web ui idle sessions")
rootCmd.Flags().Duration("scaninterval", viper.GetDuration("scaninterval"), "how frequently to scan for changes in your music library")
rootCmd.Flags().String("baseurl", viper.GetString("baseurl"), "base URL (only the path part) to configure Navidrome behind a proxy (ex: /music)")
rootCmd.Flags().String("uiloginbackgroundurl", viper.GetString("uiloginbackgroundurl"), "URL to a backaground image used in the Login page")
rootCmd.Flags().Bool("enabletranscodingconfig", viper.GetBool("enabletranscodingconfig"), "enables transcoding configuration in the UI")
rootCmd.Flags().String("transcodingcachesize", viper.GetString("transcodingcachesize"), "size of transcoding cache")
Expand All @@ -178,9 +181,12 @@ func init() {

_ = viper.BindPFlag("address", rootCmd.Flags().Lookup("address"))
_ = viper.BindPFlag("port", rootCmd.Flags().Lookup("port"))
_ = viper.BindPFlag("tlscert", rootCmd.Flags().Lookup("tlscert"))
_ = viper.BindPFlag("tlskey", rootCmd.Flags().Lookup("tlskey"))
_ = viper.BindPFlag("baseurl", rootCmd.Flags().Lookup("baseurl"))

_ = viper.BindPFlag("sessiontimeout", rootCmd.Flags().Lookup("sessiontimeout"))
_ = viper.BindPFlag("scaninterval", rootCmd.Flags().Lookup("scaninterval"))
_ = viper.BindPFlag("baseurl", rootCmd.Flags().Lookup("baseurl"))
_ = viper.BindPFlag("uiloginbackgroundurl", rootCmd.Flags().Lookup("uiloginbackgroundurl"))

_ = viper.BindPFlag("prometheus.enabled", rootCmd.Flags().Lookup("prometheus.enabled"))
Expand Down
4 changes: 4 additions & 0 deletions conf/configuration.go
Expand Up @@ -32,6 +32,8 @@ type configOptions struct {
BasePath string
BaseHost string
BaseScheme string
TLSCert string
TLSKey string
UILoginBackgroundURL string
UIWelcomeMessage string
MaxSidebarPlaylists int
Expand Down Expand Up @@ -246,6 +248,8 @@ func init() {
viper.SetDefault("scaninterval", -1)
viper.SetDefault("scanschedule", "@every 1m")
viper.SetDefault("baseurl", "")
viper.SetDefault("tlscert", "")
viper.SetDefault("tlskey", "")
viper.SetDefault("uiloginbackgroundurl", consts.DefaultUILoginBackgroundURL)
viper.SetDefault("uiwelcomemessage", "")
viper.SetDefault("maxsidebarplaylists", consts.DefaultMaxSidebarPlaylists)
Expand Down
43 changes: 35 additions & 8 deletions server/server.go
Expand Up @@ -46,30 +46,57 @@ func (s *Server) MountRouter(description, urlPath string, subRouter http.Handler
})
}

func (s *Server) Run(ctx context.Context, addr string) error {
// Run starts the server with the given address, and if specified, with TLS enabled.
func (s *Server) Run(ctx context.Context, addr string, tlsCert string, tlsKey string) error {
// Mount the router for the frontend assets
s.MountRouter("WebUI", consts.URLPathUI, s.frontendAssetsHandler())

// Create a new http.Server with the specified address and read header timeout
server := &http.Server{
Addr: addr,
ReadHeaderTimeout: consts.ServerReadHeaderTimeout,
Handler: s.router,
}

// Start HTTP server in its own goroutine, send a signal (errC) if failed to start
// Determine if TLS is enabled
tlsEnabled := tlsCert != "" && tlsKey != ""

// Start the server in a new goroutine and send an error signal to errC if there's an error
errC := make(chan error)
go func() {
if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
log.Error(ctx, "Could not start server. Aborting", err)
errC <- err
if tlsEnabled {
// Start the HTTPS server
log.Info("Starting server with TLS (HTTPS) enabled", "tlsCert", tlsCert, "tlsKey", tlsKey)
if err := server.ListenAndServeTLS(tlsCert, tlsKey); !errors.Is(err, http.ErrServerClosed) {
errC <- err
}
} else {
// Start the HTTP server
if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
errC <- err
}
}
}()

log.Info(ctx, "Navidrome server is ready!", "address", addr, "startupTime", time.Since(consts.ServerStart))
// Measure server startup time
startupTime := time.Since(consts.ServerStart)

// Wait a short time before checking if the server has started successfully
time.Sleep(50 * time.Millisecond)
select {
case err := <-errC:
log.Error(ctx, "Could not start server. Aborting", err)
return fmt.Errorf("error starting server: %w", err)
default:
log.Info(ctx, "----> Navidrome server is ready!", "address", addr, "startupTime", startupTime, "tlsEnabled", tlsEnabled)
}

// Wait for a signal to terminate (or an error during startup)
// Wait for a signal to terminate
select {
case err := <-errC:
return err
return fmt.Errorf("error running server: %w", err)
case <-ctx.Done():
// If the context is done (i.e. the server should stop), proceed to shutting down the server
}

// Try to stop the HTTP server gracefully
Expand Down

0 comments on commit 673880d

Please sign in to comment.