diff --git a/config/config.go b/config/config.go index 35f410d3..c0dd4a11 100644 --- a/config/config.go +++ b/config/config.go @@ -165,6 +165,7 @@ func init() { replacer := strings.NewReplacer(".", "__") viper.SetEnvKeyReplacer(replacer) viper.SetConfigName(DefaultConfigName) + setViperDefaults() viper.AutomaticEnv() viper.AllowEmptyEnv(true) } @@ -252,10 +253,8 @@ func LoadConfig(configDir, configName string) error { viper.AddConfigPath(".") viper.SetConfigName(configName) if err = viper.ReadInConfig(); err != nil { - logger.Warn(logSender, "", "error loading configuration file: %v. Default configuration will be used: %+v", - err, getRedactedGlobalConf()) - logger.WarnToConsole("error loading configuration file: %v. Default configuration will be used.", err) - return err + logger.Warn(logSender, "", "error loading configuration file: %v", err) + logger.WarnToConsole("error loading configuration file: %v", err) } err = viper.Unmarshal(&globalConf) if err != nil { @@ -349,3 +348,91 @@ func checkCommonParamsCompatibility() { 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) +} diff --git a/config/config_test.go b/config/config_test.go index 94bc3ec8..c8624d7b 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -36,11 +36,11 @@ func TestLoadConfigTest(t *testing.T) { confName := tempConfigName + ".json" configFilePath := filepath.Join(configDir, confName) err = config.LoadConfig(configDir, tempConfigName) - assert.NotNil(t, err) + assert.NoError(t, err) err = ioutil.WriteFile(configFilePath, []byte("{invalid json}"), os.ModePerm) assert.NoError(t, err) err = config.LoadConfig(configDir, tempConfigName) - assert.NotNil(t, err) + assert.NoError(t, err) err = ioutil.WriteFile(configFilePath, []byte("{\"sftpd\": {\"bind_port\": \"a\"}}"), os.ModePerm) assert.NoError(t, err) err = config.LoadConfig(configDir, tempConfigName) @@ -280,3 +280,25 @@ func TestSetGetConfig(t *testing.T) { assert.Equal(t, webDavConf.CertificateFile, config.GetWebDAVDConfig().CertificateFile) assert.Equal(t, webDavConf.CertificateKeyFile, config.GetWebDAVDConfig().CertificateKeyFile) } + +func TestConfigFromEnv(t *testing.T) { + os.Setenv("SFTPGO_SFTPD__BIND_ADDRESS", "127.0.0.1") + os.Setenv("SFTPGO_DATA_PROVIDER__PASSWORD_HASHING__ARGON2_OPTIONS__ITERATIONS", "41") + os.Setenv("SFTPGO_DATA_PROVIDER__POOL_SIZE", "10") + os.Setenv("SFTPGO_DATA_PROVIDER__ACTIONS__EXECUTE_ON", "add") + t.Cleanup(func() { + os.Unsetenv("SFTPGO_SFTPD__BIND_ADDRESS") + os.Unsetenv("SFTPGO_DATA_PROVIDER__PASSWORD_HASHING__ARGON2_OPTIONS__ITERATIONS") + os.Unsetenv("SFTPGO_DATA_PROVIDER__POOL_SIZE") + os.Unsetenv("SFTPGO_DATA_PROVIDER__ACTIONS__EXECUTE_ON") + }) + err := config.LoadConfig(".", "invalid config") + assert.NoError(t, err) + sftpdConfig := config.GetSFTPDConfig() + assert.Equal(t, "127.0.0.1", sftpdConfig.BindAddress) + dataProviderConf := config.GetProviderConf() + assert.Equal(t, uint32(41), dataProviderConf.PasswordHashing.Argon2Options.Iterations) + assert.Equal(t, 10, dataProviderConf.PoolSize) + assert.Len(t, dataProviderConf.Actions.ExecuteOn, 1) + assert.Contains(t, dataProviderConf.Actions.ExecuteOn, "add") +} diff --git a/docs/account.md b/docs/account.md index a25ed3eb..2240592f 100644 --- a/docs/account.md +++ b/docs/account.md @@ -45,7 +45,7 @@ For each account, the following properties can be configured: - `allowed_extensions`, list of, case insensitive, allowed files extension. Shell like expansion is not supported so you have to specify `.jpg` and not `*.jpg`. Any file that does not end with this suffix will be denied - `denied_extensions`, list of, case insensitive, denied files extension. Denied file extensions are evaluated before the allowed ones - `path`, SFTP/SCP path, if no other specific filter is defined, the filter apply for sub directories too. For example if filters are defined for the paths `/` and `/sub` then the filters for `/` are applied for any file outside the `/sub` directory -- `fs_provider`, filesystem to serve via SFTP. Local filesystem, S3 Compatible Object Storage, Google Cloud Storage and Azure Blob Storage are supported +- `fs_provider`, filesystem to serve via SFTP. Local filesystem (0), S3 Compatible Object Storage (1), Google Cloud Storage (2) and Azure Blob Storage (3) are supported - `s3_bucket`, required for S3 filesystem - `s3_region`, required for S3 filesystem. Must match the region for your bucket. You can find here the list of available [AWS regions](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions). For example if your bucket is at `Frankfurt` you have to set the region to `eu-central-1` - `s3_access_key` diff --git a/docs/full-configuration.md b/docs/full-configuration.md index 3988b218..2c9af933 100644 --- a/docs/full-configuration.md +++ b/docs/full-configuration.md @@ -200,5 +200,3 @@ Let's see some examples: - To set sftpd `bind_port`, you need to define the env var `SFTPGO_SFTPD__BIND_PORT` - To set the `execute_on` actions, you need to define the env var `SFTPGO_COMMON__ACTIONS__EXECUTE_ON`. For example `SFTPGO_COMMON__ACTIONS__EXECUTE_ON=upload,download` - -Please note that in order to override configuration options with environment variables, you need a configuration file containing the options to override, this is a [viper bug](https://github.com/spf13/viper/issues/584). For example, you can deploy the default configuration file and then override the options to customize using environment variables.