From 32a476b840435958d622e7489a3fbaebae9cc701 Mon Sep 17 00:00:00 2001 From: chlins Date: Fri, 4 Aug 2023 15:04:43 +0800 Subject: [PATCH] feat: added support for redis username configuration Redis introduced an Access Control List (ACL) mechanism since version 6.0. This commit implements the necessary changes to support configuring the username for Redis. Users can now define a specific username to authenticate with Redis and enhance security through the ACL feature. Signed-off-by: chlins --- configuration/configuration.go | 3 ++ configuration/configuration_test.go | 82 +++++++++++++++++++++++++++++ registry/handlers/app.go | 10 +++- 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/configuration/configuration.go b/configuration/configuration.go index 6a0af0af..beec7f89 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -174,6 +174,9 @@ type Configuration struct { // Addr specifies the the redis instance available to the application. Addr string `yaml:"addr,omitempty"` + // Usernames can be used as a finer-grained permission control since the introduction of the redis 6.0. + Username string `yaml:"username,omitempty"` + // Password string to use when making a connection. Password string `yaml:"password,omitempty"` diff --git a/configuration/configuration_test.go b/configuration/configuration_test.go index 24cd680d..064bae4a 100644 --- a/configuration/configuration_test.go +++ b/configuration/configuration_test.go @@ -131,6 +131,40 @@ var configStruct = Configuration{ Disabled: false, }, }, + Redis: struct { + Addr string `yaml:"addr,omitempty"` + Username string `yaml:"username,omitempty"` + Password string `yaml:"password,omitempty"` + DB int `yaml:"db,omitempty"` + TLS struct { + Enabled bool `yaml:"enabled,omitempty"` + } `yaml:"tls,omitempty"` + DialTimeout time.Duration `yaml:"dialtimeout,omitempty"` + ReadTimeout time.Duration `yaml:"readtimeout,omitempty"` + WriteTimeout time.Duration `yaml:"writetimeout,omitempty"` + Pool struct { + MaxIdle int `yaml:"maxidle,omitempty"` + MaxActive int `yaml:"maxactive,omitempty"` + IdleTimeout time.Duration `yaml:"idletimeout,omitempty"` + } `yaml:"pool,omitempty"` + }{ + Addr: "localhost:6379", + Username: "alice", + Password: "123456", + DB: 1, + Pool: struct { + MaxIdle int `yaml:"maxidle,omitempty"` + MaxActive int `yaml:"maxactive,omitempty"` + IdleTimeout time.Duration `yaml:"idletimeout,omitempty"` + }{ + MaxIdle: 16, + MaxActive: 64, + IdleTimeout: time.Second * 300, + }, + DialTimeout: time.Millisecond * 10, + ReadTimeout: time.Millisecond * 10, + WriteTimeout: time.Millisecond * 10, + }, } // configYamlV0_1 is a Version 0.1 yaml document representing configStruct @@ -175,6 +209,18 @@ http: - /path/to/ca.pem headers: X-Content-Type-Options: [nosniff] +redis: + addr: localhost:6379 + username: alice + password: 123456 + db: 1 + pool: + maxidle: 16 + maxactive: 64 + idletimeout: 300s + dialtimeout: 10ms + readtimeout: 10ms + writetimeout: 10ms ` // inmemoryConfigYamlV0_1 is a Version 0.1 yaml document specifying an inmemory @@ -242,6 +288,23 @@ func (suite *ConfigSuite) TestParseInmemory(c *check.C) { suite.expectedConfig.Storage = Storage{"inmemory": Parameters{}} suite.expectedConfig.Reporting = Reporting{} suite.expectedConfig.Log.Fields = nil + suite.expectedConfig.Redis = struct { + Addr string `yaml:"addr,omitempty"` + Username string `yaml:"username,omitempty"` + Password string `yaml:"password,omitempty"` + DB int `yaml:"db,omitempty"` + TLS struct { + Enabled bool `yaml:"enabled,omitempty"` + } `yaml:"tls,omitempty"` + DialTimeout time.Duration `yaml:"dialtimeout,omitempty"` + ReadTimeout time.Duration `yaml:"readtimeout,omitempty"` + WriteTimeout time.Duration `yaml:"writetimeout,omitempty"` + Pool struct { + MaxIdle int `yaml:"maxidle,omitempty"` + MaxActive int `yaml:"maxactive,omitempty"` + IdleTimeout time.Duration `yaml:"idletimeout,omitempty"` + } `yaml:"pool,omitempty"` + }{} config, err := Parse(bytes.NewReader([]byte(inmemoryConfigYamlV0_1))) c.Assert(err, check.IsNil) @@ -262,6 +325,23 @@ func (suite *ConfigSuite) TestParseIncomplete(c *check.C) { suite.expectedConfig.Reporting = Reporting{} suite.expectedConfig.Notifications = Notifications{} suite.expectedConfig.HTTP.Headers = nil + suite.expectedConfig.Redis = struct { + Addr string `yaml:"addr,omitempty"` + Username string `yaml:"username,omitempty"` + Password string `yaml:"password,omitempty"` + DB int `yaml:"db,omitempty"` + TLS struct { + Enabled bool `yaml:"enabled,omitempty"` + } `yaml:"tls,omitempty"` + DialTimeout time.Duration `yaml:"dialtimeout,omitempty"` + ReadTimeout time.Duration `yaml:"readtimeout,omitempty"` + WriteTimeout time.Duration `yaml:"writetimeout,omitempty"` + Pool struct { + MaxIdle int `yaml:"maxidle,omitempty"` + MaxActive int `yaml:"maxactive,omitempty"` + IdleTimeout time.Duration `yaml:"idletimeout,omitempty"` + } `yaml:"pool,omitempty"` + }{} // Note: this also tests that REGISTRY_STORAGE and // REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY can be used together @@ -555,5 +635,7 @@ func copyConfig(config Configuration) *Configuration { configCopy.HTTP.Headers[k] = v } + configCopy.Redis = config.Redis + return configCopy } diff --git a/registry/handlers/app.go b/registry/handlers/app.go index cdcb326f..4567f27c 100644 --- a/registry/handlers/app.go +++ b/registry/handlers/app.go @@ -546,8 +546,16 @@ func (app *App) configureRedis(configuration *configuration.Configuration) { } // authorize the connection + authArgs := make([]interface{}, 0, 2) + if configuration.Redis.Username != "" { + authArgs = append(authArgs, configuration.Redis.Username) + } if configuration.Redis.Password != "" { - if _, err = conn.Do("AUTH", configuration.Redis.Password); err != nil { + authArgs = append(authArgs, configuration.Redis.Password) + } + + if len(authArgs) > 0 { + if _, err = conn.Do("AUTH", authArgs...); err != nil { defer conn.Close() done(err) return nil, err