Skip to content

Commit

Permalink
db_redis: Adding TLS support
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
joelbax authored and miconda committed Jun 20, 2023
1 parent 87959c2 commit 67c7728
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 28 deletions.
24 changes: 21 additions & 3 deletions src/modules/db_redis/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,31 @@ 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
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
Expand All @@ -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)
Expand Down
13 changes: 12 additions & 1 deletion src/modules/db_redis/db_redis_mod.c
Original file line number Diff line number Diff line change
Expand Up @@ -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("");
Expand All @@ -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 = {
Expand Down
5 changes: 5 additions & 0 deletions src/modules/db_redis/doc/db_redis.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<surname>Balashov</surname>
<email>abalashov@evaristesys.com</email>
</editor>
<editor>
<firstname>Joel</firstname>
<surname>Centelles Martin</surname>
<email>joel_centellesmartin@baxter.com</email>
</editor>
</authorgroup>
<copyright>
<year>2018</year>
Expand Down
59 changes: 59 additions & 0 deletions src/modules/db_redis/doc/db_redis_admin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,61 @@ modparam("db_redis", "keys", "version=entry:table_name;location=entry:ruid&amp;u
<programlisting format="linespecific">
...
modparam("db_redis", "verbosity", 0)
...
</programlisting>
</example>
</section>

<section id="db_redis.p.opt_tls">
<title><varname>opt_tls</varname> (int)</title>
<para>
Controls TLS usage while connecting to a remote DB.
If set to 1, TLS is used to connect to the DB.
</para>
<para>
Default value: 0.
</para>
<example>
<title>Enabling TLS connection</title>
<programlisting format="linespecific">
...
modparam("db_redis", "opt_tls", 1)
...
</programlisting>
</example>
</section>

<section id="db_redis.p.db_pass">
<title><varname>db_pass</varname> (string)</title>
<para>
Sets the password to connect to the DB.
</para>
<para>
Default value: "" (empty).
</para>
<example>
<title>Setting a password</title>
<programlisting format="linespecific">
...
modparam("db_redis", "db_pass", "r3d1sPass")
...
</programlisting>
</example>
</section>

<section id="db_redis.p.ca_path">
<title><varname>ac_path</varname> (string)</title>
<para>
Sets the path where Certificates Authorities certs are stored.
</para>
<para>
Default value: "" (empty).
</para>
<example>
<title>Setting CA path</title>
<programlisting format="linespecific">
...
modparam("db_redis", "ca_path", "/etc/ssl/certs")
...
</programlisting>
</example>
Expand All @@ -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.
</para>
<para>
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.
</para>
<example>
<title>Usage</title>
<programlisting format="linespecific">
Expand Down
98 changes: 74 additions & 24 deletions src/modules/db_redis/redis_connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}

Expand Down Expand Up @@ -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) {
Expand Down
6 changes: 6 additions & 0 deletions src/modules/db_redis/redis_connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,14 @@
#else
#ifdef WITH_HIREDIS_PATH
#include <hiredis/hiredis.h>
#ifdef WITH_SSL
#include <hiredis/hiredis_ssl.h>
#endif
#else
#include <hiredis.h>
#ifdef WITH_SSL
#include <hiredis_ssl.h>
#endif
#endif
#endif

Expand Down

0 comments on commit 67c7728

Please sign in to comment.