webdavd.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. // Package webdavd implements the WebDAV protocol
  2. package webdavd
  3. import (
  4. "fmt"
  5. "net"
  6. "path/filepath"
  7. "github.com/go-chi/chi/v5/middleware"
  8. "github.com/drakkan/sftpgo/v2/common"
  9. "github.com/drakkan/sftpgo/v2/dataprovider"
  10. "github.com/drakkan/sftpgo/v2/logger"
  11. "github.com/drakkan/sftpgo/v2/util"
  12. )
  13. type ctxReqParams int
  14. const (
  15. requestIDKey ctxReqParams = iota
  16. requestStartKey
  17. )
  18. const (
  19. logSender = "webdavd"
  20. )
  21. var (
  22. certMgr *common.CertManager
  23. serviceStatus ServiceStatus
  24. )
  25. // ServiceStatus defines the service status
  26. type ServiceStatus struct {
  27. IsActive bool `json:"is_active"`
  28. Bindings []Binding `json:"bindings"`
  29. }
  30. // CorsConfig defines the CORS configuration
  31. type CorsConfig struct {
  32. AllowedOrigins []string `json:"allowed_origins" mapstructure:"allowed_origins"`
  33. AllowedMethods []string `json:"allowed_methods" mapstructure:"allowed_methods"`
  34. AllowedHeaders []string `json:"allowed_headers" mapstructure:"allowed_headers"`
  35. ExposedHeaders []string `json:"exposed_headers" mapstructure:"exposed_headers"`
  36. AllowCredentials bool `json:"allow_credentials" mapstructure:"allow_credentials"`
  37. Enabled bool `json:"enabled" mapstructure:"enabled"`
  38. MaxAge int `json:"max_age" mapstructure:"max_age"`
  39. }
  40. // UsersCacheConfig defines the cache configuration for users
  41. type UsersCacheConfig struct {
  42. ExpirationTime int `json:"expiration_time" mapstructure:"expiration_time"`
  43. MaxSize int `json:"max_size" mapstructure:"max_size"`
  44. }
  45. // MimeCacheConfig defines the cache configuration for mime types
  46. type MimeCacheConfig struct {
  47. Enabled bool `json:"enabled" mapstructure:"enabled"`
  48. MaxSize int `json:"max_size" mapstructure:"max_size"`
  49. }
  50. // Cache configuration
  51. type Cache struct {
  52. Users UsersCacheConfig `json:"users" mapstructure:"users"`
  53. MimeTypes MimeCacheConfig `json:"mime_types" mapstructure:"mime_types"`
  54. }
  55. // Binding defines the configuration for a network listener
  56. type Binding struct {
  57. // The address to listen on. A blank value means listen on all available network interfaces.
  58. Address string `json:"address" mapstructure:"address"`
  59. // The port used for serving requests
  60. Port int `json:"port" mapstructure:"port"`
  61. // you also need to provide a certificate for enabling HTTPS
  62. EnableHTTPS bool `json:"enable_https" mapstructure:"enable_https"`
  63. // Defines the minimum TLS version. 13 means TLS 1.3, default is TLS 1.2
  64. MinTLSVersion int `json:"min_tls_version" mapstructure:"min_tls_version"`
  65. // set to 1 to require client certificate authentication in addition to basic auth.
  66. // You need to define at least a certificate authority for this to work
  67. ClientAuthType int `json:"client_auth_type" mapstructure:"client_auth_type"`
  68. // TLSCipherSuites is a list of supported cipher suites for TLS version 1.2.
  69. // If CipherSuites is nil/empty, a default list of secure cipher suites
  70. // is used, with a preference order based on hardware performance.
  71. // Note that TLS 1.3 ciphersuites are not configurable.
  72. // The supported ciphersuites names are defined here:
  73. //
  74. // https://github.com/golang/go/blob/master/src/crypto/tls/cipher_suites.go#L52
  75. //
  76. // any invalid name will be silently ignored.
  77. // The order matters, the ciphers listed first will be the preferred ones.
  78. TLSCipherSuites []string `json:"tls_cipher_suites" mapstructure:"tls_cipher_suites"`
  79. // Prefix for WebDAV resources, if empty WebDAV resources will be available at the
  80. // root ("/") URI. If defined it must be an absolute URI.
  81. Prefix string `json:"prefix" mapstructure:"prefix"`
  82. // List of IP addresses and IP ranges allowed to set X-Forwarded-For/X-Real-IP headers.
  83. ProxyAllowed []string `json:"proxy_allowed" mapstructure:"proxy_allowed"`
  84. allowHeadersFrom []func(net.IP) bool
  85. }
  86. func (b *Binding) parseAllowedProxy() error {
  87. allowedFuncs, err := util.ParseAllowedIPAndRanges(b.ProxyAllowed)
  88. if err != nil {
  89. return err
  90. }
  91. b.allowHeadersFrom = allowedFuncs
  92. return nil
  93. }
  94. func (b *Binding) isMutualTLSEnabled() bool {
  95. return b.ClientAuthType == 1 || b.ClientAuthType == 2
  96. }
  97. // GetAddress returns the binding address
  98. func (b *Binding) GetAddress() string {
  99. return fmt.Sprintf("%s:%d", b.Address, b.Port)
  100. }
  101. // IsValid returns true if the binding port is > 0
  102. func (b *Binding) IsValid() bool {
  103. return b.Port > 0
  104. }
  105. // Configuration defines the configuration for the WevDAV server
  106. type Configuration struct {
  107. // Addresses and ports to bind to
  108. Bindings []Binding `json:"bindings" mapstructure:"bindings"`
  109. // If files containing a certificate and matching private key for the server are provided the server will expect
  110. // HTTPS connections.
  111. // Certificate and key files can be reloaded on demand sending a "SIGHUP" signal on Unix based systems and a
  112. // "paramchange" request to the running service on Windows.
  113. CertificateFile string `json:"certificate_file" mapstructure:"certificate_file"`
  114. CertificateKeyFile string `json:"certificate_key_file" mapstructure:"certificate_key_file"`
  115. // CACertificates defines the set of root certificate authorities to be used to verify client certificates.
  116. CACertificates []string `json:"ca_certificates" mapstructure:"ca_certificates"`
  117. // CARevocationLists defines a set a revocation lists, one for each root CA, to be used to check
  118. // if a client certificate has been revoked
  119. CARevocationLists []string `json:"ca_revocation_lists" mapstructure:"ca_revocation_lists"`
  120. // CORS configuration
  121. Cors CorsConfig `json:"cors" mapstructure:"cors"`
  122. // Cache configuration
  123. Cache Cache `json:"cache" mapstructure:"cache"`
  124. }
  125. // GetStatus returns the server status
  126. func GetStatus() ServiceStatus {
  127. return serviceStatus
  128. }
  129. // ShouldBind returns true if there is at least a valid binding
  130. func (c *Configuration) ShouldBind() bool {
  131. for _, binding := range c.Bindings {
  132. if binding.IsValid() {
  133. return true
  134. }
  135. }
  136. return false
  137. }
  138. // Initialize configures and starts the WebDAV server
  139. func (c *Configuration) Initialize(configDir string) error {
  140. logger.Info(logSender, "", "initializing WebDAV server with config %+v", *c)
  141. mimeTypeCache = mimeCache{
  142. maxSize: c.Cache.MimeTypes.MaxSize,
  143. mimeTypes: make(map[string]string),
  144. }
  145. if !c.Cache.MimeTypes.Enabled {
  146. mimeTypeCache.maxSize = 0
  147. }
  148. if !c.ShouldBind() {
  149. return common.ErrNoBinding
  150. }
  151. certificateFile := getConfigPath(c.CertificateFile, configDir)
  152. certificateKeyFile := getConfigPath(c.CertificateKeyFile, configDir)
  153. if certificateFile != "" && certificateKeyFile != "" {
  154. mgr, err := common.NewCertManager(certificateFile, certificateKeyFile, configDir, logSender)
  155. if err != nil {
  156. return err
  157. }
  158. mgr.SetCACertificates(c.CACertificates)
  159. if err := mgr.LoadRootCAs(); err != nil {
  160. return err
  161. }
  162. mgr.SetCARevocationLists(c.CARevocationLists)
  163. if err := mgr.LoadCRLs(); err != nil {
  164. return err
  165. }
  166. certMgr = mgr
  167. }
  168. compressor := middleware.NewCompressor(5, "text/*")
  169. dataprovider.InitializeWebDAVUserCache(c.Cache.Users.MaxSize)
  170. serviceStatus = ServiceStatus{
  171. Bindings: nil,
  172. }
  173. exitChannel := make(chan error, 1)
  174. for _, binding := range c.Bindings {
  175. if !binding.IsValid() {
  176. continue
  177. }
  178. if err := binding.parseAllowedProxy(); err != nil {
  179. return err
  180. }
  181. go func(binding Binding) {
  182. server := webDavServer{
  183. config: c,
  184. binding: binding,
  185. }
  186. exitChannel <- server.listenAndServe(compressor)
  187. }(binding)
  188. }
  189. serviceStatus.IsActive = true
  190. return <-exitChannel
  191. }
  192. // ReloadCertificateMgr reloads the certificate manager
  193. func ReloadCertificateMgr() error {
  194. if certMgr != nil {
  195. return certMgr.Reload()
  196. }
  197. return nil
  198. }
  199. func getConfigPath(name, configDir string) string {
  200. if !util.IsFileInputValid(name) {
  201. return ""
  202. }
  203. if name != "" && !filepath.IsAbs(name) {
  204. return filepath.Join(configDir, name)
  205. }
  206. return name
  207. }