summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYossi Gottlieb <yossigo@gmail.com>2020-12-11 18:31:40 +0200
committerGitHub <noreply@github.com>2020-12-11 18:31:40 +0200
commit8c291b97b95f2e011977b522acf77ead23e26f55 (patch)
tree14935b675574e1f8f2cc79f90219de537c8fc0f0
parent4e064fbab4d310b508593b46ed6ce539aea7aa25 (diff)
downloadredis-8c291b97b95f2e011977b522acf77ead23e26f55.tar.gz
TLS: Add different client cert support. (#8076)
This adds a new `tls-client-cert-file` and `tls-client-key-file` configuration directives which make it possible to use different certificates for the TLS-server and TLS-client functions of Redis. This is an optional directive. If it is not specified the `tls-cert-file` and `tls-key-file` directives are used for TLS client functions as well. Also, `utils/gen-test-certs.sh` now creates additional server-only and client-only certs and will skip intensive operations if target files already exist.
-rw-r--r--redis.conf12
-rw-r--r--src/config.c2
-rw-r--r--src/sentinel.c3
-rw-r--r--src/server.h6
-rw-r--r--src/tls.c162
-rw-r--r--tests/instances.tcl10
-rw-r--r--tests/support/benchmark.tcl4
-rw-r--r--tests/support/cli.tcl4
-rw-r--r--tests/support/redis.tcl4
-rw-r--r--tests/support/server.tcl15
-rw-r--r--tests/test_helper.tcl4
-rw-r--r--tests/unit/introspection.tcl2
-rw-r--r--tests/unit/tls.tcl21
-rwxr-xr-xutils/gen-test-certs.sh65
14 files changed, 220 insertions, 94 deletions
diff --git a/redis.conf b/redis.conf
index 13766171e..849f171bc 100644
--- a/redis.conf
+++ b/redis.conf
@@ -151,6 +151,18 @@ tcp-keepalive 300
# tls-cert-file redis.crt
# tls-key-file redis.key
+# Normally Redis uses the same certificate for both server functions (accepting
+# connections) and client functions (replicating from a master, establishing
+# cluster bus connections, etc.).
+#
+# Sometimes certificates are issued with attributes that designate them as
+# client-only or server-only certificates. In that case it may be desired to use
+# different certificates for incoming (server) and outgoing (client)
+# connections. To do that, use the following directives:
+#
+# tls-client-cert-file client.crt
+# tls-client-key-file client.key
+
# Configure a DH parameters file to enable Diffie-Hellman (DH) key exchange:
#
# tls-dh-params-file redis.dh
diff --git a/src/config.c b/src/config.c
index 5fdde44cd..28b756b5e 100644
--- a/src/config.c
+++ b/src/config.c
@@ -2450,6 +2450,8 @@ standardConfig configs[] = {
createBoolConfig("tls-session-caching", NULL, MODIFIABLE_CONFIG, server.tls_ctx_config.session_caching, 1, NULL, updateTlsCfgBool),
createStringConfig("tls-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.cert_file, NULL, NULL, updateTlsCfg),
createStringConfig("tls-key-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.key_file, NULL, NULL, updateTlsCfg),
+ createStringConfig("tls-client-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_cert_file, NULL, NULL, updateTlsCfg),
+ createStringConfig("tls-client-key-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_key_file, NULL, NULL, updateTlsCfg),
createStringConfig("tls-dh-params-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.dh_params_file, NULL, NULL, updateTlsCfg),
createStringConfig("tls-ca-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_file, NULL, NULL, updateTlsCfg),
createStringConfig("tls-ca-cert-dir", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_dir, NULL, NULL, updateTlsCfg),
diff --git a/src/sentinel.c b/src/sentinel.c
index e05be4384..95cfa84ad 100644
--- a/src/sentinel.c
+++ b/src/sentinel.c
@@ -46,6 +46,7 @@ extern char **environ;
#ifdef USE_OPENSSL
extern SSL_CTX *redis_tls_ctx;
+extern SSL_CTX *redis_tls_client_ctx;
#endif
#define REDIS_SENTINEL_PORT 26379
@@ -2077,7 +2078,7 @@ static int instanceLinkNegotiateTLS(redisAsyncContext *context) {
(void) context;
#else
if (!redis_tls_ctx) return C_ERR;
- SSL *ssl = SSL_new(redis_tls_ctx);
+ SSL *ssl = SSL_new(redis_tls_client_ctx ? redis_tls_client_ctx : redis_tls_ctx);
if (!ssl) return C_ERR;
if (redisInitiateSSL(&context->c, ssl) == REDIS_ERR) return C_ERR;
diff --git a/src/server.h b/src/server.h
index 97604a11b..35b695a42 100644
--- a/src/server.h
+++ b/src/server.h
@@ -1068,8 +1068,10 @@ struct malloc_stats {
*----------------------------------------------------------------------------*/
typedef struct redisTLSContextConfig {
- char *cert_file;
- char *key_file;
+ char *cert_file; /* Server side and optionally client side cert file name */
+ char *key_file; /* Private key filename for cert_file */
+ char *client_cert_file; /* Certificate to use as a client; if none, use cert_file */
+ char *client_key_file; /* Private key filename for client_cert_file */
char *dh_params_file;
char *ca_cert_file;
char *ca_cert_dir;
diff --git a/src/tls.c b/src/tls.c
index af16d2a41..caa987965 100644
--- a/src/tls.c
+++ b/src/tls.c
@@ -54,7 +54,8 @@
extern ConnectionType CT_Socket;
-SSL_CTX *redis_tls_ctx;
+SSL_CTX *redis_tls_ctx = NULL;
+SSL_CTX *redis_tls_client_ctx = NULL;
static int parseProtocolsConfig(const char *str) {
int i, count = 0;
@@ -163,50 +164,22 @@ void tlsInit(void) {
pending_list = listCreate();
}
-/* Attempt to configure/reconfigure TLS. This operation is atomic and will
- * leave the SSL_CTX unchanged if fails.
+/* Create a *base* SSL_CTX using the SSL configuration provided. The base context
+ * includes everything that's common for both client-side and server-side connections.
*/
-int tlsConfigure(redisTLSContextConfig *ctx_config) {
+static SSL_CTX *createSSLContext(redisTLSContextConfig *ctx_config, int protocols,
+ const char *cert_file, const char *key_file) {
char errbuf[256];
SSL_CTX *ctx = NULL;
- if (!ctx_config->cert_file) {
- serverLog(LL_WARNING, "No tls-cert-file configured!");
- goto error;
- }
-
- if (!ctx_config->key_file) {
- serverLog(LL_WARNING, "No tls-key-file configured!");
- goto error;
- }
-
- if (((server.tls_auth_clients != TLS_CLIENT_AUTH_NO) || server.tls_cluster || server.tls_replication) &&
- !ctx_config->ca_cert_file && !ctx_config->ca_cert_dir) {
- serverLog(LL_WARNING, "Either tls-ca-cert-file or tls-ca-cert-dir must be specified when tls-cluster, tls-replication or tls-auth-clients are enabled!");
- goto error;
- }
-
ctx = SSL_CTX_new(SSLv23_method());
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3);
- SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
SSL_CTX_set_options(ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
#endif
- if (ctx_config->session_caching) {
- SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
- SSL_CTX_sess_set_cache_size(ctx, ctx_config->session_cache_size);
- SSL_CTX_set_timeout(ctx, ctx_config->session_cache_timeout);
- SSL_CTX_set_session_id_context(ctx, (void *) "redis", 5);
- } else {
- SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
- }
-
- int protocols = parseProtocolsConfig(ctx_config->protocols);
- if (protocols == -1) goto error;
-
if (!(protocols & REDIS_TLS_PROTO_TLSv1))
SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
if (!(protocols & REDIS_TLS_PROTO_TLSv1_1))
@@ -224,31 +197,21 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) {
SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION);
#endif
-#ifdef SSL_OP_NO_CLIENT_RENEGOTIATION
- SSL_CTX_set_options(ctx, SSL_OP_NO_CLIENT_RENEGOTIATION);
-#endif
-
- if (ctx_config->prefer_server_ciphers)
- SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
-
SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
-#if defined(SSL_CTX_set_ecdh_auto)
- SSL_CTX_set_ecdh_auto(ctx, 1);
-#endif
- if (SSL_CTX_use_certificate_chain_file(ctx, ctx_config->cert_file) <= 0) {
+ if (SSL_CTX_use_certificate_chain_file(ctx, cert_file) <= 0) {
ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
- serverLog(LL_WARNING, "Failed to load certificate: %s: %s", ctx_config->cert_file, errbuf);
+ serverLog(LL_WARNING, "Failed to load certificate: %s: %s", cert_file, errbuf);
goto error;
}
-
- if (SSL_CTX_use_PrivateKey_file(ctx, ctx_config->key_file, SSL_FILETYPE_PEM) <= 0) {
+
+ if (SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) <= 0) {
ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
- serverLog(LL_WARNING, "Failed to load private key: %s: %s", ctx_config->key_file, errbuf);
+ serverLog(LL_WARNING, "Failed to load private key: %s: %s", key_file, errbuf);
goto error;
}
-
+
if ((ctx_config->ca_cert_file || ctx_config->ca_cert_dir) &&
SSL_CTX_load_verify_locations(ctx, ctx_config->ca_cert_file, ctx_config->ca_cert_dir) <= 0) {
ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
@@ -256,6 +219,77 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) {
goto error;
}
+ if (ctx_config->ciphers && !SSL_CTX_set_cipher_list(ctx, ctx_config->ciphers)) {
+ serverLog(LL_WARNING, "Failed to configure ciphers: %s", ctx_config->ciphers);
+ goto error;
+ }
+
+#ifdef TLS1_3_VERSION
+ if (ctx_config->ciphersuites && !SSL_CTX_set_ciphersuites(ctx, ctx_config->ciphersuites)) {
+ serverLog(LL_WARNING, "Failed to configure ciphersuites: %s", ctx_config->ciphersuites);
+ goto error;
+ }
+#endif
+
+ return ctx;
+
+error:
+ if (ctx) SSL_CTX_free(ctx);
+ return NULL;
+}
+
+/* Attempt to configure/reconfigure TLS. This operation is atomic and will
+ * leave the SSL_CTX unchanged if fails.
+ */
+int tlsConfigure(redisTLSContextConfig *ctx_config) {
+ char errbuf[256];
+ SSL_CTX *ctx = NULL;
+ SSL_CTX *client_ctx = NULL;
+
+ if (!ctx_config->cert_file) {
+ serverLog(LL_WARNING, "No tls-cert-file configured!");
+ goto error;
+ }
+
+ if (!ctx_config->key_file) {
+ serverLog(LL_WARNING, "No tls-key-file configured!");
+ goto error;
+ }
+
+ if (((server.tls_auth_clients != TLS_CLIENT_AUTH_NO) || server.tls_cluster || server.tls_replication) &&
+ !ctx_config->ca_cert_file && !ctx_config->ca_cert_dir) {
+ serverLog(LL_WARNING, "Either tls-ca-cert-file or tls-ca-cert-dir must be specified when tls-cluster, tls-replication or tls-auth-clients are enabled!");
+ goto error;
+ }
+
+ int protocols = parseProtocolsConfig(ctx_config->protocols);
+ if (protocols == -1) goto error;
+
+ /* Create server side/generla context */
+ ctx = createSSLContext(ctx_config, protocols, ctx_config->cert_file, ctx_config->key_file);
+ if (!ctx) goto error;
+
+ if (ctx_config->session_caching) {
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
+ SSL_CTX_sess_set_cache_size(ctx, ctx_config->session_cache_size);
+ SSL_CTX_set_timeout(ctx, ctx_config->session_cache_timeout);
+ SSL_CTX_set_session_id_context(ctx, (void *) "redis", 5);
+ } else {
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
+ }
+
+#ifdef SSL_OP_NO_CLIENT_RENEGOTIATION
+ SSL_CTX_set_options(ctx, SSL_OP_NO_CLIENT_RENEGOTIATION);
+#endif
+
+ if (ctx_config->prefer_server_ciphers)
+ SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
+
+#if defined(SSL_CTX_set_ecdh_auto)
+ SSL_CTX_set_ecdh_auto(ctx, 1);
+#endif
+ SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
+
if (ctx_config->dh_params_file) {
FILE *dhfile = fopen(ctx_config->dh_params_file, "r");
DH *dh = NULL;
@@ -281,25 +315,22 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) {
DH_free(dh);
}
- if (ctx_config->ciphers && !SSL_CTX_set_cipher_list(ctx, ctx_config->ciphers)) {
- serverLog(LL_WARNING, "Failed to configure ciphers: %s", ctx_config->ciphers);
- goto error;
+ /* If a client-side certificate is configured, create an explicit client context */
+ if (ctx_config->client_cert_file && ctx_config->client_key_file) {
+ client_ctx = createSSLContext(ctx_config, protocols, ctx_config->client_cert_file, ctx_config->client_key_file);
+ if (!client_ctx) goto error;
}
-#ifdef TLS1_3_VERSION
- if (ctx_config->ciphersuites && !SSL_CTX_set_ciphersuites(ctx, ctx_config->ciphersuites)) {
- serverLog(LL_WARNING, "Failed to configure ciphersuites: %s", ctx_config->ciphersuites);
- goto error;
- }
-#endif
-
SSL_CTX_free(redis_tls_ctx);
+ SSL_CTX_free(redis_tls_client_ctx);
redis_tls_ctx = ctx;
+ redis_tls_client_ctx = client_ctx;
return C_OK;
error:
if (ctx) SSL_CTX_free(ctx);
+ if (client_ctx) SSL_CTX_free(client_ctx);
return C_ERR;
}
@@ -344,14 +375,21 @@ typedef struct tls_connection {
listNode *pending_list_node;
} tls_connection;
-connection *connCreateTLS(void) {
+static connection *createTLSConnection(bool client_side) {
+ SSL_CTX *ctx = redis_tls_ctx;
+ if (client_side && redis_tls_client_ctx)
+ ctx = redis_tls_client_ctx;
tls_connection *conn = zcalloc(sizeof(tls_connection));
conn->c.type = &CT_TLS;
conn->c.fd = -1;
- conn->ssl = SSL_new(redis_tls_ctx);
+ conn->ssl = SSL_new(ctx);
return (connection *) conn;
}
+connection *connCreateTLS(void) {
+ return createTLSConnection(true);
+}
+
/* Fetch the latest OpenSSL error and store it in the connection */
static void updateTLSError(tls_connection *conn) {
conn->c.last_errno = 0;
@@ -370,7 +408,7 @@ static void updateTLSError(tls_connection *conn) {
* is not in an error state.
*/
connection *connCreateAcceptedTLS(int fd, int require_auth) {
- tls_connection *conn = (tls_connection *) connCreateTLS();
+ tls_connection *conn = (tls_connection *) createTLSConnection(false);
conn->c.fd = fd;
conn->c.state = CONN_STATE_ACCEPTING;
diff --git a/tests/instances.tcl b/tests/instances.tcl
index 156c92706..a9cc01008 100644
--- a/tests/instances.tcl
+++ b/tests/instances.tcl
@@ -76,8 +76,10 @@ proc spawn_instance {type base_port count {conf {}}} {
puts $cfg "tls-replication yes"
puts $cfg "tls-cluster yes"
puts $cfg "port 0"
- puts $cfg [format "tls-cert-file %s/../../tls/redis.crt" [pwd]]
- puts $cfg [format "tls-key-file %s/../../tls/redis.key" [pwd]]
+ puts $cfg [format "tls-cert-file %s/../../tls/server.crt" [pwd]]
+ puts $cfg [format "tls-key-file %s/../../tls/server.key" [pwd]]
+ puts $cfg [format "tls-client-cert-file %s/../../tls/client.crt" [pwd]]
+ puts $cfg [format "tls-client-key-file %s/../../tls/client.key" [pwd]]
puts $cfg [format "tls-dh-params-file %s/../../tls/redis.dh" [pwd]]
puts $cfg [format "tls-ca-cert-file %s/../../tls/ca.crt" [pwd]]
puts $cfg "loglevel debug"
@@ -234,8 +236,8 @@ proc parse_options {} {
package require tls 1.6
::tls::init \
-cafile "$::tlsdir/ca.crt" \
- -certfile "$::tlsdir/redis.crt" \
- -keyfile "$::tlsdir/redis.key"
+ -certfile "$::tlsdir/client.crt" \
+ -keyfile "$::tlsdir/client.key"
set ::tls 1
} elseif {$opt eq "--help"} {
puts "--single <pattern> Only runs tests specified by pattern."
diff --git a/tests/support/benchmark.tcl b/tests/support/benchmark.tcl
index ed75bfeda..3d08b76f5 100644
--- a/tests/support/benchmark.tcl
+++ b/tests/support/benchmark.tcl
@@ -1,7 +1,7 @@
proc redisbenchmark_tls_config {testsdir} {
set tlsdir [file join $testsdir tls]
- set cert [file join $tlsdir redis.crt]
- set key [file join $tlsdir redis.key]
+ set cert [file join $tlsdir client.crt]
+ set key [file join $tlsdir client.key]
set cacert [file join $tlsdir ca.crt]
if {$::tls} {
diff --git a/tests/support/cli.tcl b/tests/support/cli.tcl
index d55487931..19e306e24 100644
--- a/tests/support/cli.tcl
+++ b/tests/support/cli.tcl
@@ -1,7 +1,7 @@
proc rediscli_tls_config {testsdir} {
set tlsdir [file join $testsdir tls]
- set cert [file join $tlsdir redis.crt]
- set key [file join $tlsdir redis.key]
+ set cert [file join $tlsdir client.crt]
+ set key [file join $tlsdir client.key]
set cacert [file join $tlsdir ca.crt]
if {$::tls} {
diff --git a/tests/support/redis.tcl b/tests/support/redis.tcl
index 26b4510ac..9eb5b94e2 100644
--- a/tests/support/redis.tcl
+++ b/tests/support/redis.tcl
@@ -44,8 +44,8 @@ proc redis {{server 127.0.0.1} {port 6379} {defer 0} {tls 0} {tlsoptions {}}} {
package require tls
::tls::init \
-cafile "$::tlsdir/ca.crt" \
- -certfile "$::tlsdir/redis.crt" \
- -keyfile "$::tlsdir/redis.key" \
+ -certfile "$::tlsdir/client.crt" \
+ -keyfile "$::tlsdir/client.key" \
{*}$tlsoptions
set fd [::tls::socket $server $port]
} else {
diff --git a/tests/support/server.tcl b/tests/support/server.tcl
index e5b167a35..1cddb7068 100644
--- a/tests/support/server.tcl
+++ b/tests/support/server.tcl
@@ -229,6 +229,7 @@ proc start_server {options {code undefined}} {
# setup defaults
set baseconfig "default.conf"
set overrides {}
+ set omit {}
set tags {}
set keep_persistence false
@@ -241,6 +242,9 @@ proc start_server {options {code undefined}} {
"overrides" {
set overrides $value
}
+ "omit" {
+ set omit $value
+ }
"tags" {
# If we 'tags' contain multiple tags, quoted and seperated by spaces,
# we want to get rid of the quotes in order to have a proper list
@@ -306,8 +310,10 @@ proc start_server {options {code undefined}} {
set data [split [exec cat "tests/assets/$baseconfig"] "\n"]
set config {}
if {$::tls} {
- dict set config "tls-cert-file" [format "%s/tests/tls/redis.crt" [pwd]]
- dict set config "tls-key-file" [format "%s/tests/tls/redis.key" [pwd]]
+ dict set config "tls-cert-file" [format "%s/tests/tls/server.crt" [pwd]]
+ dict set config "tls-key-file" [format "%s/tests/tls/server.key" [pwd]]
+ dict set config "tls-client-cert-file" [format "%s/tests/tls/client.crt" [pwd]]
+ dict set config "tls-client-key-file" [format "%s/tests/tls/client.key" [pwd]]
dict set config "tls-dh-params-file" [format "%s/tests/tls/redis.dh" [pwd]]
dict set config "tls-ca-cert-file" [format "%s/tests/tls/ca.crt" [pwd]]
dict set config "loglevel" "debug"
@@ -343,6 +349,11 @@ proc start_server {options {code undefined}} {
dict set config $directive $arguments
}
+ # remove directives that are marked to be omitted
+ foreach directive $omit {
+ dict unset config $directive
+ }
+
# write new configuration to temporary file
set config_file [tmpfile redis.conf]
create_server_config_file $config_file $config
diff --git a/tests/test_helper.tcl b/tests/test_helper.tcl
index 29ebdd7bb..3b8dc16da 100644
--- a/tests/test_helper.tcl
+++ b/tests/test_helper.tcl
@@ -602,8 +602,8 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
set ::tls 1
::tls::init \
-cafile "$::tlsdir/ca.crt" \
- -certfile "$::tlsdir/redis.crt" \
- -keyfile "$::tlsdir/redis.key"
+ -certfile "$::tlsdir/client.crt" \
+ -keyfile "$::tlsdir/client.key"
} elseif {$opt eq {--host}} {
set ::external 1
set ::host $arg
diff --git a/tests/unit/introspection.tcl b/tests/unit/introspection.tcl
index a250762f2..0a7f7a9c9 100644
--- a/tests/unit/introspection.tcl
+++ b/tests/unit/introspection.tcl
@@ -122,6 +122,8 @@ start_server {tags {"introspection"}} {
tls-session-caching
tls-cert-file
tls-key-file
+ tls-client-cert-file
+ tls-client-key-file
tls-dh-params-file
tls-ca-cert-file
tls-ca-cert-dir
diff --git a/tests/unit/tls.tcl b/tests/unit/tls.tcl
index bb5b6d034..a6c2f3c2c 100644
--- a/tests/unit/tls.tcl
+++ b/tests/unit/tls.tcl
@@ -93,5 +93,26 @@ start_server {tags {"tls"}} {
r CONFIG SET tls-protocols ""
r CONFIG SET tls-ciphers "DEFAULT"
}
+
+ test {TLS: Verify tls-cert-file is also used as a client cert if none specified} {
+ set master [srv 0 client]
+ set master_host [srv 0 host]
+ set master_port [srv 0 port]
+
+ # Use a non-restricted client/server cert for the replica
+ set redis_crt [format "%s/tests/tls/redis.crt" [pwd]]
+ set redis_key [format "%s/tests/tls/redis.key" [pwd]]
+
+ start_server [list overrides [list tls-cert-file $redis_crt tls-key-file $redis_key] \
+ omit [list tls-client-cert-file tls-client-key-file]] {
+ set replica [srv 0 client]
+ $replica replicaof $master_host $master_port
+ wait_for_condition 30 100 {
+ [string match {*master_link_status:up*} [$replica info replication]]
+ } else {
+ fail "Can't authenticate to master using just tls-cert-file!"
+ }
+ }
+ }
}
}
diff --git a/utils/gen-test-certs.sh b/utils/gen-test-certs.sh
index a46edc55a..60814483b 100755
--- a/utils/gen-test-certs.sh
+++ b/utils/gen-test-certs.sh
@@ -1,23 +1,58 @@
#!/bin/bash
+
+# Generate some test certificates which are used by the regression test suite:
+#
+# tests/tls/ca.{crt,key} Self signed CA certificate.
+# tests/tls/redis.{crt,key} A certificate with no key usage/policy restrictions.
+# tests/tls/client.{crt,key} A certificate restricted for SSL client usage.
+# tests/tls/server.{crt,key} A certificate restricted fro SSL server usage.
+# tests/tls/redis.dh DH Params file.
+
+generate_cert() {
+ local name=$1
+ local cn="$2"
+ local opts="$3"
+
+ local keyfile=tests/tls/${name}.key
+ local certfile=tests/tls/${name}.crt
+
+ [ -f $keyfile ] || openssl genrsa -out $keyfile 2048
+ openssl req \
+ -new -sha256 \
+ -subj "/O=Redis Test/CN=$cn" \
+ -key $keyfile | \
+ openssl x509 \
+ -req -sha256 \
+ -CA tests/tls/ca.crt \
+ -CAkey tests/tls/ca.key \
+ -CAserial tests/tls/ca.txt \
+ -CAcreateserial \
+ -days 365 \
+ $opts \
+ -out $certfile
+}
+
mkdir -p tests/tls
-openssl genrsa -out tests/tls/ca.key 4096
+[ -f tests/tls/ca.key ] || openssl genrsa -out tests/tls/ca.key 4096
openssl req \
-x509 -new -nodes -sha256 \
-key tests/tls/ca.key \
-days 3650 \
-subj '/O=Redis Test/CN=Certificate Authority' \
-out tests/tls/ca.crt
-openssl genrsa -out tests/tls/redis.key 2048
-openssl req \
- -new -sha256 \
- -key tests/tls/redis.key \
- -subj '/O=Redis Test/CN=Server' | \
- openssl x509 \
- -req -sha256 \
- -CA tests/tls/ca.crt \
- -CAkey tests/tls/ca.key \
- -CAserial tests/tls/ca.txt \
- -CAcreateserial \
- -days 365 \
- -out tests/tls/redis.crt
-openssl dhparam -out tests/tls/redis.dh 2048
+
+cat > tests/tls/openssl.cnf <<_END_
+[ server_cert ]
+keyUsage = digitalSignature, keyEncipherment
+nsCertType = server
+
+[ client_cert ]
+keyUsage = digitalSignature, keyEncipherment
+nsCertType = client
+_END_
+
+generate_cert server "Server-only" "-extfile tests/tls/openssl.cnf -extensions server_cert"
+generate_cert client "Client-only" "-extfile tests/tls/openssl.cnf -extensions client_cert"
+generate_cert redis "Generic-cert"
+
+[ -f tests/tls/redis.dh ] || openssl dhparam -out tests/tls/redis.dh 2048