Redis 开启 TLS

实验环境为 Redis 7.0。

让当前节点支持 TLS ,需要添加参数:

tls-port 6380
# 单向 TLS,客户端验证服务端,服务端不需要验证客户端
tls-auth-clients no
tls-cert-file "/etc/redis/tls/server.crt"
tls-key-file "/etc/redis/tls/server.key"
tls-ca-cert-file "/etc/redis/tls/ca.crt"

这个时候,客户端(包括 replica )可以通过 6380 端口进行 TLS 访问。

主从

稍微复杂一点,对于主从结构,需要告诉 replica 节点是否使用 TLS 连接, replica 参数修改:

# master 节点信息
replicaof {MASTER_IP}:{MASTER_TLS_PORT}
# 是否使用 TLS 协议连接 master
tls-replication yes

HA

redis 节点类似, sentinel 也支持同时开启非 TLS TLS 端口:

tls-port 26380
tls-auth-clients no
tls-cert-file "/etc/redis/tls/server.crt"
tls-key-file "/etc/redis/tls/server.key"
tls-ca-cert-file "/etc/redis/tls/ca.crt"

同时,可以通过 tls-replication yes 控制 sentinel 访问其他服务的协议:

  • 访问 redis 服务使用 TLS 协议
  • 访问 sentinel 服务使用 TLS 协议
  • master/replica``channel **__sentinel__:hello** **发送自身的端口号是 ** **TLS** 端口

Cluster

cluster 增加了 cluster port ,通过这个端口集群中的节点可以互相交换更新信息。每个节点开启自身的 TLS

tls-port 6380
# 单向 TLS,客户端验证服务端,服务端不需要验证客户端
tls-auth-clients no
tls-cert-file "/etc/redis/tls/server.crt"
tls-key-file "/etc/redis/tls/server.key"
tls-ca-cert-file "/etc/redis/tls/ca.crt"

需要注意的是,此时可以通过非 TLS 使用集群,但是如果通过 TLS 使用的话, MOOVE 命令会返回 {IP}:{PLAINTEXT_PORT} ,也就是返回非 TLS 端口,这个时候,客户端通过 TLS 重定向到 {IP}:{PLAINTEXT_PORT} 就会出现错误,因为使用 TLS 协议访问非 TLS 端口。 解决办法是:

# cluster port 使用 TLS
tls-cluster yes

这样的话,无论客户端使用 TLS 或者非 TLS ,重定向都会正确:

# TLS 返回 6380 端口
> redis-cli -p 6380
127.0.0.1:6380> cluster nodes
cc79e30de86f3e3382189bdc9bffb871fc681f7b 192.168.3.216:6380@16379 myself,slave 6337cc0f450ecb76cf274f6cacae0058ecd082af 0 1680953046000 3 connected
2b657d5e8d7155d0d1702397142fa6d255d51d42 192.168.3.213:6380@16379 master - 0 1680953043000 2 connected 5461-10922
88ef8e929af72575465a2a16ab76f0e8b1f28778 192.168.3.214:6380@16379 slave 2b657d5e8d7155d0d1702397142fa6d255d51d42 0 1680953045002 2 connected
723e655feda26443d83468811ce714040a1f6115 192.168.3.217:6380@16379 slave ffd94f2da729711752872ff9daaa6da286017ed6 0 1680953043000 1 connected
ffd94f2da729711752872ff9daaa6da286017ed6 192.168.3.212:6380@16379 master - 0 1680953046006 1 connected 0-5460
6337cc0f450ecb76cf274f6cacae0058ecd082af 192.168.3.215:6380@16379 master - 0 1680953043997 3 connected 10923-16383
# 非 TLS 返回 6379 端口
> redis-cli -p 6379
127.0.0.1:6379> cluster nodes
cc79e30de86f3e3382189bdc9bffb871fc681f7b 192.168.3.216:6379@16379 myself,slave 6337cc0f450ecb76cf274f6cacae0058ecd082af 0 1680953061000 3 connected
2b657d5e8d7155d0d1702397142fa6d255d51d42 192.168.3.213:6379@16379 master - 0 1680953064082 2 connected 5461-10922
88ef8e929af72575465a2a16ab76f0e8b1f28778 192.168.3.214:6379@16379 slave 2b657d5e8d7155d0d1702397142fa6d255d51d42 0 1680953063000 2 connected
723e655feda26443d83468811ce714040a1f6115 192.168.3.217:6379@16379 slave ffd94f2da729711752872ff9daaa6da286017ed6 0 1680953063000 1 connected
ffd94f2da729711752872ff9daaa6da286017ed6 192.168.3.212:6379@16379 master - 0 1680953062000 1 connected 0-5460
6337cc0f450ecb76cf274f6cacae0058ecd082af 192.168.3.215:6379@16379 master - 0 1680953063078 3 connected 10923-16383

