service.go 14 KB

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