TLS: Connections refactoring and TLS support.

* Introduce a connection abstraction layer for all socket operations and
integrate it across the code base.
* Provide an optional TLS connections implementation based on OpenSSL.
* Pull a newer version of hiredis with TLS support.
* Tests, redis-cli updates for TLS support.
This commit is contained in:
Yossi Gottlieb
2019-09-12 10:56:54 +03:00
parent f4d37173fe
commit b087dd1db6
85 changed files with 4625 additions and 835 deletions

View File

@ -47,6 +47,9 @@
#include <math.h>
#include <hiredis.h>
#ifdef USE_OPENSSL
#include <hiredis_ssl.h>
#endif
#include <sds.h> /* use sds.h from hiredis, so that only one set of sds functions will be present in the binary */
#include "dict.h"
#include "adlist.h"
@ -188,6 +191,11 @@ static struct config {
char *hostip;
int hostport;
char *hostsocket;
int tls;
char *sni;
char *cacert;
char *cert;
char *key;
long repeat;
long interval;
int dbnum;
@ -751,6 +759,18 @@ static int cliSelect(void) {
return REDIS_ERR;
}
/* Wrapper around redisSecureConnection to avoid hiredis_ssl dependencies if
* not building with TLS support.
*/
static int cliSecureConnection(redisContext *c) {
#ifdef USE_OPENSSL
return redisSecureConnection(c, config.cacert, config.cert, config.key, config.sni);
#else
(void) c;
return REDIS_OK;
#endif
}
/* Connect to the server. It is possible to pass certain flags to the function:
* CC_FORCE: The connection is performed even if there is already
* a connected socket.
@ -767,6 +787,16 @@ static int cliConnect(int flags) {
context = redisConnectUnix(config.hostsocket);
}
if (!context->err && config.tls) {
if (cliSecureConnection(context) == REDIS_ERR && !context->err) {
/* TODO: this check should be redundant, redis-cli should set err=1 */
fprintf(stderr, "Could not negotiate a TLS connection.\n");
context = NULL;
redisFree(context);
return REDIS_ERR;
}
}
if (context->err) {
if (!(flags & CC_QUIET)) {
fprintf(stderr,"Could not connect to Redis at ");
@ -782,6 +812,7 @@ static int cliConnect(int flags) {
return REDIS_ERR;
}
/* Set aggressive KEEP_ALIVE socket option in the Redis context socket
* in order to prevent timeouts caused by the execution of long
* commands. At the same time this improves the detection of real
@ -1245,6 +1276,9 @@ static redisReply *reconnectingRedisCommand(redisContext *c, const char *fmt, ..
redisFree(c);
c = redisConnect(config.hostip,config.hostport);
if (!c->err && config.tls) {
cliSecureConnection(c);
}
usleep(1000000);
}
@ -1434,6 +1468,18 @@ static int parseOptions(int argc, char **argv) {
} else if (!strcmp(argv[i],"--cluster-search-multiple-owners")) {
config.cluster_manager_command.flags |=
CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS;
#ifdef USE_OPENSSL
} else if (!strcmp(argv[i],"--tls")) {
config.tls = 1;
} else if (!strcmp(argv[i],"--sni")) {
config.sni = argv[++i];
} else if (!strcmp(argv[i],"--cacert")) {
config.cacert = argv[++i];
} else if (!strcmp(argv[i],"--cert")) {
config.cert = argv[++i];
} else if (!strcmp(argv[i],"--key")) {
config.key = argv[++i];
#endif
} else if (!strcmp(argv[i],"-v") || !strcmp(argv[i], "--version")) {
sds version = cliVersion();
printf("redis-cli %s\n", version);
@ -1522,6 +1568,12 @@ static void usage(void) {
" -x Read last argument from STDIN.\n"
" -d <delimiter> Multi-bulk delimiter in for raw formatting (default: \\n).\n"
" -c Enable cluster mode (follow -ASK and -MOVED redirections).\n"
#ifdef USE_OPENSSL
" --tls Establish a secure TLS connection.\n"
" --cacert CA Certificate file to verify with.\n"
" --cert Client certificate to authenticate with.\n"
" --key Private key file to authenticate with.\n"
#endif
" --raw Use raw formatting for replies (default when STDOUT is\n"
" not a tty).\n"
" --no-raw Force formatted output even when STDOUT is not a tty.\n"
@ -1544,7 +1596,9 @@ static void usage(void) {
" --pipe Transfer raw Redis protocol from stdin to server.\n"
" --pipe-timeout <n> In --pipe mode, abort with error if after sending all data.\n"
" no reply is received within <n> seconds.\n"
" Default timeout: %d. Use 0 to wait forever.\n"
" Default timeout: %d. Use 0 to wait forever.\n",
version, REDIS_CLI_DEFAULT_PIPE_TIMEOUT);
fprintf(stderr,
" --bigkeys Sample Redis keys looking for keys with many elements (complexity).\n"
" --memkeys Sample Redis keys looking for keys consuming a lot of memory.\n"
" --memkeys-samples <n> Sample Redis keys looking for keys consuming a lot of memory.\n"
@ -1567,8 +1621,7 @@ static void usage(void) {
" line interface.\n"
" --help Output this help and exit.\n"
" --version Output version and exit.\n"
"\n",
version, REDIS_CLI_DEFAULT_PIPE_TIMEOUT);
"\n");
/* Using another fprintf call to avoid -Woverlength-strings compile warning */
fprintf(stderr,
"Cluster Manager Commands:\n"
@ -2336,6 +2389,9 @@ cleanup:
static int clusterManagerNodeConnect(clusterManagerNode *node) {
if (node->context) redisFree(node->context);
node->context = redisConnect(node->ip, node->port);
if (!node->context->err && config.tls) {
cliSecureConnection(node->context);
}
if (node->context->err) {
fprintf(stderr,"Could not connect to Redis at ");
fprintf(stderr,"%s:%d: %s\n", node->ip, node->port,