目前,就可以实现集群内部使用 TLS,外部同时支持两种协议。 主从在集群中也比较特殊,想一下:从节点如何获取主节点的端口。 其实和上面很像,如果 tls-cluster yes 返回 6380 ,否则返回 6379 。 这个时候就需要注意 tls-replication 这个配置,它决定了 replica 使用什么协议连接 master

  • no ,且 tls-cluster yes ,那么使用非 TLS 连接 master 6380 端口,自然连接不上。
  • yes ,且 tls-cluster yes ,那么使用 TLS 连接 master 6380 端口,正常。

所以设置 tls-cluster yes 时,需要设置 tls-replication yes

一个有意思的现象:如果查看 cluster config 文件:

  • tls-cluster yes 时,里面所有节点的 IP 显示的都是 6380
  • tls-cluster no 时,里面所有节点的 IP 显示的都是 6379

那么问题来了,既然 cluster config 存的就是 cluster nodes 信息,那为什么上文执行 cluster nodes 可以根据客户端是否通过 TLS 连接返回不同的端口呢? cluster config 中每一行对应 clusterNode 对象:

typedef struct clusterNode {
    mstime_t ctime; /* Node object creation time. */
    char name[CLUSTER_NAMELEN]; /* Node name, hex string, sha1-size */
    int flags;      /* CLUSTER_NODE_... */
    uint64_t configEpoch; /* Last configEpoch observed for this node */
    unsigned char slots[CLUSTER_SLOTS/8]; /* slots handled by this node */
    uint16_t *slot_info_pairs; /* Slots info represented as (start/end) pair (consecutive index). */
    int slot_info_pairs_count; /* Used number of slots in slot_info_pairs */
    int numslots;   /* Number of slots handled by this node */
    int numslaves;  /* Number of slave nodes, if this is a master */
    struct clusterNode **slaves; /* pointers to slave nodes */
    struct clusterNode *slaveof; /* pointer to the master node. Note that it
may be NULL even if the node is a slave
if we don't have the master node in our
tables. */
    unsigned long long last_in_ping_gossip; /* The number of the last carried in the ping gossip section */
    mstime_t ping_sent;      /* Unix time we sent latest ping */
    mstime_t pong_received;  /* Unix time we received the pong */
    mstime_t data_received;  /* Unix time we received any data */
    mstime_t fail_time;      /* Unix time when FAIL flag was set */
    mstime_t voted_time;     /* Last time we voted for a slave of this master */
    mstime_t repl_offset_time;  /* Unix time we received offset for this node */
    mstime_t orphaned_time;     /* Starting time of orphaned master condition */
    long long repl_offset;      /* Last known repl offset for this node. */
    char ip[NET_IP_STR_LEN];    /* Latest known IP address of this node */
    sds hostname;               /* The known hostname 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 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 */
    list *fail_reports;         /* List of nodes signaling this as failing */
} clusterNode;
  • port tls-cluster no 时保存非 TLS 端口,否则保存 TLS 端口
  • pport tls-cluster no 时不使用,否则保存非 TLS 端口
  • cport :保存 cluster port

cluster config 一直保存着 port 这个值,但是如果执行 cluster nodes 会根据客户端的协议以及 tls-cluster 选择返回 port 还是 pport