server.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. // Package http provides a registration interface for http services
  2. package http
  3. import (
  4. "bytes"
  5. "context"
  6. "crypto/tls"
  7. "crypto/x509"
  8. "errors"
  9. "fmt"
  10. "html/template"
  11. "log"
  12. "net"
  13. "net/http"
  14. "os"
  15. "path/filepath"
  16. "strings"
  17. "sync"
  18. "time"
  19. "github.com/go-chi/chi/v5"
  20. "github.com/rclone/rclone/fs/config/flags"
  21. "github.com/rclone/rclone/lib/atexit"
  22. "github.com/spf13/pflag"
  23. )
  24. // Help returns text describing the http server to add to the command
  25. // help.
  26. func Help(prefix string) string {
  27. help := `### Server options
  28. Use ` + "`--{{ .Prefix }}addr`" + ` to specify which IP address and port the server should
  29. listen on, eg ` + "`--{{ .Prefix }}addr 1.2.3.4:8000` or `--{{ .Prefix }}addr :8080`" + ` to listen to all
  30. IPs. By default it only listens on localhost. You can use port
  31. :0 to let the OS choose an available port.
  32. If you set ` + "`--{{ .Prefix }}addr`" + ` to listen on a public or LAN accessible IP address
  33. then using Authentication is advised - see the next section for info.
  34. You can use a unix socket by setting the url to ` + "`unix:///path/to/socket`" + `
  35. or just by using an absolute path name. Note that unix sockets bypass the
  36. authentication - this is expected to be done with file system permissions.
  37. ` + "`--{{ .Prefix }}addr`" + ` may be repeated to listen on multiple IPs/ports/sockets.
  38. ` + "`--{{ .Prefix }}server-read-timeout` and `--{{ .Prefix }}server-write-timeout`" + ` can be used to
  39. control the timeouts on the server. Note that this is the total time
  40. for a transfer.
  41. ` + "`--{{ .Prefix }}max-header-bytes`" + ` controls the maximum number of bytes the server will
  42. accept in the HTTP header.
  43. ` + "`--{{ .Prefix }}baseurl`" + ` controls the URL prefix that rclone serves from. By default
  44. rclone will serve from the root. If you used ` + "`--{{ .Prefix }}baseurl \"/rclone\"`" + ` then
  45. rclone would serve from a URL starting with "/rclone/". This is
  46. useful if you wish to proxy rclone serve. Rclone automatically
  47. inserts leading and trailing "/" on ` + "`--{{ .Prefix }}baseurl`" + `, so ` + "`--{{ .Prefix }}baseurl \"rclone\"`" + `,
  48. ` + "`--{{ .Prefix }}baseurl \"/rclone\"` and `--{{ .Prefix }}baseurl \"/rclone/\"`" + ` are all treated
  49. identically.
  50. #### TLS (SSL)
  51. By default this will serve over http. If you want you can serve over
  52. https. You will need to supply the ` + "`--{{ .Prefix }}cert` and `--{{ .Prefix }}key`" + ` flags.
  53. If you wish to do client side certificate validation then you will need to
  54. supply ` + "`--{{ .Prefix }}client-ca`" + ` also.
  55. ` + "`--{{ .Prefix }}cert`" + ` should be a either a PEM encoded certificate or a concatenation
  56. of that with the CA certificate. ` + "`--k{{ .Prefix }}ey`" + ` should be the PEM encoded
  57. private key and ` + "`--{{ .Prefix }}client-ca`" + ` should be the PEM encoded client
  58. certificate authority certificate.
  59. ` + "`--{{ .Prefix }}min-tls-version`" + ` is minimum TLS version that is acceptable. Valid
  60. values are "tls1.0", "tls1.1", "tls1.2" and "tls1.3" (default
  61. "tls1.0").
  62. `
  63. tmpl, err := template.New("server help").Parse(help)
  64. if err != nil {
  65. log.Fatal("Fatal error parsing template", err)
  66. }
  67. data := struct {
  68. Prefix string
  69. }{
  70. Prefix: prefix,
  71. }
  72. buf := &bytes.Buffer{}
  73. err = tmpl.Execute(buf, data)
  74. if err != nil {
  75. log.Fatal("Fatal error executing template", err)
  76. }
  77. return buf.String()
  78. }
  79. // Middleware function signature required by chi.Router.Use()
  80. type Middleware func(http.Handler) http.Handler
  81. // Config contains options for the http Server
  82. type Config struct {
  83. ListenAddr []string // Port to listen on
  84. BaseURL string // prefix to strip from URLs
  85. ServerReadTimeout time.Duration // Timeout for server reading data
  86. ServerWriteTimeout time.Duration // Timeout for server writing data
  87. MaxHeaderBytes int // Maximum size of request header
  88. TLSCert string // Path to TLS PEM key (concatenation of certificate and CA certificate)
  89. TLSKey string // Path to TLS PEM Private key
  90. TLSCertBody []byte // TLS PEM key (concatenation of certificate and CA certificate) body, ignores TLSCert
  91. TLSKeyBody []byte // TLS PEM Private key body, ignores TLSKey
  92. ClientCA string // Client certificate authority to verify clients with
  93. MinTLSVersion string // MinTLSVersion contains the minimum TLS version that is acceptable.
  94. AllowOrigin string // AllowOrigin sets the Access-Control-Allow-Origin header
  95. }
  96. // AddFlagsPrefix adds flags for the httplib
  97. func (cfg *Config) AddFlagsPrefix(flagSet *pflag.FlagSet, prefix string) {
  98. flags.StringArrayVarP(flagSet, &cfg.ListenAddr, prefix+"addr", "", cfg.ListenAddr, "IPaddress:Port or :Port to bind server to", prefix)
  99. flags.DurationVarP(flagSet, &cfg.ServerReadTimeout, prefix+"server-read-timeout", "", cfg.ServerReadTimeout, "Timeout for server reading data", prefix)
  100. flags.DurationVarP(flagSet, &cfg.ServerWriteTimeout, prefix+"server-write-timeout", "", cfg.ServerWriteTimeout, "Timeout for server writing data", prefix)
  101. flags.IntVarP(flagSet, &cfg.MaxHeaderBytes, prefix+"max-header-bytes", "", cfg.MaxHeaderBytes, "Maximum size of request header", prefix)
  102. flags.StringVarP(flagSet, &cfg.TLSCert, prefix+"cert", "", cfg.TLSCert, "TLS PEM key (concatenation of certificate and CA certificate)", prefix)
  103. flags.StringVarP(flagSet, &cfg.TLSKey, prefix+"key", "", cfg.TLSKey, "TLS PEM Private key", prefix)
  104. flags.StringVarP(flagSet, &cfg.ClientCA, prefix+"client-ca", "", cfg.ClientCA, "Client certificate authority to verify clients with", prefix)
  105. flags.StringVarP(flagSet, &cfg.BaseURL, prefix+"baseurl", "", cfg.BaseURL, "Prefix for URLs - leave blank for root", prefix)
  106. flags.StringVarP(flagSet, &cfg.MinTLSVersion, prefix+"min-tls-version", "", cfg.MinTLSVersion, "Minimum TLS version that is acceptable", prefix)
  107. flags.StringVarP(flagSet, &cfg.AllowOrigin, prefix+"allow-origin", "", cfg.AllowOrigin, "Origin which cross-domain request (CORS) can be executed from", prefix)
  108. }
  109. // AddHTTPFlagsPrefix adds flags for the httplib
  110. func AddHTTPFlagsPrefix(flagSet *pflag.FlagSet, prefix string, cfg *Config) {
  111. cfg.AddFlagsPrefix(flagSet, prefix)
  112. }
  113. // DefaultCfg is the default values used for Config
  114. func DefaultCfg() Config {
  115. return Config{
  116. ListenAddr: []string{"127.0.0.1:8080"},
  117. ServerReadTimeout: 1 * time.Hour,
  118. ServerWriteTimeout: 1 * time.Hour,
  119. MaxHeaderBytes: 4096,
  120. MinTLSVersion: "tls1.0",
  121. }
  122. }
  123. type instance struct {
  124. url string
  125. listener net.Listener
  126. httpServer *http.Server
  127. }
  128. func (s instance) serve(wg *sync.WaitGroup) {
  129. defer wg.Done()
  130. err := s.httpServer.Serve(s.listener)
  131. if err != http.ErrServerClosed && err != nil {
  132. log.Printf("%s: unexpected error: %s", s.listener.Addr(), err.Error())
  133. }
  134. }
  135. // Server contains info about the running http server
  136. type Server struct {
  137. wg sync.WaitGroup
  138. mux chi.Router
  139. tlsConfig *tls.Config
  140. instances []instance
  141. auth AuthConfig
  142. cfg Config
  143. template *TemplateConfig
  144. htmlTemplate *template.Template
  145. usingAuth bool // set if we are using auth middleware
  146. atexitHandle atexit.FnHandle
  147. }
  148. // Option allows customizing the server
  149. type Option func(*Server)
  150. // WithAuth option initializes the appropriate auth middleware
  151. func WithAuth(cfg AuthConfig) Option {
  152. return func(s *Server) {
  153. s.auth = cfg
  154. }
  155. }
  156. // WithConfig option applies the Config to the server, overriding defaults
  157. func WithConfig(cfg Config) Option {
  158. return func(s *Server) {
  159. s.cfg = cfg
  160. }
  161. }
  162. // WithTemplate option allows the parsing of a template
  163. func WithTemplate(cfg TemplateConfig) Option {
  164. return func(s *Server) {
  165. s.template = &cfg
  166. }
  167. }
  168. // NewServer instantiates a new http server using provided listeners and options
  169. // This function is provided if the default http server does not meet a services requirements and should not generally be used
  170. // A http server can listen using multiple listeners. For example, a listener for port 80, and a listener for port 443.
  171. // tlsListeners are ignored if opt.TLSKey is not provided
  172. func NewServer(ctx context.Context, options ...Option) (*Server, error) {
  173. s := &Server{
  174. mux: chi.NewRouter(),
  175. cfg: DefaultCfg(),
  176. }
  177. // Make sure default logger is logging where everything else is
  178. // middleware.DefaultLogger = middleware.RequestLogger(&middleware.DefaultLogFormatter{Logger: log.Default(), NoColor: true})
  179. // Log requests
  180. // s.mux.Use(middleware.Logger)
  181. for _, opt := range options {
  182. opt(s)
  183. }
  184. // Build base router
  185. s.mux.MethodNotAllowed(func(w http.ResponseWriter, _ *http.Request) {
  186. http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
  187. })
  188. s.mux.NotFound(func(w http.ResponseWriter, _ *http.Request) {
  189. http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
  190. })
  191. // Ignore passing "/" for BaseURL
  192. s.cfg.BaseURL = strings.Trim(s.cfg.BaseURL, "/")
  193. if s.cfg.BaseURL != "" {
  194. s.cfg.BaseURL = "/" + s.cfg.BaseURL
  195. s.mux.Use(MiddlewareStripPrefix(s.cfg.BaseURL))
  196. }
  197. err := s.initTemplate()
  198. if err != nil {
  199. return nil, err
  200. }
  201. err = s.initTLS()
  202. if err != nil {
  203. return nil, err
  204. }
  205. s.mux.Use(MiddlewareCORS(s.cfg.AllowOrigin))
  206. s.initAuth()
  207. for _, addr := range s.cfg.ListenAddr {
  208. var url string
  209. var network = "tcp"
  210. var tlsCfg *tls.Config
  211. if strings.HasPrefix(addr, "unix://") || filepath.IsAbs(addr) {
  212. network = "unix"
  213. addr = strings.TrimPrefix(addr, "unix://")
  214. url = addr
  215. } else if strings.HasPrefix(addr, "tls://") || (len(s.cfg.ListenAddr) == 1 && s.tlsConfig != nil) {
  216. tlsCfg = s.tlsConfig
  217. addr = strings.TrimPrefix(addr, "tls://")
  218. }
  219. var listener net.Listener
  220. if tlsCfg == nil {
  221. listener, err = net.Listen(network, addr)
  222. } else {
  223. listener, err = tls.Listen(network, addr, tlsCfg)
  224. }
  225. if err != nil {
  226. return nil, err
  227. }
  228. if network == "tcp" {
  229. var secure string
  230. if tlsCfg != nil {
  231. secure = "s"
  232. }
  233. url = fmt.Sprintf("http%s://%s%s/", secure, listener.Addr().String(), s.cfg.BaseURL)
  234. }
  235. ii := instance{
  236. url: url,
  237. listener: listener,
  238. httpServer: &http.Server{
  239. Handler: s.mux,
  240. ReadTimeout: s.cfg.ServerReadTimeout,
  241. WriteTimeout: s.cfg.ServerWriteTimeout,
  242. MaxHeaderBytes: s.cfg.MaxHeaderBytes,
  243. ReadHeaderTimeout: 10 * time.Second, // time to send the headers
  244. IdleTimeout: 60 * time.Second, // time to keep idle connections open
  245. TLSConfig: tlsCfg,
  246. BaseContext: NewBaseContext(ctx, url),
  247. },
  248. }
  249. s.instances = append(s.instances, ii)
  250. }
  251. return s, nil
  252. }
  253. func (s *Server) initAuth() {
  254. s.usingAuth = false
  255. authCertificateUserEnabled := s.tlsConfig != nil && s.tlsConfig.ClientAuth != tls.NoClientCert && s.auth.HtPasswd == "" && s.auth.BasicUser == ""
  256. if authCertificateUserEnabled {
  257. s.usingAuth = true
  258. s.mux.Use(MiddlewareAuthCertificateUser())
  259. }
  260. if s.auth.CustomAuthFn != nil {
  261. s.usingAuth = true
  262. s.mux.Use(MiddlewareAuthCustom(s.auth.CustomAuthFn, s.auth.Realm, authCertificateUserEnabled))
  263. return
  264. }
  265. if s.auth.HtPasswd != "" {
  266. s.usingAuth = true
  267. s.mux.Use(MiddlewareAuthHtpasswd(s.auth.HtPasswd, s.auth.Realm))
  268. return
  269. }
  270. if s.auth.BasicUser != "" {
  271. s.usingAuth = true
  272. s.mux.Use(MiddlewareAuthBasic(s.auth.BasicUser, s.auth.BasicPass, s.auth.Realm, s.auth.Salt))
  273. return
  274. }
  275. }
  276. func (s *Server) initTemplate() error {
  277. if s.template == nil {
  278. return nil
  279. }
  280. var err error
  281. s.htmlTemplate, err = GetTemplate(s.template.Path)
  282. if err != nil {
  283. err = fmt.Errorf("failed to get template: %w", err)
  284. }
  285. return err
  286. }
  287. var (
  288. // ErrInvalidMinTLSVersion - hard coded errors, allowing for easier testing
  289. ErrInvalidMinTLSVersion = errors.New("invalid value for --min-tls-version")
  290. // ErrTLSBodyMismatch - hard coded errors, allowing for easier testing
  291. ErrTLSBodyMismatch = errors.New("need both TLSCertBody and TLSKeyBody to use TLS")
  292. // ErrTLSFileMismatch - hard coded errors, allowing for easier testing
  293. ErrTLSFileMismatch = errors.New("need both --cert and --key to use TLS")
  294. // ErrTLSParseCA - hard coded errors, allowing for easier testing
  295. ErrTLSParseCA = errors.New("unable to parse client certificate authority")
  296. )
  297. func (s *Server) initTLS() error {
  298. if s.cfg.TLSCert == "" && s.cfg.TLSKey == "" && len(s.cfg.TLSCertBody) == 0 && len(s.cfg.TLSKeyBody) == 0 {
  299. return nil
  300. }
  301. if (len(s.cfg.TLSCertBody) > 0) != (len(s.cfg.TLSKeyBody) > 0) {
  302. return ErrTLSBodyMismatch
  303. }
  304. if (s.cfg.TLSCert != "") != (s.cfg.TLSKey != "") {
  305. return ErrTLSFileMismatch
  306. }
  307. var cert tls.Certificate
  308. var err error
  309. if len(s.cfg.TLSCertBody) > 0 {
  310. cert, err = tls.X509KeyPair(s.cfg.TLSCertBody, s.cfg.TLSKeyBody)
  311. } else {
  312. cert, err = tls.LoadX509KeyPair(s.cfg.TLSCert, s.cfg.TLSKey)
  313. }
  314. if err != nil {
  315. return err
  316. }
  317. var minTLSVersion uint16
  318. switch s.cfg.MinTLSVersion {
  319. case "tls1.0":
  320. minTLSVersion = tls.VersionTLS10
  321. case "tls1.1":
  322. minTLSVersion = tls.VersionTLS11
  323. case "tls1.2":
  324. minTLSVersion = tls.VersionTLS12
  325. case "tls1.3":
  326. minTLSVersion = tls.VersionTLS13
  327. default:
  328. return fmt.Errorf("%w: %s", ErrInvalidMinTLSVersion, s.cfg.MinTLSVersion)
  329. }
  330. s.tlsConfig = &tls.Config{
  331. MinVersion: minTLSVersion,
  332. Certificates: []tls.Certificate{cert},
  333. }
  334. if s.cfg.ClientCA != "" {
  335. // if !useTLS {
  336. // err := errors.New("can't use --client-ca without --cert and --key")
  337. // log.Fatalf(err.Error())
  338. // }
  339. certpool := x509.NewCertPool()
  340. pem, err := os.ReadFile(s.cfg.ClientCA)
  341. if err != nil {
  342. return err
  343. }
  344. if !certpool.AppendCertsFromPEM(pem) {
  345. return ErrTLSParseCA
  346. }
  347. s.tlsConfig.ClientCAs = certpool
  348. s.tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
  349. }
  350. return nil
  351. }
  352. // Serve starts the HTTP server on each listener
  353. func (s *Server) Serve() {
  354. s.wg.Add(len(s.instances))
  355. for _, ii := range s.instances {
  356. // TODO: decide how/when to log listening url
  357. // log.Printf("listening on %s", ii.url)
  358. go ii.serve(&s.wg)
  359. }
  360. // Install an atexit handler to shutdown gracefully
  361. s.atexitHandle = atexit.Register(func() { _ = s.Shutdown() })
  362. }
  363. // Wait blocks while the server is serving requests
  364. func (s *Server) Wait() {
  365. s.wg.Wait()
  366. }
  367. // Router returns the server base router
  368. func (s *Server) Router() chi.Router {
  369. return s.mux
  370. }
  371. // Time to wait to Shutdown an HTTP server
  372. const gracefulShutdownTime = 10 * time.Second
  373. // Shutdown gracefully shuts down the server
  374. func (s *Server) Shutdown() error {
  375. // Stop the atexit handler
  376. if s.atexitHandle != nil {
  377. atexit.Unregister(s.atexitHandle)
  378. s.atexitHandle = nil
  379. }
  380. for _, ii := range s.instances {
  381. expiry := time.Now().Add(gracefulShutdownTime)
  382. ctx, cancel := context.WithDeadline(context.Background(), expiry)
  383. if err := ii.httpServer.Shutdown(ctx); err != nil {
  384. log.Printf("error shutting down server: %s", err)
  385. }
  386. cancel()
  387. }
  388. s.wg.Wait()
  389. return nil
  390. }
  391. // HTMLTemplate returns the parsed template, if WithTemplate option was passed.
  392. func (s *Server) HTMLTemplate() *template.Template {
  393. return s.htmlTemplate
  394. }
  395. // URLs returns all configured URLS
  396. func (s *Server) URLs() []string {
  397. var out []string
  398. for _, ii := range s.instances {
  399. if ii.listener.Addr().Network() == "unix" {
  400. continue
  401. }
  402. out = append(out, ii.url)
  403. }
  404. return out
  405. }
  406. // UsingAuth returns true if authentication is required
  407. func (s *Server) UsingAuth() bool {
  408. return s.usingAuth
  409. }