From 67c77289afb994178afc1fd7558833097eb2f5b9 Mon Sep 17 00:00:00 2001 From: Joel Centelles Date: Fri, 2 Jun 2023 18:42:16 +0200 Subject: [PATCH] db_redis: Adding TLS support Enhancing security options by enabling TLS connections and password definition. Added 3 new parameters: * opt_tls: For enabling TLS connections. * ca_path: For specifying a folder containing valid certification chains. * password: For providing DB access password. If opt_tls is provided a temporary SSL context is created to pass it to existing cluster or normal redis context. TLS support is automatically enabled/disabled by checking libhiredis_ssl.so existence. --- src/modules/db_redis/Makefile | 24 ++++- src/modules/db_redis/db_redis_mod.c | 13 ++- src/modules/db_redis/doc/db_redis.xml | 5 ++ src/modules/db_redis/doc/db_redis_admin.xml | 59 +++++++++++++ src/modules/db_redis/redis_connection.c | 98 ++++++++++++++++----- src/modules/db_redis/redis_connection.h | 6 ++ 6 files changed, 177 insertions(+), 28 deletions(-) diff --git a/src/modules/db_redis/Makefile b/src/modules/db_redis/Makefile index 179bc6f4068..e3a2abdd09c 100644 --- a/src/modules/db_redis/Makefile +++ b/src/modules/db_redis/Makefile @@ -20,10 +20,19 @@ endif ifeq ($(HIREDIS_BUILDER),) HIREDISDEFS=-I$(LOCALBASE)/include -I$(LOCALBASE)/include/hiredis -I/usr/include/hiredis - HIREDISLIBS=-L$(LOCALBASE)/lib -lhiredis + HIREDISLIBS=-L$(LOCALBASE)/lib -lhiredis_ssl -lhiredis + ifneq ($(shell ls $(LOCALBASE) | grep libhiredis_ssl.so),) + HIREDISDEFS += -DWITH_SSL + HIREDISLIBS += -lhiredis_ssl + endif else HIREDISDEFS = $(shell $(HIREDIS_BUILDER) --cflags) - HIREDISLIBS = $(shell $(HIREDIS_BUILDER) --libs) + HIREDISLIBS = $(shell $(HIREDIS_BUILDER) --libs) -lhiredis_ssl + HIREDISLIBSPATH = $(shell $(HIREDIS_BUILDER) --libs-only-L | cut -c 3-) + ifneq ($(shell ls $(HIREDISLIBSPATH) | grep libhiredis_ssl.so),) + HIREDISDEFS += -DWITH_SSL + HIREDISLIBS += -lhiredis_ssl + endif ifeq (,$(findstring hiredis,$(HIREDISDEFS))) DEFS+=-DWITH_HIREDIS_PATH @@ -31,7 +40,11 @@ endif ifeq ($(HIREDISLIBS),-L -lhiredis) HIREDISDEFS = $(shell $(HIREDIS_BUILDER) --cflags) /opt/local/include - HIREDISLIBS = -L/opt/local/lib -lhiredis + HIREDISLIBS = -L/opt/local/lib -lhiredis -lhiredis_ssl + ifneq ($(shell ls /opt/local/lib | grep libhiredis_ssl.so),) + HIREDISDEFS += -DWITH_SSL + HIREDISLIBS += -lhiredis_ssl + endif endif endif @@ -42,6 +55,11 @@ LIBS=$(HIREDISLIBS) ifneq ($(HIREDIS_CLUSTER_BUILDER),) HIREDISCLUSTERDEFS = $(shell $(HIREDIS_CLUSTER_BUILDER) --cflags) HIREDISCLUSTERLIBS = $(shell $(HIREDIS_CLUSTER_BUILDER) --libs) + HIREDISCLUSTERLIBSPATH = $(shell $(HIREDIS_CLUSTER_BUILDER) --libs-only-L | cut -c 3-) + ifneq ($(shell ls $(HIREDISCLUSTERLIBSPATH) | grep libhiredis_ssl.so),) + HIREDISCLUSTERDEFS += -DWITH_SSL + HIREDISCLUSTERLIBS += -lhiredis_ssl + endif DEFS+=-DWITH_HIREDIS_CLUSTER DEFS+=$(HIREDISCLUSTERDEFS) LIBS+=$(HIREDISCLUSTERLIBS) diff --git a/src/modules/db_redis/db_redis_mod.c b/src/modules/db_redis/db_redis_mod.c index 837b979956f..9d95686b1b7 100644 --- a/src/modules/db_redis/db_redis_mod.c +++ b/src/modules/db_redis/db_redis_mod.c @@ -29,6 +29,12 @@ #include "redis_dbase.h" #include "redis_table.h" +#ifdef WITH_SSL +int db_redis_opt_tls = 0; +char *ca_path = 0; +#endif +char *db_pass = 0; + MODULE_VERSION str redis_keys = str_init(""); @@ -51,7 +57,12 @@ static cmd_export_t cmds[] = { static param_export_t params[] = { {"keys", PARAM_STRING | USE_FUNC_PARAM, (void *)keys_param}, {"schema_path", PARAM_STR, &redis_schema_path}, - {"verbosity", PARAM_INT, &db_redis_verbosity}, {0, 0, 0}}; + {"verbosity", PARAM_INT, &db_redis_verbosity}, +#ifdef WITH_SSL + {"opt_tls", PARAM_INT, &db_redis_opt_tls}, + {"ca_path", PARAM_STRING, &ca_path }, +#endif + {"db_pass", PARAM_STRING, &db_pass},{0, 0, 0}}; struct module_exports exports = { diff --git a/src/modules/db_redis/doc/db_redis.xml b/src/modules/db_redis/doc/db_redis.xml index 99eea0987f6..6837a4b5a68 100644 --- a/src/modules/db_redis/doc/db_redis.xml +++ b/src/modules/db_redis/doc/db_redis.xml @@ -27,6 +27,11 @@ Balashov abalashov@evaristesys.com + + Joel + Centelles Martin + joel_centellesmartin@baxter.com + 2018 diff --git a/src/modules/db_redis/doc/db_redis_admin.xml b/src/modules/db_redis/doc/db_redis_admin.xml index b3b54c08a20..a7e7c3bb3a9 100644 --- a/src/modules/db_redis/doc/db_redis_admin.xml +++ b/src/modules/db_redis/doc/db_redis_admin.xml @@ -213,6 +213,61 @@ modparam("db_redis", "keys", "version=entry:table_name;location=entry:ruid&u ... modparam("db_redis", "verbosity", 0) +... + + + + +
+ <varname>opt_tls</varname> (int) + + Controls TLS usage while connecting to a remote DB. + If set to 1, TLS is used to connect to the DB. + + + Default value: 0. + + + Enabling TLS connection + +... +modparam("db_redis", "opt_tls", 1) +... + + +
+ +
+ <varname>db_pass</varname> (string) + + Sets the password to connect to the DB. + + + Default value: "" (empty). + + + Setting a password + +... +modparam("db_redis", "db_pass", "r3d1sPass") +... + + +
+ +
+ <varname>ac_path</varname> (string) + + Sets the path where Certificates Authorities certs are stored. + + + Default value: "" (empty). + + + Setting CA path + +... +modparam("db_redis", "ca_path", "/etc/ssl/certs") ... @@ -231,6 +286,10 @@ modparam("db_redis", "verbosity", 0) For cluster support you need to set the "db_url" modparam with a comma separated list of cluster hosts: 'redis://host1:port1,host2:port2/'. The database portion is not supported in cluster mode. + + If accessed DB requires TLS connections, you need to enable TLS support setting the "opt_tls" parameter to 1. + In case the DB requires a password, that should be set using the "db_pass" parameter. + Usage diff --git a/src/modules/db_redis/redis_connection.c b/src/modules/db_redis/redis_connection.c index 2afc7e86349..ea8f94fff8d 100644 --- a/src/modules/db_redis/redis_connection.c +++ b/src/modules/db_redis/redis_connection.c @@ -35,6 +35,11 @@ static unsigned int MAX_URL_LENGTH = 1023; #endif extern int db_redis_verbosity; +#ifdef WITH_SSL +extern int db_redis_opt_tls; +extern char *ca_path; +#endif +extern char *db_pass; static void print_query(redis_key_t *query) { @@ -120,12 +125,17 @@ int db_redis_connect(km_redis_con_t *con) #ifndef WITH_HIREDIS_CLUSTER int db; #endif +#ifdef WITH_SSL + redisSSLContext *ssl = NULL; +#endif + char* password = NULL; tv.tv_sec = 1; tv.tv_usec = 0; #ifndef WITH_HIREDIS_CLUSTER db = atoi(con->id->database); #endif + redisSSLContext *ssl = NULL; reply = NULL; if(con->con) { @@ -139,69 +149,109 @@ int db_redis_connect(km_redis_con_t *con) #ifdef WITH_HIREDIS_CLUSTER int status; char hosts[MAX_URL_LENGTH]; - char *host_begin; - char *host_end; - LM_DBG("connecting to redis cluster at %.*s\n", con->id->url.len, + char* host_begin; + char* host_end; + LM_DBG("connecting to redis cluster at %.*s\n", con->id->url.len, con->id->url.s); host_begin = strstr(con->id->url.s, "redis://"); - if(host_begin) { + if (host_begin) { host_begin += 8; } else { LM_ERR("invalid url scheme\n"); goto err; } + +#ifdef WITH_SSL + if (db_redis_opt_tls != 0) { + /* Create SSL context*/ + redisInitOpenSSL(); + ssl = redisCreateSSLContext(NULL, ca_path, NULL, NULL, NULL, NULL); + if (ssl == NULL) { + LM_ERR("Unable to create Redis SSL Context.\n"); + goto err; + } + } +#endif + host_end = strstr(host_begin, "/"); - if(!host_end) { + if (! host_end) { LM_ERR("invalid url: cannot find end of host part\n"); goto err; } - if((host_end - host_begin) > (MAX_URL_LENGTH - 1)) { + if ((host_end - host_begin) > (MAX_URL_LENGTH-1)) { LM_ERR("url too long\n"); goto err; } strncpy(hosts, host_begin, (host_end - host_begin)); - hosts[MAX_URL_LENGTH - 1] = '\0'; + hosts[MAX_URL_LENGTH-1] = '\0'; con->con = redisClusterContextInit(); - if(!con->con) { + if (! con->con) { LM_ERR("no private memory left\n"); goto err; } redisClusterSetOptionAddNodes(con->con, hosts); redisClusterSetOptionConnectTimeout(con->con, tv); +#ifdef WITH_SSL + if (ssl) { + redisClusterSetOptionEnableSSL(con->con, ssl); + } +#endif status = redisClusterConnect2(con->con); - if(status != REDIS_OK) { - LM_ERR("cannot open connection to cluster with hosts: %s, error: %s\n", + if (status != REDIS_OK) { + LM_ERR("cannot open connection to cluster with hosts: %s, error: %s\n", hosts, con->con->errstr); goto err; } #else LM_DBG("connecting to redis at %s:%d\n", con->id->host, con->id->port); + +#ifdef WITH_SSL + if (db_redis_opt_tls != 0) { + /* Create SSL context*/ + redisInitOpenSSL(); + ssl = redisCreateSSLContext(NULL, ca_path, NULL, NULL, NULL, NULL); + if (ssl == NULL) { + LM_ERR("Unable to create Redis SSL Context.\n"); + goto err; + } + } +#endif + con->con = redisConnectWithTimeout(con->id->host, con->id->port, tv); - if(!con->con) { - LM_ERR("cannot open connection: %.*s\n", con->id->url.len, + if (!con->con) { + LM_ERR("cannot open connection: %.*s\n", con->id->url.len, con->id->url.s); goto err; } - if(con->con->err) { - LM_ERR("cannot open connection to %.*s: %s\n", con->id->url.len, + if (con->con->err) { + LM_ERR("cannot open connection to %.*s: %s\n", con->id->url.len, con->id->url.s, con->con->errstr); goto err; } +#ifdef WITH_SSL + if (ssl) { + redisInitiateSSLWithContext(con->con, ssl); + } +#endif #endif - if(con->id->password) { - reply = redisCommand(con->con, "AUTH %s", con->id->password); - if(!reply) { + password = con->id->password; + if (!password) { + password = db_pass; + } + if (password) { + reply = redisCommand(con->con, "AUTH %s", password); + if (!reply) { LM_ERR("cannot authenticate connection %.*s: %s\n", con->id->url.len, con->id->url.s, con->con->errstr); goto err; } - if(reply->type == REDIS_REPLY_ERROR) { + if (reply->type == REDIS_REPLY_ERROR) { LM_ERR("cannot authenticate connection %.*s: %s\n", con->id->url.len, con->id->url.s, reply->str); goto err; } - freeReplyObject(reply); + freeReplyObject(reply); reply = NULL; } @@ -299,11 +349,11 @@ km_redis_con_t *db_redis_new_connection(const struct db_id *id) ptr->id = (struct db_id *)id; /* - LM_DBG("trying to initialize connection to '%.*s' with schema path '%.*s' and keys '%.*s'\n", - id->url.len, id->url.s, - redis_schema_path.len, redis_schema_path.s, - redis_keys.len, redis_keys.s); - */ + LM_DBG("trying to initialize connection to '%.*s' with schema path '%.*s' and keys '%.*s'\n", + id->url.len, id->url.s, + redis_schema_path.len, redis_schema_path.s, + redis_keys.len, redis_keys.s); + */ LM_DBG("trying to initialize connection to '%.*s'\n", id->url.len, id->url.s); if(db_redis_parse_schema(ptr) != 0) { diff --git a/src/modules/db_redis/redis_connection.h b/src/modules/db_redis/redis_connection.h index e3ae36131c2..62bb81f40f2 100644 --- a/src/modules/db_redis/redis_connection.h +++ b/src/modules/db_redis/redis_connection.h @@ -28,8 +28,14 @@ #else #ifdef WITH_HIREDIS_PATH #include +#ifdef WITH_SSL +#include +#endif #else #include +#ifdef WITH_SSL +#include +#endif #endif #endif