server.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package server // import "miniflux.app/v2/internal/http/server"
  4. import (
  5. "crypto/tls"
  6. "fmt"
  7. "log/slog"
  8. "net"
  9. "net/http"
  10. "os"
  11. "strconv"
  12. "strings"
  13. "miniflux.app/v2/internal/config"
  14. "miniflux.app/v2/internal/storage"
  15. "miniflux.app/v2/internal/worker"
  16. "golang.org/x/crypto/acme"
  17. "golang.org/x/crypto/acme/autocert"
  18. )
  19. func StartWebServer(store *storage.Storage, pool *worker.Pool) []*http.Server {
  20. var servers []*http.Server
  21. autocertTLSConfig, challengeServer := setupAutocert(store)
  22. if challengeServer != nil {
  23. servers = append(servers, challengeServer)
  24. }
  25. certFile := config.Opts.CertFile()
  26. keyFile := config.Opts.CertKeyFile()
  27. certDomain := config.Opts.CertDomain()
  28. targets := determineListenTargets(config.Opts.ListenAddr(), certDomain, certFile, keyFile)
  29. if autocertTLSConfig != nil || anyTLS(targets) {
  30. config.Opts.SetHTTPSValue(true)
  31. }
  32. for _, t := range targets {
  33. srv := &http.Server{
  34. Addr: t.address,
  35. ReadTimeout: config.Opts.HTTPServerTimeout(),
  36. WriteTimeout: config.Opts.HTTPServerTimeout(),
  37. IdleTimeout: config.Opts.HTTPServerTimeout(),
  38. ReadHeaderTimeout: config.Opts.HTTPServerTimeout(),
  39. Handler: newRouter(store, pool),
  40. }
  41. switch t.mode {
  42. case modeSystemd:
  43. startSystemdSocketServer(srv)
  44. case modeUnixSocket:
  45. startUnixSocketServer(srv, t.address)
  46. case modeUnixSocketTLS:
  47. startUnixSocketTLSServer(srv, t.address, t.certFile, t.keyFile)
  48. case modeAutocertTLS:
  49. startAutoCertTLSServer(srv, autocertTLSConfig)
  50. case modeTLS:
  51. startTLSServer(srv, t.certFile, t.keyFile)
  52. default:
  53. startHTTPServer(srv)
  54. }
  55. servers = append(servers, srv)
  56. }
  57. return servers
  58. }
  59. type listenerMode int
  60. const (
  61. modeHTTP listenerMode = iota
  62. modeTLS
  63. modeAutocertTLS
  64. modeUnixSocket
  65. modeUnixSocketTLS
  66. modeSystemd
  67. )
  68. type listenTarget struct {
  69. address string
  70. mode listenerMode
  71. certFile string
  72. keyFile string
  73. }
  74. func determineListenTargets(addresses []string, certDomain, certFile, keyFile string) []listenTarget {
  75. isSystemd := os.Getenv("LISTEN_PID") == strconv.Itoa(os.Getpid())
  76. hasCertFiles := certFile != "" && keyFile != ""
  77. hasAutocert := certDomain != ""
  78. var targets []listenTarget
  79. for i, addr := range addresses {
  80. if isSystemd {
  81. if i == 0 {
  82. targets = append(targets, listenTarget{address: addr, mode: modeSystemd})
  83. } else {
  84. slog.Warn("Systemd socket activation: only the first listen address is used, others are ignored",
  85. slog.String("skipped_address", addr),
  86. )
  87. }
  88. continue
  89. }
  90. isUnix := strings.HasPrefix(addr, "/")
  91. switch {
  92. case isUnix && hasCertFiles:
  93. targets = append(targets, listenTarget{address: addr, mode: modeUnixSocketTLS, certFile: certFile, keyFile: keyFile})
  94. case isUnix:
  95. targets = append(targets, listenTarget{address: addr, mode: modeUnixSocket})
  96. case hasAutocert && (addr == ":https" || (i == 0 && strings.Contains(addr, ":"))):
  97. targets = append(targets, listenTarget{address: addr, mode: modeAutocertTLS})
  98. case hasCertFiles:
  99. targets = append(targets, listenTarget{address: addr, mode: modeTLS, certFile: certFile, keyFile: keyFile})
  100. default:
  101. targets = append(targets, listenTarget{address: addr, mode: modeHTTP})
  102. }
  103. }
  104. return targets
  105. }
  106. func anyTLS(targets []listenTarget) bool {
  107. for _, t := range targets {
  108. switch t.mode {
  109. case modeTLS, modeAutocertTLS, modeUnixSocketTLS:
  110. return true
  111. }
  112. }
  113. return false
  114. }
  115. func setupAutocert(store *storage.Storage) (*tls.Config, *http.Server) {
  116. certDomain := config.Opts.CertDomain()
  117. if certDomain == "" {
  118. return nil, nil
  119. }
  120. slog.Debug("Configuring autocert manager", slog.String("domain", certDomain))
  121. certManager := autocert.Manager{
  122. Cache: storage.NewCertificateCache(store),
  123. Prompt: autocert.AcceptTOS,
  124. HostPolicy: autocert.HostWhitelist(certDomain),
  125. }
  126. tlsConfig := &tls.Config{
  127. NextProtos: []string{"h2", "http/1.1", acme.ALPNProto},
  128. }
  129. tlsConfig.GetCertificate = certManager.GetCertificate
  130. challengeServer := &http.Server{
  131. Handler: certManager.HTTPHandler(nil),
  132. Addr: ":http",
  133. }
  134. slog.Info("Starting ACME HTTP challenge server", slog.String("address", challengeServer.Addr))
  135. go func() {
  136. if err := challengeServer.ListenAndServe(); err != http.ErrServerClosed {
  137. slog.Error("ACME HTTP challenge server failed", slog.Any("error", err))
  138. }
  139. }()
  140. return tlsConfig, challengeServer
  141. }
  142. func startSystemdSocketServer(server *http.Server) {
  143. go func() {
  144. f := os.NewFile(3, "systemd socket")
  145. listener, err := net.FileListener(f)
  146. if err != nil {
  147. printErrorAndExit(`Unable to create listener from systemd socket: %v`, err)
  148. }
  149. slog.Info(`Starting server using systemd socket`)
  150. if err := server.Serve(listener); err != http.ErrServerClosed {
  151. printErrorAndExit(`Systemd socket server failed to start: %v`, err)
  152. }
  153. }()
  154. }
  155. func startUnixSocketServer(server *http.Server, socketFile string) {
  156. listener := createUnixSocketListener(socketFile)
  157. go func() {
  158. slog.Info("Starting server using a Unix socket", slog.String("socket", socketFile))
  159. if err := server.Serve(listener); err != http.ErrServerClosed {
  160. printErrorAndExit("Unix socket server failed to start on %s: %v", socketFile, err)
  161. }
  162. }()
  163. }
  164. func startUnixSocketTLSServer(server *http.Server, socketFile, certFile, keyFile string) {
  165. listener := createUnixSocketListener(socketFile)
  166. go func() {
  167. slog.Info("Starting TLS server using a Unix socket",
  168. slog.String("socket", socketFile),
  169. slog.String("cert_file", certFile),
  170. slog.String("key_file", keyFile),
  171. )
  172. if err := server.ServeTLS(listener, certFile, keyFile); err != http.ErrServerClosed {
  173. printErrorAndExit("TLS Unix socket server failed to start on %s: %v", socketFile, err)
  174. }
  175. }()
  176. }
  177. func createUnixSocketListener(socketFile string) net.Listener {
  178. if err := os.Remove(socketFile); err != nil && !os.IsNotExist(err) {
  179. printErrorAndExit("Unable to remove existing Unix socket %s: %v", socketFile, err)
  180. }
  181. listener, err := net.Listen("unix", socketFile)
  182. if err != nil {
  183. printErrorAndExit(`Server failed to listen on Unix socket %s: %v`, socketFile, err)
  184. }
  185. if err := os.Chmod(socketFile, 0666); err != nil {
  186. printErrorAndExit(`Unable to change socket permission for %s: %v`, socketFile, err)
  187. }
  188. return listener
  189. }
  190. func startAutoCertTLSServer(server *http.Server, autoTLSConfig *tls.Config) {
  191. if server.TLSConfig == nil {
  192. server.TLSConfig = &tls.Config{}
  193. }
  194. server.TLSConfig.GetCertificate = autoTLSConfig.GetCertificate
  195. server.TLSConfig.NextProtos = autoTLSConfig.NextProtos
  196. go func() {
  197. slog.Info("Starting TLS server using automatic certificate management",
  198. slog.String("listen_address", server.Addr),
  199. )
  200. if err := server.ListenAndServeTLS("", ""); err != http.ErrServerClosed {
  201. printErrorAndExit("Autocert server failed to start on %s: %v", server.Addr, err)
  202. }
  203. }()
  204. }
  205. func startTLSServer(server *http.Server, certFile, keyFile string) {
  206. go func() {
  207. slog.Info("Starting TLS server using a certificate",
  208. slog.String("listen_address", server.Addr),
  209. slog.String("cert_file", certFile),
  210. slog.String("key_file", keyFile),
  211. )
  212. if err := server.ListenAndServeTLS(certFile, keyFile); err != http.ErrServerClosed {
  213. printErrorAndExit("TLS server failed to start on %s: %v", server.Addr, err)
  214. }
  215. }()
  216. }
  217. func startHTTPServer(server *http.Server) {
  218. go func() {
  219. slog.Info("Starting HTTP server",
  220. slog.String("listen_address", server.Addr),
  221. )
  222. if err := server.ListenAndServe(); err != http.ErrServerClosed {
  223. printErrorAndExit("HTTP server failed to start on %s: %v", server.Addr, err)
  224. }
  225. }()
  226. }
  227. func printErrorAndExit(format string, a ...any) {
  228. message := fmt.Sprintf(format, a...)
  229. slog.Error(message)
  230. fmt.Fprintf(os.Stderr, "%v\n", message)
  231. os.Exit(1)
  232. }