Add support for reading encrypted keyfiles. (#8644)

This commit is contained in:
Yossi Gottlieb 2021-03-22 13:27:46 +02:00 committed by GitHub
parent c3df27d1ea
commit c4ef1efdb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 53 additions and 4 deletions

View File

@ -150,6 +150,11 @@ tcp-keepalive 300
#
# tls-cert-file redis.crt
# tls-key-file redis.key
#
# If the key file is encrypted using a passphrase, it can be included here
# as well.
#
# tls-key-file-pass secret
# Normally Redis uses the same certificate for both server functions (accepting
# connections) and client functions (replicating from a master, establishing
@ -162,6 +167,11 @@ tcp-keepalive 300
#
# tls-client-cert-file client.crt
# tls-client-key-file client.key
#
# If the key file is encrypted using a passphrase, it can be included here
# as well.
#
# tls-client-key-file-pass secret
# Configure a DH parameters file to enable Diffie-Hellman (DH) key exchange:
#

View File

@ -2543,8 +2543,10 @@ 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-key-file-pass", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.key_file_pass, 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-client-key-file-pass", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_key_file_pass, 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),

View File

@ -1119,8 +1119,10 @@ typedef struct socketFds {
typedef struct redisTLSContextConfig {
char *cert_file; /* Server side and optionally client side cert file name */
char *key_file; /* Private key filename for cert_file */
char *key_file_pass; /* Optional password for key_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 *client_key_file_pass; /* Optional password for client_key_file */
char *dh_params_file;
char *ca_cert_file;
char *ca_cert_dir;

View File

@ -179,11 +179,28 @@ void tlsCleanup(void) {
#endif
}
/* Callback for passing a keyfile password stored as an sds to OpenSSL */
static int tlsPasswordCallback(char *buf, int size, int rwflag, void *u) {
UNUSED(rwflag);
const char *pass = u;
size_t pass_len;
if (!pass) return -1;
pass_len = strlen(pass);
if (pass_len > (size_t) size) return -1;
memcpy(buf, pass, pass_len);
return (int) pass_len;
}
/* 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.
*/
static SSL_CTX *createSSLContext(redisTLSContextConfig *ctx_config, int protocols,
const char *cert_file, const char *key_file) {
static SSL_CTX *createSSLContext(redisTLSContextConfig *ctx_config, int protocols, int client) {
const char *cert_file = client ? ctx_config->client_cert_file : ctx_config->cert_file;
const char *key_file = client ? ctx_config->client_key_file : ctx_config->key_file;
const char *key_file_pass = client ? ctx_config->client_key_file_pass : ctx_config->key_file_pass;
char errbuf[256];
SSL_CTX *ctx = NULL;
@ -215,6 +232,9 @@ static SSL_CTX *createSSLContext(redisTLSContextConfig *ctx_config, int protocol
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);
SSL_CTX_set_default_passwd_cb(ctx, tlsPasswordCallback);
SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *) key_file_pass);
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);
@ -281,7 +301,7 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) {
if (protocols == -1) goto error;
/* Create server side/generla context */
ctx = createSSLContext(ctx_config, protocols, ctx_config->cert_file, ctx_config->key_file);
ctx = createSSLContext(ctx_config, protocols, 0);
if (!ctx) goto error;
if (ctx_config->session_caching) {
@ -332,7 +352,7 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) {
/* 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);
client_ctx = createSSLContext(ctx_config, protocols, 1);
if (!client_ctx) goto error;
}

View File

@ -139,5 +139,20 @@ start_server {tags {"tls"}} {
$rd PING
$rd close
}
test {TLS: Working with an encrypted keyfile} {
# Create an encrypted version
set keyfile [lindex [r config get tls-key-file] 1]
set keyfile_encrypted "$keyfile.encrypted"
exec -ignorestderr openssl rsa -in $keyfile -out $keyfile_encrypted -aes256 -passout pass:1234 2>/dev/null
# Using it without a password fails
catch {r config set tls-key-file $keyfile_encrypted} e
assert_match {*Unable to update TLS*} $e
# Now use a password
r config set tls-key-file-pass 1234
r config set tls-key-file $keyfile_encrypted
}
}
}