photoprism/internal/server/start.go
Michael Mayer f98aa619ec Security: Disable HTTPS redirect by default #98
Signed-off-by: Michael Mayer <michael@photoprism.app>
2022-10-13 23:44:51 +02:00

160 lines
4.2 KiB
Go

package server
import (
"context"
"fmt"
"net/http"
"time"
"golang.org/x/crypto/acme/autocert"
"golang.org/x/sync/errgroup"
"github.com/gin-contrib/gzip"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/config"
)
// Start the REST API server using the configuration provided
func Start(ctx context.Context, conf *config.Config) {
defer func() {
if err := recover(); err != nil {
log.Error(err)
}
}()
start := time.Now()
// Set HTTP server mode.
if conf.HttpMode() != "" {
gin.SetMode(conf.HttpMode())
} else if conf.Debug() == false {
gin.SetMode(gin.ReleaseMode)
}
// Create new HTTP router engine without standard middleware.
router := gin.New()
// Set proxy addresses from which headers related to the client and protocol can be trusted
if err := router.SetTrustedProxies(conf.TrustedProxies()); err != nil {
log.Warnf("server: %s", err)
}
// Register common middleware.
router.Use(Recovery(), Security(conf), Logger())
// Initialize package extensions.
Ext().Init(router, conf)
// Enable HTTP compression?
switch conf.HttpCompression() {
case "gzip":
log.Infof("server: enabling gzip compression")
router.Use(gzip.Gzip(
gzip.DefaultCompression,
gzip.WithExcludedPaths([]string{
conf.BaseUri(config.ApiUri + "/t"),
conf.BaseUri(config.ApiUri + "/folders/t"),
conf.BaseUri(config.ApiUri + "/zip"),
conf.BaseUri(config.ApiUri + "/albums"),
conf.BaseUri(config.ApiUri + "/labels"),
conf.BaseUri(config.ApiUri + "/videos"),
})))
}
// Find and load templates.
router.LoadHTMLFiles(conf.TemplateFiles()...)
// Register HTTP route handlers.
registerRoutes(router, conf)
var tlsErr error
var tlsManager *autocert.Manager
var server *http.Server
// Enable TLS?
if tlsManager, tlsErr = AutoTLS(conf); tlsErr == nil {
httpsRedirect = conf.HttpsRedirect()
server = &http.Server{
Addr: fmt.Sprintf("%s:%d", conf.HttpHost(), conf.HttpsPort()),
TLSConfig: tlsManager.TLSConfig(),
Handler: router,
}
log.Infof("server: starting in auto tls mode on %s [%s]", server.Addr, time.Since(start))
go StartAutoTLS(server, tlsManager, conf)
} else if httpsCert, privateKey := conf.TLS(); httpsCert != "" && privateKey != "" {
log.Infof("server: starting in manual tls mode")
server = &http.Server{
Addr: fmt.Sprintf("%s:%d", conf.HttpHost(), conf.HttpsPort()),
Handler: router,
}
log.Infof("server: listening on %s [%s]", server.Addr, time.Since(start))
go StartTLS(server, httpsCert, privateKey)
} else {
log.Infof("server: %s", tlsErr)
server = &http.Server{
Addr: fmt.Sprintf("%s:%d", conf.HttpHost(), conf.HttpPort()),
Handler: router,
}
log.Infof("server: listening on %s [%s]", server.Addr, time.Since(start))
go StartHttp(server)
}
// Graceful HTTP server shutdown.
<-ctx.Done()
log.Info("server: shutting down")
err := server.Close()
if err != nil {
log.Errorf("server: shutdown failed (%s)", err)
}
}
// StartHttp starts the web server in http mode.
func StartHttp(s *http.Server) {
if err := s.ListenAndServe(); err != nil {
if err == http.ErrServerClosed {
log.Info("server: shutdown complete")
} else {
log.Errorf("server: %s", err)
}
}
}
// StartTLS starts the web server in https mode.
func StartTLS(s *http.Server, httpsCert, privateKey string) {
if err := s.ListenAndServeTLS(httpsCert, privateKey); err != nil {
if err == http.ErrServerClosed {
log.Info("server: shutdown complete")
} else {
log.Errorf("server: %s", err)
}
}
}
// StartAutoTLS starts the web server with auto tls enabled.
func StartAutoTLS(s *http.Server, m *autocert.Manager, conf *config.Config) {
var g errgroup.Group
g.Go(func() error {
return http.ListenAndServe(fmt.Sprintf("%s:%d", conf.HttpHost(), conf.HttpPort()), m.HTTPHandler(http.HandlerFunc(redirect)))
})
g.Go(func() error {
return s.ListenAndServeTLS("", "")
})
if err := g.Wait(); err != nil {
if err == http.ErrServerClosed {
log.Info("server: shutdown complete")
} else {
log.Errorf("server: %s", err)
}
}
}
func redirect(w http.ResponseWriter, req *http.Request) {
target := "https://" + req.Host + req.RequestURI
http.Redirect(w, req, target, httpsRedirect)
}