commit 75fdc82: Added support for Redis 6 ACL (username/password)

laodc github at laodc.com
Mon Aug 21 15:21:03 UTC 2023


Author: laodc
Date: 2023-08-21 15:45:58 +0700
URL: https://github.com/rspamd/rspamd/commit/75fdc829bacbdc767b20d3f0e40b91215fce14fe

Added support for Redis 6 ACL (username/password)

---
 conf/modules.d/redis.conf                         |  1 +
 lualib/lua_redis.lua                              | 37 +++++++++++++++-
 lualib/rspamadm/configwizard.lua                  | 16 ++++++-
 lualib/rspamadm/fuzzy_convert.lua                 | 34 ++++++++++-----
 src/libserver/fuzzy_backend/fuzzy_backend_redis.c | 26 +++++++++--
 src/libserver/redis_pool.cxx                      | 53 ++++++++++++++++-------
 src/libserver/redis_pool.h                        |  3 +-
 src/libstat/backends/redis_backend.c              | 21 ++++++++-
 src/libstat/learn_cache/redis_cache.c             | 21 ++++++++-
 src/lua/lua_redis.c                               | 17 +++++---
 src/plugins/lua/bimi.lua                          |  9 +++-
 src/rspamadm/fuzzy_convert.c                      |  8 ++++
 src/rspamadm/stat_convert.c                       |  9 ++++
 13 files changed, 212 insertions(+), 43 deletions(-)

