bigywater.tech
Redis · 127

Redis TLS 连接

给 Redis 加 TLS,配起来不难,难的是知道每个开关到底挡住了什么、代价多大。这篇用 openssl 自己生成一套测试 CA 和证书,在本机跑通明文口 + TLS 口并存,把证书校验、双向认证、握手与往返代价、以及把证书 CN 映射到 ACL 用户都量一遍。

0.14 → 0.86 ms
新建连接 + 1 次 PING 的中位耗时:明文 → TLS(本机回环)
24 → 26 µs
已建立连接上一次 PING 往返的中位耗时:明文 → TLS
回落 default
开了 tls-auth-clients-user CN,证书 CN 没有对应用户时,连接不会被拒,而是当作 default 用户

测试证书

实测 · 本机执行输出
  ca.crt      subject=CN=Test Root CA | issuer=CN=Test Root CA
  server.crt  subject=CN=redis-server | issuer=CN=Test Root CA
  client.crt  subject=CN=svc-app | issuer=CN=Test Root CA
  rogue.crt   subject=CN=svc-app | issuer=CN=Rogue CA
  server.crt SAN: DNS:localhost, IP Address:127.0.0.1

全部是本机 openssl 现生成的测试证书(EC P-256,30 天):一个 CA,一张 SAN 为 localhost 和 127.0.0.1 的服务端证书,一张 CN=svc-app 的客户端证书,以及另一个 CA(rogue)签的同名 CN 证书,用来验证「CN 相同但 CA 不受信任」的情形。

服务端证书校验

实测 · 本机执行输出
  明文 redis-cli 连 TLS 口                         → Error: Connection reset by peer
  --tls 但没给 --cacert                           → Could not connect to Redis at 127.0.0.1:9152: SSL_connect failed: certificate verify failed
  --tls --cacert ca.crt                        → PONG
  --tls --cacert(不相关的 CA)                      → Could not connect to Redis at 127.0.0.1:9152: SSL_connect failed: certificate verify failed
  --tls --insecure(不校验服务端证书)                   → PONG
  明文 redis-cli 连明文口                            → PONG
  用原始套接字向 TLS 口发明文 PING: ConnectionResetError (没有任何回复,或连接被关)

TLS 口拒绝明文协议(连接被重置)。客户端不给 CA、给了不相关的 CA,都是 certificate verify failed。--insecure 能连上,但客户端什么都不校验——它防不了中间人,只在测试里用。

客户端证书:tls-auth-clients

实测 · 本机执行输出
  tls-auth-clients = yes
  yes:不带客户端证书                                  → Error: Server closed the connection
  yes:带受信任 CA 签的客户端证书                          → PONG
  yes:带不受信任 CA 签的证书(CN 也叫 svc-app)             → Error: Server closed the connection
  optional:不带证书                                → PONG
  optional:带不受信任的证书                            → Error: Server closed the connection
  openssl verify -CAfile ca.crt rogue.crt → CN=svc-app / error 20 at 0 depth lookup: unable to get local issuer certificate / error rogue.crt: verification failed

yes 要求每个客户端出示受信任 CA 签发的证书,没有或不受信任都在握手阶段被拒(服务端直接关闭连接,客户端看到 Server closed the connection)。optional 允许不出示,但出示了就必须可信。注意 rogue 证书的 CN 与合法证书相同也没用,信任来自签发者。

协议版本与主机名

实测 · 本机执行输出
  协议 TLSv1.3  套件 TLS_AES_256_GCM_SHA384
  server_hostname=wrong.example → [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'w
  客户端只允许 TLSv1_2 → TLSv1.2
  客户端只允许 TLSv1_3 → TLSv1.3
  服务端 tls-protocols 配置: ['tls-protocols']

握手协商到 TLS 1.3。本文没有测试强制服务端只允许旧版本(tls-protocols 保持默认为空)。主机名校验是客户端的事:证书里没有的名字握手就失败。

代价

实测 · 本机执行输出
  新建连接 + 1 次 PING(300 次):明文 中位 0.14 ms / p99 0.35 ms;TLS 中位 0.86 ms / p99 1.09 ms
  已建立连接上的 PING 往返(20000 次):明文 中位 24 µs / p99 42 µs;TLS 中位 26 µs / p99 41 µs
  redis-benchmark(-c 50 -n 200000,无流水线)明文: SET: 113507 req/s p50=0.287 ms | GET: 117096 req/s p50=0.279 ms
  redis-benchmark 同参数 TLS: SET: 94429 req/s p50=0.327 ms | GET: 96899 req/s p50=0.319 ms

建连的代价明显(握手),已建立连接上单次往返的差别很小,吞吐下降的幅度见上面的 redis-benchmark 数字。都是本机回环、单次运行的数字,不是通用结论;真实网络里延迟主要来自往返本身。

证书 CN → ACL 用户

实测 · 本机执行输出
  CONFIG GET tls-auth-clients-user → tls-auth-clients-user off
  映射关闭(off)时,带 CN=svc-app 的证书,ACL WHOAMI                     → default
  CONFIG SET tls-auth-clients-user CN → OK
  映射 CN 时,带 CN=svc-app 的证书,ACL WHOAMI                        → NOPERM User svc-app has no permissions to run the 'acl|whoami' command
    同一证书的连接 SET app:1(用户有 +set ~app:*)                       → OK
    同一证书的连接 SET other(没有权限的 key)                             → NOPERM No permissions to access a key
  映射 CN 时,带 CN=nobody 的证书(没有这个用户)                            → default
  带证书但又显式 AUTH default(此处 default 是 nopass)                  → default / Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
  INFO stats: ['acl_access_denied_auth:0', 'acl_access_denied_tls_cert:1']

8.10.2 里有 tls-auth-clients-user(默认 off):设成 CN 后,客户端证书的 CN 直接作为登录的 ACL 用户名,不用再 AUTH。关键的坑:CN 对应的用户不存在时,连接没有被拒绝,而是当作 default 用户继续用——只有把 default 用户关掉或设了密码,这里才安全(INFO 里的 acl_access_denied_tls_cert 会计数)。

分步演示:TLS 的每一层

Redis TLS
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Redis 8.10.2 独立实例(本机回环端口,save 关闭),redis-cli 风格输出由自写的 RESP 客户端打印;计时用 time.perf_counter,数字每次运行会有小幅抖动。openssl 3.6.4,证书为测试用 EC P-256;明文口和 TLS 口在同一个实例上
    • 没有验证:tls-replication / tls-cluster、tls-protocols 限制旧版本、tls-ciphers 定制、证书轮换(tls-cert-file 变更后 CONFIG SET / 重载)、CRL / OCSP、跨机器网络下的延迟、其它客户端库的默认校验行为
    京ICP备2026067502号-1京公网安备11010502063487号