config.go 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919
  1. // Package config manages the configuration
  2. package config
  3. import (
  4. "errors"
  5. "fmt"
  6. "os"
  7. "path/filepath"
  8. "strconv"
  9. "strings"
  10. "github.com/spf13/viper"
  11. "github.com/drakkan/sftpgo/common"
  12. "github.com/drakkan/sftpgo/dataprovider"
  13. "github.com/drakkan/sftpgo/ftpd"
  14. "github.com/drakkan/sftpgo/httpclient"
  15. "github.com/drakkan/sftpgo/httpd"
  16. "github.com/drakkan/sftpgo/kms"
  17. "github.com/drakkan/sftpgo/logger"
  18. "github.com/drakkan/sftpgo/sftpd"
  19. "github.com/drakkan/sftpgo/telemetry"
  20. "github.com/drakkan/sftpgo/utils"
  21. "github.com/drakkan/sftpgo/version"
  22. "github.com/drakkan/sftpgo/webdavd"
  23. )
  24. const (
  25. logSender = "config"
  26. // configName defines the name for config file.
  27. // This name does not include the extension, viper will search for files
  28. // with supported extensions such as "sftpgo.json", "sftpgo.yaml" and so on
  29. configName = "sftpgo"
  30. // ConfigEnvPrefix defines a prefix that environment variables will use
  31. configEnvPrefix = "sftpgo"
  32. )
  33. var (
  34. globalConf globalConfig
  35. defaultSFTPDBanner = fmt.Sprintf("SFTPGo_%v", version.Get().Version)
  36. defaultFTPDBanner = fmt.Sprintf("SFTPGo %v ready", version.Get().Version)
  37. defaultSFTPDBinding = sftpd.Binding{
  38. Address: "",
  39. Port: 2022,
  40. ApplyProxyConfig: true,
  41. }
  42. defaultFTPDBinding = ftpd.Binding{
  43. Address: "",
  44. Port: 0,
  45. ApplyProxyConfig: true,
  46. TLSMode: 0,
  47. ForcePassiveIP: "",
  48. ClientAuthType: 0,
  49. TLSCipherSuites: nil,
  50. }
  51. defaultWebDAVDBinding = webdavd.Binding{
  52. Address: "",
  53. Port: 0,
  54. EnableHTTPS: false,
  55. ClientAuthType: 0,
  56. TLSCipherSuites: nil,
  57. Prefix: "",
  58. }
  59. defaultHTTPDBinding = httpd.Binding{
  60. Address: "127.0.0.1",
  61. Port: 8080,
  62. EnableWebAdmin: true,
  63. EnableHTTPS: false,
  64. ClientAuthType: 0,
  65. TLSCipherSuites: nil,
  66. }
  67. )
  68. type globalConfig struct {
  69. Common common.Configuration `json:"common" mapstructure:"common"`
  70. SFTPD sftpd.Configuration `json:"sftpd" mapstructure:"sftpd"`
  71. FTPD ftpd.Configuration `json:"ftpd" mapstructure:"ftpd"`
  72. WebDAVD webdavd.Configuration `json:"webdavd" mapstructure:"webdavd"`
  73. ProviderConf dataprovider.Config `json:"data_provider" mapstructure:"data_provider"`
  74. HTTPDConfig httpd.Conf `json:"httpd" mapstructure:"httpd"`
  75. HTTPConfig httpclient.Config `json:"http" mapstructure:"http"`
  76. KMSConfig kms.Configuration `json:"kms" mapstructure:"kms"`
  77. TelemetryConfig telemetry.Conf `json:"telemetry" mapstructure:"telemetry"`
  78. }
  79. func init() {
  80. Init()
  81. }
  82. // Init initializes the global configuration.
  83. // It is not supposed to be called outside of this package.
  84. // It is exported to minimize refactoring efforts. Will eventually disappear.
  85. func Init() {
  86. // create a default configuration to use if no config file is provided
  87. globalConf = globalConfig{
  88. Common: common.Configuration{
  89. IdleTimeout: 15,
  90. UploadMode: 0,
  91. Actions: common.ProtocolActions{
  92. ExecuteOn: []string{},
  93. Hook: "",
  94. },
  95. SetstatMode: 0,
  96. ProxyProtocol: 0,
  97. ProxyAllowed: []string{},
  98. PostConnectHook: "",
  99. MaxTotalConnections: 0,
  100. DefenderConfig: common.DefenderConfig{
  101. Enabled: false,
  102. BanTime: 30,
  103. BanTimeIncrement: 50,
  104. Threshold: 15,
  105. ScoreInvalid: 2,
  106. ScoreValid: 1,
  107. ObservationTime: 30,
  108. EntriesSoftLimit: 100,
  109. EntriesHardLimit: 150,
  110. SafeListFile: "",
  111. BlockListFile: "",
  112. },
  113. },
  114. SFTPD: sftpd.Configuration{
  115. Banner: defaultSFTPDBanner,
  116. Bindings: []sftpd.Binding{defaultSFTPDBinding},
  117. MaxAuthTries: 0,
  118. HostKeys: []string{},
  119. KexAlgorithms: []string{},
  120. Ciphers: []string{},
  121. MACs: []string{},
  122. TrustedUserCAKeys: []string{},
  123. LoginBannerFile: "",
  124. EnabledSSHCommands: sftpd.GetDefaultSSHCommands(),
  125. KeyboardInteractiveHook: "",
  126. PasswordAuthentication: true,
  127. },
  128. FTPD: ftpd.Configuration{
  129. Bindings: []ftpd.Binding{defaultFTPDBinding},
  130. Banner: defaultFTPDBanner,
  131. BannerFile: "",
  132. ActiveTransfersPortNon20: true,
  133. PassivePortRange: ftpd.PortRange{
  134. Start: 50000,
  135. End: 50100,
  136. },
  137. DisableActiveMode: false,
  138. EnableSite: false,
  139. HASHSupport: 0,
  140. CombineSupport: 0,
  141. CertificateFile: "",
  142. CertificateKeyFile: "",
  143. CACertificates: []string{},
  144. CARevocationLists: []string{},
  145. },
  146. WebDAVD: webdavd.Configuration{
  147. Bindings: []webdavd.Binding{defaultWebDAVDBinding},
  148. CertificateFile: "",
  149. CertificateKeyFile: "",
  150. CACertificates: []string{},
  151. CARevocationLists: []string{},
  152. Cors: webdavd.Cors{
  153. Enabled: false,
  154. AllowedOrigins: []string{},
  155. AllowedMethods: []string{},
  156. AllowedHeaders: []string{},
  157. ExposedHeaders: []string{},
  158. AllowCredentials: false,
  159. MaxAge: 0,
  160. },
  161. Cache: webdavd.Cache{
  162. Users: webdavd.UsersCacheConfig{
  163. ExpirationTime: 0,
  164. MaxSize: 50,
  165. },
  166. MimeTypes: webdavd.MimeCacheConfig{
  167. Enabled: true,
  168. MaxSize: 1000,
  169. },
  170. },
  171. },
  172. ProviderConf: dataprovider.Config{
  173. Driver: "sqlite",
  174. Name: "sftpgo.db",
  175. Host: "",
  176. Port: 5432,
  177. Username: "",
  178. Password: "",
  179. ConnectionString: "",
  180. SQLTablesPrefix: "",
  181. SSLMode: 0,
  182. TrackQuota: 1,
  183. PoolSize: 0,
  184. UsersBaseDir: "",
  185. Actions: dataprovider.UserActions{
  186. ExecuteOn: []string{},
  187. Hook: "",
  188. },
  189. ExternalAuthHook: "",
  190. ExternalAuthScope: 0,
  191. CredentialsPath: "credentials",
  192. PreLoginHook: "",
  193. PostLoginHook: "",
  194. PostLoginScope: 0,
  195. CheckPasswordHook: "",
  196. CheckPasswordScope: 0,
  197. PasswordHashing: dataprovider.PasswordHashing{
  198. Argon2Options: dataprovider.Argon2Options{
  199. Memory: 65536,
  200. Iterations: 1,
  201. Parallelism: 2,
  202. },
  203. },
  204. UpdateMode: 0,
  205. PreferDatabaseCredentials: false,
  206. SkipNaturalKeysValidation: false,
  207. },
  208. HTTPDConfig: httpd.Conf{
  209. Bindings: []httpd.Binding{defaultHTTPDBinding},
  210. TemplatesPath: "templates",
  211. StaticFilesPath: "static",
  212. BackupsPath: "backups",
  213. WebAdminRoot: "",
  214. CertificateFile: "",
  215. CertificateKeyFile: "",
  216. CACertificates: nil,
  217. CARevocationLists: nil,
  218. },
  219. HTTPConfig: httpclient.Config{
  220. Timeout: 20,
  221. RetryWaitMin: 2,
  222. RetryWaitMax: 30,
  223. RetryMax: 3,
  224. CACertificates: nil,
  225. Certificates: nil,
  226. SkipTLSVerify: false,
  227. },
  228. KMSConfig: kms.Configuration{
  229. Secrets: kms.Secrets{
  230. URL: "",
  231. MasterKeyPath: "",
  232. },
  233. },
  234. TelemetryConfig: telemetry.Conf{
  235. BindPort: 10000,
  236. BindAddress: "127.0.0.1",
  237. EnableProfiler: false,
  238. AuthUserFile: "",
  239. CertificateFile: "",
  240. CertificateKeyFile: "",
  241. TLSCipherSuites: nil,
  242. },
  243. }
  244. viper.SetEnvPrefix(configEnvPrefix)
  245. replacer := strings.NewReplacer(".", "__")
  246. viper.SetEnvKeyReplacer(replacer)
  247. viper.SetConfigName(configName)
  248. setViperDefaults()
  249. viper.AutomaticEnv()
  250. viper.AllowEmptyEnv(true)
  251. }
  252. // GetCommonConfig returns the common protocols configuration
  253. func GetCommonConfig() common.Configuration {
  254. return globalConf.Common
  255. }
  256. // SetCommonConfig sets the common protocols configuration
  257. func SetCommonConfig(config common.Configuration) {
  258. globalConf.Common = config
  259. }
  260. // GetSFTPDConfig returns the configuration for the SFTP server
  261. func GetSFTPDConfig() sftpd.Configuration {
  262. return globalConf.SFTPD
  263. }
  264. // SetSFTPDConfig sets the configuration for the SFTP server
  265. func SetSFTPDConfig(config sftpd.Configuration) {
  266. globalConf.SFTPD = config
  267. }
  268. // GetFTPDConfig returns the configuration for the FTP server
  269. func GetFTPDConfig() ftpd.Configuration {
  270. return globalConf.FTPD
  271. }
  272. // SetFTPDConfig sets the configuration for the FTP server
  273. func SetFTPDConfig(config ftpd.Configuration) {
  274. globalConf.FTPD = config
  275. }
  276. // GetWebDAVDConfig returns the configuration for the WebDAV server
  277. func GetWebDAVDConfig() webdavd.Configuration {
  278. return globalConf.WebDAVD
  279. }
  280. // SetWebDAVDConfig sets the configuration for the WebDAV server
  281. func SetWebDAVDConfig(config webdavd.Configuration) {
  282. globalConf.WebDAVD = config
  283. }
  284. // GetHTTPDConfig returns the configuration for the HTTP server
  285. func GetHTTPDConfig() httpd.Conf {
  286. return globalConf.HTTPDConfig
  287. }
  288. // SetHTTPDConfig sets the configuration for the HTTP server
  289. func SetHTTPDConfig(config httpd.Conf) {
  290. globalConf.HTTPDConfig = config
  291. }
  292. // GetProviderConf returns the configuration for the data provider
  293. func GetProviderConf() dataprovider.Config {
  294. return globalConf.ProviderConf
  295. }
  296. // SetProviderConf sets the configuration for the data provider
  297. func SetProviderConf(config dataprovider.Config) {
  298. globalConf.ProviderConf = config
  299. }
  300. // GetHTTPConfig returns the configuration for HTTP clients
  301. func GetHTTPConfig() httpclient.Config {
  302. return globalConf.HTTPConfig
  303. }
  304. // GetKMSConfig returns the KMS configuration
  305. func GetKMSConfig() kms.Configuration {
  306. return globalConf.KMSConfig
  307. }
  308. // SetKMSConfig sets the kms configuration
  309. func SetKMSConfig(config kms.Configuration) {
  310. globalConf.KMSConfig = config
  311. }
  312. // GetTelemetryConfig returns the telemetry configuration
  313. func GetTelemetryConfig() telemetry.Conf {
  314. return globalConf.TelemetryConfig
  315. }
  316. // SetTelemetryConfig sets the telemetry configuration
  317. func SetTelemetryConfig(config telemetry.Conf) {
  318. globalConf.TelemetryConfig = config
  319. }
  320. // HasServicesToStart returns true if the config defines at least a service to start.
  321. // Supported services are SFTP, FTP and WebDAV
  322. func HasServicesToStart() bool {
  323. if globalConf.SFTPD.ShouldBind() {
  324. return true
  325. }
  326. if globalConf.FTPD.ShouldBind() {
  327. return true
  328. }
  329. if globalConf.WebDAVD.ShouldBind() {
  330. return true
  331. }
  332. return false
  333. }
  334. func getRedactedGlobalConf() globalConfig {
  335. conf := globalConf
  336. conf.ProviderConf.Password = "[redacted]"
  337. return conf
  338. }
  339. func setConfigFile(configDir, configFile string) {
  340. if configFile == "" {
  341. return
  342. }
  343. if !filepath.IsAbs(configFile) && utils.IsFileInputValid(configFile) {
  344. configFile = filepath.Join(configDir, configFile)
  345. }
  346. viper.SetConfigFile(configFile)
  347. }
  348. // LoadConfig loads the configuration
  349. // configDir will be added to the configuration search paths.
  350. // The search path contains by default the current directory and on linux it contains
  351. // $HOME/.config/sftpgo and /etc/sftpgo too.
  352. // configFile is an absolute or relative path (to the config dir) to the configuration file.
  353. func LoadConfig(configDir, configFile string) error {
  354. var err error
  355. viper.AddConfigPath(configDir)
  356. setViperAdditionalConfigPaths()
  357. viper.AddConfigPath(".")
  358. setConfigFile(configDir, configFile)
  359. if err = viper.ReadInConfig(); err != nil {
  360. // if the user specify a configuration file we get os.ErrNotExist.
  361. // viper.ConfigFileNotFoundError is returned if viper is unable
  362. // to find sftpgo.{json,yaml, etc..} in any of the search paths
  363. if errors.As(err, &viper.ConfigFileNotFoundError{}) {
  364. logger.Debug(logSender, "", "no configuration file found")
  365. } else {
  366. // should we return the error and not start here?
  367. logger.Warn(logSender, "", "error loading configuration file: %v", err)
  368. logger.WarnToConsole("error loading configuration file: %v", err)
  369. }
  370. }
  371. err = viper.Unmarshal(&globalConf)
  372. if err != nil {
  373. logger.Warn(logSender, "", "error parsing configuration file: %v", err)
  374. logger.WarnToConsole("error parsing configuration file: %v", err)
  375. return err
  376. }
  377. // viper only supports slice of strings from env vars, so we use our custom method
  378. loadBindingsFromEnv()
  379. if strings.TrimSpace(globalConf.SFTPD.Banner) == "" {
  380. globalConf.SFTPD.Banner = defaultSFTPDBanner
  381. }
  382. if strings.TrimSpace(globalConf.FTPD.Banner) == "" {
  383. globalConf.FTPD.Banner = defaultFTPDBanner
  384. }
  385. if globalConf.ProviderConf.UsersBaseDir != "" && !utils.IsFileInputValid(globalConf.ProviderConf.UsersBaseDir) {
  386. err = fmt.Errorf("invalid users base dir %#v will be ignored", globalConf.ProviderConf.UsersBaseDir)
  387. globalConf.ProviderConf.UsersBaseDir = ""
  388. logger.Warn(logSender, "", "Configuration error: %v", err)
  389. logger.WarnToConsole("Configuration error: %v", err)
  390. }
  391. if globalConf.Common.UploadMode < 0 || globalConf.Common.UploadMode > 2 {
  392. warn := fmt.Sprintf("invalid upload_mode 0, 1 and 2 are supported, configured: %v reset upload_mode to 0",
  393. globalConf.Common.UploadMode)
  394. globalConf.Common.UploadMode = 0
  395. logger.Warn(logSender, "", "Configuration error: %v", warn)
  396. logger.WarnToConsole("Configuration error: %v", warn)
  397. }
  398. if globalConf.Common.ProxyProtocol < 0 || globalConf.Common.ProxyProtocol > 2 {
  399. warn := fmt.Sprintf("invalid proxy_protocol 0, 1 and 2 are supported, configured: %v reset proxy_protocol to 0",
  400. globalConf.Common.ProxyProtocol)
  401. globalConf.Common.ProxyProtocol = 0
  402. logger.Warn(logSender, "", "Configuration error: %v", warn)
  403. logger.WarnToConsole("Configuration error: %v", warn)
  404. }
  405. if globalConf.ProviderConf.ExternalAuthScope < 0 || globalConf.ProviderConf.ExternalAuthScope > 15 {
  406. warn := fmt.Sprintf("invalid external_auth_scope: %v reset to 0", globalConf.ProviderConf.ExternalAuthScope)
  407. globalConf.ProviderConf.ExternalAuthScope = 0
  408. logger.Warn(logSender, "", "Configuration error: %v", warn)
  409. logger.WarnToConsole("Configuration error: %v", warn)
  410. }
  411. if globalConf.ProviderConf.CredentialsPath == "" {
  412. warn := "invalid credentials path, reset to \"credentials\""
  413. globalConf.ProviderConf.CredentialsPath = "credentials"
  414. logger.Warn(logSender, "", "Configuration error: %v", warn)
  415. logger.WarnToConsole("Configuration error: %v", warn)
  416. }
  417. logger.Debug(logSender, "", "config file used: '%#v', config loaded: %+v", viper.ConfigFileUsed(), getRedactedGlobalConf())
  418. return nil
  419. }
  420. func checkSFTPDBindingsCompatibility() {
  421. if globalConf.SFTPD.BindPort == 0 { //nolint:staticcheck
  422. return
  423. }
  424. // we copy deprecated fields to new ones to keep backward compatibility so lint is disabled
  425. binding := sftpd.Binding{
  426. ApplyProxyConfig: true,
  427. }
  428. if globalConf.SFTPD.BindPort > 0 { //nolint:staticcheck
  429. binding.Port = globalConf.SFTPD.BindPort //nolint:staticcheck
  430. }
  431. if globalConf.SFTPD.BindAddress != "" { //nolint:staticcheck
  432. binding.Address = globalConf.SFTPD.BindAddress //nolint:staticcheck
  433. }
  434. globalConf.SFTPD.Bindings = []sftpd.Binding{binding}
  435. }
  436. func checkFTPDBindingCompatibility() {
  437. if globalConf.FTPD.BindPort == 0 { //nolint:staticcheck
  438. return
  439. }
  440. binding := ftpd.Binding{
  441. ApplyProxyConfig: true,
  442. }
  443. if globalConf.FTPD.BindPort > 0 { //nolint:staticcheck
  444. binding.Port = globalConf.FTPD.BindPort //nolint:staticcheck
  445. }
  446. if globalConf.FTPD.BindAddress != "" { //nolint:staticcheck
  447. binding.Address = globalConf.FTPD.BindAddress //nolint:staticcheck
  448. }
  449. if globalConf.FTPD.TLSMode > 0 { //nolint:staticcheck
  450. binding.TLSMode = globalConf.FTPD.TLSMode //nolint:staticcheck
  451. }
  452. if globalConf.FTPD.ForcePassiveIP != "" { //nolint:staticcheck
  453. binding.ForcePassiveIP = globalConf.FTPD.ForcePassiveIP //nolint:staticcheck
  454. }
  455. globalConf.FTPD.Bindings = []ftpd.Binding{binding}
  456. }
  457. func checkWebDAVDBindingCompatibility() {
  458. if globalConf.WebDAVD.BindPort == 0 { //nolint:staticcheck
  459. return
  460. }
  461. binding := webdavd.Binding{
  462. EnableHTTPS: globalConf.WebDAVD.CertificateFile != "" && globalConf.WebDAVD.CertificateKeyFile != "",
  463. }
  464. if globalConf.WebDAVD.BindPort > 0 { //nolint:staticcheck
  465. binding.Port = globalConf.WebDAVD.BindPort //nolint:staticcheck
  466. }
  467. if globalConf.WebDAVD.BindAddress != "" { //nolint:staticcheck
  468. binding.Address = globalConf.WebDAVD.BindAddress //nolint:staticcheck
  469. }
  470. globalConf.WebDAVD.Bindings = []webdavd.Binding{binding}
  471. }
  472. func checkHTTPDBindingCompatibility() {
  473. if globalConf.HTTPDConfig.BindPort == 0 { //nolint:staticcheck
  474. return
  475. }
  476. binding := httpd.Binding{
  477. EnableWebAdmin: globalConf.HTTPDConfig.StaticFilesPath != "" && globalConf.HTTPDConfig.TemplatesPath != "",
  478. EnableHTTPS: globalConf.HTTPDConfig.CertificateFile != "" && globalConf.HTTPDConfig.CertificateKeyFile != "",
  479. }
  480. if globalConf.HTTPDConfig.BindPort > 0 { //nolint:staticcheck
  481. binding.Port = globalConf.HTTPDConfig.BindPort //nolint:staticcheck
  482. }
  483. if globalConf.HTTPDConfig.BindAddress != "" { //nolint:staticcheck
  484. binding.Address = globalConf.HTTPDConfig.BindAddress //nolint:staticcheck
  485. }
  486. globalConf.HTTPDConfig.Bindings = []httpd.Binding{binding}
  487. }
  488. func loadBindingsFromEnv() {
  489. checkSFTPDBindingsCompatibility()
  490. checkFTPDBindingCompatibility()
  491. checkWebDAVDBindingCompatibility()
  492. checkHTTPDBindingCompatibility()
  493. maxBindings := make([]int, 10)
  494. for idx := range maxBindings {
  495. getSFTPDBindindFromEnv(idx)
  496. getFTPDBindingFromEnv(idx)
  497. getWebDAVDBindingFromEnv(idx)
  498. getHTTPDBindingFromEnv(idx)
  499. getHTTPClientCertificatesFromEnv(idx)
  500. }
  501. }
  502. func getSFTPDBindindFromEnv(idx int) {
  503. binding := sftpd.Binding{}
  504. if len(globalConf.SFTPD.Bindings) > idx {
  505. binding = globalConf.SFTPD.Bindings[idx]
  506. }
  507. isSet := false
  508. port, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_SFTPD__BINDINGS__%v__PORT", idx))
  509. if ok {
  510. binding.Port = port
  511. isSet = true
  512. }
  513. address, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_SFTPD__BINDINGS__%v__ADDRESS", idx))
  514. if ok {
  515. binding.Address = address
  516. isSet = true
  517. }
  518. applyProxyConfig, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_SFTPD__BINDINGS__%v__APPLY_PROXY_CONFIG", idx))
  519. if ok {
  520. binding.ApplyProxyConfig = applyProxyConfig
  521. isSet = true
  522. }
  523. if isSet {
  524. if len(globalConf.SFTPD.Bindings) > idx {
  525. globalConf.SFTPD.Bindings[idx] = binding
  526. } else {
  527. globalConf.SFTPD.Bindings = append(globalConf.SFTPD.Bindings, binding)
  528. }
  529. }
  530. }
  531. func getFTPDBindingFromEnv(idx int) {
  532. binding := ftpd.Binding{}
  533. if len(globalConf.FTPD.Bindings) > idx {
  534. binding = globalConf.FTPD.Bindings[idx]
  535. }
  536. isSet := false
  537. port, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__PORT", idx))
  538. if ok {
  539. binding.Port = port
  540. isSet = true
  541. }
  542. address, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__ADDRESS", idx))
  543. if ok {
  544. binding.Address = address
  545. isSet = true
  546. }
  547. applyProxyConfig, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__APPLY_PROXY_CONFIG", idx))
  548. if ok {
  549. binding.ApplyProxyConfig = applyProxyConfig
  550. isSet = true
  551. }
  552. tlsMode, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__TLS_MODE", idx))
  553. if ok {
  554. binding.TLSMode = tlsMode
  555. isSet = true
  556. }
  557. passiveIP, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__FORCE_PASSIVE_IP", idx))
  558. if ok {
  559. binding.ForcePassiveIP = passiveIP
  560. isSet = true
  561. }
  562. clientAuthType, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__CLIENT_AUTH_TYPE", idx))
  563. if ok {
  564. binding.ClientAuthType = clientAuthType
  565. isSet = true
  566. }
  567. tlsCiphers, ok := lookupStringListFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__TLS_CIPHER_SUITES", idx))
  568. if ok {
  569. binding.TLSCipherSuites = tlsCiphers
  570. isSet = true
  571. }
  572. if isSet {
  573. if len(globalConf.FTPD.Bindings) > idx {
  574. globalConf.FTPD.Bindings[idx] = binding
  575. } else {
  576. globalConf.FTPD.Bindings = append(globalConf.FTPD.Bindings, binding)
  577. }
  578. }
  579. }
  580. func getWebDAVDBindingFromEnv(idx int) {
  581. binding := webdavd.Binding{}
  582. if len(globalConf.WebDAVD.Bindings) > idx {
  583. binding = globalConf.WebDAVD.Bindings[idx]
  584. }
  585. isSet := false
  586. port, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__PORT", idx))
  587. if ok {
  588. binding.Port = port
  589. isSet = true
  590. }
  591. address, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__ADDRESS", idx))
  592. if ok {
  593. binding.Address = address
  594. isSet = true
  595. }
  596. enableHTTPS, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__ENABLE_HTTPS", idx))
  597. if ok {
  598. binding.EnableHTTPS = enableHTTPS
  599. isSet = true
  600. }
  601. clientAuthType, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__CLIENT_AUTH_TYPE", idx))
  602. if ok {
  603. binding.ClientAuthType = clientAuthType
  604. isSet = true
  605. }
  606. tlsCiphers, ok := lookupStringListFromEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__TLS_CIPHER_SUITES", idx))
  607. if ok {
  608. binding.TLSCipherSuites = tlsCiphers
  609. isSet = true
  610. }
  611. prefix, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__PREFIX", idx))
  612. if ok {
  613. binding.Prefix = prefix
  614. isSet = true
  615. }
  616. if isSet {
  617. if len(globalConf.WebDAVD.Bindings) > idx {
  618. globalConf.WebDAVD.Bindings[idx] = binding
  619. } else {
  620. globalConf.WebDAVD.Bindings = append(globalConf.WebDAVD.Bindings, binding)
  621. }
  622. }
  623. }
  624. func getHTTPDBindingFromEnv(idx int) {
  625. binding := httpd.Binding{}
  626. if len(globalConf.HTTPDConfig.Bindings) > idx {
  627. binding = globalConf.HTTPDConfig.Bindings[idx]
  628. }
  629. isSet := false
  630. port, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__PORT", idx))
  631. if ok {
  632. binding.Port = port
  633. isSet = true
  634. }
  635. address, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__ADDRESS", idx))
  636. if ok {
  637. binding.Address = address
  638. isSet = true
  639. }
  640. enableWebAdmin, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__ENABLE_WEB_ADMIN", idx))
  641. if ok {
  642. binding.EnableWebAdmin = enableWebAdmin
  643. isSet = true
  644. }
  645. enableHTTPS, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__ENABLE_HTTPS", idx))
  646. if ok {
  647. binding.EnableHTTPS = enableHTTPS
  648. isSet = true
  649. }
  650. clientAuthType, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__CLIENT_AUTH_TYPE", idx))
  651. if ok {
  652. binding.ClientAuthType = clientAuthType
  653. isSet = true
  654. }
  655. tlsCiphers, ok := lookupStringListFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__TLS_CIPHER_SUITES", idx))
  656. if ok {
  657. binding.TLSCipherSuites = tlsCiphers
  658. isSet = true
  659. }
  660. if isSet {
  661. if len(globalConf.HTTPDConfig.Bindings) > idx {
  662. globalConf.HTTPDConfig.Bindings[idx] = binding
  663. } else {
  664. globalConf.HTTPDConfig.Bindings = append(globalConf.HTTPDConfig.Bindings, binding)
  665. }
  666. }
  667. }
  668. func getHTTPClientCertificatesFromEnv(idx int) {
  669. tlsCert := httpclient.TLSKeyPair{}
  670. cert, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_HTTP__CERTIFICATES__%v__CERT", idx))
  671. if ok {
  672. tlsCert.Cert = cert
  673. }
  674. key, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_HTTP__CERTIFICATES__%v__KEY", idx))
  675. if ok {
  676. tlsCert.Key = key
  677. }
  678. if tlsCert.Cert != "" && tlsCert.Key != "" {
  679. if len(globalConf.HTTPConfig.Certificates) > idx {
  680. globalConf.HTTPConfig.Certificates[idx] = tlsCert
  681. } else {
  682. globalConf.HTTPConfig.Certificates = append(globalConf.HTTPConfig.Certificates, tlsCert)
  683. }
  684. }
  685. }
  686. func setViperDefaults() {
  687. viper.SetDefault("common.idle_timeout", globalConf.Common.IdleTimeout)
  688. viper.SetDefault("common.upload_mode", globalConf.Common.UploadMode)
  689. viper.SetDefault("common.actions.execute_on", globalConf.Common.Actions.ExecuteOn)
  690. viper.SetDefault("common.actions.hook", globalConf.Common.Actions.Hook)
  691. viper.SetDefault("common.setstat_mode", globalConf.Common.SetstatMode)
  692. viper.SetDefault("common.proxy_protocol", globalConf.Common.ProxyProtocol)
  693. viper.SetDefault("common.proxy_allowed", globalConf.Common.ProxyAllowed)
  694. viper.SetDefault("common.post_connect_hook", globalConf.Common.PostConnectHook)
  695. viper.SetDefault("common.max_total_connections", globalConf.Common.MaxTotalConnections)
  696. viper.SetDefault("common.defender.enabled", globalConf.Common.DefenderConfig.Enabled)
  697. viper.SetDefault("common.defender.ban_time", globalConf.Common.DefenderConfig.BanTime)
  698. viper.SetDefault("common.defender.ban_time_increment", globalConf.Common.DefenderConfig.BanTimeIncrement)
  699. viper.SetDefault("common.defender.threshold", globalConf.Common.DefenderConfig.Threshold)
  700. viper.SetDefault("common.defender.score_invalid", globalConf.Common.DefenderConfig.ScoreInvalid)
  701. viper.SetDefault("common.defender.score_valid", globalConf.Common.DefenderConfig.ScoreValid)
  702. viper.SetDefault("common.defender.observation_time", globalConf.Common.DefenderConfig.ObservationTime)
  703. viper.SetDefault("common.defender.entries_soft_limit", globalConf.Common.DefenderConfig.EntriesSoftLimit)
  704. viper.SetDefault("common.defender.entries_hard_limit", globalConf.Common.DefenderConfig.EntriesHardLimit)
  705. viper.SetDefault("common.defender.safelist_file", globalConf.Common.DefenderConfig.SafeListFile)
  706. viper.SetDefault("common.defender.blocklist_file", globalConf.Common.DefenderConfig.BlockListFile)
  707. viper.SetDefault("sftpd.max_auth_tries", globalConf.SFTPD.MaxAuthTries)
  708. viper.SetDefault("sftpd.banner", globalConf.SFTPD.Banner)
  709. viper.SetDefault("sftpd.host_keys", globalConf.SFTPD.HostKeys)
  710. viper.SetDefault("sftpd.kex_algorithms", globalConf.SFTPD.KexAlgorithms)
  711. viper.SetDefault("sftpd.ciphers", globalConf.SFTPD.Ciphers)
  712. viper.SetDefault("sftpd.macs", globalConf.SFTPD.MACs)
  713. viper.SetDefault("sftpd.trusted_user_ca_keys", globalConf.SFTPD.TrustedUserCAKeys)
  714. viper.SetDefault("sftpd.login_banner_file", globalConf.SFTPD.LoginBannerFile)
  715. viper.SetDefault("sftpd.enabled_ssh_commands", globalConf.SFTPD.EnabledSSHCommands)
  716. viper.SetDefault("sftpd.keyboard_interactive_auth_hook", globalConf.SFTPD.KeyboardInteractiveHook)
  717. viper.SetDefault("sftpd.password_authentication", globalConf.SFTPD.PasswordAuthentication)
  718. viper.SetDefault("ftpd.banner", globalConf.FTPD.Banner)
  719. viper.SetDefault("ftpd.banner_file", globalConf.FTPD.BannerFile)
  720. viper.SetDefault("ftpd.active_transfers_port_non_20", globalConf.FTPD.ActiveTransfersPortNon20)
  721. viper.SetDefault("ftpd.passive_port_range.start", globalConf.FTPD.PassivePortRange.Start)
  722. viper.SetDefault("ftpd.passive_port_range.end", globalConf.FTPD.PassivePortRange.End)
  723. viper.SetDefault("ftpd.disable_active_mode", globalConf.FTPD.DisableActiveMode)
  724. viper.SetDefault("ftpd.enable_site", globalConf.FTPD.EnableSite)
  725. viper.SetDefault("ftpd.hash_support", globalConf.FTPD.HASHSupport)
  726. viper.SetDefault("ftpd.combine_support", globalConf.FTPD.CombineSupport)
  727. viper.SetDefault("ftpd.certificate_file", globalConf.FTPD.CertificateFile)
  728. viper.SetDefault("ftpd.certificate_key_file", globalConf.FTPD.CertificateKeyFile)
  729. viper.SetDefault("ftpd.ca_certificates", globalConf.FTPD.CACertificates)
  730. viper.SetDefault("ftpd.ca_revocation_lists", globalConf.FTPD.CARevocationLists)
  731. viper.SetDefault("webdavd.certificate_file", globalConf.WebDAVD.CertificateFile)
  732. viper.SetDefault("webdavd.certificate_key_file", globalConf.WebDAVD.CertificateKeyFile)
  733. viper.SetDefault("webdavd.ca_certificates", globalConf.WebDAVD.CACertificates)
  734. viper.SetDefault("webdavd.ca_revocation_lists", globalConf.WebDAVD.CARevocationLists)
  735. viper.SetDefault("webdavd.cors.enabled", globalConf.WebDAVD.Cors.Enabled)
  736. viper.SetDefault("webdavd.cors.allowed_origins", globalConf.WebDAVD.Cors.AllowedOrigins)
  737. viper.SetDefault("webdavd.cors.allowed_methods", globalConf.WebDAVD.Cors.AllowedMethods)
  738. viper.SetDefault("webdavd.cors.allowed_headers", globalConf.WebDAVD.Cors.AllowedHeaders)
  739. viper.SetDefault("webdavd.cors.exposed_headers", globalConf.WebDAVD.Cors.ExposedHeaders)
  740. viper.SetDefault("webdavd.cors.allow_credentials", globalConf.WebDAVD.Cors.AllowCredentials)
  741. viper.SetDefault("webdavd.cors.max_age", globalConf.WebDAVD.Cors.MaxAge)
  742. viper.SetDefault("webdavd.cache.users.expiration_time", globalConf.WebDAVD.Cache.Users.ExpirationTime)
  743. viper.SetDefault("webdavd.cache.users.max_size", globalConf.WebDAVD.Cache.Users.MaxSize)
  744. viper.SetDefault("webdavd.cache.mime_types.enabled", globalConf.WebDAVD.Cache.MimeTypes.Enabled)
  745. viper.SetDefault("webdavd.cache.mime_types.max_size", globalConf.WebDAVD.Cache.MimeTypes.MaxSize)
  746. viper.SetDefault("data_provider.driver", globalConf.ProviderConf.Driver)
  747. viper.SetDefault("data_provider.name", globalConf.ProviderConf.Name)
  748. viper.SetDefault("data_provider.host", globalConf.ProviderConf.Host)
  749. viper.SetDefault("data_provider.port", globalConf.ProviderConf.Port)
  750. viper.SetDefault("data_provider.username", globalConf.ProviderConf.Username)
  751. viper.SetDefault("data_provider.password", globalConf.ProviderConf.Password)
  752. viper.SetDefault("data_provider.sslmode", globalConf.ProviderConf.SSLMode)
  753. viper.SetDefault("data_provider.connection_string", globalConf.ProviderConf.ConnectionString)
  754. viper.SetDefault("data_provider.sql_tables_prefix", globalConf.ProviderConf.SQLTablesPrefix)
  755. viper.SetDefault("data_provider.track_quota", globalConf.ProviderConf.TrackQuota)
  756. viper.SetDefault("data_provider.pool_size", globalConf.ProviderConf.PoolSize)
  757. viper.SetDefault("data_provider.users_base_dir", globalConf.ProviderConf.UsersBaseDir)
  758. viper.SetDefault("data_provider.actions.execute_on", globalConf.ProviderConf.Actions.ExecuteOn)
  759. viper.SetDefault("data_provider.actions.hook", globalConf.ProviderConf.Actions.Hook)
  760. viper.SetDefault("data_provider.external_auth_hook", globalConf.ProviderConf.ExternalAuthHook)
  761. viper.SetDefault("data_provider.external_auth_scope", globalConf.ProviderConf.ExternalAuthScope)
  762. viper.SetDefault("data_provider.credentials_path", globalConf.ProviderConf.CredentialsPath)
  763. viper.SetDefault("data_provider.prefer_database_credentials", globalConf.ProviderConf.PreferDatabaseCredentials)
  764. viper.SetDefault("data_provider.pre_login_hook", globalConf.ProviderConf.PreLoginHook)
  765. viper.SetDefault("data_provider.post_login_hook", globalConf.ProviderConf.PostLoginHook)
  766. viper.SetDefault("data_provider.post_login_scope", globalConf.ProviderConf.PostLoginScope)
  767. viper.SetDefault("data_provider.check_password_hook", globalConf.ProviderConf.CheckPasswordHook)
  768. viper.SetDefault("data_provider.check_password_scope", globalConf.ProviderConf.CheckPasswordScope)
  769. viper.SetDefault("data_provider.password_hashing.argon2_options.memory", globalConf.ProviderConf.PasswordHashing.Argon2Options.Memory)
  770. viper.SetDefault("data_provider.password_hashing.argon2_options.iterations", globalConf.ProviderConf.PasswordHashing.Argon2Options.Iterations)
  771. viper.SetDefault("data_provider.password_hashing.argon2_options.parallelism", globalConf.ProviderConf.PasswordHashing.Argon2Options.Parallelism)
  772. viper.SetDefault("data_provider.update_mode", globalConf.ProviderConf.UpdateMode)
  773. viper.SetDefault("data_provider.skip_natural_keys_validation", globalConf.ProviderConf.SkipNaturalKeysValidation)
  774. viper.SetDefault("httpd.templates_path", globalConf.HTTPDConfig.TemplatesPath)
  775. viper.SetDefault("httpd.static_files_path", globalConf.HTTPDConfig.StaticFilesPath)
  776. viper.SetDefault("httpd.backups_path", globalConf.HTTPDConfig.BackupsPath)
  777. viper.SetDefault("httpd.web_admin_root", globalConf.HTTPDConfig.WebAdminRoot)
  778. viper.SetDefault("httpd.certificate_file", globalConf.HTTPDConfig.CertificateFile)
  779. viper.SetDefault("httpd.certificate_key_file", globalConf.HTTPDConfig.CertificateKeyFile)
  780. viper.SetDefault("httpd.ca_certificates", globalConf.HTTPDConfig.CACertificates)
  781. viper.SetDefault("httpd.ca_revocation_lists", globalConf.HTTPDConfig.CARevocationLists)
  782. viper.SetDefault("http.timeout", globalConf.HTTPConfig.Timeout)
  783. viper.SetDefault("http.retry_wait_min", globalConf.HTTPConfig.RetryWaitMin)
  784. viper.SetDefault("http.retry_wait_max", globalConf.HTTPConfig.RetryWaitMax)
  785. viper.SetDefault("http.retry_max", globalConf.HTTPConfig.RetryMax)
  786. viper.SetDefault("http.ca_certificates", globalConf.HTTPConfig.CACertificates)
  787. viper.SetDefault("http.skip_tls_verify", globalConf.HTTPConfig.SkipTLSVerify)
  788. viper.SetDefault("kms.secrets.url", globalConf.KMSConfig.Secrets.URL)
  789. viper.SetDefault("kms.secrets.master_key_path", globalConf.KMSConfig.Secrets.MasterKeyPath)
  790. viper.SetDefault("telemetry.bind_port", globalConf.TelemetryConfig.BindPort)
  791. viper.SetDefault("telemetry.bind_address", globalConf.TelemetryConfig.BindAddress)
  792. viper.SetDefault("telemetry.enable_profiler", globalConf.TelemetryConfig.EnableProfiler)
  793. viper.SetDefault("telemetry.auth_user_file", globalConf.TelemetryConfig.AuthUserFile)
  794. viper.SetDefault("telemetry.certificate_file", globalConf.TelemetryConfig.CertificateFile)
  795. viper.SetDefault("telemetry.certificate_key_file", globalConf.TelemetryConfig.CertificateKeyFile)
  796. viper.SetDefault("telemetry.tls_cipher_suites", globalConf.TelemetryConfig.TLSCipherSuites)
  797. }
  798. func lookupBoolFromEnv(envName string) (bool, bool) {
  799. value, ok := os.LookupEnv(envName)
  800. if ok {
  801. converted, err := strconv.ParseBool(value)
  802. if err == nil {
  803. return converted, ok
  804. }
  805. }
  806. return false, false
  807. }
  808. func lookupIntFromEnv(envName string) (int, bool) {
  809. value, ok := os.LookupEnv(envName)
  810. if ok {
  811. converted, err := strconv.ParseInt(value, 10, 16)
  812. if err == nil {
  813. return int(converted), ok
  814. }
  815. }
  816. return 0, false
  817. }
  818. func lookupStringListFromEnv(envName string) ([]string, bool) {
  819. value, ok := os.LookupEnv(envName)
  820. if ok {
  821. var result []string
  822. for _, v := range strings.Split(value, ",") {
  823. result = append(result, strings.TrimSpace(v))
  824. }
  825. return result, true
  826. }
  827. return nil, false
  828. }