diff --git a/conf/modules.d/redis.conf b/conf/modules.d/redis.conf
index eb430cbb1..24948b150 100644
--- a/conf/modules.d/redis.conf
+++ b/conf/modules.d/redis.conf
@@ -19,6 +19,7 @@ redis {
   #disabled_modules = ["ratelimit"]; # List of modules that should not use redis from this section
   #timeout = 1s;
   #db = "0";
+  #username = "some_username";
   #password = "some_password";
   .include(try=true,priority=5) "${DBDIR}/dynamic/redis.conf"
   .include(try=true,priority=1,duplicate=merge) "$LOCAL_CONFDIR/local.d/redis.conf"
diff --git a/lualib/lua_redis.lua b/lualib/lua_redis.lua
index b03160bb0..377541f87 100644
--- a/lualib/lua_redis.lua
+++ b/lualib/lua_redis.lua
@@ -30,12 +30,14 @@ local common_schema = {
   database = ts.string:is_optional():describe("Database number"),
   dbname = ts.string:is_optional():describe("Database number"),
   prefix = ts.string:is_optional():describe("Key prefix"),
+  username = ts.string:is_optional():describe("Username"),
   password = ts.string:is_optional():describe("Password"),
   expand_keys = ts.boolean:is_optional():describe("Expand keys"),
   sentinels = (ts.string + ts.array_of(ts.string)):is_optional():describe("Sentinel servers"),
   sentinel_watch_time = (ts.number + ts.string / lutil.parse_time_interval):is_optional():describe("Sentinel watch time"),
   sentinel_masters_pattern = ts.string:is_optional():describe("Sentinel masters pattern"),
   sentinel_master_maxerrors = (ts.number + ts.string / tonumber):is_optional():describe("Sentinel master max errors"),
+  sentinel_username = ts.string:is_optional():describe("sentinel username"),
   sentinel_password = ts.string:is_optional():describe("Sentinel password"),
 }
 
@@ -153,6 +155,7 @@ local function redis_query_sentinel(ev_base, params, initialised)
         local ret = rspamd_redis.make_request {
           host = addr:get_addr(),
           timeout = params.timeout,
+          username = params.sentinel_username,
           password = params.sentinel_password,
           config = rspamd_config,
           ev_base = ev_base,
@@ -184,6 +187,7 @@ local function redis_query_sentinel(ev_base, params, initialised)
     timeout = params.timeout,
     config = rspamd_config,
     ev_base = ev_base,
+    username = params.sentinel_username,
     password = params.sentinel_password,
     cmd = 'SENTINEL',
     args = { 'masters' },
@@ -366,6 +370,9 @@ local function process_redis_opts(options, redis_params)
       redis_params['db'] = tostring(options['database'])
     end
   end
+  if options['username'] and not redis_params['username'] then
+    redis_params['username'] = options['username']
+  end
   if options['password'] and not redis_params['password'] then
     redis_params['password'] = options['password']
   end
@@ -996,6 +1003,10 @@ local function rspamd_redis_make_request(task, redis_params, key, is_write,
     end
   end
 
+  if redis_params['username'] then
+    options['username'] = redis_params['username']
+  end
+
   if redis_params['password'] then
     options['password'] = redis_params['password']
   end
@@ -1086,6 +1097,10 @@ local function redis_make_request_taskless(ev_base, cfg, redis_params, key,
     end
   end
 
+  if redis_params['username'] then
+    options['username'] = redis_params['username']
+  end
+
   if redis_params['password'] then
     options['password'] = redis_params['password']
   end
@@ -1157,6 +1172,10 @@ local function prepare_redis_call(script)
       upstream = s
     }
 
+    if script.redis_params['username'] then
+      cur_opts['username'] = script.redis_params['username']
+    end
+
     if script.redis_params['password'] then
       cur_opts['password'] = script.redis_params['password']
     end
@@ -1497,7 +1516,15 @@ local function redis_connect_sync(redis_params, is_write, key, cfg, ev_base)
 
   if conn then
     local need_exec = false
-    if redis_params['password'] then
+    if redis_params['username'] then
+      if redis_params['password'] then
+        conn:add_cmd('AUTH', { redis_params['username'], redis_params['password'] })
+        need_exec = true
+      else
+        logger.errx('Redis requires a password when username is supplied')
+        return false, nil, addr
+      end
+    elseif redis_params['password'] then
       conn:add_cmd('AUTH', { redis_params['password'] })
       need_exec = true
     end
@@ -1602,6 +1629,10 @@ exports.request = function(redis_params, attrs, req)
     opts.args = req
   end
 
+  if redis_params.username then
+    opts.username = redis_params.username
+  end
+
   if redis_params.password then
     opts.password = redis_params.password
   end
@@ -1701,6 +1732,10 @@ exports.connect = function(redis_params, attrs)
   opts.host = addr:get_addr()
   opts.timeout = redis_params.timeout
 
+  if redis_params.username then
+    opts.username = redis_params.username
+  end
+
   if redis_params.password then
     opts.password = redis_params.password
   end
diff --git a/lualib/rspamadm/configwizard.lua b/lualib/rspamadm/configwizard.lua
index 27c358e3b..7bcda5a01 100644
--- a/lualib/rspamadm/configwizard.lua
+++ b/lualib/rspamadm/configwizard.lua
@@ -249,7 +249,21 @@ local function setup_redis(cfg, changes)
       redis_params['write_servers'] = ws
     end
 
-    if ask_yes_no('Do you have any password set for your Redis?') then
+    if ask_yes_no('Do you have any username set for your Redis?') then
+      local usernm = readline_default("Enter Redis username:", nil)
+
+      if usernm then
+        changes.l['redis.conf']['username'] = usernm
+        redis_params['username'] = usernm
+      end
+
+      local passwd = readline_default("Enter Redis password:", nil)
+
+      if passwd then
+        changes.l['redis.conf']['password'] = passwd
+        redis_params['password'] = passwd
+      end
+    elseif ask_yes_no('Do you have any password set for your Redis?') then
       local passwd = readline_default("Enter Redis password:", nil)
 
       if passwd then
diff --git a/lualib/rspamadm/fuzzy_convert.lua b/lualib/rspamadm/fuzzy_convert.lua
index a31baa4e2..67a2664bc 100644
--- a/lualib/rspamadm/fuzzy_convert.lua
+++ b/lualib/rspamadm/fuzzy_convert.lua
@@ -12,7 +12,16 @@ local function connect_redis(server, password, db)
     return nil, 'Cannot connect: ' .. err
   end
 
-  if password then
+  if username then
+    if password then
+      ret = conn:add_cmd('AUTH', { username, password })
+      if not ret then
+        return nil, 'Cannot queue command'
+      end
+    else
+      return nil, 'Redis requires a password when username is supplied'
+    end
+  else if password then
     ret = conn:add_cmd('AUTH', { password })
     if not ret then
       return nil, 'Cannot queue command'
@@ -28,8 +37,8 @@ local function connect_redis(server, password, db)
   return conn, nil
 end
 
-local function send_digests(digests, redis_host, redis_password, redis_db)
-  local conn, err = connect_redis(redis_host, redis_password, redis_db)
+local function send_digests(digests, redis_host, redis_username, redis_password, redis_db)
+  local conn, err = connect_redis(redis_host, redis_username, redis_password, redis_db)
   if err then
     print(err)
     return false
@@ -62,8 +71,8 @@ local function send_digests(digests, redis_host, redis_password, redis_db)
   return true
 end
 
-local function send_shingles(shingles, redis_host, redis_password, redis_db)
-  local conn, err = connect_redis(redis_host, redis_password, redis_db)
+local function send_shingles(shingles, redis_host, redis_username, redis_password, redis_db)
+  local conn, err = connect_redis(redis_host, redis_username, redis_password, redis_db)
   if err then
     print("Redis error: " .. err)
     return false
@@ -95,8 +104,8 @@ local function send_shingles(shingles, redis_host, redis_password, redis_db)
   return true
 end
 
-local function update_counters(total, redis_host, redis_password, redis_db)
-  local conn, err = connect_redis(redis_host, redis_password, redis_db)
+local function update_counters(total, redis_host, redis_username, redis_password, redis_db)
+  local conn, err = connect_redis(redis_host, redis_username, redis_password, redis_db)
   if err then
     print(err)
     return false
@@ -135,6 +144,7 @@ return function(_, res)
   local total_digests = 0
   local total_shingles = 0
   local lim_batch = 1000 -- Update each 1000 entries
+  local redis_username = res['redis_username']
   local redis_password = res['redis_password']
   local redis_db = nil
 
@@ -162,14 +172,14 @@ return function(_, res)
       end
     end
     if num_batch_digests >= lim_batch then
-      if not send_digests(digests, res['redis_host'], redis_password, redis_db) then
+      if not send_digests(digests, res['redis_host'], redis_username, redis_password, redis_db) then
         return
       end
       num_batch_digests = 0
       digests = {}
     end
     if num_batch_shingles >= lim_batch then
-      if not send_shingles(shingles, res['redis_host'], redis_password, redis_db) then
+      if not send_shingles(shingles, res['redis_host'], redis_username, redis_password, redis_db) then
         return
       end
       num_batch_shingles = 0
@@ -177,12 +187,12 @@ return function(_, res)
     end
   end
   if digests[1] then
-    if not send_digests(digests, res['redis_host'], redis_password, redis_db) then
+    if not send_digests(digests, res['redis_host'], redis_username, redis_password, redis_db) then
       return
     end
   end
   if shingles[1] then
-    if not send_shingles(shingles, res['redis_host'], redis_password, redis_db) then
+    if not send_shingles(shingles, res['redis_host'], redis_username, redis_password, redis_db) then
       return
     end
   end
@@ -191,7 +201,7 @@ return function(_, res)
       'Migrated %d digests and %d shingles',
       total_digests, total_shingles
   )
-  if not update_counters(total_digests, res['redis_host'], redis_password, redis_db) then
+  if not update_counters(total_digests, res['redis_host'], redis_username, redis_password, redis_db) then
     message = message .. ' but failed to update counters'
   end
   print(message)
diff --git a/src/libserver/fuzzy_backend/fuzzy_backend_redis.c b/src/libserver/fuzzy_backend/fuzzy_backend_redis.c
index 9b5b3bc02..7ab7ca63e 100644
--- a/src/libserver/fuzzy_backend/fuzzy_backend_redis.c
+++ b/src/libserver/fuzzy_backend/fuzzy_backend_redis.c
@@ -52,6 +52,7 @@ INIT_LOG_MODULE(fuzzy_redis)
 struct rspamd_fuzzy_backend_redis {
 	lua_State *L;
 	const gchar *redis_object;
+	const gchar *username;
 	const gchar *password;
 	const gchar *dbname;
 	gchar *id;
@@ -256,6 +257,14 @@ rspamd_fuzzy_backend_init_redis(struct rspamd_fuzzy_backend *bk,
 	}
 	lua_pop(L, 1);
 
+	lua_pushstring(L, "username");
+	lua_gettable(L, -2);
+	if (lua_type(L, -1) == LUA_TSTRING) {
+		backend->username = rspamd_mempool_strdup(cfg->cfg_pool,
+												  lua_tostring(L, -1));
+	}
+	lua_pop(L, 1);
+
 	lua_pushstring(L, "password");
 	lua_gettable(L, -2);
 	if (lua_type(L, -1) == LUA_TSTRING) {
@@ -277,6 +286,11 @@ rspamd_fuzzy_backend_init_redis(struct rspamd_fuzzy_backend *bk,
 									 strlen(backend->dbname));
 	}
 
+	if (backend->username) {
+		rspamd_cryptobox_hash_update(&st, backend->username,
+									 strlen(backend->username));
+	}
+
 	if (backend->password) {
 		rspamd_cryptobox_hash_update(&st, backend->password,
 									 strlen(backend->password));
@@ -681,7 +695,8 @@ void rspamd_fuzzy_backend_check_redis(struct rspamd_fuzzy_backend *bk,
 	addr = rspamd_upstream_addr_next(up);
 	g_assert(addr != NULL);
 	session->ctx = rspamd_redis_pool_connect(backend->pool,
-											 backend->dbname, backend->password,
+											 backend->dbname,
+											 backend->username, backend->password,
 											 rspamd_inet_address_to_string(addr),
 											 rspamd_inet_address_get_port(addr));
 
@@ -819,7 +834,8 @@ void rspamd_fuzzy_backend_count_redis(struct rspamd_fuzzy_backend *bk,
 	addr = rspamd_upstream_addr_next(up);
 	g_assert(addr != NULL);
 	session->ctx = rspamd_redis_pool_connect(backend->pool,
-											 backend->dbname, backend->password,
+											 backend->dbname,
+											 backend->username, backend->password,
 											 rspamd_inet_address_to_string(addr),
 											 rspamd_inet_address_get_port(addr));
 
@@ -956,7 +972,8 @@ void rspamd_fuzzy_backend_version_redis(struct rspamd_fuzzy_backend *bk,
 	addr = rspamd_upstream_addr_next(up);
 	g_assert(addr != NULL);
 	session->ctx = rspamd_redis_pool_connect(backend->pool,
-											 backend->dbname, backend->password,
+											 backend->dbname,
+											 backend->username, backend->password,
 											 rspamd_inet_address_to_string(addr),
 											 rspamd_inet_address_get_port(addr));
 
@@ -1527,7 +1544,8 @@ void rspamd_fuzzy_backend_update_redis(struct rspamd_fuzzy_backend *bk,
 	addr = rspamd_upstream_addr_next(up);
 	g_assert(addr != NULL);
 	session->ctx = rspamd_redis_pool_connect(backend->pool,
-											 backend->dbname, backend->password,
+											 backend->dbname,
+											 backend->username, backend->password,
 											 rspamd_inet_address_to_string(addr),
 											 rspamd_inet_address_get_port(addr));
 
diff --git a/src/libserver/redis_pool.cxx b/src/libserver/redis_pool.cxx
index 86ff2adb2..34c09d055 100644
--- a/src/libserver/redis_pool.cxx
+++ b/src/libserver/redis_pool.cxx
@@ -62,6 +62,7 @@ struct redis_pool_connection {
 	explicit redis_pool_connection(redis_pool *_pool,
 								   redis_pool_elt *_elt,
 								   const std::string &db,
+								   const std::string &username,
 								   const std::string &password,
 								   struct redisAsyncContext *_ctx);
 
@@ -87,6 +88,7 @@ class redis_pool_elt {
 	std::list<redis_pool_connection_ptr> terminating;
 	std::string ip;
 	std::string db;
+	std::string username;
 	std::string password;
 	int port;
 	redis_pool_key_t key;
@@ -100,16 +102,20 @@ public:
 	redis_pool_elt(redis_pool_elt &&other) = default;
 
 	explicit redis_pool_elt(redis_pool *_pool,
-							const gchar *_db, const gchar *_password,
+							const gchar *_db, const gchar *_username,
+							const gchar *_password,
 							const char *_ip, int _port)
 		: pool(_pool), ip(_ip), port(_port),
-		  key(redis_pool_elt::make_key(_db, _password, _ip, _port))
+		  key(redis_pool_elt::make_key(_db, _username, _password, _ip, _port))
 	{
 		is_unix = ip[0] == '.' || ip[0] == '/';
 
 		if (_db) {
 			db = _db;
 		}
+		if( _username ) {
+			username = _username;
+		}
 		if (_password) {
 			password = _password;
 		}
@@ -144,8 +150,8 @@ public:
 		conn->elt_pos = std::prev(std::end(terminating));
 	}
 
-	inline static auto make_key(const gchar *db, const gchar *password,
-								const char *ip, int port) -> redis_pool_key_t
+	inline static auto make_key(const gchar *db, const gchar *username,
+								const gchar *password, const char *ip, int port) -> redis_pool_key_t
 	{
 		rspamd_cryptobox_fast_hash_state_t st;
 
@@ -154,6 +160,9 @@ public:
 		if (db) {
 			rspamd_cryptobox_fast_hash_update(&st, db, strlen(db));
 		}
+		if (username) {
+			rspamd_cryptobox_fast_hash_update(&st, username, strlen(username));
+		}
 		if (password) {
 			rspamd_cryptobox_fast_hash_update(&st, password, strlen(password));
 		}
@@ -232,8 +241,8 @@ public:
 		cfg = _cfg;
 	}
 
-	auto new_connection(const gchar *db, const gchar *password,
-						const char *ip, int port) -> redisAsyncContext *;
+	auto new_connection(const gchar *db, const gchar *username,
+						const gchar *password, const char *ip, int port) -> redisAsyncContext *;
 
 	auto release_connection(redisAsyncContext *ctx,
 							enum rspamd_redis_pool_release_type how) -> void;
@@ -398,6 +407,7 @@ auto redis_pool_connection::schedule_timeout() -> void
 redis_pool_connection::redis_pool_connection(redis_pool *_pool,
 											 redis_pool_elt *_elt,
 											 const std::string &db,
+											 const std::string &username,
 											 const std::string &password,
 											 struct redisAsyncContext *_ctx)
 	: ctx(_ctx), elt(_elt), pool(_pool)
@@ -413,7 +423,18 @@ redis_pool_connection::redis_pool_connection(redis_pool *_pool,
 	redisLibevAttach(pool->event_loop, ctx);
 	redisAsyncSetDisconnectCallback(ctx, redis_pool_connection::redis_on_disconnect);
 
-	if (!password.empty()) {
+	if (!username.empty()) {
+		if (!password.empty()) {
+			redisAsyncCommand(ctx, nullptr, nullptr,
+						  "AUTH %s %s", username.c_str(), password.c_str());
+		}
+		else {
+			msg_err("Redis requires a password when username is supplied");
+			redisAsyncFree(ctx);
+			return nullptr;
+		}
+	}
+	else if (!password.empty()) {
 		redisAsyncCommand(ctx, nullptr, nullptr,
 						  "AUTH %s", password.c_str());
 	}
@@ -464,7 +485,7 @@ auto redis_pool_elt::new_connection() -> redisAsyncContext *
 			auto *nctx = redis_async_new();
 			if (nctx) {
 				active.emplace_front(std::make_unique<redis_pool_connection>(pool, this,
-																			 db.c_str(), password.c_str(), nctx));
+																			 db.c_str(), username.c_str(), password.c_str(), nctx));
 				active.front()->elt_pos = active.begin();
 			}
 
@@ -475,7 +496,7 @@ auto redis_pool_elt::new_connection() -> redisAsyncContext *
 		auto *nctx = redis_async_new();
 		if (nctx) {
 			active.emplace_front(std::make_unique<redis_pool_connection>(pool, this,
-																		 db.c_str(), password.c_str(), nctx));
+																		 db.c_str(), username.c_str(), password.c_str(), nctx));
 			active.front()->elt_pos = active.begin();
 		}
 
@@ -485,12 +506,12 @@ auto redis_pool_elt::new_connection() -> redisAsyncContext *
 	RSPAMD_UNREACHABLE;
 }
 
-auto redis_pool::new_connection(const gchar *db, const gchar *password,
-								const char *ip, int port) -> redisAsyncContext *
+auto redis_pool::new_connection(const gchar *db, const gchar *username,
+								const gchar *password, const char *ip, int port) -> redisAsyncContext *
 {
 
 	if (!wanna_die) {
-		auto key = redis_pool_elt::make_key(db, password, ip, port);
+		auto key = redis_pool_elt::make_key(db, username, password, ip, port);
 		auto found_elt = elts_by_key.find(key);
 
 		if (found_elt != elts_by_key.end()) {
@@ -501,7 +522,7 @@ auto redis_pool::new_connection(const gchar *db, const gchar *password,
 		else {
 			/* Need to create a pool */
 			auto nelt = elts_by_key.try_emplace(key,
-												this, db, password, ip, port);
+												this, db, username, password, ip, port);
 
 			return nelt.first->second.new_connection();
 		}
@@ -583,13 +604,13 @@ void rspamd_redis_pool_config(void *p,
 
 struct redisAsyncContext *
 rspamd_redis_pool_connect(void *p,
-						  const gchar *db, const gchar *password,
-						  const char *ip, int port)
+						  const gchar *db, const gchar *username,
+						  const gchar *password, const char *ip, int port)
 {
 	g_assert(p != NULL);
 	auto *pool = reinterpret_cast<class rspamd::redis_pool *>(p);
 
-	return pool->new_connection(db, password, ip, port);
+	return pool->new_connection(db, username, password, ip, port);
 }
 
 
diff --git a/src/libserver/redis_pool.h b/src/libserver/redis_pool.h
index 339bf5f53..ecdaa0f85 100644
--- a/src/libserver/redis_pool.h
+++ b/src/libserver/redis_pool.h
@@ -45,6 +45,7 @@ void rspamd_redis_pool_config(void *pool,
  * Create or reuse the specific redis connection
  * @param pool
  * @param db
+ * @param username
  * @param password
  * @param ip
  * @param port
@@ -52,7 +53,7 @@ void rspamd_redis_pool_config(void *pool,
  */
 struct redisAsyncContext *rspamd_redis_pool_connect(
 	void *pool,
-	const gchar *db, const gchar *password,
+	const gchar *db, const gchar *username, const gchar *password,
 	const char *ip, int port);
 
 enum rspamd_redis_pool_release_type {
diff --git a/src/libstat/backends/redis_backend.c b/src/libstat/backends/redis_backend.c
index a0d11bb0d..72ffd6c44 100644
--- a/src/libstat/backends/redis_backend.c
+++ b/src/libstat/backends/redis_backend.c
@@ -45,6 +45,7 @@ struct redis_stat_ctx {
 	gint conf_ref;
 	struct rspamd_stat_async_elt *stat_elt;
 	const gchar *redis_object;
+	const gchar *username;
 	const gchar *password;
 	const gchar *dbname;
 	gdouble timeout;
@@ -363,7 +364,17 @@ gsize rspamd_redis_expand_object(const gchar *pattern,
 static void
 rspamd_redis_maybe_auth(struct redis_stat_ctx *ctx, redisAsyncContext *redis)
 {
-	if (ctx->password) {
+	if (ctx->username) {
+		if (ctx->password) {
+			redisAsyncCommand(redis, NULL, NULL, "AUTH %s %s", ctx->username, ctx->password);
+		}
+		else {
+			msg_err("Redis requires a password when username is supplied");
+			redisAsyncFree(ctx);
+			return NULL;
+		}
+	}
+	else if (ctx->password) {
 		redisAsyncCommand(redis, NULL, NULL, "AUTH %s", ctx->password);
 	}
 	if (ctx->dbname) {
@@ -1603,6 +1614,14 @@ rspamd_redis_init(struct rspamd_stat_ctx *ctx,
 	}
 	lua_pop(L, 1);
 
+	lua_pushstring(L, "username");
+	lua_gettable(L, -2);
+	if (lua_type(L, -1) == LUA_TSTRING) {
+		backend->username = rspamd_mempool_strdup(cfg->cfg_pool,
+												  lua_tostring(L, -1));
+	}
+	lua_pop(L, 1);
+
 	lua_pushstring(L, "password");
 	lua_gettable(L, -2);
 	if (lua_type(L, -1) == LUA_TSTRING) {
diff --git a/src/libstat/learn_cache/redis_cache.c b/src/libstat/learn_cache/redis_cache.c
index 3026009bc..0bbae8560 100644
--- a/src/libstat/learn_cache/redis_cache.c
+++ b/src/libstat/learn_cache/redis_cache.c
@@ -35,6 +35,7 @@ static const gchar *M = "redis learn cache";
 struct rspamd_redis_cache_ctx {
 	lua_State *L;
 	struct rspamd_statfile_config *stcf;
+	const gchar *username;
 	const gchar *password;
 	const gchar *dbname;
 	const gchar *redis_object;
@@ -77,7 +78,17 @@ static void
 rspamd_redis_cache_maybe_auth(struct rspamd_redis_cache_ctx *ctx,
 							  redisAsyncContext *redis)
 {
-	if (ctx->password) {
+	if (ctx->username) {
+		if (ctx->password) {
+			redisAsyncCommand(redis, NULL, NULL, "AUTH %s %s", ctx->username, ctx->password);
+		}
+		else {
+			msg_err("Redis requires a password when username is supplied");
+			redisAsyncFree(ctx);
+			return NULL;
+		}
+	}
+	else if (ctx->password) {
 		redisAsyncCommand(redis, NULL, NULL, "AUTH %s", ctx->password);
 	}
 	if (ctx->dbname) {
@@ -328,6 +339,14 @@ rspamd_stat_cache_redis_init(struct rspamd_stat_ctx *ctx,
 	}
 	lua_pop(L, 1);
 
+	lua_pushstring(L, "username");
+	lua_gettable(L, -2);
+	if (lua_type(L, -1) == LUA_TSTRING) {
+		cache_ctx->username = rspamd_mempool_strdup(cfg->cfg_pool,
+													lua_tostring(L, -1));
+	}
+	lua_pop(L, 1);
+
 	lua_pushstring(L, "password");
 	lua_gettable(L, -2);
 	if (lua_type(L, -1) == LUA_TSTRING) {
diff --git a/src/lua/lua_redis.c b/src/lua/lua_redis.c
index 2ac5a47b7..1ad3b3d17 100644
--- a/src/lua/lua_redis.c
+++ b/src/lua/lua_redis.c
@@ -110,9 +110,9 @@ struct lua_redis_userdata {
 };
 
 #define msg_debug_lua_redis(...) rspamd_conditional_debug_fast(NULL, NULL,                                        \
-															   rspamd_lua_redis_log_id, "lua_redis", ud->log_tag, \
-															   G_STRFUNC,                                         \
-															   __VA_ARGS__)
+																 rspamd_lua_redis_log_id, "lua_redis", ud->log_tag, \
+																 G_STRFUNC,                                         \
+																 __VA_ARGS__)
 INIT_LOG_MODULE(lua_redis)
 
 #define LUA_REDIS_SPECIFIC_REPLIED (1 << 0)
@@ -875,7 +875,7 @@ rspamd_lua_redis_prepare_connection(lua_State *L, gint *pcbref, gboolean is_asyn
 	struct rspamd_lua_ip *addr = NULL;
 	struct rspamd_task *task = NULL;
 	const gchar *host = NULL;
-	const gchar *password = NULL, *dbname = NULL, *log_tag = NULL;
+	const gchar *username = NULL, *password = NULL, *dbname = NULL, *log_tag = NULL;
 	gint cbref = -1;
 	struct rspamd_config *cfg = NULL;
 	struct rspamd_async_session *session = NULL;
@@ -970,6 +970,13 @@ rspamd_lua_redis_prepare_connection(lua_State *L, gint *pcbref, gboolean is_asyn
 		}
 		lua_pop(L, 1);
 
+		lua_pushstring(L, "username");
+		lua_gettable(L, -2);
+		if (lua_type(L, -1) == LUA_TSTRING) {
+			username = lua_tostring(L, -1);
+		}
+		lua_pop(L, 1);
+
 		lua_pushstring(L, "password");
 		lua_gettable(L, -2);
 		if (lua_type(L, -1) == LUA_TSTRING) {
@@ -1053,7 +1060,7 @@ rspamd_lua_redis_prepare_connection(lua_State *L, gint *pcbref, gboolean is_asyn
 	if (ret) {
 		ud->terminated = 0;
 		ud->ctx = rspamd_redis_pool_connect(ud->pool,
-											dbname, password,
+											dbname, username, password,
 											rspamd_inet_address_to_string(addr->addr),
 											rspamd_inet_address_get_port(addr->addr));
 
diff --git a/src/plugins/lua/bimi.lua b/src/plugins/lua/bimi.lua
index 914484543..5053cc941 100644
--- a/src/plugins/lua/bimi.lua
+++ b/src/plugins/lua/bimi.lua
@@ -263,7 +263,14 @@ local function check_bimi_vmc(task, domain, record)
         if redis_params.db then
           db = string.format('/%s', redis_params.db)
         end
-        if redis_params.password then
+        if redis_params.username then
+          if redis_params.password then
+            password = string.format( '%s:%s@', redis_params.username, redis_params.password)
+          else
+            rspamd_logger.errx(task, "Redis requires a password when username is supplied")
*** OUTPUT TRUNCATED, 87 LINES SKIPPED ***


More information about the Commits mailing list