Skip to content

Commit

Permalink
Merge pull request #4578 from laodc/redis-username-support
Browse files Browse the repository at this point in the history
[Feature] Added support for Redis 6 ACL (username/password)
  • Loading branch information
vstakhov committed Aug 21, 2023
2 parents 1931487 + 6c3d5c7 commit 460de60
Show file tree
Hide file tree
Showing 13 changed files with 206 additions and 44 deletions.
1 change: 1 addition & 0 deletions conf/modules.d/redis.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
37 changes: 36 additions & 1 deletion lualib/lua_redis.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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' },
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.warnx('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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
16 changes: 15 additions & 1 deletion lualib/rspamadm/configwizard.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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 (ACL SETUSER and Redis 6.0+)') then
local username = readline_default("Enter Redis username:", nil)

if username then
changes.l['redis.conf'].username = username
redis_params.username = username
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
Expand Down
36 changes: 23 additions & 13 deletions lualib/rspamadm/fuzzy_convert.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ local sqlite3 = require "rspamd_sqlite3"
local redis = require "rspamd_redis"
local util = require "rspamd_util"

local function connect_redis(server, password, db)
local function connect_redis(server, username, password, db)
local ret
local conn, err = redis.connect_sync({
host = server,
Expand All @@ -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
elseif password then
ret = conn:add_cmd('AUTH', { password })
if not ret then
return nil, 'Cannot queue command'
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -162,27 +172,27 @@ 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
shingles = {}
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
Expand All @@ -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)
Expand Down
26 changes: 22 additions & 4 deletions src/libserver/fuzzy_backend/fuzzy_backend_redis.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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));
Expand Down Expand Up @@ -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));

Expand Down Expand Up @@ -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));

Expand Down Expand Up @@ -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));

Expand Down Expand Up @@ -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));

Expand Down

0 comments on commit 460de60

Please sign in to comment.