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,6 +197,87 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) { SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION); #endif + 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 (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", cert_file, errbuf); + goto error; + } + + 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", 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)); + serverLog(LL_WARNING, "Failed to configure CA certificate(s) file/directory: %s", errbuf); + 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 @@ -231,30 +285,10 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) { 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) { - ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf)); - serverLog(LL_WARNING, "Failed to load certificate: %s: %s", ctx_config->cert_file, errbuf); - goto error; - } - - if (SSL_CTX_use_PrivateKey_file(ctx, ctx_config->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); - 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)); - serverLog(LL_WARNING, "Failed to configure CA certificate(s) file/directory: %s", errbuf); - goto error; - } + 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"); @@ -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 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