webdavd.go 7.5 KB

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