Browse Source

config: fix for slices with default values

Signed-off-by: Nicola Murino <[email protected]>
Nicola Murino 3 years ago
parent
commit
07b3f2f4d6
5 changed files with 135 additions and 18 deletions
  1. 6 6
      go.mod
  2. 8 8
      go.sum
  3. 36 2
      internal/config/config.go
  4. 81 2
      internal/config/config_test.go
  5. 4 0
      windows-installer/README.txt

+ 6 - 6
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
 )

+ 8 - 8
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=

+ 36 - 2
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)

+ 81 - 2
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()
 

+ 4 - 0
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