service.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. // Copyright (C) 2019 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 %s, config dir: %s, config file: %s, log max size: %d log max backups: %d "+
  86. "log max age: %d log level: %s, log compress: %t, log utc time: %t, 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. const 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. // We may have KMS plugins and their schema needs to be registered before
  120. // initializing the data provider which may contain KMS secrets.
  121. if err := plugin.Initialize(config.GetPluginsConfig(), s.LogLevel); err != nil {
  122. logger.Error(logSender, "", "unable to initialize plugin system: %v", err)
  123. logger.ErrorToConsole("unable to initialize plugin system: %v", err)
  124. return err
  125. }
  126. mfaConfig := config.GetMFAConfig()
  127. err = mfaConfig.Initialize()
  128. if err != nil {
  129. logger.Error(logSender, "", "unable to initialize MFA: %v", err)
  130. logger.ErrorToConsole("unable to initialize MFA: %v", err)
  131. return err
  132. }
  133. err = dataprovider.Initialize(providerConf, s.ConfigDir, s.PortableMode == 0)
  134. if err != nil {
  135. logger.Error(logSender, "", "error initializing data provider: %v", err)
  136. logger.ErrorToConsole("error initializing data provider: %v", err)
  137. return err
  138. }
  139. smtpConfig := config.GetSMTPConfig()
  140. err = smtpConfig.Initialize(s.ConfigDir, s.PortableMode != 1)
  141. if err != nil {
  142. logger.Error(logSender, "", "unable to initialize SMTP configuration: %v", err)
  143. logger.ErrorToConsole("unable to initialize SMTP configuration: %v", err)
  144. return err
  145. }
  146. err = common.Initialize(config.GetCommonConfig(), providerConf.GetShared())
  147. if err != nil {
  148. logger.Error(logSender, "", "%v", err)
  149. logger.ErrorToConsole("%v", err)
  150. return err
  151. }
  152. if s.PortableMode == 1 {
  153. // create the user for portable mode
  154. err = dataprovider.AddUser(&s.PortableUser, dataprovider.ActionExecutorSystem, "", "")
  155. if err != nil {
  156. logger.ErrorToConsole("error adding portable user: %v", err)
  157. return err
  158. }
  159. } else {
  160. acmeConfig := config.GetACMEConfig()
  161. err = acme.Initialize(acmeConfig, s.ConfigDir, true)
  162. if err != nil {
  163. logger.Error(logSender, "", "error initializing ACME configuration: %v", err)
  164. logger.ErrorToConsole("error initializing ACME configuration: %v", err)
  165. return err
  166. }
  167. }
  168. if err := registerAWSContainer(disableAWSInstallationCode); err != nil {
  169. logger.Error(logSender, "", "error registering AWS container: %v", err)
  170. logger.ErrorToConsole("error registering AWS container: %v", err)
  171. return err
  172. }
  173. httpConfig := config.GetHTTPConfig()
  174. err = httpConfig.Initialize(s.ConfigDir)
  175. if err != nil {
  176. logger.Error(logSender, "", "error initializing http client: %v", err)
  177. logger.ErrorToConsole("error initializing http client: %v", err)
  178. return err
  179. }
  180. commandConfig := config.GetCommandConfig()
  181. if err := commandConfig.Initialize(); err != nil {
  182. logger.Error(logSender, "", "error initializing commands configuration: %v", err)
  183. logger.ErrorToConsole("error initializing commands configuration: %v", err)
  184. return err
  185. }
  186. return nil
  187. }
  188. func (s *Service) startServices() {
  189. err := s.LoadInitialData()
  190. if err != nil {
  191. logger.Error(logSender, "", "unable to load initial data: %v", err)
  192. logger.ErrorToConsole("unable to load initial data: %v", err)
  193. }
  194. sftpdConf := config.GetSFTPDConfig()
  195. ftpdConf := config.GetFTPDConfig()
  196. httpdConf := config.GetHTTPDConfig()
  197. webDavDConf := config.GetWebDAVDConfig()
  198. telemetryConf := config.GetTelemetryConfig()
  199. if sftpdConf.ShouldBind() {
  200. go func() {
  201. redactedConf := sftpdConf
  202. redactedConf.KeyboardInteractiveHook = util.GetRedactedURL(sftpdConf.KeyboardInteractiveHook)
  203. logger.Info(logSender, "", "initializing SFTP server with config %+v", redactedConf)
  204. if err := sftpdConf.Initialize(s.ConfigDir); err != nil {
  205. logger.Error(logSender, "", "could not start SFTP server: %v", err)
  206. logger.ErrorToConsole("could not start SFTP server: %v", err)
  207. s.Error = err
  208. }
  209. s.Shutdown <- true
  210. }()
  211. } else {
  212. logger.Info(logSender, "", "SFTP server not started, disabled in config file")
  213. }
  214. if httpdConf.ShouldBind() {
  215. go func() {
  216. providerConf := config.GetProviderConf()
  217. if err := httpdConf.Initialize(s.ConfigDir, providerConf.GetShared()); err != nil {
  218. logger.Error(logSender, "", "could not start HTTP server: %v", err)
  219. logger.ErrorToConsole("could not start HTTP server: %v", err)
  220. s.Error = err
  221. }
  222. s.Shutdown <- true
  223. }()
  224. } else {
  225. logger.Info(logSender, "", "HTTP server not started, disabled in config file")
  226. if s.PortableMode != 1 {
  227. logger.InfoToConsole("HTTP server not started, disabled in config file")
  228. }
  229. }
  230. if ftpdConf.ShouldBind() {
  231. go func() {
  232. if err := ftpdConf.Initialize(s.ConfigDir); err != nil {
  233. logger.Error(logSender, "", "could not start FTP server: %v", err)
  234. logger.ErrorToConsole("could not start FTP server: %v", err)
  235. s.Error = err
  236. }
  237. s.Shutdown <- true
  238. }()
  239. } else {
  240. logger.Info(logSender, "", "FTP server not started, disabled in config file")
  241. }
  242. if webDavDConf.ShouldBind() {
  243. go func() {
  244. if err := webDavDConf.Initialize(s.ConfigDir); err != nil {
  245. logger.Error(logSender, "", "could not start WebDAV server: %v", err)
  246. logger.ErrorToConsole("could not start WebDAV server: %v", err)
  247. s.Error = err
  248. }
  249. s.Shutdown <- true
  250. }()
  251. } else {
  252. logger.Info(logSender, "", "WebDAV server not started, disabled in config file")
  253. }
  254. if telemetryConf.ShouldBind() {
  255. go func() {
  256. if err := telemetryConf.Initialize(s.ConfigDir); err != nil {
  257. logger.Error(logSender, "", "could not start telemetry server: %v", err)
  258. logger.ErrorToConsole("could not start telemetry server: %v", err)
  259. s.Error = err
  260. }
  261. s.Shutdown <- true
  262. }()
  263. } else {
  264. logger.Info(logSender, "", "telemetry server not started, disabled in config file")
  265. if s.PortableMode != 1 {
  266. logger.InfoToConsole("telemetry server not started, disabled in config file")
  267. }
  268. }
  269. }
  270. // Wait blocks until the service exits
  271. func (s *Service) Wait() {
  272. if s.PortableMode != 1 {
  273. registerSignals()
  274. }
  275. <-s.Shutdown
  276. }
  277. // Stop terminates the service unblocking the Wait method
  278. func (s *Service) Stop() {
  279. close(s.Shutdown)
  280. logger.Debug(logSender, "", "Service stopped")
  281. }
  282. // LoadInitialData if a data file is set
  283. func (s *Service) LoadInitialData() error {
  284. if s.LoadDataFrom == "" {
  285. return nil
  286. }
  287. if !filepath.IsAbs(s.LoadDataFrom) {
  288. return fmt.Errorf("invalid input_file %q, it must be an absolute path", s.LoadDataFrom)
  289. }
  290. if s.LoadDataMode < 0 || s.LoadDataMode > 1 {
  291. return fmt.Errorf("invalid loaddata-mode %v", s.LoadDataMode)
  292. }
  293. if s.LoadDataQuotaScan < 0 || s.LoadDataQuotaScan > 2 {
  294. return fmt.Errorf("invalid loaddata-scan %v", s.LoadDataQuotaScan)
  295. }
  296. info, err := os.Stat(s.LoadDataFrom)
  297. if err != nil {
  298. return fmt.Errorf("unable to stat file %q: %w", s.LoadDataFrom, err)
  299. }
  300. if info.Size() > httpd.MaxRestoreSize {
  301. return fmt.Errorf("unable to restore input file %q size too big: %d/%d bytes",
  302. s.LoadDataFrom, info.Size(), httpd.MaxRestoreSize)
  303. }
  304. content, err := os.ReadFile(s.LoadDataFrom)
  305. if err != nil {
  306. return fmt.Errorf("unable to read input file %q: %w", s.LoadDataFrom, err)
  307. }
  308. dump, err := dataprovider.ParseDumpData(content)
  309. if err != nil {
  310. return fmt.Errorf("unable to parse file to restore %q: %w", s.LoadDataFrom, err)
  311. }
  312. err = s.restoreDump(&dump)
  313. if err != nil {
  314. return err
  315. }
  316. logger.Info(logSender, "", "data loaded from file %q mode: %v", s.LoadDataFrom, s.LoadDataMode)
  317. logger.InfoToConsole("data loaded from file %q mode: %v", s.LoadDataFrom, s.LoadDataMode)
  318. if s.LoadDataClean {
  319. err = os.Remove(s.LoadDataFrom)
  320. if err == nil {
  321. logger.Info(logSender, "", "file %q deleted after successful load", s.LoadDataFrom)
  322. logger.InfoToConsole("file %q deleted after successful load", s.LoadDataFrom)
  323. } else {
  324. logger.Warn(logSender, "", "unable to delete file %q after successful load: %v", s.LoadDataFrom, err)
  325. logger.WarnToConsole("unable to delete file %q after successful load: %v", s.LoadDataFrom, err)
  326. }
  327. }
  328. return nil
  329. }
  330. func (s *Service) restoreDump(dump *dataprovider.BackupData) error {
  331. err := httpd.RestoreConfigs(dump.Configs, s.LoadDataMode, dataprovider.ActionExecutorSystem, "", "")
  332. if err != nil {
  333. return fmt.Errorf("unable to restore configs from file %q: %v", s.LoadDataFrom, err)
  334. }
  335. err = httpd.RestoreIPListEntries(dump.IPLists, s.LoadDataFrom, s.LoadDataMode, dataprovider.ActionExecutorSystem, "", "")
  336. if err != nil {
  337. return fmt.Errorf("unable to restore IP list entries from file %q: %v", s.LoadDataFrom, err)
  338. }
  339. err = httpd.RestoreRoles(dump.Roles, s.LoadDataFrom, s.LoadDataMode, dataprovider.ActionExecutorSystem, "", "")
  340. if err != nil {
  341. return fmt.Errorf("unable to restore roles from file %q: %v", s.LoadDataFrom, err)
  342. }
  343. err = httpd.RestoreFolders(dump.Folders, s.LoadDataFrom, s.LoadDataMode, s.LoadDataQuotaScan, dataprovider.ActionExecutorSystem, "", "")
  344. if err != nil {
  345. return fmt.Errorf("unable to restore folders from file %q: %v", s.LoadDataFrom, err)
  346. }
  347. err = httpd.RestoreGroups(dump.Groups, s.LoadDataFrom, s.LoadDataMode, dataprovider.ActionExecutorSystem, "", "")
  348. if err != nil {
  349. return fmt.Errorf("unable to restore groups from file %q: %v", s.LoadDataFrom, err)
  350. }
  351. err = httpd.RestoreUsers(dump.Users, s.LoadDataFrom, s.LoadDataMode, s.LoadDataQuotaScan, dataprovider.ActionExecutorSystem, "", "")
  352. if err != nil {
  353. return fmt.Errorf("unable to restore users from file %q: %v", s.LoadDataFrom, err)
  354. }
  355. err = httpd.RestoreAdmins(dump.Admins, s.LoadDataFrom, s.LoadDataMode, dataprovider.ActionExecutorSystem, "", "")
  356. if err != nil {
  357. return fmt.Errorf("unable to restore admins from file %q: %v", s.LoadDataFrom, err)
  358. }
  359. err = httpd.RestoreAPIKeys(dump.APIKeys, s.LoadDataFrom, s.LoadDataMode, dataprovider.ActionExecutorSystem, "", "")
  360. if err != nil {
  361. return fmt.Errorf("unable to restore API keys from file %q: %v", s.LoadDataFrom, err)
  362. }
  363. err = httpd.RestoreShares(dump.Shares, s.LoadDataFrom, s.LoadDataMode, dataprovider.ActionExecutorSystem, "", "")
  364. if err != nil {
  365. return fmt.Errorf("unable to restore API keys from file %q: %v", s.LoadDataFrom, err)
  366. }
  367. err = httpd.RestoreEventActions(dump.EventActions, s.LoadDataFrom, s.LoadDataMode, dataprovider.ActionExecutorSystem, "", "")
  368. if err != nil {
  369. return fmt.Errorf("unable to restore event actions from file %q: %v", s.LoadDataFrom, err)
  370. }
  371. err = httpd.RestoreEventRules(dump.EventRules, s.LoadDataFrom, s.LoadDataMode, dataprovider.ActionExecutorSystem,
  372. "", "", dump.Version)
  373. if err != nil {
  374. return fmt.Errorf("unable to restore event rules from file %q: %v", s.LoadDataFrom, err)
  375. }
  376. return nil
  377. }
  378. // SetGraceTime sets the grace time
  379. func SetGraceTime(val int) {
  380. graceTime = val
  381. }