service_portable.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. // +build !noportable
  2. package service
  3. import (
  4. "fmt"
  5. "math/rand"
  6. "os"
  7. "os/signal"
  8. "strings"
  9. "syscall"
  10. "time"
  11. "github.com/grandcat/zeroconf"
  12. "github.com/drakkan/sftpgo/config"
  13. "github.com/drakkan/sftpgo/dataprovider"
  14. "github.com/drakkan/sftpgo/logger"
  15. "github.com/drakkan/sftpgo/sftpd"
  16. "github.com/drakkan/sftpgo/utils"
  17. "github.com/drakkan/sftpgo/version"
  18. )
  19. // StartPortableMode starts the service in portable mode
  20. func (s *Service) StartPortableMode(sftpdPort, ftpPort, webdavPort int, enabledSSHCommands []string, advertiseService, advertiseCredentials bool,
  21. ftpsCert, ftpsKey, webDavCert, webDavKey string) error {
  22. if s.PortableMode != 1 {
  23. return fmt.Errorf("service is not configured for portable mode")
  24. }
  25. var err error
  26. rand.Seed(time.Now().UnixNano())
  27. if len(s.PortableUser.Username) == 0 {
  28. s.PortableUser.Username = "user"
  29. }
  30. printablePassword := ""
  31. if len(s.PortableUser.Password) > 0 {
  32. printablePassword = "[redacted]"
  33. }
  34. if len(s.PortableUser.PublicKeys) == 0 && len(s.PortableUser.Password) == 0 {
  35. var b strings.Builder
  36. for i := 0; i < 8; i++ {
  37. b.WriteRune(chars[rand.Intn(len(chars))])
  38. }
  39. s.PortableUser.Password = b.String()
  40. printablePassword = s.PortableUser.Password
  41. }
  42. dataProviderConf := config.GetProviderConf()
  43. dataProviderConf.Driver = dataprovider.MemoryDataProviderName
  44. dataProviderConf.Name = ""
  45. dataProviderConf.PreferDatabaseCredentials = true
  46. config.SetProviderConf(dataProviderConf)
  47. httpdConf := config.GetHTTPDConfig()
  48. httpdConf.BindPort = 0
  49. config.SetHTTPDConfig(httpdConf)
  50. sftpdConf := config.GetSFTPDConfig()
  51. sftpdConf.MaxAuthTries = 12
  52. if sftpdPort > 0 {
  53. sftpdConf.BindPort = sftpdPort
  54. } else {
  55. // dynamic ports starts from 49152
  56. sftpdConf.BindPort = 49152 + rand.Intn(15000)
  57. }
  58. if utils.IsStringInSlice("*", enabledSSHCommands) {
  59. sftpdConf.EnabledSSHCommands = sftpd.GetSupportedSSHCommands()
  60. } else {
  61. sftpdConf.EnabledSSHCommands = enabledSSHCommands
  62. }
  63. config.SetSFTPDConfig(sftpdConf)
  64. if ftpPort >= 0 {
  65. ftpConf := config.GetFTPDConfig()
  66. if ftpPort > 0 {
  67. ftpConf.BindPort = ftpPort
  68. } else {
  69. ftpConf.BindPort = 49152 + rand.Intn(15000)
  70. }
  71. ftpConf.Banner = fmt.Sprintf("SFTPGo portable %v ready", version.Get().Version)
  72. ftpConf.CertificateFile = ftpsCert
  73. ftpConf.CertificateKeyFile = ftpsKey
  74. config.SetFTPDConfig(ftpConf)
  75. }
  76. if webdavPort >= 0 {
  77. webDavConf := config.GetWebDAVDConfig()
  78. if webdavPort > 0 {
  79. webDavConf.BindPort = webdavPort
  80. } else {
  81. webDavConf.BindPort = 49152 + rand.Intn(15000)
  82. }
  83. webDavConf.CertificateFile = webDavCert
  84. webDavConf.CertificateKeyFile = webDavKey
  85. config.SetWebDAVDConfig(webDavConf)
  86. }
  87. err = s.Start()
  88. if err != nil {
  89. return err
  90. }
  91. s.advertiseServices(advertiseService, advertiseCredentials)
  92. logger.InfoToConsole("Portable mode ready, SFTP port: %v, user: %#v, password: %#v, public keys: %v, directory: %#v, "+
  93. "permissions: %+v, enabled ssh commands: %v file extensions filters: %+v %v", sftpdConf.BindPort, s.PortableUser.Username,
  94. printablePassword, s.PortableUser.PublicKeys, s.getPortableDirToServe(), s.PortableUser.Permissions,
  95. sftpdConf.EnabledSSHCommands, s.PortableUser.Filters.FileExtensions, s.getServiceOptionalInfoString())
  96. return nil
  97. }
  98. func (s *Service) getServiceOptionalInfoString() string {
  99. var info strings.Builder
  100. if config.GetFTPDConfig().BindPort > 0 {
  101. info.WriteString(fmt.Sprintf("FTP port: %v ", config.GetFTPDConfig().BindPort))
  102. }
  103. if config.GetWebDAVDConfig().BindPort > 0 {
  104. if info.Len() == 0 {
  105. info.WriteString(" ")
  106. }
  107. scheme := "http"
  108. if len(config.GetWebDAVDConfig().CertificateFile) > 0 && len(config.GetWebDAVDConfig().CertificateKeyFile) > 0 {
  109. scheme = "https"
  110. }
  111. info.WriteString(fmt.Sprintf("WebDAV URL: %v://<your IP>:%v/%v",
  112. scheme, config.GetWebDAVDConfig().BindPort, s.PortableUser.Username))
  113. }
  114. return info.String()
  115. }
  116. func (s *Service) advertiseServices(advertiseService, advertiseCredentials bool) {
  117. var mDNSServiceSFTP *zeroconf.Server
  118. var mDNSServiceFTP *zeroconf.Server
  119. var mDNSServiceDAV *zeroconf.Server
  120. var err error
  121. if advertiseService {
  122. meta := []string{
  123. fmt.Sprintf("version=%v", version.Get().Version),
  124. }
  125. if advertiseCredentials {
  126. logger.InfoToConsole("Advertising credentials via multicast DNS")
  127. meta = append(meta, fmt.Sprintf("user=%v", s.PortableUser.Username))
  128. if len(s.PortableUser.Password) > 0 {
  129. meta = append(meta, fmt.Sprintf("password=%v", s.PortableUser.Password))
  130. } else {
  131. logger.InfoToConsole("Unable to advertise key based credentials via multicast DNS, we don't have the private key")
  132. }
  133. }
  134. sftpdConf := config.GetSFTPDConfig()
  135. if sftpdConf.BindPort > 0 {
  136. mDNSServiceSFTP, err = zeroconf.Register(
  137. fmt.Sprintf("SFTPGo portable %v", sftpdConf.BindPort), // service instance name
  138. "_sftp-ssh._tcp", // service type and protocol
  139. "local.", // service domain
  140. sftpdConf.BindPort, // service port
  141. meta, // service metadata
  142. nil, // register on all network interfaces
  143. )
  144. if err != nil {
  145. mDNSServiceSFTP = nil
  146. logger.WarnToConsole("Unable to advertise SFTP service via multicast DNS: %v", err)
  147. } else {
  148. logger.InfoToConsole("SFTP service advertised via multicast DNS")
  149. }
  150. }
  151. ftpdConf := config.GetFTPDConfig()
  152. if ftpdConf.BindPort > 0 {
  153. mDNSServiceFTP, err = zeroconf.Register(
  154. fmt.Sprintf("SFTPGo portable %v", ftpdConf.BindPort),
  155. "_ftp._tcp",
  156. "local.",
  157. ftpdConf.BindPort,
  158. meta,
  159. nil,
  160. )
  161. if err != nil {
  162. mDNSServiceFTP = nil
  163. logger.WarnToConsole("Unable to advertise FTP service via multicast DNS: %v", err)
  164. } else {
  165. logger.InfoToConsole("FTP service advertised via multicast DNS")
  166. }
  167. }
  168. webdavConf := config.GetWebDAVDConfig()
  169. if webdavConf.BindPort > 0 {
  170. mDNSServiceDAV, err = zeroconf.Register(
  171. fmt.Sprintf("SFTPGo portable %v", webdavConf.BindPort),
  172. "_http._tcp",
  173. "local.",
  174. webdavConf.BindPort,
  175. meta,
  176. nil,
  177. )
  178. if err != nil {
  179. mDNSServiceDAV = nil
  180. logger.WarnToConsole("Unable to advertise WebDAV service via multicast DNS: %v", err)
  181. } else {
  182. logger.InfoToConsole("WebDAV service advertised via multicast DNS")
  183. }
  184. }
  185. }
  186. sig := make(chan os.Signal, 1)
  187. signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
  188. go func() {
  189. <-sig
  190. if mDNSServiceSFTP != nil {
  191. logger.InfoToConsole("unregistering multicast DNS SFTP service")
  192. mDNSServiceSFTP.Shutdown()
  193. }
  194. if mDNSServiceFTP != nil {
  195. logger.InfoToConsole("unregistering multicast DNS FTP service")
  196. mDNSServiceFTP.Shutdown()
  197. }
  198. if mDNSServiceDAV != nil {
  199. logger.InfoToConsole("unregistering multicast DNS WebDAV service")
  200. mDNSServiceDAV.Shutdown()
  201. }
  202. s.Stop()
  203. }()
  204. }
  205. func (s *Service) getPortableDirToServe() string {
  206. var dirToServe string
  207. if s.PortableUser.FsConfig.Provider == dataprovider.S3FilesystemProvider {
  208. dirToServe = s.PortableUser.FsConfig.S3Config.KeyPrefix
  209. } else if s.PortableUser.FsConfig.Provider == dataprovider.GCSFilesystemProvider {
  210. dirToServe = s.PortableUser.FsConfig.GCSConfig.KeyPrefix
  211. } else {
  212. dirToServe = s.PortableUser.HomeDir
  213. }
  214. return dirToServe
  215. }