diff --git a/go.mod b/go.mod index 592ad491..79e5a2d6 100644 --- a/go.mod +++ b/go.mod @@ -66,10 +66,10 @@ require ( go.etcd.io/bbolt v1.3.6 go.uber.org/automaxprocs v1.5.1 gocloud.dev v0.27.0 - golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2 - golang.org/x/net v0.0.0-20221004154528-8021a29435af + golang.org/x/crypto v0.0.0-20221012134737-56aed061732a + golang.org/x/net v0.0.0-20221014081412-f15817d10f9b golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1 - golang.org/x/sys v0.0.0-20221010170243-090e33056c14 + golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 golang.org/x/time v0.0.0-20220922220347-f3bd1da661af google.golang.org/api v0.98.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 @@ -159,7 +159,7 @@ require ( golang.org/x/tools v0.1.12 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e // indirect + google.golang.org/genproto v0.0.0-20221013201013-33fc6f83cba4 // indirect google.golang.org/grpc v1.50.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect @@ -171,6 +171,6 @@ require ( replace ( github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 github.com/pkg/sftp => github.com/drakkan/sftp v0.0.0-20220930161944-e8c89afc13a7 - golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20221011170652-7c454d6a47a0 - golang.org/x/net => github.com/drakkan/net v0.0.0-20221011170324-793589996ca2 + golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20221014140914-137f4b1d754c + golang.org/x/net => github.com/drakkan/net v0.0.0-20221014140113-499335f62da1 ) diff --git a/go.sum b/go.sum index dbb8c684..53a00419 100644 --- a/go.sum +++ b/go.sum @@ -535,12 +535,12 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/drakkan/crypto v0.0.0-20221011170652-7c454d6a47a0 h1:H8T0AOkrwrWacTEY8nP1PDQ+kUMeTQbCu8OY+x0/mXY= -github.com/drakkan/crypto v0.0.0-20221011170652-7c454d6a47a0/go.mod h1:SiM6ypd8Xu1xldObYtbDztuUU7xUzMnUULfphXFZmro= +github.com/drakkan/crypto v0.0.0-20221014140914-137f4b1d754c h1:McuxdVQM/jDDxZHZrtQySRNKaAqevQQcicZKisIAJ7Y= +github.com/drakkan/crypto v0.0.0-20221014140914-137f4b1d754c/go.mod h1:SiM6ypd8Xu1xldObYtbDztuUU7xUzMnUULfphXFZmro= github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 h1:LPH1dEblAOO/LoG7yHPMtBLXhQmjaga91/DDjWk9jWA= github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU= -github.com/drakkan/net v0.0.0-20221011170324-793589996ca2 h1:nFZmCADOhW2YZ51/IKcODGsJsg3cX1VGVgjFDQYuVKs= -github.com/drakkan/net v0.0.0-20221011170324-793589996ca2/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +github.com/drakkan/net v0.0.0-20221014140113-499335f62da1 h1:v/AU5W67QpUA+kOOqxRosCTuvtzh9XYN8UAV448XaO0= +github.com/drakkan/net v0.0.0-20221014140113-499335f62da1/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= github.com/drakkan/sftp v0.0.0-20220930161944-e8c89afc13a7 h1:Hj7AAfZ5yt9QuCxSQDllRygmL33xJ2sZLOmcyyOAdYU= github.com/drakkan/sftp v0.0.0-20220930161944-e8c89afc13a7/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -1918,8 +1918,8 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20221010170243-090e33056c14 h1:k5II8e6QD8mITdi+okbbmR/cIyEbeXLBhy5Ha4nevyc= -golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4= +golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -2209,8 +2209,8 @@ google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljW google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220802133213-ce4fa296bf78/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e h1:halCgTFuLWDRD61piiNSxPsARANGD3Xl16hPrLgLiIg= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221013201013-33fc6f83cba4 h1:nZ28yoLJWNLTcERW43BN+JDsNQOdiZOFB9Dly/IUrjw= +google.golang.org/genproto v0.0.0-20221013201013-33fc6f83cba4/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= diff --git a/internal/config/config.go b/internal/config/config.go index 0a115b39..ab61f72a 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -427,7 +427,7 @@ func Init() { }, }, MFAConfig: mfa.Config{ - TOTP: nil, + TOTP: []mfa.TOTPConfig{defaultTOTP}, }, TelemetryConfig: telemetry.Conf{ BindPort: 0, @@ -659,6 +659,40 @@ func readEnvFiles(configDir string) { } } +func checkOverrideDefaultSettings() { + // for slices we need to set the defaults to nil if the key is set in the config file, + // otherwise the values are merged and not replaced as expected + rateLimiters := viper.Get("common.rate_limiters") + if val, ok := rateLimiters.([]any); ok { + if len(val) > 0 { + if rl, ok := val[0].(map[string]any); ok { + if _, ok := rl["protocols"]; ok { + globalConf.Common.RateLimitersConfig[0].Protocols = nil + } + } + } + } + + httpdBindings := viper.Get("httpd.bindings") + if val, ok := httpdBindings.([]any); ok { + if len(val) > 0 { + if binding, ok := val[0].(map[string]any); ok { + if val, ok := binding["oidc"]; ok { + if oidc, ok := val.(map[string]any); ok { + if _, ok := oidc["scopes"]; ok { + globalConf.HTTPDConfig.Bindings[0].OIDC.Scopes = nil + } + } + } + } + } + } + + if util.Contains(viper.AllKeys(), "mfa.totp") { + globalConf.MFAConfig.TOTP = nil + } +} + // 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 @@ -682,8 +716,8 @@ func LoadConfig(configDir, configFile string) error { logger.Warn(logSender, "", "error loading configuration file: %v", err) logger.WarnToConsole("error loading configuration file: %v", err) } - globalConf.MFAConfig.TOTP = []mfa.TOTPConfig{defaultTOTP} } + checkOverrideDefaultSettings() err = viper.Unmarshal(&globalConf) if err != nil { logger.Warn(logSender, "", "error parsing configuration file: %v", err) diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 3ce44558..9ed9492c 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -84,9 +84,13 @@ func TestLoadConfigFileNotFound(t *testing.T) { viper.SetConfigName("configfile") err := config.LoadConfig(os.TempDir(), "") - assert.NoError(t, err) + require.NoError(t, err) mfaConf := config.GetMFAConfig() - assert.Len(t, mfaConf.TOTP, 1) + require.Len(t, mfaConf.TOTP, 1) + require.Len(t, config.GetCommonConfig().RateLimitersConfig, 1) + require.Len(t, config.GetCommonConfig().RateLimitersConfig[0].Protocols, 4) + require.Len(t, config.GetHTTPDConfig().Bindings, 1) + require.Len(t, config.GetHTTPDConfig().Bindings[0].OIDC.Scopes, 3) } func TestReadEnvFiles(t *testing.T) { @@ -489,6 +493,81 @@ func TestDisabledMFAConfig(t *testing.T) { assert.NoError(t, err) } +func TestOverrideSliceValues(t *testing.T) { + reset() + + confName := tempConfigName + ".json" + configFilePath := filepath.Join(configDir, confName) + c := make(map[string]any) + c["common"] = common.Configuration{ + RateLimitersConfig: []common.RateLimiterConfig{ + { + Type: 1, + Protocols: []string{"HTTP"}, + }, + }, + } + jsonConf, err := json.Marshal(c) + assert.NoError(t, err) + err = os.WriteFile(configFilePath, jsonConf, os.ModePerm) + assert.NoError(t, err) + err = config.LoadConfig(configDir, confName) + assert.NoError(t, err) + require.Len(t, config.GetCommonConfig().RateLimitersConfig, 1) + require.Equal(t, []string{"HTTP"}, config.GetCommonConfig().RateLimitersConfig[0].Protocols) + + reset() + + // empty ratelimiters, default value should be used + c["common"] = common.Configuration{} + jsonConf, err = json.Marshal(c) + assert.NoError(t, err) + err = os.WriteFile(configFilePath, jsonConf, os.ModePerm) + assert.NoError(t, err) + err = config.LoadConfig(configDir, confName) + assert.NoError(t, err) + require.Len(t, config.GetCommonConfig().RateLimitersConfig, 1) + rl := config.GetCommonConfig().RateLimitersConfig[0] + require.Equal(t, []string{"SSH", "FTP", "DAV", "HTTP"}, rl.Protocols) + require.Equal(t, int64(1000), rl.Period) + + reset() + + c = make(map[string]any) + c["httpd"] = httpd.Conf{ + Bindings: []httpd.Binding{ + { + OIDC: httpd.OIDC{ + Scopes: []string{"scope1"}, + }, + }, + }, + } + jsonConf, err = json.Marshal(c) + assert.NoError(t, err) + err = os.WriteFile(configFilePath, jsonConf, os.ModePerm) + assert.NoError(t, err) + err = config.LoadConfig(configDir, confName) + assert.NoError(t, err) + require.Len(t, config.GetHTTPDConfig().Bindings, 1) + require.Equal(t, []string{"scope1"}, config.GetHTTPDConfig().Bindings[0].OIDC.Scopes) + + reset() + + c = make(map[string]any) + c["httpd"] = httpd.Conf{ + Bindings: []httpd.Binding{}, + } + jsonConf, err = json.Marshal(c) + assert.NoError(t, err) + err = os.WriteFile(configFilePath, jsonConf, os.ModePerm) + assert.NoError(t, err) + err = config.LoadConfig(configDir, confName) + assert.NoError(t, err) + require.Len(t, config.GetHTTPDConfig().Bindings, 1) + require.Equal(t, []string{"openid", "profile", "email"}, config.GetHTTPDConfig().Bindings[0].OIDC.Scopes) +} + func TestFTPDOverridesFromEnv(t *testing.T) { reset() diff --git a/windows-installer/README.txt b/windows-installer/README.txt index ebc7ee93..e4659560 100644 --- a/windows-installer/README.txt +++ b/windows-installer/README.txt @@ -21,6 +21,10 @@ Configuration file location: C:\ProgramData\SFTPGo\sftpgo.json +Directory to create environment variable files to set configuration options: + +C:\ProgramData\SFTPGo\env.d + Getting started guide: https://github.com/drakkan/sftpgo/blob/main/docs/howto/getting-started.md