登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  Redis

Redis TLS 客户端证书配置的连接检查

来源:17golang原创

时间:2026-10-28 08:39:07 475浏览 收藏

我第一次接手启用双向 TLS 的 Redis 连接时,最容易把三件事混在一起:客户端是否信任 Redis 服务端、Redis 是否信任客户端证书,以及 TLS 握手成功后 ACL 用户能不能执行命令。连接检查应该先把这三层拆开,再从命令行切到应用代码。

双向 TLS 的最小材料是服务端信任的 CA、客户端证书和与之匹配的私钥。--cacert 负责验证服务端证书,--cert 与 --key 负责让客户端出示身份;握手完成后,用户名和密码仍属于 Redis ACL 层。
  • 先确认:CA 文件能验证服务端证书,客户端证书由 Redis 信任的 CA 签发。
  • 再检查:redis-cli 使用 --tls、--cacert、--cert 和 --key。
  • 最后区分:TLS 错误发生在握手前,ACL 错误发生在握手后。

官方地址:https://redis.io/docs/latest/operate/oss_and_stack/management/security/encryption/

先分清服务端信任链和客户端证书

Redis 官方 TLS 文档把服务端证书、私钥和 CA 信任根分开配置。客户端连接时,--cacert 不是“客户端证书”,而是客户端用来验证服务端证书的信任材料;只有服务端要求双向认证时,才需要再提供客户端证书和私钥。

我会先把文件职责写在项目的安全配置说明里,避免把 CA 文件误当成用户证书:

材料连接检查中的职责缺失或不匹配时的现象
服务端 CA验证 Redis 服务端证书客户端无法建立可信 TLS 握手
客户端证书向 Redis 出示客户端身份服务端要求客户端认证时握手被拒绝
客户端私钥证明客户端证书确实由当前客户端持有私钥不存在、权限不足或与证书不匹配
ACL 用户密码握手完成后的 Redis 命令授权TLS 成功但 AUTH 或命令执行失败
Redis TLS 服务端证书、受信任 CA、客户端证书和私钥的静态关系说明图
图1:静态结构说明图,查看服务端信任链与客户端证书材料的关系;这不是证书文件或连接截图。

用 redis-cli 完成最小连接检查

先用命令行做最小检查,能把应用框架、连接池和配置中心的变量都排除掉。下面只使用占位文件名,真实证书路径应从安全的部署目录或密钥挂载点读取,不要把私钥内容贴进脚本。

# 使用 TLS、服务端 CA、客户端证书和私钥连接 Redis
redis-cli \
  -h "$REDIS_HOST" \
  -p "$REDIS_TLS_PORT" \
  --tls \
  --cacert "$REDIS_CA_FILE" \
  --cert "$REDIS_CLIENT_CERT" \
  --key "$REDIS_CLIENT_KEY" \
  PING

# 如果 Redis 还要求 ACL 登录,再把用户名和密码交给认证层
redis-cli \
  -h "$REDIS_HOST" \
  -p "$REDIS_TLS_PORT" \
  --tls \
  --cacert "$REDIS_CA_FILE" \
  --cert "$REDIS_CLIENT_CERT" \
  --key "$REDIS_CLIENT_KEY" \
  --user "$REDIS_USER" \
  -a "$REDIS_PASSWORD" \
  PING

这里的判断顺序很重要:如果客户端先报证书校验、私钥读取或 TLS 握手错误,先处理证书材料;如果已经能进入 Redis 但返回认证错误,再检查 ACL 用户和密码。命令出现 PONG 只能说明这次调用完成了连接、认证和命令执行,不能替代证书到期、权限轮换和部署目录权限的长期管理。

把同一套材料接入 Go 客户端

命令行确认材料大致正确后,再接入应用。Redis 官方 Go 客户端示例使用 tls.LoadX509KeyPair 加载客户端证书和私钥,再把 CA 加入 x509.CertPool,最后放进客户端的 TLSConfig。示例中的地址、用户名和密码都通过环境变量提供。

package main

import (
    "context"
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "os"

    "github.com/redis/go-redis/v9"
)

