service.go 14 KB

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