service.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. // Package service allows to start and stop the SFTPGo service
  2. package service
  3. import (
  4. "errors"
  5. "fmt"
  6. "os"
  7. "path/filepath"
  8. "github.com/rs/zerolog"
  9. "github.com/drakkan/sftpgo/v2/common"
  10. "github.com/drakkan/sftpgo/v2/config"
  11. "github.com/drakkan/sftpgo/v2/dataprovider"
  12. "github.com/drakkan/sftpgo/v2/httpd"
  13. "github.com/drakkan/sftpgo/v2/logger"
  14. "github.com/drakkan/sftpgo/v2/plugin"
  15. "github.com/drakkan/sftpgo/v2/util"
  16. "github.com/drakkan/sftpgo/v2/version"
  17. )
  18. const (
  19. logSender = "service"
  20. )
  21. var (
  22. chars = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
  23. )
  24. // Service defines the SFTPGo service
  25. type Service struct {
  26. ConfigDir string
  27. ConfigFile string
  28. LogFilePath string
  29. LogMaxSize int
  30. LogMaxBackups int
  31. LogMaxAge int
  32. PortableMode int
  33. PortableUser dataprovider.User
  34. LogCompress bool
  35. LogVerbose bool
  36. LogUTCTime bool
  37. LoadDataClean bool
  38. LoadDataFrom string
  39. LoadDataMode int
  40. LoadDataQuotaScan int
  41. Shutdown chan bool
  42. Error error
  43. }
  44. func (s *Service) initLogger() {
  45. logLevel := zerolog.DebugLevel
  46. if !s.LogVerbose {
  47. logLevel = zerolog.InfoLevel
  48. }
  49. if !filepath.IsAbs(s.LogFilePath) && util.IsFileInputValid(s.LogFilePath) {
  50. s.LogFilePath = filepath.Join(s.ConfigDir, s.LogFilePath)
  51. }
  52. logger.InitLogger(s.LogFilePath, s.LogMaxSize, s.LogMaxBackups, s.LogMaxAge, s.LogCompress, s.LogUTCTime, logLevel)
  53. if s.PortableMode == 1 {
  54. logger.EnableConsoleLogger(logLevel)
  55. if s.LogFilePath == "" {
  56. logger.DisableLogger()
  57. }
  58. }
  59. }
  60. // Start initializes and starts the service
  61. func (s *Service) Start(disableAWSInstallationCode bool) error {
  62. s.initLogger()
  63. logger.Info(logSender, "", "starting SFTPGo %v, config dir: %v, config file: %v, log max size: %v log max backups: %v "+
  64. "log max age: %v log verbose: %v, log compress: %v, log utc time: %v, load data from: %#v", version.GetAsString(), s.ConfigDir, s.ConfigFile,
  65. s.LogMaxSize, s.LogMaxBackups, s.LogMaxAge, s.LogVerbose, s.LogCompress, s.LogUTCTime, s.LoadDataFrom)
  66. // in portable mode we don't read configuration from file
  67. if s.PortableMode != 1 {
  68. err := config.LoadConfig(s.ConfigDir, s.ConfigFile)
  69. if err != nil {
  70. logger.Error(logSender, "", "error loading configuration: %v", err)
  71. return err
  72. }
  73. }
  74. if !config.HasServicesToStart() {
  75. infoString := "no service configured, nothing to do"
  76. logger.Info(logSender, "", infoString)
  77. logger.InfoToConsole(infoString)
  78. return errors.New(infoString)
  79. }
  80. if err := s.initializeServices(disableAWSInstallationCode); err != nil {
  81. return err
  82. }
  83. s.startServices()
  84. go common.Config.ExecuteStartupHook() //nolint:errcheck
  85. return nil
  86. }
  87. func (s *Service) initializeServices(disableAWSInstallationCode bool) error {
  88. providerConf := config.GetProviderConf()
  89. err := common.Initialize(config.GetCommonConfig(), providerConf.GetShared())
  90. if err != nil {
  91. logger.Error(logSender, "", "%v", err)
  92. logger.ErrorToConsole("%v", err)
  93. return err
  94. }
  95. kmsConfig := config.GetKMSConfig()
  96. err = kmsConfig.Initialize()
  97. if err != nil {
  98. logger.Error(logSender, "", "unable to initialize KMS: %v", err)
  99. logger.ErrorToConsole("unable to initialize KMS: %v", err)
  100. return err
  101. }
  102. mfaConfig := config.GetMFAConfig()
  103. err = mfaConfig.Initialize()
  104. if err != nil {
  105. logger.Error(logSender, "", "unable to initialize MFA: %v", err)
  106. logger.ErrorToConsole("unable to initialize MFA: %v", err)
  107. return err
  108. }
  109. if err := plugin.Initialize(config.GetPluginsConfig(), s.LogVerbose); err != nil {
  110. logger.Error(logSender, "", "unable to initialize plugin system: %v", err)
  111. logger.ErrorToConsole("unable to initialize plugin system: %v", err)
  112. return err
  113. }
  114. smtpConfig := config.GetSMTPConfig()
  115. err = smtpConfig.Initialize(s.ConfigDir)
  116. if err != nil {
  117. logger.Error(logSender, "", "unable to initialize SMTP configuration: %v", err)
  118. logger.ErrorToConsole("unable to initialize SMTP configuration: %v", err)
  119. return err
  120. }
  121. err = dataprovider.Initialize(providerConf, s.ConfigDir, s.PortableMode == 0)
  122. if err != nil {
  123. logger.Error(logSender, "", "error initializing data provider: %v", err)
  124. logger.ErrorToConsole("error initializing data provider: %v", err)
  125. return err
  126. }
  127. if s.PortableMode == 1 {
  128. // create the user for portable mode
  129. err = dataprovider.AddUser(&s.PortableUser, dataprovider.ActionExecutorSystem, "")
  130. if err != nil {
  131. logger.ErrorToConsole("error adding portable user: %v", err)
  132. return err
  133. }
  134. } else {
  135. acmeConfig := config.GetACMEConfig()
  136. err = acmeConfig.Initialize(s.ConfigDir, true)
  137. if err != nil {
  138. logger.Error(logSender, "", "error initializing ACME configuration: %v", err)
  139. logger.ErrorToConsole("error initializing ACME configuration: %v", err)
  140. return err
  141. }
  142. }
  143. if err := registerAWSContainer(disableAWSInstallationCode); err != nil {
  144. logger.Error(logSender, "", "error registering AWS container: %v", err)
  145. logger.ErrorToConsole("error registering AWS container: %v", err)
  146. return err
  147. }
  148. httpConfig := config.GetHTTPConfig()
  149. err = httpConfig.Initialize(s.ConfigDir)
  150. if err != nil {
  151. logger.Error(logSender, "", "error initializing http client: %v", err)
  152. logger.ErrorToConsole("error initializing http client: %v", err)
  153. return err
  154. }
  155. commandConfig := config.GetCommandConfig()
  156. if err := commandConfig.Initialize(); err != nil {
  157. logger.Error(logSender, "", "error initializing commands configuration: %v", err)
  158. logger.ErrorToConsole("error initializing commands configuration: %v", err)
  159. return err
  160. }
  161. return nil
  162. }
  163. func (s *Service) startServices() {
  164. err := s.LoadInitialData()
  165. if err != nil {
  166. logger.Error(logSender, "", "unable to load initial data: %v", err)
  167. logger.ErrorToConsole("unable to load initial data: %v", err)
  168. }
  169. sftpdConf := config.GetSFTPDConfig()
  170. ftpdConf := config.GetFTPDConfig()
  171. httpdConf := config.GetHTTPDConfig()
  172. webDavDConf := config.GetWebDAVDConfig()
  173. telemetryConf := config.GetTelemetryConfig()
  174. if sftpdConf.ShouldBind() {
  175. go func() {
  176. redactedConf := sftpdConf
  177. redactedConf.KeyboardInteractiveHook = util.GetRedactedURL(sftpdConf.KeyboardInteractiveHook)
  178. logger.Info(logSender, "", "initializing SFTP server with config %+v", redactedConf)
  179. if err := sftpdConf.Initialize(s.ConfigDir); err != nil {
  180. logger.Error(logSender, "", "could not start SFTP server: %v", err)
  181. logger.ErrorToConsole("could not start SFTP server: %v", err)
  182. s.Error = err
  183. }
  184. s.Shutdown <- true
  185. }()
  186. } else {
  187. logger.Info(logSender, "", "SFTP server not started, disabled in config file")
  188. }
  189. if httpdConf.ShouldBind() {
  190. go func() {
  191. providerConf := config.GetProviderConf()
  192. if err := httpdConf.Initialize(s.ConfigDir, providerConf.GetShared()); err != nil {
  193. logger.Error(logSender, "", "could not start HTTP server: %v", err)
  194. logger.ErrorToConsole("could not start HTTP server: %v", err)
  195. s.Error = err
  196. }
  197. s.Shutdown <- true
  198. }()
  199. } else {
  200. logger.Info(logSender, "", "HTTP server not started, disabled in config file")
  201. if s.PortableMode != 1 {
  202. logger.InfoToConsole("HTTP server not started, disabled in config file")
  203. }
  204. }
  205. if ftpdConf.ShouldBind() {
  206. go func() {
  207. if err := ftpdConf.Initialize(s.ConfigDir); err != nil {
  208. logger.Error(logSender, "", "could not start FTP server: %v", err)
  209. logger.ErrorToConsole("could not start FTP server: %v", err)
  210. s.Error = err
  211. }
  212. s.Shutdown <- true
  213. }()
  214. } else {
  215. logger.Info(logSender, "", "FTP server not started, disabled in config file")
  216. }
  217. if webDavDConf.ShouldBind() {
  218. go func() {
  219. if err := webDavDConf.Initialize(s.ConfigDir); err != nil {
  220. logger.Error(logSender, "", "could not start WebDAV server: %v", err)
  221. logger.ErrorToConsole("could not start WebDAV server: %v", err)
  222. s.Error = err
  223. }
  224. s.Shutdown <- true
  225. }()
  226. } else {
  227. logger.Info(logSender, "", "WebDAV server not started, disabled in config file")
  228. }
  229. if telemetryConf.ShouldBind() {
  230. go func() {
  231. if err := telemetryConf.Initialize(s.ConfigDir); err != nil {
  232. logger.Error(logSender, "", "could not start telemetry server: %v", err)
  233. logger.ErrorToConsole("could not start telemetry server: %v", err)
  234. s.Error = err
  235. }
  236. s.Shutdown <- true
  237. }()
  238. } else {
  239. logger.Info(logSender, "", "telemetry server not started, disabled in config file")
  240. if s.PortableMode != 1 {
  241. logger.InfoToConsole("telemetry server not started, disabled in config file")
  242. }
  243. }
  244. }
  245. // Wait blocks until the service exits
  246. func (s *Service) Wait() {
  247. if s.PortableMode != 1 {
  248. registerSignals()
  249. }
  250. <-s.Shutdown
  251. }
  252. // Stop terminates the service unblocking the Wait method
  253. func (s *Service) Stop() {
  254. close(s.Shutdown)
  255. logger.Debug(logSender, "", "Service stopped")
  256. }
  257. // LoadInitialData if a data file is set
  258. func (s *Service) LoadInitialData() error {
  259. if s.LoadDataFrom == "" {
  260. return nil
  261. }
  262. if !filepath.IsAbs(s.LoadDataFrom) {
  263. return fmt.Errorf("invalid input_file %#v, it must be an absolute path", s.LoadDataFrom)
  264. }
  265. if s.LoadDataMode < 0 || s.LoadDataMode > 1 {
  266. return fmt.Errorf("invalid loaddata-mode %v", s.LoadDataMode)
  267. }
  268. if s.LoadDataQuotaScan < 0 || s.LoadDataQuotaScan > 2 {
  269. return fmt.Errorf("invalid loaddata-scan %v", s.LoadDataQuotaScan)
  270. }
  271. info, err := os.Stat(s.LoadDataFrom)
  272. if err != nil {
  273. return fmt.Errorf("unable to stat file %#v: %w", s.LoadDataFrom, err)
  274. }
  275. if info.Size() > httpd.MaxRestoreSize {
  276. return fmt.Errorf("unable to restore input file %#v size too big: %v/%v bytes",
  277. s.LoadDataFrom, info.Size(), httpd.MaxRestoreSize)
  278. }
  279. content, err := os.ReadFile(s.LoadDataFrom)
  280. if err != nil {
  281. return fmt.Errorf("unable to read input file %#v: %w", s.LoadDataFrom, err)
  282. }
  283. dump, err := dataprovider.ParseDumpData(content)
  284. if err != nil {
  285. return fmt.Errorf("unable to parse file to restore %#v: %w", s.LoadDataFrom, err)
  286. }
  287. err = s.restoreDump(&dump)
  288. if err != nil {
  289. return err
  290. }
  291. logger.Info(logSender, "", "data loaded from file %#v mode: %v", s.LoadDataFrom, s.LoadDataMode)
  292. logger.InfoToConsole("data loaded from file %#v mode: %v", s.LoadDataFrom, s.LoadDataMode)
  293. if s.LoadDataClean {
  294. err = os.Remove(s.LoadDataFrom)
  295. if err == nil {
  296. logger.Info(logSender, "", "file %#v deleted after successful load", s.LoadDataFrom)
  297. logger.InfoToConsole("file %#v deleted after successful load", s.LoadDataFrom)
  298. } else {
  299. logger.Warn(logSender, "", "unable to delete file %#v after successful load: %v", s.LoadDataFrom, err)
  300. logger.WarnToConsole("unable to delete file %#v after successful load: %v", s.LoadDataFrom, err)
  301. }
  302. }
  303. return nil
  304. }
  305. func (s *Service) restoreDump(dump *dataprovider.BackupData) error {
  306. err := httpd.RestoreFolders(dump.Folders, s.LoadDataFrom, s.LoadDataMode, s.LoadDataQuotaScan, dataprovider.ActionExecutorSystem, "")
  307. if err != nil {
  308. return fmt.Errorf("unable to restore folders from file %#v: %v", s.LoadDataFrom, err)
  309. }
  310. err = httpd.RestoreGroups(dump.Groups, s.LoadDataFrom, s.LoadDataMode, dataprovider.ActionExecutorSystem, "")
  311. if err != nil {
  312. return fmt.Errorf("unable to restore groups from file %#v: %v", s.LoadDataFrom, err)
  313. }
  314. err = httpd.RestoreUsers(dump.Users, s.LoadDataFrom, s.LoadDataMode, s.LoadDataQuotaScan, dataprovider.ActionExecutorSystem, "")
  315. if err != nil {
  316. return fmt.Errorf("unable to restore users from file %#v: %v", s.LoadDataFrom, err)
  317. }
  318. err = httpd.RestoreAdmins(dump.Admins, s.LoadDataFrom, s.LoadDataMode, dataprovider.ActionExecutorSystem, "")
  319. if err != nil {
  320. return fmt.Errorf("unable to restore admins from file %#v: %v", s.LoadDataFrom, err)
  321. }
  322. err = httpd.RestoreAPIKeys(dump.APIKeys, s.LoadDataFrom, s.LoadDataMode, dataprovider.ActionExecutorSystem, "")
  323. if err != nil {
  324. return fmt.Errorf("unable to restore API keys from file %#v: %v", s.LoadDataFrom, err)
  325. }
  326. err = httpd.RestoreShares(dump.Shares, s.LoadDataFrom, s.LoadDataMode, dataprovider.ActionExecutorSystem, "")
  327. if err != nil {
  328. return fmt.Errorf("unable to restore API keys from file %#v: %v", s.LoadDataFrom, err)
  329. }
  330. err = httpd.RestoreEventActions(dump.EventActions, s.LoadDataFrom, s.LoadDataMode, dataprovider.ActionExecutorSystem, "")
  331. if err != nil {
  332. return fmt.Errorf("unable to restore event actions from file %#v: %v", s.LoadDataFrom, err)
  333. }
  334. err = httpd.RestoreEventRules(dump.EventRules, s.LoadDataFrom, s.LoadDataMode, dataprovider.ActionExecutorSystem, "")
  335. if err != nil {
  336. return fmt.Errorf("unable to restore event rules from file %#v: %v", s.LoadDataFrom, err)
  337. }
  338. return nil
  339. }