func newRedisClient() (*redis.Client, error) {
    // 加载客户端证书和与之匹配的私钥,失败时不要继续创建连接池
    cert, err := tls.LoadX509KeyPair(os.Getenv("REDIS_CLIENT_CERT"), os.Getenv("REDIS_CLIENT_KEY"))
    if err != nil {
        return nil, fmt.Errorf("load client certificate: %w", err)
    }

    // 把 Redis 服务端 CA 放入自定义信任池,避免用跳过校验的配置掩盖问题
    caPEM, err := os.ReadFile(os.Getenv("REDIS_CA_FILE"))
    if err != nil {
        return nil, fmt.Errorf("read redis CA: %w", err)
    }
    roots := x509.NewCertPool()
    if !roots.AppendCertsFromPEM(caPEM) {
        return nil, fmt.Errorf("append redis CA: no certificate found")
    }

    return redis.NewClient(&redis.Options{
        Addr:     os.Getenv("REDIS_ADDR"),
        Username: os.Getenv("REDIS_USER"),
        Password: os.Getenv("REDIS_PASSWORD"),
        TLSConfig: &tls.Config{
            MinVersion:   tls.VersionTLS12, // 只接受不低于 TLS 1.2 的协议
            Certificates: []tls.Certificate{cert},
            RootCAs:      roots,
        },
    }), nil
}

func main() {
    client, err := newRedisClient()
    if err != nil {
        panic(err)
    }
    defer client.Close() // 小项目也要释放连接池资源

    // PING 把 TLS 握手、ACL 认证和一次命令执行串成最小验收动作
    pong, err := client.Ping(context.Background()).Result()
    if err != nil {
        panic(err)
    }
    fmt.Println(pong)
}

生产代码不建议为了“先连上”而设置 InsecureSkipVerify: true。它会跳过服务端证书名称和信任链的关键校验,不能用来证明 TLS 配置正确。若 Redis 只要求服务端 TLS 而不要求客户端证书,可以省略 Certificates;若服务端启用了双向认证,就必须保留证书和私钥。

按错误位置定位配置问题

我通常把错误分成四类。第一类是本地文件问题:路径不存在、进程用户没有读取权限,或者证书与私钥不是一对。第二类是服务端信任问题:客户端证书的签发 CA 不在 Redis 的信任范围内,或者证书已经过期。第三类是地址问题:把普通 TCP 端口当成 TLS 端口,或者连错了代理入口。第四类才是 Redis ACL:TLS 已经建立,但用户没有密码或没有执行 PING 所需的权限。

redis-cli、Go tls.Config、TLS 握手、Redis ACL 与 PING 响应的静态检查边界图
图2:静态检查边界说明图,区分证书材料、TLS 握手、Redis ACL 与 PING 响应各自负责的判断。

排查时可以按下面的顺序缩小范围:

  1. 先确认 REDIS_HOST、TLS 端口和服务端 CA 来自同一套部署配置。
  2. 再确认客户端证书是 PEM 格式、私钥可读,并且二者确实匹配。
  3. 如果握手成功但 PING 被拒绝,转去检查 ACL 用户、密码和命令权限。
  4. 轮换证书时让旧连接自然退出并重新建连,不能只替换磁盘文件就假设连接池已经使用新证书。

常见问题

只有 CA 文件也能连接 Redis 吗?

如果服务端没有要求客户端证书,通常只需要 --tls 和 --cacert。如果 Redis 开启了客户端认证,就还要提供客户端证书和对应私钥。

TLS 握手成功后为什么还会报 NOAUTH?

TLS 只保护传输并完成证书身份验证,Redis ACL 仍然单独检查用户名、密码和命令权限。先看到握手成功,不代表 ACL 已经通过。

可以把 InsecureSkipVerify 当成排障开关吗?

不应把它当成正式修复。它最多帮助区分“网络可达”和“服务端证书校验失败”,排障结束后必须恢复信任链和名称校验。

小结

Redis TLS 连接检查的关键不是多重试几次,而是分清材料和边界:--cacert 验证服务端,--cert 与 --key 代表客户端身份,ACL 再负责 Redis 命令授权。先用 redis-cli PING 做最小检查,再把同一套材料装入 Go 的 tls.Config,定位问题会比直接追应用日志更快。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>