Support TLS service when "tls-cluster" is not enabled and persist both plain and TLS port in nodes.conf (#12233)

Originally, when "tls-cluster" is enabled, `port` is set to TLS port. In order to support non-TLS clients, `pport` is used to propagate TCP port across cluster nodes. However when "tls-cluster" is disabled, `port` is set to TCP port, and `pport` is not used, which means the cluster cannot provide TLS service unless "tls-cluster" is on.
```
typedef struct {
    // ...
    uint16_t port;  /* Latest known clients port (TLS or plain). */
    uint16_t pport; /* Latest known clients plaintext port. Only used if the main clients port is for TLS. */
    // ...
} clusterNode;
```
```
typedef struct {
    // ...
    uint16_t port;   /* TCP base port number. */
    uint16_t pport;  /* Sender TCP plaintext port, if base port is TLS */
    // ...
} clusterMsg;
```
This PR renames `port` and `pport` in `clusterNode` to `tcp_port` and `tls_port`, to record both ports no matter "tls-cluster" is enabled or disabled.

This allows to provide TLS service to clients when "tls-cluster" is disabled: when displaying cluster topology, or giving `MOVED` error, server can provide TLS or TCP port according to client's connection type, no matter what type of connection cluster bus is using.

For backwards compatibility, `port` and `pport` in `clusterMsg` are preserved, when "tls-cluster" is enabled, `port` is set to TLS port and `pport` is set to TCP port, when "tls-cluster" is disabled, `port` is set to TCP port and `pport` is set to TLS port (instead of 0).

Also, in the nodes.conf file, a new aux field displaying an extra port is added to complete the persisted info. We may have `tls_port=xxxxx` or `tcp_port=xxxxx` in the aux field, to complete the cluster topology, while the other port is stored in the normal `<ip>:<port>` field. The format is shown below.
```
<node-id> <ip>:<tcp_port>@<cport>,<hostname>,shard-id=...,tls-port=6379 myself,master - 0 0 0 connected 0-1000
```
Or we can switch the position of two ports, both can be correctly resolved.
```
<node-id> <ip>:<tls_port>@<cport>,<hostname>,shard-id=...,tcp-port=6379 myself,master - 0 0 0 connected 0-1000
```
This commit is contained in:
Chen Tianjie 2023-06-26 22:43:38 +08:00 committed by GitHub
parent 9600553ef2
commit 22a29935ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 334 additions and 96 deletions

View File

@ -31,6 +31,7 @@
#include "server.h"
#include "cluster.h"
#include "endianconv.h"
#include "connection.h"
#include <sys/types.h>
#include <sys/socket.h>
@ -92,8 +93,30 @@ int auxShardIdPresent(clusterNode *n);
int auxHumanNodenameSetter(clusterNode *n, void *value, int length);
sds auxHumanNodenameGetter(clusterNode *n, sds s);
int auxHumanNodenamePresent(clusterNode *n);
int auxTcpPortSetter(clusterNode *n, void *value, int length);
sds auxTcpPortGetter(clusterNode *n, sds s);
int auxTcpPortPresent(clusterNode *n);
int auxTlsPortSetter(clusterNode *n, void *value, int length);
sds auxTlsPortGetter(clusterNode *n, sds s);
int auxTlsPortPresent(clusterNode *n);
static void clusterBuildMessageHdr(clusterMsg *hdr, int type, size_t msglen);
int getNodeDefaultClientPort(clusterNode *n) {
return server.tls_cluster ? n->tls_port : n->tcp_port;
}
static inline int getNodeDefaultReplicationPort(clusterNode *n) {
return server.tls_replication ? n->tls_port : n->tcp_port;
}
static inline int getNodeClientPort(clusterNode *n, int use_tls) {
return use_tls ? n->tls_port : n->tcp_port;
}
static inline int defaultClientPort(void) {
return server.tls_cluster ? server.tls_port : server.port;
}
/* Links to the next and previous entries for keys in the same slot are stored
* in the dict entry metadata. See Slot to Key API below. */
#define dictEntryNextInSlot(de) \
@ -176,6 +199,8 @@ typedef struct {
typedef enum {
af_shard_id,
af_human_nodename,
af_tcp_port,
af_tls_port,
af_count,
} auxFieldIndex;
@ -186,6 +211,8 @@ typedef enum {
auxFieldHandler auxFieldHandlers[] = {
{"shard-id", auxShardIdSetter, auxShardIdGetter, auxShardIdPresent},
{"nodename", auxHumanNodenameSetter, auxHumanNodenameGetter, auxHumanNodenamePresent},
{"tcp-port", auxTcpPortSetter, auxTcpPortGetter, auxTcpPortPresent},
{"tls-port", auxTlsPortSetter, auxTlsPortGetter, auxTlsPortPresent},
};
int isValidAuxChar(int c) {
@ -247,6 +274,44 @@ int auxHumanNodenamePresent(clusterNode *n) {
return sdslen(n->human_nodename);
}
int auxTcpPortSetter(clusterNode *n, void *value, int length) {
if (length > 5 || length < 1) {
return C_ERR;
}
char buf[length + 1];
memcpy(buf, (char*)value, length);
buf[length] = '\0';
n->tcp_port = atoi(buf);
return (n->tcp_port < 0 || n->tcp_port >= 65536) ? C_ERR : C_OK;
}
sds auxTcpPortGetter(clusterNode *n, sds s) {
return sdscatprintf(s, "%d", n->tcp_port);
}
int auxTcpPortPresent(clusterNode *n) {
return n->tcp_port >= 0 && n->tcp_port < 65536;
}
int auxTlsPortSetter(clusterNode *n, void *value, int length) {
if (length > 5 || length < 1) {
return C_ERR;
}
char buf[length + 1];
memcpy(buf, (char*)value, length);
buf[length] = '\0';
n->tls_port = atoi(buf);
return (n->tls_port < 0 || n->tls_port >= 65536) ? C_ERR : C_OK;
}
sds auxTlsPortGetter(clusterNode *n, sds s) {
return sdscatprintf(s, "%d", n->tls_port);
}
int auxTlsPortPresent(clusterNode *n) {
return n->tls_port >= 0 && n->tls_port < 65536;
}
/* clusterLink send queue blocks */
typedef struct {
size_t totlen; /* Total length of this block including the message */
@ -376,7 +441,8 @@ int clusterLoadConfig(char *filename) {
* the format of "aux=val" where both aux and val can contain
* characters that pass the isValidAuxChar check only. The order
* of the aux fields is insignificant. */
int aux_tcp_port = 0;
int aux_tls_port = 0;
for (int i = 2; i < aux_argc; i++) {
int field_argc;
sds *field_argv;
@ -407,6 +473,8 @@ int clusterLoadConfig(char *filename) {
continue;
}
field_found = 1;
aux_tcp_port |= j == af_tcp_port;
aux_tls_port |= j == af_tls_port;
if (auxFieldHandlers[j].setter(n, field_argv[1], sdslen(field_argv[1])) != C_OK) {
/* Invalid aux field format */
sdsfreesplitres(field_argv, field_argc);
@ -438,11 +506,23 @@ int clusterLoadConfig(char *filename) {
*busp = '\0';
busp++;
}
n->port = atoi(port);
/* If neither TCP or TLS port is found in aux field, it is considered
* an old version of nodes.conf file.*/
if (!aux_tcp_port && !aux_tls_port) {
if (server.tls_cluster) {
n->tls_port = atoi(port);
} else {
n->tcp_port = atoi(port);
}
} else if (!aux_tcp_port) {
n->tcp_port = atoi(port);
} else if (!aux_tls_port) {
n->tls_port = atoi(port);
}
/* In older versions of nodes.conf the "@busport" part is missing.
* In this case we set it to the default offset of 10000 from the
* base port. */
n->cport = busp ? atoi(busp) : n->port + CLUSTER_PORT_INCR;
n->cport = busp ? atoi(busp) : (getNodeDefaultClientPort(n) + CLUSTER_PORT_INCR);
/* The plaintext port for client in a TLS cluster (n->pport) is not
* stored in nodes.conf. It is received later over the bus protocol. */
@ -747,23 +827,20 @@ int clusterLockConfig(char *filename) {
}
/* Derives our ports to be announced in the cluster bus. */
void deriveAnnouncedPorts(int *announced_port, int *announced_pport,
void deriveAnnouncedPorts(int *announced_tcp_port, int *announced_tls_port,
int *announced_cport) {
int port = server.tls_cluster ? server.tls_port : server.port;
/* Default announced ports. */
*announced_port = port;
*announced_pport = server.tls_cluster ? server.port : 0;
*announced_cport = server.cluster_port ? server.cluster_port : port + CLUSTER_PORT_INCR;
/* Config overriding announced ports. */
if (server.tls_cluster && server.cluster_announce_tls_port) {
*announced_port = server.cluster_announce_tls_port;
*announced_pport = server.cluster_announce_port;
} else if (server.cluster_announce_port) {
*announced_port = server.cluster_announce_port;
}
*announced_tcp_port = server.cluster_announce_port ?
server.cluster_announce_port : server.port;
*announced_tls_port = server.cluster_announce_tls_port ?
server.cluster_announce_tls_port : server.tls_port;
/* Derive cluster bus port. */
if (server.cluster_announce_bus_port) {
*announced_cport = server.cluster_announce_bus_port;
} else if (server.cluster_port) {
*announced_cport = server.cluster_port;
} else {
*announced_cport = defaultClientPort() + CLUSTER_PORT_INCR;
}
}
@ -790,7 +867,7 @@ void clusterUpdateMyselfFlags(void) {
* The option can be set at runtime via CONFIG SET. */
void clusterUpdateMyselfAnnouncedPorts(void) {
if (!myself) return;
deriveAnnouncedPorts(&myself->port,&myself->pport,&myself->cport);
deriveAnnouncedPorts(&myself->tcp_port,&myself->tls_port,&myself->cport);
}
/* We want to take myself->ip in sync with the cluster-announce-ip option.
@ -938,7 +1015,7 @@ void clusterInit(void) {
/* Port sanity check II
* The other handshake port check is triggered too late to stop
* us from trying to use a too-high cluster port number. */
int port = server.tls_cluster ? server.tls_port : server.port;
int port = defaultClientPort();
if (!server.cluster_port && port > (65535-CLUSTER_PORT_INCR)) {
serverLog(LL_WARNING, "Redis port number too high. "
"Cluster communication port is 10,000 port "
@ -959,7 +1036,7 @@ void clusterInit(void) {
/* Set myself->port/cport/pport to my listening ports, we'll just need to
* discover the IP address via MEET messages. */
deriveAnnouncedPorts(&myself->port, &myself->pport, &myself->cport);
deriveAnnouncedPorts(&myself->tcp_port, &myself->tls_port, &myself->cport);
server.cluster->mf_end = 0;
server.cluster->mf_slave = NULL;
@ -976,7 +1053,7 @@ void clusterInitListeners(void) {
exit(1);
}
int port = server.tls_cluster ? server.tls_port : server.port;
int port = defaultClientPort();
connListener *listener = &server.clistener;
listener->count = 0;
listener->bindaddr = server.bindaddr;
@ -1306,9 +1383,9 @@ clusterNode *createClusterNode(char *nodename, int flags) {
memset(node->ip,0,sizeof(node->ip));
node->hostname = sdsempty();
node->human_nodename = sdsempty();
node->port = 0;
node->tcp_port = 0;
node->cport = 0;
node->pport = 0;
node->tls_port = 0;
node->fail_reports = listCreate();
node->voted_time = 0;
node->orphaned_time = 0;
@ -1927,7 +2004,7 @@ int clusterHandshakeInProgress(char *ip, int port, int cport) {
if (!nodeInHandshake(node)) continue;
if (!strcasecmp(node->ip,ip) &&
node->port == port &&
getNodeDefaultClientPort(node) == port &&
node->cport == cport) break;
}
dictReleaseIterator(di);
@ -1988,12 +2065,36 @@ int clusterStartHandshake(char *ip, int port, int cport) {
* handshake. */
n = createClusterNode(NULL,CLUSTER_NODE_HANDSHAKE|CLUSTER_NODE_MEET);
memcpy(n->ip,norm_ip,sizeof(n->ip));
n->port = port;
if (server.tls_cluster) {
n->tls_port = port;
} else {
n->tcp_port = port;
}
n->cport = cport;
clusterAddNode(n);
return 1;
}
static void getClientPortFromClusterMsg(clusterMsg *hdr, int *tls_port, int *tcp_port) {
if (server.tls_cluster) {
*tls_port = ntohs(hdr->port);
*tcp_port = ntohs(hdr->pport);
} else {
*tls_port = ntohs(hdr->pport);
*tcp_port = ntohs(hdr->port);
}
}
static void getClientPortFromGossip(clusterMsgDataGossip *g, int *tls_port, int *tcp_port) {
if (server.tls_cluster) {
*tls_port = ntohs(g->port);
*tcp_port = ntohs(g->pport);
} else {
*tls_port = ntohs(g->pport);
*tcp_port = ntohs(g->port);
}
}
/* Process the gossip section of PING or PONG packets.
* Note that this function assumes that the packet is already sanity-checked
* by the caller, not in the content of the gossip section, but in the
@ -2019,6 +2120,10 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {
sdsfree(ci);
}
/* Convert port and pport into TCP port and TLS port. */
int msg_tls_port, msg_tcp_port;
getClientPortFromGossip(g, &msg_tls_port, &msg_tcp_port);
/* Update our state accordingly to the gossip sections */
node = clusterLookupNode(g->nodename, CLUSTER_NAMELEN);
if (node) {
@ -2072,13 +2177,14 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {
!(flags & CLUSTER_NODE_NOADDR) &&
!(flags & (CLUSTER_NODE_FAIL|CLUSTER_NODE_PFAIL)) &&
(strcasecmp(node->ip,g->ip) ||
node->port != ntohs(g->port) ||
node->tls_port != (server.tls_cluster ? ntohs(g->port) : ntohs(g->pport)) ||
node->tcp_port != (server.tls_cluster ? ntohs(g->pport) : ntohs(g->port)) ||
node->cport != ntohs(g->cport)))
{
if (node->link) freeClusterLink(node->link);
memcpy(node->ip,g->ip,NET_IP_STR_LEN);
node->port = ntohs(g->port);
node->pport = ntohs(g->pport);
node->tcp_port = msg_tcp_port;
node->tls_port = msg_tls_port;
node->cport = ntohs(g->cport);
node->flags &= ~CLUSTER_NODE_NOADDR;
}
@ -2099,8 +2205,8 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {
clusterNode *node;
node = createClusterNode(g->nodename, flags);
memcpy(node->ip,g->ip,NET_IP_STR_LEN);
node->port = ntohs(g->port);
node->pport = ntohs(g->pport);
node->tcp_port = msg_tcp_port;
node->tls_port = msg_tls_port;
node->cport = ntohs(g->cport);
clusterAddNode(node);
}
@ -2145,9 +2251,9 @@ int nodeUpdateAddressIfNeeded(clusterNode *node, clusterLink *link,
clusterMsg *hdr)
{
char ip[NET_IP_STR_LEN] = {0};
int port = ntohs(hdr->port);
int pport = ntohs(hdr->pport);
int cport = ntohs(hdr->cport);
int tcp_port, tls_port;
getClientPortFromClusterMsg(hdr, &tls_port, &tcp_port);
/* We don't proceed if the link is the same as the sender link, as this
* function is designed to see if the node link is consistent with the
@ -2162,23 +2268,23 @@ int nodeUpdateAddressIfNeeded(clusterNode *node, clusterLink *link,
* in the next round of PINGs */
if (nodeIp2String(ip,link,hdr->myip) == C_ERR) return 0;
if (node->port == port && node->cport == cport && node->pport == pport &&
if (node->tcp_port == tcp_port && node->cport == cport && node->tls_port == tls_port &&
strcmp(ip,node->ip) == 0) return 0;
/* IP / port is different, update it. */
memcpy(node->ip,ip,sizeof(ip));
node->port = port;
node->pport = pport;
node->tcp_port = tcp_port;
node->tls_port = tls_port;
node->cport = cport;
if (node->link) freeClusterLink(node->link);
node->flags &= ~CLUSTER_NODE_NOADDR;
serverLog(LL_NOTICE,"Address updated for node %.40s (%s), now %s:%d",
node->name, node->human_nodename, node->ip, node->port);
node->name, node->human_nodename, node->ip, getNodeDefaultClientPort(node));
/* Check if this is our master and we have to change the
* replication target as well. */
if (nodeIsSlave(myself) && myself->slaveof == node)
replicationSetMaster(node->ip, node->port);
replicationSetMaster(node->ip, getNodeDefaultReplicationPort(node));
return 1;
}
@ -2739,8 +2845,7 @@ int clusterProcessPacket(clusterLink *link) {
node = createClusterNode(NULL,CLUSTER_NODE_HANDSHAKE);
serverAssert(nodeIp2String(node->ip,link,hdr->myip) == C_OK);
node->port = ntohs(hdr->port);
node->pport = ntohs(hdr->pport);
getClientPortFromClusterMsg(hdr, &node->tls_port, &node->tcp_port);
node->cport = ntohs(hdr->cport);
clusterAddNode(node);
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);
@ -2802,8 +2907,8 @@ int clusterProcessPacket(clusterLink *link) {
link->node->flags);
link->node->flags |= CLUSTER_NODE_NOADDR;
link->node->ip[0] = '\0';
link->node->port = 0;
link->node->pport = 0;
link->node->tcp_port = 0;
link->node->tls_port = 0;
link->node->cport = 0;
freeClusterLink(link);
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);
@ -3351,15 +3456,20 @@ static void clusterBuildMessageHdr(clusterMsg *hdr, int type, size_t msglen) {
}
/* Handle cluster-announce-[tls-|bus-]port. */
int announced_port, announced_pport, announced_cport;
deriveAnnouncedPorts(&announced_port, &announced_pport, &announced_cport);
int announced_tcp_port, announced_tls_port, announced_cport;
deriveAnnouncedPorts(&announced_tcp_port, &announced_tls_port, &announced_cport);
memcpy(hdr->myslots,master->slots,sizeof(hdr->myslots));
memset(hdr->slaveof,0,CLUSTER_NAMELEN);
if (myself->slaveof != NULL)
memcpy(hdr->slaveof,myself->slaveof->name, CLUSTER_NAMELEN);
hdr->port = htons(announced_port);
hdr->pport = htons(announced_pport);
if (server.tls_cluster) {
hdr->port = htons(announced_tls_port);
hdr->pport = htons(announced_tcp_port);
} else {
hdr->port = htons(announced_tcp_port);
hdr->pport = htons(announced_tls_port);
}
hdr->cport = htons(announced_cport);
hdr->flags = htons(myself->flags);
hdr->state = server.cluster->state;
@ -3391,10 +3501,15 @@ void clusterSetGossipEntry(clusterMsg *hdr, int i, clusterNode *n) {
gossip->ping_sent = htonl(n->ping_sent/1000);
gossip->pong_received = htonl(n->pong_received/1000);
memcpy(gossip->ip,n->ip,sizeof(n->ip));
gossip->port = htons(n->port);
if (server.tls_cluster) {
gossip->port = htons(n->tls_port);
gossip->pport = htons(n->tcp_port);
} else {
gossip->port = htons(n->tcp_port);
gossip->pport = htons(n->tls_port);
}
gossip->cport = htons(n->cport);
gossip->flags = htons(n->flags);
gossip->pport = htons(n->pport);
gossip->notused1 = 0;
}
@ -4614,7 +4729,7 @@ void clusterCron(void) {
myself->slaveof &&
nodeHasAddr(myself->slaveof))
{
replicationSetMaster(myself->slaveof->ip, myself->slaveof->port);
replicationSetMaster(myself->slaveof->ip, getNodeDefaultReplicationPort(myself->slaveof));
}
/* Abort a manual failover if the timeout is reached. */
@ -5017,7 +5132,7 @@ void clusterSetMaster(clusterNode *n) {
myself->slaveof = n;
updateShardId(myself, n->shard_id);
clusterNodeAddSlave(n,myself);
replicationSetMaster(n->ip, n->port);
replicationSetMaster(n->ip, getNodeDefaultReplicationPort(n));
resetManualFailover();
}
@ -5076,10 +5191,10 @@ sds representSlotInfo(sds ci, uint16_t *slot_info_pairs, int slot_info_pairs_cou
* See clusterGenNodesDescription() top comment for more information.
*
* The function returns the string representation as an SDS string. */
sds clusterGenNodeDescription(client *c, clusterNode *node, int use_pport) {
sds clusterGenNodeDescription(client *c, clusterNode *node, int tls_primary) {
int j, start;
sds ci;
int port = use_pport && node->pport ? node->pport : node->port;
int port = getNodeClientPort(node, tls_primary);
/* Node coordinates */
ci = sdscatlen(sdsempty(),node->name,CLUSTER_NAMELEN);
@ -5097,6 +5212,9 @@ sds clusterGenNodeDescription(client *c, clusterNode *node, int use_pport) {
* to be persisted to nodes.conf */
if (c == NULL) {
for (int i = af_count-1; i >=0; i--) {
if ((tls_primary && i == af_tls_port) || (!tls_primary && i == af_tcp_port)) {
continue;
}
if (auxFieldHandlers[i].isPresent(node)) {
ci = sdscatprintf(ci, ",%s=", auxFieldHandlers[i].field);
ci = auxFieldHandlers[i].getter(node, ci);
@ -5219,13 +5337,13 @@ void clusterFreeNodesSlotsInfo(clusterNode *n) {
* include all the known nodes in the representation, including nodes in
* the HANDSHAKE state.
*
* Setting use_pport to 1 in a TLS cluster makes the result contain the
* plaintext client port rather then the TLS client port of each node.
* Setting tls_primary to 1 to put TLS port in the main <ip>:<port>
* field and put TCP port in aux field, instead of the opposite way.
*
* The representation obtained using this function is used for the output
* of the CLUSTER NODES function, and as format for the cluster
* configuration file (nodes.conf) for a given node. */
sds clusterGenNodesDescription(client *c, int filter, int use_pport) {
sds clusterGenNodesDescription(client *c, int filter, int tls_primary) {
sds ci = sdsempty(), ni;
dictIterator *di;
dictEntry *de;
@ -5238,7 +5356,7 @@ sds clusterGenNodesDescription(client *c, int filter, int use_pport) {
clusterNode *node = dictGetVal(de);
if (node->flags & filter) continue;
ni = clusterGenNodeDescription(c, node, use_pport);
ni = clusterGenNodeDescription(c, node, tls_primary);
ci = sdscatsds(ci,ni);
sdsfree(ni);
ci = sdscatlen(ci,"\n",1);
@ -5426,10 +5544,8 @@ void addNodeToNodeReply(client *c, clusterNode *node) {
serverPanic("Unrecognized preferred endpoint type");
}
/* Report non-TLS ports to non-TLS client in TLS cluster if available. */
int use_pport = (server.tls_cluster &&
c->conn && (c->conn->type != connectionTypeTls()));
addReplyLongLong(c, use_pport && node->pport ? node->pport : node->port);
/* Report TLS ports to TLS client, and report non-TLS port to non-TLS client. */
addReplyLongLong(c, getNodeClientPort(node, connIsTLS(c->conn)));
addReplyBulkCBuffer(c, node->name, CLUSTER_NAMELEN);
/* Add the additional endpoint information, this is all the known networking information
@ -5492,19 +5608,15 @@ void addNodeDetailsToShardReply(client *c, clusterNode *node) {
addReplyBulkCBuffer(c, node->name, CLUSTER_NAMELEN);
reply_count++;
/* We use server.tls_cluster as a proxy for whether or not
* the remote port is the tls port or not */
int plaintext_port = server.tls_cluster ? node->pport : node->port;
int tls_port = server.tls_cluster ? node->port : 0;
if (plaintext_port) {
if (node->tcp_port) {
addReplyBulkCString(c, "port");
addReplyLongLong(c, plaintext_port);
addReplyLongLong(c, node->tcp_port);
reply_count++;
}
if (tls_port) {
if (node->tls_port) {
addReplyBulkCString(c, "tls-port");
addReplyLongLong(c, tls_port);
addReplyLongLong(c, node->tls_port);
reply_count++;
}
@ -5783,14 +5895,14 @@ NULL
long long port, cport;
if (getLongLongFromObject(c->argv[3], &port) != C_OK) {
addReplyErrorFormat(c,"Invalid TCP base port specified: %s",
addReplyErrorFormat(c,"Invalid base port specified: %s",
(char*)c->argv[3]->ptr);
return;
}
if (c->argc == 5) {
if (getLongLongFromObject(c->argv[4], &cport) != C_OK) {
addReplyErrorFormat(c,"Invalid TCP bus port specified: %s",
addReplyErrorFormat(c,"Invalid bus port specified: %s",
(char*)c->argv[4]->ptr);
return;
}
@ -5808,11 +5920,8 @@ NULL
}
} else if (!strcasecmp(c->argv[1]->ptr,"nodes") && c->argc == 2) {
/* CLUSTER NODES */
/* Report plaintext ports, only if cluster is TLS but client is known to
* be non-TLS). */
int use_pport = (server.tls_cluster &&
c->conn && (c->conn->type != connectionTypeTls()));
sds nodes = clusterGenNodesDescription(c, 0, use_pport);
/* Report TLS ports to TLS client, and report non-TLS port to non-TLS client. */
sds nodes = clusterGenNodesDescription(c, 0, connIsTLS(c->conn));
addReplyVerbatim(c,nodes,sdslen(nodes),"txt");
sdsfree(nodes);
} else if (!strcasecmp(c->argv[1]->ptr,"myid") && c->argc == 2) {
@ -6174,12 +6283,10 @@ NULL
return;
}
/* Use plaintext port if cluster is TLS but client is non-TLS. */
int use_pport = (server.tls_cluster &&
c->conn && (c->conn->type != connectionTypeTls()));
/* Report TLS ports to TLS client, and report non-TLS port to non-TLS client. */
addReplyArrayLen(c,n->numslaves);
for (j = 0; j < n->numslaves; j++) {
sds ni = clusterGenNodeDescription(c, n->slaves[j], use_pport);
sds ni = clusterGenNodeDescription(c, n->slaves[j], connIsTLS(c->conn));
addReplyBulkCString(c,ni);
sdsfree(ni);
}
@ -7304,11 +7411,8 @@ void clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_co
} else if (error_code == CLUSTER_REDIR_MOVED ||
error_code == CLUSTER_REDIR_ASK)
{
/* Redirect to IP:port. Include plaintext port if cluster is TLS but
* client is non-TLS. */
int use_pport = (server.tls_cluster &&
c->conn && (c->conn->type != connectionTypeTls()));
int port = use_pport && n->pport ? n->pport : n->port;
/* Report TLS ports to TLS client, and report non-TLS port to non-TLS client. */
int port = getNodeClientPort(n, connIsTLS(c->conn));
addReplyErrorSds(c,sdscatprintf(sdsempty(),
"-%s %d %s:%d",
(error_code == CLUSTER_REDIR_ASK) ? "ASK" : "MOVED",

View File

@ -142,9 +142,8 @@ typedef struct clusterNode {
char ip[NET_IP_STR_LEN]; /* Latest known IP address of this node */
sds hostname; /* The known hostname for this node */
sds human_nodename; /* The known human readable nodename for this node */
int port; /* Latest known clients port (TLS or plain). */
int pport; /* Latest known clients plaintext port. Only used
if the main clients port is for TLS. */
int tcp_port; /* Latest known clients TCP port. */
int tls_port; /* Latest known clients TLS port */
int cport; /* Latest known cluster port of this node. */
clusterLink *link; /* TCP/IP link established toward this node */
clusterLink *inbound_link; /* TCP/IP link accepted from this node */
@ -226,10 +225,10 @@ typedef struct {
uint32_t ping_sent;
uint32_t pong_received;
char ip[NET_IP_STR_LEN]; /* IP address last time it was seen */
uint16_t port; /* base port last time it was seen */
uint16_t port; /* primary port last time it was seen */
uint16_t cport; /* cluster port last time it was seen */
uint16_t flags; /* node->flags copy */
uint16_t pport; /* plaintext-port, when base port is TLS */
uint16_t pport; /* secondary port last time it was seen */
uint16_t notused1;
} clusterMsgDataGossip;
@ -338,7 +337,7 @@ typedef struct {
char sig[4]; /* Signature "RCmb" (Redis Cluster message bus). */
uint32_t totlen; /* Total length of this message */
uint16_t ver; /* Protocol version, currently set to 1. */
uint16_t port; /* TCP base port number. */
uint16_t port; /* Primary port number (TCP or TLS). */
uint16_t type; /* Message type */
uint16_t count; /* Only used for some kind of messages. */
uint64_t currentEpoch; /* The epoch accordingly to the sending node. */
@ -353,7 +352,8 @@ typedef struct {
char myip[NET_IP_STR_LEN]; /* Sender IP, if not all zeroed. */
uint16_t extensions; /* Number of extensions sent along with this packet. */
char notused1[30]; /* 30 bytes reserved for future usage. */
uint16_t pport; /* Sender TCP plaintext port, if base port is TLS */
uint16_t pport; /* Secondary port number: if primary port is TCP port, this is
TLS port, and if primary port is TLS port, this is TCP port.*/
uint16_t cport; /* Sender TCP cluster bus port */
uint16_t flags; /* Sender node flags */
unsigned char state; /* Cluster state from the POV of the sender */
@ -429,9 +429,11 @@ void slotToChannelAdd(sds channel);
void slotToChannelDel(sds channel);
void clusterUpdateMyselfHostname(void);
void clusterUpdateMyselfAnnouncedPorts(void);
sds clusterGenNodesDescription(client *c, int filter, int use_pport);
sds clusterGenNodesDescription(client *c, int filter, int tls_primary);
sds genClusterInfoString(void);
void freeClusterLink(clusterLink *link);
void clusterUpdateMyselfHumanNodename(void);
int isValidAuxString(char *s, unsigned int length);
int getNodeDefaultClientPort(clusterNode *n);
#endif /* __CLUSTER_H */

View File

@ -446,4 +446,9 @@ int RedisRegisterConnectionTypeSocket(void);
int RedisRegisterConnectionTypeUnix(void);
int RedisRegisterConnectionTypeTLS(void);
/* Return 1 if connection is using TLS protocol, 0 if otherwise. */
static inline int connIsTLS(connection *conn) {
return conn && conn->type == connectionTypeTls();
}
#endif /* __REDIS_CONNECTION_H */

View File

@ -8944,7 +8944,7 @@ int RM_GetClusterNodeInfo(RedisModuleCtx *ctx, const char *id, char *ip, char *m
else
memset(master_id,0,REDISMODULE_NODE_ID_LEN);
}
if (port) *port = node->port;
if (port) *port = getNodeDefaultClientPort(node);
/* As usually we have to remap flags for modules, in order to ensure
* we can provide binary compatibility. */

View File

@ -70,9 +70,18 @@ proc continuous_slot_allocation {masters} {
# tests run.
proc cluster_setup {masters node_count slot_allocator code} {
# Have all nodes meet
for {set i 1} {$i < $node_count} {incr i} {
R 0 CLUSTER MEET [srv -$i host] [srv -$i port]
if {$::tls} {
set tls_cluster [lindex [R 0 CONFIG GET tls-cluster] 1]
}
if {$::tls && !$tls_cluster} {
for {set i 1} {$i < $node_count} {incr i} {
R 0 CLUSTER MEET [srv -$i host] [srv -$i pport]
}
} else {
for {set i 1} {$i < $node_count} {incr i} {
R 0 CLUSTER MEET [srv -$i host] [srv -$i port]
}
}
$slot_allocator $masters

View File

@ -496,7 +496,8 @@ proc start_server {options {code undefined}} {
# start every server on a different port
set port [find_available_port $::baseport $::portcount]
if {$::tls} {
dict set config "port" 0
set pport [find_available_port $::baseport $::portcount]
dict set config "port" $pport
dict set config "tls-port" $port
dict set config "tls-cluster" "yes"
dict set config "tls-replication" "yes"
@ -567,6 +568,8 @@ proc start_server {options {code undefined}} {
puts "Port $port was already busy, trying another port..."
set port [find_available_port $::baseport $::portcount]
if {$::tls} {
set pport [find_available_port $::baseport $::portcount]
dict set config port $pport
dict set config "tls-port" $port
} else {
dict set config port $port
@ -615,6 +618,9 @@ proc start_server {options {code undefined}} {
dict set srv "stdout" $stdout
dict set srv "stderr" $stderr
dict set srv "unixsocket" $unixsocket
if {$::tls} {
dict set srv "pport" $pport
}
# if a block of code is supplied, we wait for the server to become
# available, create a client object and kill the server afterwards

View File

@ -102,6 +102,7 @@ set ::all_tests {
unit/cluster/multi-slot-operations
unit/cluster/slot-ownership
unit/cluster/links
unit/cluster/cluster-response-tls
}
# Index to the next test to run in the ::all_tests list.
set ::next_test 0

View File

@ -0,0 +1,110 @@
source tests/support/cluster.tcl
proc get_port_from_moved_error {e} {
set ip_port [lindex [split $e " "] 2]
return [lindex [split $ip_port ":"] 1]
}
proc get_pport_by_port {port} {
foreach srv $::servers {
set srv_port [dict get $srv port]
if {$port == $srv_port} {
return [dict get $srv pport]
}
}
return 0
}
proc get_port_from_node_info {line} {
set fields [split $line " "]
set addr [lindex $fields 1]
set ip_port [lindex [split $addr "@"] 0]
return [lindex [split $ip_port ":"] 1]
}
proc cluster_response_tls {tls_cluster} {
test "CLUSTER SLOTS with different connection type -- tls-cluster $tls_cluster" {
set slots1 [R 0 cluster slots]
set pport [srv 0 pport]
set cluster_client [redis_cluster 127.0.0.1:$pport 0]
set slots2 [$cluster_client cluster slots]
$cluster_client close
# Compare the ports in the first row
assert_no_match [lindex $slots1 0 2 1] [lindex $slots2 0 2 1]
}
test "CLUSTER NODES return port according to connection type -- tls-cluster $tls_cluster" {
set nodes [R 0 cluster nodes]
set port1 [get_port_from_node_info [lindex [split $nodes "\r\n"] 0]]
set pport [srv 0 pport]
set cluster_client [redis_cluster 127.0.0.1:$pport 0]
set nodes [$cluster_client cluster nodes]
set port2 [get_port_from_node_info [lindex [split $nodes "\r\n"] 0]]
$cluster_client close
assert_not_equal $port1 $port2
}
set cluster [redis_cluster 127.0.0.1:[srv 0 port]]
set cluster_pport [redis_cluster 127.0.0.1:[srv 0 pport] 0]
$cluster refresh_nodes_map
test "Set many keys in the cluster -- tls-cluster $tls_cluster" {
for {set i 0} {$i < 5000} {incr i} {
$cluster set $i $i
assert { [$cluster get $i] eq $i }
}
}
test "Test cluster responses during migration of slot x -- tls-cluster $tls_cluster" {
set slot 10
array set nodefrom [$cluster masternode_for_slot $slot]
array set nodeto [$cluster masternode_notfor_slot $slot]
$nodeto(link) cluster setslot $slot importing $nodefrom(id)
$nodefrom(link) cluster setslot $slot migrating $nodeto(id)
# Get a key from that slot
set key [$nodefrom(link) cluster GETKEYSINSLOT $slot "1"]
# MOVED REPLY
catch {$nodeto(link) set $key "newVal"} e_moved1
assert_match "*MOVED*" $e_moved1
# ASK REPLY
catch {$nodefrom(link) set "abc{$key}" "newVal"} e_ask1
assert_match "*ASK*" $e_ask1
# UNSTABLE REPLY
assert_error "*TRYAGAIN*" {$nodefrom(link) mset "a{$key}" "newVal" $key "newVal2"}
# Connecting using another protocol
array set nodefrom_pport [$cluster_pport masternode_for_slot $slot]
array set nodeto_pport [$cluster_pport masternode_notfor_slot $slot]
# MOVED REPLY
catch {$nodeto_pport(link) set $key "newVal"} e_moved2
assert_match "*MOVED*" $e_moved2
# ASK REPLY
catch {$nodefrom_pport(link) set "abc{$key}" "newVal"} e_ask2
assert_match "*ASK*" $e_ask2
# Compare MOVED error's port
set port1 [get_port_from_moved_error $e_moved1]
set port2 [get_port_from_moved_error $e_moved2]
assert_not_equal $port1 $port2
assert_equal $port1 $nodefrom(port)
assert_equal $port2 [get_pport_by_port $nodefrom(port)]
# Compare ASK error's port
set port1 [get_port_from_moved_error $e_ask1]
set port2 [get_port_from_moved_error $e_ask2]
assert_not_equal $port1 $port2
assert_equal $port1 $nodeto(port)
assert_equal $port2 [get_pport_by_port $nodeto(port)]
}
}
if {$::tls} {
start_cluster 3 3 {tags {external:skip cluster tls} overrides {tls-cluster yes tls-replication yes}} {
cluster_response_tls yes
}
start_cluster 3 3 {tags {external:skip cluster tls} overrides {tls-cluster no tls-replication no}} {
cluster_response_tls no
}
}

View File

@ -404,13 +404,14 @@ start_server {tags {"other external:skip"}} {
assert_match "*/redis-server" [lindex $cmdline 1]
if {$::tls} {
set expect_port 0
set expect_port [srv 0 pport]
set expect_tls_port [srv 0 port]
set port [srv 0 pport]
} else {
set expect_port [srv 0 port]
set expect_tls_port 0
set port [srv 0 port]
}
set port [srv 0 port]
assert_equal "$::host:$port" [lindex $cmdline 2]
assert_equal $expect_port [lindex $cmdline 3]