| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 |
- // Package config manages the configuration
- package config
- import (
- "fmt"
- "strings"
- "github.com/spf13/viper"
- "github.com/drakkan/sftpgo/common"
- "github.com/drakkan/sftpgo/dataprovider"
- "github.com/drakkan/sftpgo/ftpd"
- "github.com/drakkan/sftpgo/httpclient"
- "github.com/drakkan/sftpgo/httpd"
- "github.com/drakkan/sftpgo/logger"
- "github.com/drakkan/sftpgo/sftpd"
- "github.com/drakkan/sftpgo/utils"
- "github.com/drakkan/sftpgo/version"
- "github.com/drakkan/sftpgo/webdavd"
- )
- const (
- logSender = "config"
- // DefaultConfigName defines the name for the default config file.
- // This is the file name without extension, we use viper and so we
- // support all the config files format supported by viper
- DefaultConfigName = "sftpgo"
- // ConfigEnvPrefix defines a prefix that ENVIRONMENT variables will use
- configEnvPrefix = "sftpgo"
- )
- var (
- globalConf globalConfig
- defaultSFTPDBanner = fmt.Sprintf("SFTPGo_%v", version.Get().Version)
- defaultFTPDBanner = fmt.Sprintf("SFTPGo %v ready", version.Get().Version)
- )
- type globalConfig struct {
- Common common.Configuration `json:"common" mapstructure:"common"`
- SFTPD sftpd.Configuration `json:"sftpd" mapstructure:"sftpd"`
- FTPD ftpd.Configuration `json:"ftpd" mapstructure:"ftpd"`
- WebDAVD webdavd.Configuration `json:"webdavd" mapstructure:"webdavd"`
- ProviderConf dataprovider.Config `json:"data_provider" mapstructure:"data_provider"`
- HTTPDConfig httpd.Conf `json:"httpd" mapstructure:"httpd"`
- HTTPConfig httpclient.Config `json:"http" mapstructure:"http"`
- }
- func init() {
- // create a default configuration to use if no config file is provided
- globalConf = globalConfig{
- Common: common.Configuration{
- IdleTimeout: 15,
- UploadMode: 0,
- Actions: common.ProtocolActions{
- ExecuteOn: []string{},
- Hook: "",
- },
- SetstatMode: 0,
- ProxyProtocol: 0,
- ProxyAllowed: []string{},
- },
- SFTPD: sftpd.Configuration{
- Banner: defaultSFTPDBanner,
- BindPort: 2022,
- BindAddress: "",
- MaxAuthTries: 0,
- HostKeys: []string{},
- KexAlgorithms: []string{},
- Ciphers: []string{},
- MACs: []string{},
- TrustedUserCAKeys: []string{},
- LoginBannerFile: "",
- EnabledSSHCommands: sftpd.GetDefaultSSHCommands(),
- KeyboardInteractiveHook: "",
- PasswordAuthentication: true,
- },
- FTPD: ftpd.Configuration{
- BindPort: 0,
- BindAddress: "",
- Banner: defaultFTPDBanner,
- BannerFile: "",
- ActiveTransfersPortNon20: false,
- ForcePassiveIP: "",
- PassivePortRange: ftpd.PortRange{
- Start: 50000,
- End: 50100,
- },
- CertificateFile: "",
- CertificateKeyFile: "",
- },
- WebDAVD: webdavd.Configuration{
- BindPort: 0,
- BindAddress: "",
- CertificateFile: "",
- CertificateKeyFile: "",
- Cors: webdavd.Cors{
- Enabled: false,
- AllowedOrigins: []string{},
- AllowedMethods: []string{},
- AllowedHeaders: []string{},
- ExposedHeaders: []string{},
- AllowCredentials: false,
- MaxAge: 0,
- },
- Cache: webdavd.Cache{
- Enabled: true,
- ExpirationTime: 0,
- MaxSize: 50,
- },
- },
- ProviderConf: dataprovider.Config{
- Driver: "sqlite",
- Name: "sftpgo.db",
- Host: "",
- Port: 5432,
- Username: "",
- Password: "",
- ConnectionString: "",
- SQLTablesPrefix: "",
- ManageUsers: 1,
- SSLMode: 0,
- TrackQuota: 1,
- PoolSize: 0,
- UsersBaseDir: "",
- Actions: dataprovider.UserActions{
- ExecuteOn: []string{},
- Hook: "",
- },
- ExternalAuthHook: "",
- ExternalAuthScope: 0,
- CredentialsPath: "credentials",
- PreLoginHook: "",
- PostLoginHook: "",
- PostLoginScope: 0,
- CheckPasswordHook: "",
- CheckPasswordScope: 0,
- PasswordHashing: dataprovider.PasswordHashing{
- Argon2Options: dataprovider.Argon2Options{
- Memory: 65536,
- Iterations: 1,
- Parallelism: 2,
- },
- },
- UpdateMode: 0,
- PreferDatabaseCredentials: false,
- },
- HTTPDConfig: httpd.Conf{
- BindPort: 8080,
- BindAddress: "127.0.0.1",
- TemplatesPath: "templates",
- StaticFilesPath: "static",
- BackupsPath: "backups",
- AuthUserFile: "",
- CertificateFile: "",
- CertificateKeyFile: "",
- },
- HTTPConfig: httpclient.Config{
- Timeout: 20,
- CACertificates: nil,
- SkipTLSVerify: false,
- },
- }
- viper.SetEnvPrefix(configEnvPrefix)
- replacer := strings.NewReplacer(".", "__")
- viper.SetEnvKeyReplacer(replacer)
- viper.SetConfigName(DefaultConfigName)
- setViperDefaults()
- viper.AutomaticEnv()
- viper.AllowEmptyEnv(true)
- }
- // GetCommonConfig returns the common protocols configuration
- func GetCommonConfig() common.Configuration {
- return globalConf.Common
- }
- // SetCommonConfig sets the common protocols configuration
- func SetCommonConfig(config common.Configuration) {
- globalConf.Common = config
- }
- // GetSFTPDConfig returns the configuration for the SFTP server
- func GetSFTPDConfig() sftpd.Configuration {
- return globalConf.SFTPD
- }
- // SetSFTPDConfig sets the configuration for the SFTP server
- func SetSFTPDConfig(config sftpd.Configuration) {
- globalConf.SFTPD = config
- }
- // GetFTPDConfig returns the configuration for the FTP server
- func GetFTPDConfig() ftpd.Configuration {
- return globalConf.FTPD
- }
- // SetFTPDConfig sets the configuration for the FTP server
- func SetFTPDConfig(config ftpd.Configuration) {
- globalConf.FTPD = config
- }
- // GetWebDAVDConfig returns the configuration for the WebDAV server
- func GetWebDAVDConfig() webdavd.Configuration {
- return globalConf.WebDAVD
- }
- // SetWebDAVDConfig sets the configuration for the WebDAV server
- func SetWebDAVDConfig(config webdavd.Configuration) {
- globalConf.WebDAVD = config
- }
- // GetHTTPDConfig returns the configuration for the HTTP server
- func GetHTTPDConfig() httpd.Conf {
- return globalConf.HTTPDConfig
- }
- // SetHTTPDConfig sets the configuration for the HTTP server
- func SetHTTPDConfig(config httpd.Conf) {
- globalConf.HTTPDConfig = config
- }
- //GetProviderConf returns the configuration for the data provider
- func GetProviderConf() dataprovider.Config {
- return globalConf.ProviderConf
- }
- //SetProviderConf sets the configuration for the data provider
- func SetProviderConf(config dataprovider.Config) {
- globalConf.ProviderConf = config
- }
- // GetHTTPConfig returns the configuration for HTTP clients
- func GetHTTPConfig() httpclient.Config {
- return globalConf.HTTPConfig
- }
- func getRedactedGlobalConf() globalConfig {
- conf := globalConf
- conf.ProviderConf.Password = "[redacted]"
- return conf
- }
- // LoadConfig loads the configuration
- // configDir will be added to the configuration search paths.
- // The search path contains by default the current directory and on linux it contains
- // $HOME/.config/sftpgo and /etc/sftpgo too.
- // configName is the name of the configuration to search without extension
- func LoadConfig(configDir, configName string) error {
- var err error
- viper.AddConfigPath(configDir)
- setViperAdditionalConfigPaths()
- viper.AddConfigPath(".")
- viper.SetConfigName(configName)
- if err = viper.ReadInConfig(); err != nil {
- logger.Warn(logSender, "", "error loading configuration file: %v", err)
- logger.WarnToConsole("error loading configuration file: %v", err)
- }
- err = viper.Unmarshal(&globalConf)
- if err != nil {
- logger.Warn(logSender, "", "error parsing configuration file: %v. Default configuration will be used: %+v",
- err, getRedactedGlobalConf())
- logger.WarnToConsole("error parsing configuration file: %v. Default configuration will be used.", err)
- return err
- }
- checkCommonParamsCompatibility()
- if strings.TrimSpace(globalConf.SFTPD.Banner) == "" {
- globalConf.SFTPD.Banner = defaultSFTPDBanner
- }
- if strings.TrimSpace(globalConf.FTPD.Banner) == "" {
- globalConf.FTPD.Banner = defaultFTPDBanner
- }
- if len(globalConf.ProviderConf.UsersBaseDir) > 0 && !utils.IsFileInputValid(globalConf.ProviderConf.UsersBaseDir) {
- err = fmt.Errorf("invalid users base dir %#v will be ignored", globalConf.ProviderConf.UsersBaseDir)
- globalConf.ProviderConf.UsersBaseDir = ""
- logger.Warn(logSender, "", "Configuration error: %v", err)
- logger.WarnToConsole("Configuration error: %v", err)
- }
- if globalConf.Common.UploadMode < 0 || globalConf.Common.UploadMode > 2 {
- err = fmt.Errorf("invalid upload_mode 0, 1 and 2 are supported, configured: %v reset upload_mode to 0",
- globalConf.Common.UploadMode)
- globalConf.Common.UploadMode = 0
- logger.Warn(logSender, "", "Configuration error: %v", err)
- logger.WarnToConsole("Configuration error: %v", err)
- }
- if globalConf.Common.ProxyProtocol < 0 || globalConf.Common.ProxyProtocol > 2 {
- err = fmt.Errorf("invalid proxy_protocol 0, 1 and 2 are supported, configured: %v reset proxy_protocol to 0",
- globalConf.Common.ProxyProtocol)
- globalConf.Common.ProxyProtocol = 0
- logger.Warn(logSender, "", "Configuration error: %v", err)
- logger.WarnToConsole("Configuration error: %v", err)
- }
- if globalConf.ProviderConf.ExternalAuthScope < 0 || globalConf.ProviderConf.ExternalAuthScope > 7 {
- err = fmt.Errorf("invalid external_auth_scope: %v reset to 0", globalConf.ProviderConf.ExternalAuthScope)
- globalConf.ProviderConf.ExternalAuthScope = 0
- logger.Warn(logSender, "", "Configuration error: %v", err)
- logger.WarnToConsole("Configuration error: %v", err)
- }
- if len(globalConf.ProviderConf.CredentialsPath) == 0 {
- err = fmt.Errorf("invalid credentials path, reset to \"credentials\"")
- globalConf.ProviderConf.CredentialsPath = "credentials"
- logger.Warn(logSender, "", "Configuration error: %v", err)
- logger.WarnToConsole("Configuration error: %v", err)
- }
- checkHostKeyCompatibility()
- logger.Debug(logSender, "", "config file used: '%#v', config loaded: %+v", viper.ConfigFileUsed(), getRedactedGlobalConf())
- return err
- }
- func checkHostKeyCompatibility() {
- // we copy deprecated fields to new ones to keep backward compatibility so lint is disabled
- if len(globalConf.SFTPD.Keys) > 0 && len(globalConf.SFTPD.HostKeys) == 0 { //nolint:staticcheck
- logger.Warn(logSender, "", "keys is deprecated, please use host_keys")
- logger.WarnToConsole("keys is deprecated, please use host_keys")
- for _, k := range globalConf.SFTPD.Keys { //nolint:staticcheck
- globalConf.SFTPD.HostKeys = append(globalConf.SFTPD.HostKeys, k.PrivateKey)
- }
- }
- }
- func checkCommonParamsCompatibility() {
- // we copy deprecated fields to new ones to keep backward compatibility so lint is disabled
- if globalConf.SFTPD.IdleTimeout > 0 { //nolint:staticcheck
- logger.Warn(logSender, "", "sftpd.idle_timeout is deprecated, please use common.idle_timeout")
- logger.WarnToConsole("sftpd.idle_timeout is deprecated, please use common.idle_timeout")
- globalConf.Common.IdleTimeout = globalConf.SFTPD.IdleTimeout //nolint:staticcheck
- }
- if len(globalConf.SFTPD.Actions.Hook) > 0 && len(globalConf.Common.Actions.Hook) == 0 { //nolint:staticcheck
- logger.Warn(logSender, "", "sftpd.actions is deprecated, please use common.actions")
- logger.WarnToConsole("sftpd.actions is deprecated, please use common.actions")
- globalConf.Common.Actions.ExecuteOn = globalConf.SFTPD.Actions.ExecuteOn //nolint:staticcheck
- globalConf.Common.Actions.Hook = globalConf.SFTPD.Actions.Hook //nolint:staticcheck
- }
- if globalConf.SFTPD.SetstatMode > 0 && globalConf.Common.SetstatMode == 0 { //nolint:staticcheck
- logger.Warn(logSender, "", "sftpd.setstat_mode is deprecated, please use common.setstat_mode")
- logger.WarnToConsole("sftpd.setstat_mode is deprecated, please use common.setstat_mode")
- globalConf.Common.SetstatMode = globalConf.SFTPD.SetstatMode //nolint:staticcheck
- }
- if globalConf.SFTPD.UploadMode > 0 && globalConf.Common.UploadMode == 0 { //nolint:staticcheck
- logger.Warn(logSender, "", "sftpd.upload_mode is deprecated, please use common.upload_mode")
- logger.WarnToConsole("sftpd.upload_mode is deprecated, please use common.upload_mode")
- globalConf.Common.UploadMode = globalConf.SFTPD.UploadMode //nolint:staticcheck
- }
- if globalConf.SFTPD.ProxyProtocol > 0 && globalConf.Common.ProxyProtocol == 0 { //nolint:staticcheck
- logger.Warn(logSender, "", "sftpd.proxy_protocol is deprecated, please use common.proxy_protocol")
- logger.WarnToConsole("sftpd.proxy_protocol is deprecated, please use common.proxy_protocol")
- globalConf.Common.ProxyProtocol = globalConf.SFTPD.ProxyProtocol //nolint:staticcheck
- globalConf.Common.ProxyAllowed = globalConf.SFTPD.ProxyAllowed //nolint:staticcheck
- }
- }
- func setViperDefaults() {
- viper.SetDefault("common.idle_timeout", globalConf.Common.IdleTimeout)
- viper.SetDefault("common.upload_mode", globalConf.Common.UploadMode)
- viper.SetDefault("common.actions.execute_on", globalConf.Common.Actions.ExecuteOn)
- viper.SetDefault("common.actions.hook", globalConf.Common.Actions.Hook)
- viper.SetDefault("common.setstat_mode", globalConf.Common.SetstatMode)
- viper.SetDefault("common.proxy_protocol", globalConf.Common.ProxyProtocol)
- viper.SetDefault("common.proxy_allowed", globalConf.Common.ProxyAllowed)
- viper.SetDefault("common.post_connect_hook", globalConf.Common.PostConnectHook)
- viper.SetDefault("sftpd.bind_port", globalConf.SFTPD.BindPort)
- viper.SetDefault("sftpd.bind_address", globalConf.SFTPD.BindAddress)
- viper.SetDefault("sftpd.max_auth_tries", globalConf.SFTPD.MaxAuthTries)
- viper.SetDefault("sftpd.banner", globalConf.SFTPD.Banner)
- viper.SetDefault("sftpd.host_keys", globalConf.SFTPD.HostKeys)
- viper.SetDefault("sftpd.kex_algorithms", globalConf.SFTPD.KexAlgorithms)
- viper.SetDefault("sftpd.ciphers", globalConf.SFTPD.Ciphers)
- viper.SetDefault("sftpd.macs", globalConf.SFTPD.MACs)
- viper.SetDefault("sftpd.trusted_user_ca_keys", globalConf.SFTPD.TrustedUserCAKeys)
- viper.SetDefault("sftpd.login_banner_file", globalConf.SFTPD.LoginBannerFile)
- viper.SetDefault("sftpd.enabled_ssh_commands", globalConf.SFTPD.EnabledSSHCommands)
- viper.SetDefault("sftpd.keyboard_interactive_auth_hook", globalConf.SFTPD.KeyboardInteractiveHook)
- viper.SetDefault("sftpd.password_authentication", globalConf.SFTPD.PasswordAuthentication)
- viper.SetDefault("ftpd.bind_port", globalConf.FTPD.BindPort)
- viper.SetDefault("ftpd.bind_address", globalConf.FTPD.BindAddress)
- viper.SetDefault("ftpd.banner", globalConf.FTPD.Banner)
- viper.SetDefault("ftpd.banner_file", globalConf.FTPD.BannerFile)
- viper.SetDefault("ftpd.active_transfers_port_non_20", globalConf.FTPD.ActiveTransfersPortNon20)
- viper.SetDefault("ftpd.force_passive_ip", globalConf.FTPD.ForcePassiveIP)
- viper.SetDefault("ftpd.passive_port_range.start", globalConf.FTPD.PassivePortRange.Start)
- viper.SetDefault("ftpd.passive_port_range.end", globalConf.FTPD.PassivePortRange.End)
- viper.SetDefault("ftpd.certificate_file", globalConf.FTPD.CertificateFile)
- viper.SetDefault("ftpd.certificate_key_file", globalConf.FTPD.CertificateKeyFile)
- viper.SetDefault("ftpd.tls_mode", globalConf.FTPD.TLSMode)
- viper.SetDefault("webdavd.bind_port", globalConf.WebDAVD.BindPort)
- viper.SetDefault("webdavd.bind_address", globalConf.WebDAVD.BindAddress)
- viper.SetDefault("webdavd.certificate_file", globalConf.WebDAVD.CertificateFile)
- viper.SetDefault("webdavd.certificate_key_file", globalConf.WebDAVD.CertificateKeyFile)
- viper.SetDefault("webdavd.cors.enabled", globalConf.WebDAVD.Cors.Enabled)
- viper.SetDefault("webdavd.cors.allowed_origins", globalConf.WebDAVD.Cors.AllowedOrigins)
- viper.SetDefault("webdavd.cors.allowed_methods", globalConf.WebDAVD.Cors.AllowedMethods)
- viper.SetDefault("webdavd.cors.allowed_headers", globalConf.WebDAVD.Cors.AllowedHeaders)
- viper.SetDefault("webdavd.cors.exposed_headers", globalConf.WebDAVD.Cors.ExposedHeaders)
- viper.SetDefault("webdavd.cors.allow_credentials", globalConf.WebDAVD.Cors.AllowCredentials)
- viper.SetDefault("webdavd.cors.max_age", globalConf.WebDAVD.Cors.MaxAge)
- viper.SetDefault("webdavd.cache.enabled", globalConf.WebDAVD.Cache.Enabled)
- viper.SetDefault("webdavd.cache.expiration_time", globalConf.WebDAVD.Cache.ExpirationTime)
- viper.SetDefault("webdavd.cache.max_size", globalConf.WebDAVD.Cache.MaxSize)
- viper.SetDefault("data_provider.driver", globalConf.ProviderConf.Driver)
- viper.SetDefault("data_provider.name", globalConf.ProviderConf.Name)
- viper.SetDefault("data_provider.host", globalConf.ProviderConf.Host)
- viper.SetDefault("data_provider.port", globalConf.ProviderConf.Port)
- viper.SetDefault("data_provider.username", globalConf.ProviderConf.Username)
- viper.SetDefault("data_provider.password", globalConf.ProviderConf.Password)
- viper.SetDefault("data_provider.sslmode", globalConf.ProviderConf.SSLMode)
- viper.SetDefault("data_provider.connection_string", globalConf.ProviderConf.ConnectionString)
- viper.SetDefault("data_provider.sql_tables_prefix", globalConf.ProviderConf.SQLTablesPrefix)
- viper.SetDefault("data_provider.manage_users", globalConf.ProviderConf.ManageUsers)
- viper.SetDefault("data_provider.track_quota", globalConf.ProviderConf.TrackQuota)
- viper.SetDefault("data_provider.pool_size", globalConf.ProviderConf.PoolSize)
- viper.SetDefault("data_provider.users_base_dir", globalConf.ProviderConf.UsersBaseDir)
- viper.SetDefault("data_provider.actions.execute_on", globalConf.ProviderConf.Actions.ExecuteOn)
- viper.SetDefault("data_provider.actions.hook", globalConf.ProviderConf.Actions.Hook)
- viper.SetDefault("data_provider.external_auth_hook", globalConf.ProviderConf.ExternalAuthHook)
- viper.SetDefault("data_provider.external_auth_scope", globalConf.ProviderConf.ExternalAuthScope)
- viper.SetDefault("data_provider.credentials_path", globalConf.ProviderConf.CredentialsPath)
- viper.SetDefault("data_provider.prefer_database_credentials", globalConf.ProviderConf.PreferDatabaseCredentials)
- viper.SetDefault("data_provider.pre_login_hook", globalConf.ProviderConf.PreLoginHook)
- viper.SetDefault("data_provider.post_login_hook", globalConf.ProviderConf.PostLoginHook)
- viper.SetDefault("data_provider.post_login_scope", globalConf.ProviderConf.PostLoginScope)
- viper.SetDefault("data_provider.check_password_hook", globalConf.ProviderConf.CheckPasswordHook)
- viper.SetDefault("data_provider.check_password_scope", globalConf.ProviderConf.CheckPasswordScope)
- viper.SetDefault("data_provider.password_hashing.argon2_options.memory", globalConf.ProviderConf.PasswordHashing.Argon2Options.Memory)
- viper.SetDefault("data_provider.password_hashing.argon2_options.iterations", globalConf.ProviderConf.PasswordHashing.Argon2Options.Iterations)
- viper.SetDefault("data_provider.password_hashing.argon2_options.parallelism", globalConf.ProviderConf.PasswordHashing.Argon2Options.Parallelism)
- viper.SetDefault("data_provider.update_mode", globalConf.ProviderConf.UpdateMode)
- viper.SetDefault("httpd.bind_port", globalConf.HTTPDConfig.BindPort)
- viper.SetDefault("httpd.bind_address", globalConf.HTTPDConfig.BindAddress)
- viper.SetDefault("httpd.templates_path", globalConf.HTTPDConfig.TemplatesPath)
- viper.SetDefault("httpd.static_files_path", globalConf.HTTPDConfig.StaticFilesPath)
- viper.SetDefault("httpd.backups_path", globalConf.HTTPDConfig.BackupsPath)
- viper.SetDefault("httpd.auth_user_file", globalConf.HTTPDConfig.AuthUserFile)
- viper.SetDefault("httpd.certificate_file", globalConf.HTTPDConfig.CertificateFile)
- viper.SetDefault("httpd.certificate_key_file", globalConf.HTTPDConfig.CertificateKeyFile)
- viper.SetDefault("http.timeout", globalConf.HTTPConfig.Timeout)
- viper.SetDefault("http.ca_certificates", globalConf.HTTPConfig.CACertificates)
- viper.SetDefault("http.skip_tls_verify", globalConf.HTTPConfig.SkipTLSVerify)
- }
|