#disabled_modules = ["ratelimit"]; # List of modules that should not use redis from this section | #disabled_modules = ["ratelimit"]; # List of modules that should not use redis from this section | ||||
#timeout = 1s; | #timeout = 1s; | ||||
#db = "0"; | #db = "0"; | ||||
#username = "some_username"; | |||||
#password = "some_password"; | #password = "some_password"; | ||||
.include(try=true,priority=5) "${DBDIR}/dynamic/redis.conf" | .include(try=true,priority=5) "${DBDIR}/dynamic/redis.conf" | ||||
.include(try=true,priority=1,duplicate=merge) "$LOCAL_CONFDIR/local.d/redis.conf" | .include(try=true,priority=1,duplicate=merge) "$LOCAL_CONFDIR/local.d/redis.conf" |
database = ts.string:is_optional():describe("Database number"), | database = ts.string:is_optional():describe("Database number"), | ||||
dbname = ts.string:is_optional():describe("Database number"), | dbname = ts.string:is_optional():describe("Database number"), | ||||
prefix = ts.string:is_optional():describe("Key prefix"), | prefix = ts.string:is_optional():describe("Key prefix"), | ||||
username = ts.string:is_optional():describe("Username"), | |||||
password = ts.string:is_optional():describe("Password"), | password = ts.string:is_optional():describe("Password"), | ||||
expand_keys = ts.boolean:is_optional():describe("Expand keys"), | expand_keys = ts.boolean:is_optional():describe("Expand keys"), | ||||
sentinels = (ts.string + ts.array_of(ts.string)):is_optional():describe("Sentinel servers"), | 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_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_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_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"), | sentinel_password = ts.string:is_optional():describe("Sentinel password"), | ||||
} | } | ||||
local ret = rspamd_redis.make_request { | local ret = rspamd_redis.make_request { | ||||
host = addr:get_addr(), | host = addr:get_addr(), | ||||
timeout = params.timeout, | timeout = params.timeout, | ||||
username = params.sentinel_username, | |||||
password = params.sentinel_password, | password = params.sentinel_password, | ||||
config = rspamd_config, | config = rspamd_config, | ||||
ev_base = ev_base, | ev_base = ev_base, | ||||
timeout = params.timeout, | timeout = params.timeout, | ||||
config = rspamd_config, | config = rspamd_config, | ||||
ev_base = ev_base, | ev_base = ev_base, | ||||
username = params.sentinel_username, | |||||
password = params.sentinel_password, | password = params.sentinel_password, | ||||
cmd = 'SENTINEL', | cmd = 'SENTINEL', | ||||
args = { 'masters' }, | args = { 'masters' }, | ||||
redis_params['db'] = tostring(options['database']) | redis_params['db'] = tostring(options['database']) | ||||
end | end | ||||
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 | if options['password'] and not redis_params['password'] then | ||||
redis_params['password'] = options['password'] | redis_params['password'] = options['password'] | ||||
end | end | ||||
end | end | ||||
end | end | ||||
if redis_params['username'] then | |||||
options['username'] = redis_params['username'] | |||||
end | |||||
if redis_params['password'] then | if redis_params['password'] then | ||||
options['password'] = redis_params['password'] | options['password'] = redis_params['password'] | ||||
end | end | ||||
end | end | ||||
end | end | ||||
if redis_params['username'] then | |||||
options['username'] = redis_params['username'] | |||||
end | |||||
if redis_params['password'] then | if redis_params['password'] then | ||||
options['password'] = redis_params['password'] | options['password'] = redis_params['password'] | ||||
end | end | ||||
upstream = s | upstream = s | ||||
} | } | ||||
if script.redis_params['username'] then | |||||
cur_opts['username'] = script.redis_params['username'] | |||||
end | |||||
if script.redis_params['password'] then | if script.redis_params['password'] then | ||||
cur_opts['password'] = script.redis_params['password'] | cur_opts['password'] = script.redis_params['password'] | ||||
end | end | ||||
if conn then | if conn then | ||||
local need_exec = false | 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'] }) | conn:add_cmd('AUTH', { redis_params['password'] }) | ||||
need_exec = true | need_exec = true | ||||
end | end | ||||
opts.args = req | opts.args = req | ||||
end | end | ||||
if redis_params.username then | |||||
opts.username = redis_params.username | |||||
end | |||||
if redis_params.password then | if redis_params.password then | ||||
opts.password = redis_params.password | opts.password = redis_params.password | ||||
end | end | ||||
opts.host = addr:get_addr() | opts.host = addr:get_addr() | ||||
opts.timeout = redis_params.timeout | opts.timeout = redis_params.timeout | ||||
if redis_params.username then | |||||
opts.username = redis_params.username | |||||
end | |||||
if redis_params.password then | if redis_params.password then | ||||
opts.password = redis_params.password | opts.password = redis_params.password | ||||
end | end |
redis_params['write_servers'] = ws | redis_params['write_servers'] = ws | ||||
end | 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) | local passwd = readline_default("Enter Redis password:", nil) | ||||
if passwd then | if passwd then |
return nil, 'Cannot connect: ' .. err | return nil, 'Cannot connect: ' .. err | ||||
end | 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 }) | ret = conn:add_cmd('AUTH', { password }) | ||||
if not ret then | if not ret then | ||||
return nil, 'Cannot queue command' | return nil, 'Cannot queue command' | ||||
return conn, nil | return conn, nil | ||||
end | 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 | if err then | ||||
print(err) | print(err) | ||||
return false | return false | ||||
return true | return true | ||||
end | 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 | if err then | ||||
print("Redis error: " .. err) | print("Redis error: " .. err) | ||||
return false | return false | ||||
return true | return true | ||||
end | 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 | if err then | ||||
print(err) | print(err) | ||||
return false | return false | ||||
local total_digests = 0 | local total_digests = 0 | ||||
local total_shingles = 0 | local total_shingles = 0 | ||||
local lim_batch = 1000 -- Update each 1000 entries | local lim_batch = 1000 -- Update each 1000 entries | ||||
local redis_username = res['redis_username'] | |||||
local redis_password = res['redis_password'] | local redis_password = res['redis_password'] | ||||
local redis_db = nil | local redis_db = nil | ||||
end | end | ||||
end | end | ||||
if num_batch_digests >= lim_batch then | 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 | return | ||||
end | end | ||||
num_batch_digests = 0 | num_batch_digests = 0 | ||||
digests = {} | digests = {} | ||||
end | end | ||||
if num_batch_shingles >= lim_batch then | 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 | return | ||||
end | end | ||||
num_batch_shingles = 0 | num_batch_shingles = 0 | ||||
end | end | ||||
end | end | ||||
if digests[1] then | 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 | return | ||||
end | end | ||||
end | end | ||||
if shingles[1] then | 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 | return | ||||
end | end | ||||
end | end | ||||
'Migrated %d digests and %d shingles', | 'Migrated %d digests and %d shingles', | ||||
total_digests, total_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' | message = message .. ' but failed to update counters' | ||||
end | end | ||||
print(message) | print(message) |
struct rspamd_fuzzy_backend_redis { | struct rspamd_fuzzy_backend_redis { | ||||
lua_State *L; | lua_State *L; | ||||
const gchar *redis_object; | const gchar *redis_object; | ||||
const gchar *username; | |||||
const gchar *password; | const gchar *password; | ||||
const gchar *dbname; | const gchar *dbname; | ||||
gchar *id; | gchar *id; | ||||
} | } | ||||
lua_pop(L, 1); | 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_pushstring(L, "password"); | ||||
lua_gettable(L, -2); | lua_gettable(L, -2); | ||||
if (lua_type(L, -1) == LUA_TSTRING) { | if (lua_type(L, -1) == LUA_TSTRING) { | ||||
strlen(backend->dbname)); | strlen(backend->dbname)); | ||||
} | } | ||||
if (backend->username) { | |||||
rspamd_cryptobox_hash_update(&st, backend->username, | |||||
strlen(backend->username)); | |||||
} | |||||
if (backend->password) { | if (backend->password) { | ||||
rspamd_cryptobox_hash_update(&st, backend->password, | rspamd_cryptobox_hash_update(&st, backend->password, | ||||
strlen(backend->password)); | strlen(backend->password)); | ||||
addr = rspamd_upstream_addr_next(up); | addr = rspamd_upstream_addr_next(up); | ||||
g_assert(addr != NULL); | g_assert(addr != NULL); | ||||
session->ctx = rspamd_redis_pool_connect(backend->pool, | 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_to_string(addr), | ||||
rspamd_inet_address_get_port(addr)); | rspamd_inet_address_get_port(addr)); | ||||
addr = rspamd_upstream_addr_next(up); | addr = rspamd_upstream_addr_next(up); | ||||
g_assert(addr != NULL); | g_assert(addr != NULL); | ||||
session->ctx = rspamd_redis_pool_connect(backend->pool, | 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_to_string(addr), | ||||
rspamd_inet_address_get_port(addr)); | rspamd_inet_address_get_port(addr)); | ||||
addr = rspamd_upstream_addr_next(up); | addr = rspamd_upstream_addr_next(up); | ||||
g_assert(addr != NULL); | g_assert(addr != NULL); | ||||
session->ctx = rspamd_redis_pool_connect(backend->pool, | 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_to_string(addr), | ||||
rspamd_inet_address_get_port(addr)); | rspamd_inet_address_get_port(addr)); | ||||
addr = rspamd_upstream_addr_next(up); | addr = rspamd_upstream_addr_next(up); | ||||
g_assert(addr != NULL); | g_assert(addr != NULL); | ||||
session->ctx = rspamd_redis_pool_connect(backend->pool, | 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_to_string(addr), | ||||
rspamd_inet_address_get_port(addr)); | rspamd_inet_address_get_port(addr)); | ||||
explicit redis_pool_connection(redis_pool *_pool, | explicit redis_pool_connection(redis_pool *_pool, | ||||
redis_pool_elt *_elt, | redis_pool_elt *_elt, | ||||
const std::string &db, | const std::string &db, | ||||
const std::string &username, | |||||
const std::string &password, | const std::string &password, | ||||
struct redisAsyncContext *_ctx); | struct redisAsyncContext *_ctx); | ||||
std::list<redis_pool_connection_ptr> terminating; | std::list<redis_pool_connection_ptr> terminating; | ||||
std::string ip; | std::string ip; | ||||
std::string db; | std::string db; | ||||
std::string username; | |||||
std::string password; | std::string password; | ||||
int port; | int port; | ||||
redis_pool_key_t key; | redis_pool_key_t key; | ||||
redis_pool_elt(redis_pool_elt &&other) = default; | redis_pool_elt(redis_pool_elt &&other) = default; | ||||
explicit redis_pool_elt(redis_pool *_pool, | 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) | const char *_ip, int _port) | ||||
: pool(_pool), ip(_ip), port(_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] == '/'; | is_unix = ip[0] == '.' || ip[0] == '/'; | ||||
if (_db) { | if (_db) { | ||||
db = _db; | db = _db; | ||||
} | } | ||||
if( _username ) { | |||||
username = _username; | |||||
} | |||||
if (_password) { | if (_password) { | ||||
password = _password; | password = _password; | ||||
} | } | ||||
conn->elt_pos = std::prev(std::end(terminating)); | 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; | rspamd_cryptobox_fast_hash_state_t st; | ||||
if (db) { | if (db) { | ||||
rspamd_cryptobox_fast_hash_update(&st, db, strlen(db)); | rspamd_cryptobox_fast_hash_update(&st, db, strlen(db)); | ||||
} | } | ||||
if (username) { | |||||
rspamd_cryptobox_fast_hash_update(&st, username, strlen(username)); | |||||
} | |||||
if (password) { | if (password) { | ||||
rspamd_cryptobox_fast_hash_update(&st, password, strlen(password)); | rspamd_cryptobox_fast_hash_update(&st, password, strlen(password)); | ||||
} | } | ||||
cfg = _cfg; | 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, | auto release_connection(redisAsyncContext *ctx, | ||||
enum rspamd_redis_pool_release_type how) -> void; | enum rspamd_redis_pool_release_type how) -> void; | ||||
redis_pool_connection::redis_pool_connection(redis_pool *_pool, | redis_pool_connection::redis_pool_connection(redis_pool *_pool, | ||||
redis_pool_elt *_elt, | redis_pool_elt *_elt, | ||||
const std::string &db, | const std::string &db, | ||||
const std::string &username, | |||||
const std::string &password, | const std::string &password, | ||||
struct redisAsyncContext *_ctx) | struct redisAsyncContext *_ctx) | ||||
: ctx(_ctx), elt(_elt), pool(_pool) | : ctx(_ctx), elt(_elt), pool(_pool) | ||||
redisLibevAttach(pool->event_loop, ctx); | redisLibevAttach(pool->event_loop, ctx); | ||||
redisAsyncSetDisconnectCallback(ctx, redis_pool_connection::redis_on_disconnect); | 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, | redisAsyncCommand(ctx, nullptr, nullptr, | ||||
"AUTH %s", password.c_str()); | "AUTH %s", password.c_str()); | ||||
} | } | ||||
auto *nctx = redis_async_new(); | auto *nctx = redis_async_new(); | ||||
if (nctx) { | if (nctx) { | ||||
active.emplace_front(std::make_unique<redis_pool_connection>(pool, this, | 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(); | active.front()->elt_pos = active.begin(); | ||||
} | } | ||||
auto *nctx = redis_async_new(); | auto *nctx = redis_async_new(); | ||||
if (nctx) { | if (nctx) { | ||||
active.emplace_front(std::make_unique<redis_pool_connection>(pool, this, | 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(); | active.front()->elt_pos = active.begin(); | ||||
} | } | ||||
RSPAMD_UNREACHABLE; | 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) { | 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); | auto found_elt = elts_by_key.find(key); | ||||
if (found_elt != elts_by_key.end()) { | if (found_elt != elts_by_key.end()) { | ||||
else { | else { | ||||
/* Need to create a pool */ | /* Need to create a pool */ | ||||
auto nelt = elts_by_key.try_emplace(key, | 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(); | return nelt.first->second.new_connection(); | ||||
} | } | ||||
struct redisAsyncContext * | struct redisAsyncContext * | ||||
rspamd_redis_pool_connect(void *p, | 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); | g_assert(p != NULL); | ||||
auto *pool = reinterpret_cast<class rspamd::redis_pool *>(p); | 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); | |||||
} | } | ||||
* Create or reuse the specific redis connection | * Create or reuse the specific redis connection | ||||
* @param pool | * @param pool | ||||
* @param db | * @param db | ||||
* @param username | |||||
* @param password | * @param password | ||||
* @param ip | * @param ip | ||||
* @param port | * @param port | ||||
*/ | */ | ||||
struct redisAsyncContext *rspamd_redis_pool_connect( | struct redisAsyncContext *rspamd_redis_pool_connect( | ||||
void *pool, | void *pool, | ||||
const gchar *db, const gchar *password, | |||||
const gchar *db, const gchar *username, const gchar *password, | |||||
const char *ip, int port); | const char *ip, int port); | ||||
enum rspamd_redis_pool_release_type { | enum rspamd_redis_pool_release_type { |
gint conf_ref; | gint conf_ref; | ||||
struct rspamd_stat_async_elt *stat_elt; | struct rspamd_stat_async_elt *stat_elt; | ||||
const gchar *redis_object; | const gchar *redis_object; | ||||
const gchar *username; | |||||
const gchar *password; | const gchar *password; | ||||
const gchar *dbname; | const gchar *dbname; | ||||
gdouble timeout; | gdouble timeout; | ||||
static void | static void | ||||
rspamd_redis_maybe_auth(struct redis_stat_ctx *ctx, redisAsyncContext *redis) | 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); | redisAsyncCommand(redis, NULL, NULL, "AUTH %s", ctx->password); | ||||
} | } | ||||
if (ctx->dbname) { | if (ctx->dbname) { | ||||
} | } | ||||
lua_pop(L, 1); | 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_pushstring(L, "password"); | ||||
lua_gettable(L, -2); | lua_gettable(L, -2); | ||||
if (lua_type(L, -1) == LUA_TSTRING) { | if (lua_type(L, -1) == LUA_TSTRING) { |
struct rspamd_redis_cache_ctx { | struct rspamd_redis_cache_ctx { | ||||
lua_State *L; | lua_State *L; | ||||
struct rspamd_statfile_config *stcf; | struct rspamd_statfile_config *stcf; | ||||
const gchar *username; | |||||
const gchar *password; | const gchar *password; | ||||
const gchar *dbname; | const gchar *dbname; | ||||
const gchar *redis_object; | const gchar *redis_object; | ||||
rspamd_redis_cache_maybe_auth(struct rspamd_redis_cache_ctx *ctx, | rspamd_redis_cache_maybe_auth(struct rspamd_redis_cache_ctx *ctx, | ||||
redisAsyncContext *redis) | 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); | redisAsyncCommand(redis, NULL, NULL, "AUTH %s", ctx->password); | ||||
} | } | ||||
if (ctx->dbname) { | if (ctx->dbname) { | ||||
} | } | ||||
lua_pop(L, 1); | 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_pushstring(L, "password"); | ||||
lua_gettable(L, -2); | lua_gettable(L, -2); | ||||
if (lua_type(L, -1) == LUA_TSTRING) { | if (lua_type(L, -1) == LUA_TSTRING) { |
}; | }; | ||||
#define msg_debug_lua_redis(...) rspamd_conditional_debug_fast(NULL, NULL, \ | #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) | INIT_LOG_MODULE(lua_redis) | ||||
#define LUA_REDIS_SPECIFIC_REPLIED (1 << 0) | #define LUA_REDIS_SPECIFIC_REPLIED (1 << 0) | ||||
struct rspamd_lua_ip *addr = NULL; | struct rspamd_lua_ip *addr = NULL; | ||||
struct rspamd_task *task = NULL; | struct rspamd_task *task = NULL; | ||||
const gchar *host = 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; | gint cbref = -1; | ||||
struct rspamd_config *cfg = NULL; | struct rspamd_config *cfg = NULL; | ||||
struct rspamd_async_session *session = NULL; | struct rspamd_async_session *session = NULL; | ||||
} | } | ||||
lua_pop(L, 1); | 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_pushstring(L, "password"); | ||||
lua_gettable(L, -2); | lua_gettable(L, -2); | ||||
if (lua_type(L, -1) == LUA_TSTRING) { | if (lua_type(L, -1) == LUA_TSTRING) { | ||||
if (ret) { | if (ret) { | ||||
ud->terminated = 0; | ud->terminated = 0; | ||||
ud->ctx = rspamd_redis_pool_connect(ud->pool, | ud->ctx = rspamd_redis_pool_connect(ud->pool, | ||||
dbname, password, | |||||
dbname, username, password, | |||||
rspamd_inet_address_to_string(addr->addr), | rspamd_inet_address_to_string(addr->addr), | ||||
rspamd_inet_address_get_port(addr->addr)); | rspamd_inet_address_get_port(addr->addr)); | ||||
if redis_params.db then | if redis_params.db then | ||||
db = string.format('/%s', redis_params.db) | db = string.format('/%s', redis_params.db) | ||||
end | 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") | |||||
return | |||||
end | |||||
elseif redis_params.password then | |||||
password = string.format(':%s@', redis_params.password) | password = string.format(':%s@', redis_params.password) | ||||
end | end | ||||
local redis_server = string.format('redis://%s%s:%s%s', | local redis_server = string.format('redis://%s%s:%s%s', |
static gchar *source_db = NULL; | static gchar *source_db = NULL; | ||||
static gchar *redis_host = NULL; | static gchar *redis_host = NULL; | ||||
static gchar *redis_db = NULL; | static gchar *redis_db = NULL; | ||||
static gchar *redis_username = NULL; | |||||
static gchar *redis_password = NULL; | static gchar *redis_password = NULL; | ||||
static int64_t fuzzy_expiry = 0; | static int64_t fuzzy_expiry = 0; | ||||
"Output redis ip (in format ip:port)", NULL}, | "Output redis ip (in format ip:port)", NULL}, | ||||
{"dbname", 'D', 0, G_OPTION_ARG_STRING, &redis_db, | {"dbname", 'D', 0, G_OPTION_ARG_STRING, &redis_db, | ||||
"Database in redis (should be numeric)", NULL}, | "Database in redis (should be numeric)", NULL}, | ||||
{"username", 'u', 0, G_OPTION_ARG_STRING, &redis_username, | |||||
"Username to connect to redis", NULL}, | |||||
{"password", 'p', 0, G_OPTION_ARG_STRING, &redis_password, | {"password", 'p', 0, G_OPTION_ARG_STRING, &redis_password, | ||||
"Password to connect to redis", NULL}, | "Password to connect to redis", NULL}, | ||||
{NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}}; | {NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}}; | ||||
"-d: input sqlite\n" | "-d: input sqlite\n" | ||||
"-h: output redis ip (in format ip:port)\n" | "-h: output redis ip (in format ip:port)\n" | ||||
"-D: output redis database\n" | "-D: output redis database\n" | ||||
"-u: redis username\n"; | |||||
"-p: redis password\n"; | "-p: redis password\n"; | ||||
} | } | ||||
else { | else { | ||||
ucl_object_insert_key(obj, ucl_object_fromint(fuzzy_expiry), | ucl_object_insert_key(obj, ucl_object_fromint(fuzzy_expiry), | ||||
"expiry", 0, false); | "expiry", 0, false); | ||||
if (redis_username) { | |||||
ucl_object_insert_key(obj, ucl_object_fromstring(redis_username), | |||||
"redis_username", 0, false); | |||||
} | |||||
if (redis_password) { | if (redis_password) { | ||||
ucl_object_insert_key(obj, ucl_object_fromstring(redis_password), | ucl_object_insert_key(obj, ucl_object_fromstring(redis_password), | ||||
"redis_password", 0, false); | "redis_password", 0, false); |
/* Outputs */ | /* Outputs */ | ||||
static gchar *redis_host = NULL; | static gchar *redis_host = NULL; | ||||
static gchar *redis_db = NULL; | static gchar *redis_db = NULL; | ||||
static gchar *redis_username = NULL; | |||||
static gchar *redis_password = NULL; | static gchar *redis_password = NULL; | ||||
static gboolean reset_previous = FALSE; | static gboolean reset_previous = FALSE; | ||||
"Input learn cache", NULL}, | "Input learn cache", NULL}, | ||||
{"redis-host", 'h', 0, G_OPTION_ARG_STRING, &redis_host, | {"redis-host", 'h', 0, G_OPTION_ARG_STRING, &redis_host, | ||||
"Output redis ip (in format ip:port)", NULL}, | "Output redis ip (in format ip:port)", NULL}, | ||||
{"redis-username", 'u', 0, G_OPTION_ARG_STRING, &redis_username, | |||||
"Username to connect to redis", NULL}, | |||||
{"redis-password", 'p', 0, G_OPTION_ARG_STRING, &redis_password, | {"redis-password", 'p', 0, G_OPTION_ARG_STRING, &redis_password, | ||||
"Password to connect to redis", NULL}, | "Password to connect to redis", NULL}, | ||||
{"redis-db", 'd', 0, G_OPTION_ARG_STRING, &redis_db, | {"redis-db", 'd', 0, G_OPTION_ARG_STRING, &redis_db, | ||||
"** Or specify options directly **\n" | "** Or specify options directly **\n" | ||||
"--redis-host: output redis ip (in format ip:port)\n" | "--redis-host: output redis ip (in format ip:port)\n" | ||||
"--redis-db: output redis database\n" | "--redis-db: output redis database\n" | ||||
"--redis-username: redis username\n" | |||||
"--redis-password: redis password\n" | "--redis-password: redis password\n" | ||||
"--cache: sqlite3 file for learn cache\n" | "--cache: sqlite3 file for learn cache\n" | ||||
"--spam-db: sqlite3 input file for spam data\n" | "--spam-db: sqlite3 input file for spam data\n" | ||||
"dbname", 0, false); | "dbname", 0, false); | ||||
} | } | ||||
if (redis_username) { | |||||
ucl_object_insert_key(redis, ucl_object_fromstring(redis_username), | |||||
"username", 0, false); | |||||
} | |||||
if (redis_password) { | if (redis_password) { | ||||
ucl_object_insert_key(redis, ucl_object_fromstring(redis_password), | ucl_object_insert_key(redis, ucl_object_fromstring(redis_password), | ||||
"password", 0, false); | "password", 0, false); |