diff --git a/src/config.cpp b/src/config.cpp index 41108c79a..d0e560007 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -2560,6 +2560,16 @@ static int updateTlsCfgInt(long long val, long long prev, const char **err) { return updateTlsCfg(NULL, NULL, err); } +static int updateTLSPortThread(long long val, bool fFirstCall, const char **err) +{ + if (changeListenPort(val, &serverTL->tlsfd, acceptTLSHandler, fFirstCall) == C_ERR) { + *err = "Unable to listen on this port. Check server logs."; + return 0; + } + + return 1; +} + static int updateTLSPort(long long val, long long prev, const char **err) { /* Do nothing if port is unchanged */ if (val == prev) { @@ -2567,14 +2577,25 @@ static int updateTLSPort(long long val, long long prev, const char **err) { } /* Configure TLS if tls is enabled */ - if (prev == 0 && tlsConfigure(&server.tls_ctx_config) == C_ERR) { + if (prev == 0 && tlsConfigure(&g_pserver->tls_ctx_config) == C_ERR) { *err = "Unable to update TLS configuration. Check server logs."; return 0; } -asdsa - if (changeListenPort(val, &server.tlsfd, acceptTLSHandler) == C_ERR) { - *err = "Unable to listen on this port. Check server logs."; + + // Do our thread first in case there is a config issue + if (!updateTLSPortThread(val, true /*fFirstCall*/, err)) return 0; + + for (int ithread = 0; ithread < cserver.cthreads; ++ithread) { + if (ithread == serverTL - g_pserver->rgthreadvar) + continue; // we already did our thread + aePostFunction(g_pserver->rgthreadvar[ithread].el, [val]{ + const char **err; + if (!updateTLSPortThread(val, false /*fFirstCall*/, err)) { + serverLog(LL_WARNING, "Failed to update TLS port for a thread: %s", *err); + serverLog(LL_WARNING, "\tKeyDB will still be listening on the old port for some threads."); + } + }); } return 1; @@ -2740,26 +2761,26 @@ standardConfig configs[] = { createBoolConfig("allow-write-during-load", NULL, MODIFIABLE_CONFIG, g_pserver->fWriteDuringActiveLoad, 0, NULL, NULL), #ifdef USE_OPENSSL - createIntConfig("tls-port", NULL, IMMUTABLE_CONFIG, 0, 65535, server.tls_port, 0, INTEGER_CONFIG, NULL, updateTLSPort), /* TCP port. */ - createIntConfig("tls-session-cache-size", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tls_ctx_config.session_cache_size, 20*1024, INTEGER_CONFIG, NULL, updateTlsCfgInt), - createIntConfig("tls-session-cache-timeout", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tls_ctx_config.session_cache_timeout, 300, INTEGER_CONFIG, NULL, updateTlsCfgInt), - createBoolConfig("tls-cluster", NULL, MODIFIABLE_CONFIG, server.tls_cluster, 0, NULL, updateTlsCfgBool), - createBoolConfig("tls-replication", NULL, MODIFIABLE_CONFIG, server.tls_replication, 0, NULL, updateTlsCfgBool), - createEnumConfig("tls-auth-clients", NULL, MODIFIABLE_CONFIG, tls_auth_clients_enum, server.tls_auth_clients, TLS_CLIENT_AUTH_YES, NULL, NULL), - createBoolConfig("tls-prefer-server-ciphers", NULL, MODIFIABLE_CONFIG, server.tls_ctx_config.prefer_server_ciphers, 0, NULL, updateTlsCfgBool), - 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), - createStringConfig("tls-protocols", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.protocols, NULL, NULL, updateTlsCfg), - createStringConfig("tls-ciphers", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphers, NULL, NULL, updateTlsCfg), - createStringConfig("tls-ciphersuites", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphersuites, NULL, NULL, updateTlsCfg), + createIntConfig("tls-port", NULL, IMMUTABLE_CONFIG, 0, 65535, g_pserver->tls_port, 0, INTEGER_CONFIG, NULL, updateTLSPort), /* TCP port. */ + createIntConfig("tls-session-cache-size", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->tls_ctx_config.session_cache_size, 20*1024, INTEGER_CONFIG, NULL, updateTlsCfgInt), + createIntConfig("tls-session-cache-timeout", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->tls_ctx_config.session_cache_timeout, 300, INTEGER_CONFIG, NULL, updateTlsCfgInt), + createBoolConfig("tls-cluster", NULL, MODIFIABLE_CONFIG, g_pserver->tls_cluster, 0, NULL, updateTlsCfgBool), + createBoolConfig("tls-replication", NULL, MODIFIABLE_CONFIG, g_pserver->tls_replication, 0, NULL, updateTlsCfgBool), + createEnumConfig("tls-auth-clients", NULL, MODIFIABLE_CONFIG, tls_auth_clients_enum, g_pserver->tls_auth_clients, TLS_CLIENT_AUTH_YES, NULL, NULL), + createBoolConfig("tls-prefer-server-ciphers", NULL, MODIFIABLE_CONFIG, g_pserver->tls_ctx_config.prefer_server_ciphers, 0, NULL, updateTlsCfgBool), + createBoolConfig("tls-session-caching", NULL, MODIFIABLE_CONFIG, g_pserver->tls_ctx_config.session_caching, 1, NULL, updateTlsCfgBool), + createStringConfig("tls-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.cert_file, NULL, NULL, updateTlsCfg), + createStringConfig("tls-key-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.key_file, NULL, NULL, updateTlsCfg), + createStringConfig("tls-key-file-pass", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.key_file_pass, NULL, NULL, updateTlsCfg), + createStringConfig("tls-client-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.client_cert_file, NULL, NULL, updateTlsCfg), + createStringConfig("tls-client-key-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.client_key_file, NULL, NULL, updateTlsCfg), + createStringConfig("tls-client-key-file-pass", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.client_key_file_pass, NULL, NULL, updateTlsCfg), + createStringConfig("tls-dh-params-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.dh_params_file, NULL, NULL, updateTlsCfg), + createStringConfig("tls-ca-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.ca_cert_file, NULL, NULL, updateTlsCfg), + createStringConfig("tls-ca-cert-dir", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.ca_cert_dir, NULL, NULL, updateTlsCfg), + createStringConfig("tls-protocols", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.protocols, NULL, NULL, updateTlsCfg), + createStringConfig("tls-ciphers", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.ciphers, NULL, NULL, updateTlsCfg), + createStringConfig("tls-ciphersuites", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.ciphersuites, NULL, NULL, updateTlsCfg), #endif /* NULL Terminator */ diff --git a/src/replication.cpp b/src/replication.cpp index e6c6ebede..2f5e205b1 100644 --- a/src/replication.cpp +++ b/src/replication.cpp @@ -1895,6 +1895,31 @@ void replicationCreateMasterClient(redisMaster *mi, connection *conn, int dbid) if (dbid != -1) selectDb(mi->master,dbid); } +void replicationCreateCachedMasterClone(redisMaster *mi) { + serverAssert(mi->master != nullptr); + serverLog(LL_NOTICE, "Creating cache clone of our master"); + client *c = createClient(nullptr, ielFromEventLoop(serverTL->el)); + + c->flags |= mi->master->flags; + c->authenticated = mi->master->authenticated; + c->reploff = mi->master->reploff; + c->read_reploff = mi->master->read_reploff; + c->user = mi->master->user; + + memcpy(c->uuid, mi->master->uuid, UUID_BINARY_LEN); + memcpy(c->replid, mi->master->replid, + sizeof(mi->master->replid)); + selectDb(c, mi->master->db->id); + + // Free the old one + mi->master->flags &= ~CLIENT_MASTER; + freeClientAsync(mi->master); + + // Now make this one the cache + mi->master = c; + replicationCacheMaster(mi, c); +} + /* This function will try to re-enable the AOF file after the * master-replica synchronization: if it fails after multiple attempts * the replica cannot be considered reliable and exists with an @@ -2708,6 +2733,10 @@ void syncWithMaster(connection *conn) { int psync_result; redisMaster *mi = (redisMaster*)connGetPrivateData(conn); + if (mi == nullptr) { + // We're about to be closed, bail + return; + } /* If this event fired after the user turned the instance into a master * with SLAVEOF NO ONE we must just return ASAP. */ @@ -3066,6 +3095,7 @@ write_error: /* Handle sendCommand() errors. */ int connectWithMaster(redisMaster *mi) { serverAssert(mi->master == nullptr); mi->repl_transfer_s = g_pserver->tls_replication ? connCreateTLS() : connCreateSocket(); + mi->ielReplTransfer = serverTL - g_pserver->rgthreadvar; connSetPrivateData(mi->repl_transfer_s, mi); if (connConnect(mi->repl_transfer_s, mi->masterhost, mi->masterport, NET_FIRST_BIND_ADDR, syncWithMaster) == C_ERR) { @@ -3089,7 +3119,11 @@ int connectWithMaster(redisMaster *mi) { * Never call this function directly, use cancelReplicationHandshake() instead. */ void undoConnectWithMaster(redisMaster *mi) { - connClose(mi->repl_transfer_s); + auto conn = mi->repl_transfer_s; + connSetPrivateData(conn, nullptr); + aePostFunction(g_pserver->rgthreadvar[mi->ielReplTransfer].el, [conn]{ + connClose(conn); + }); mi->repl_transfer_s = NULL; } @@ -3171,9 +3205,17 @@ struct redisMaster *replicationAddMaster(char *ip, int port) { sdsfree(mi->masterhost); mi->masterhost = nullptr; if (mi->master) { - freeClientAsync(mi->master); - mi->master = nullptr; + if (FCorrectThread(mi->master)) { + // This will cache the master and do all that fancy stuff + if (!freeClient(mi->master) && mi->master) + replicationCreateCachedMasterClone(mi); + } else { + // We're not on the same thread so we can't use the freeClient method, instead we have to clone the master + // and cache that clone + replicationCreateCachedMasterClone(mi); + } } + serverAssert(mi->master == nullptr); if (!g_pserver->fActiveReplica) disconnectAllBlockedClients(); /* Clients blocked in master, now replica. */ @@ -3248,10 +3290,15 @@ void replicationUnsetMaster(redisMaster *mi) { sdsfree(mi->masterhost); mi->masterhost = NULL; if (mi->master) { - if (FCorrectThread(mi->master)) - freeClient(mi->master); - else - freeClientAsync(mi->master); + if (FCorrectThread(mi->master)) { + // This will cache the master and do all that fancy stuff + if (!freeClient(mi->master) && mi->master) + replicationCreateCachedMasterClone(mi); + } else { + // We're not on the same thread so we can't use the freeClient method, instead we have to clone the master + // and cache that clone + replicationCreateCachedMasterClone(mi); + } } replicationDiscardCachedMaster(mi); cancelReplicationHandshake(mi,false); @@ -3666,7 +3713,8 @@ void replicationResurrectCachedMaster(redisMaster *mi, connection *conn) { /* Re-add to the list of clients. */ linkClient(mi->master); serverAssert(connGetPrivateData(mi->master->conn) == mi->master); - serverAssert(mi->master->iel == ielFromEventLoop(serverTL->el)); + serverAssert(mi->master->conn == conn); + AssertCorrectThread(mi->master); if (connSetReadHandler(mi->master->conn, readQueryFromClient, true)) { serverLog(LL_WARNING,"Error resurrecting the cached master, impossible to add the readable handler: %s", strerror(errno)); freeClientAsync(mi->master); /* Close ASAP. */ @@ -4002,10 +4050,15 @@ void replicationCron(void) { (time(NULL)-mi->master->lastinteraction) > g_pserver->repl_timeout) { serverLog(LL_WARNING,"MASTER timeout: no data nor PING received..."); - if (FCorrectThread(mi->master)) - freeClient(mi->master); - else - freeClientAsync(mi->master); + if (FCorrectThread(mi->master)) { + // This will cache the master and do all that fancy stuff + if (!freeClient(mi->master) && mi->master) + replicationCreateCachedMasterClone(mi); + } else { + // We're not on the same thread so we can't use the freeClient method, instead we have to clone the master + // and cache that clone + replicationCreateCachedMasterClone(mi); + } } /* Check if we should connect to a MASTER */ diff --git a/src/server.h b/src/server.h index 10607839a..23588ffa5 100644 --- a/src/server.h +++ b/src/server.h @@ -1528,6 +1528,7 @@ struct redisMaster { uint64_t mvccLastSync; /* During a handshake the server may have stale keys, we track these here to share once a reciprocal connection is made */ std::map> *staleKeyMap; + int ielReplTransfer = -1; }; // Const vars are not changed after worker threads are launched @@ -2406,6 +2407,7 @@ void replicationCron(void); void replicationStartPendingFork(void); void replicationHandleMasterDisconnection(struct redisMaster *mi); void replicationCacheMaster(struct redisMaster *mi, client *c); +void replicationCreateCachedMasterClone(redisMaster *mi); void resizeReplicationBacklog(long long newsize); struct redisMaster *replicationAddMaster(char *ip, int port); void replicationUnsetMaster(struct redisMaster *mi); diff --git a/src/tls.cpp b/src/tls.cpp index 2a19e1bc4..c1ad9158e 100644 --- a/src/tls.cpp +++ b/src/tls.cpp @@ -233,7 +233,7 @@ void tlsCleanup(void) { static int tlsPasswordCallback(char *buf, int size, int rwflag, void *u) { UNUSED(rwflag); - const char *pass = u; + const char *pass = (const char*)u; size_t pass_len; if (!pass) return -1; @@ -253,7 +253,6 @@ static SSL_CTX *createSSLContext(redisTLSContextConfig *ctx_config, int protocol const char *key_file_pass = client ? ctx_config->client_key_file_pass : ctx_config->key_file_pass; char errbuf[256]; SSL_CTX *ctx = NULL; - int protocols; ctx = SSL_CTX_new(SSLv23_method()); @@ -331,6 +330,7 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) { char errbuf[256]; SSL_CTX *ctx = NULL; SSL_CTX *client_ctx = NULL; + int protocols; if (!ctx_config->cert_file) { serverLog(LL_WARNING, "No tls-cert-file configured!"); @@ -342,13 +342,13 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) { goto error; } - if (((server.tls_auth_clients != TLS_CLIENT_AUTH_NO) || server.tls_cluster || server.tls_replication) && + if (((g_pserver->tls_auth_clients != TLS_CLIENT_AUTH_NO) || g_pserver->tls_cluster || g_pserver->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); + protocols = parseProtocolsConfig(ctx_config->protocols); if (protocols == -1) goto error; /* Create server side/generla context */ @@ -359,7 +359,7 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) { 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); + SSL_CTX_set_session_id_context(ctx, (const unsigned char *) "KeyDB", 5); } else { SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); } @@ -413,6 +413,7 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) { SSL_CTX_free(redis_tls_client_ctx); redis_tls_ctx = ctx; redis_tls_client_ctx = client_ctx; + } return C_OK; @@ -496,14 +497,6 @@ connection *connCreateTLS(void) { return createTLSConnection(1); } -/* Fetch the latest OpenSSL error and store it in the connection */ -static void updateTLSError(tls_connection *conn) { - conn->c.last_errno = 0; - if (conn->ssl_error) zfree(conn->ssl_error); - conn->ssl_error = zmalloc(512); - ERR_error_string_n(ERR_get_error(), conn->ssl_error, 512); -} - /* Create a new TLS connection that is already associated with * an accepted underlying file descriptor. *