ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

自托管对象存储的三种 TLS 签发:自签、Let‘s Encrypt、内网 CA 的选择与轮换

自托管对象存储的三种 TLS 签发:自签、Let‘s Encrypt、内网 CA 的选择与轮换 对象存储装完之后通常先跑 HTTP等要接进生产才想起证书这件事。三种常见签发路径自签、Let’s Encrypt、内网 CA各适配不同场景选错了不会立刻出问题但会在轮换那一天集中爆出来。这篇文章把三条路各自怎么配、证书放哪、轮换时容易踩的坑一次说清示例以 RustFS 为主关键差异处对照 MinIO 官方文档。先把结论放在最前面自签适合开发Let’s Encrypt 适合公网域名内网 CA 适合多节点生产集群。三条路径的配置成本都不高但轮换机制差别很大选错了会在几个月后付出代价。证书过期这件事在多数监控体系里不是默认项很多人是等业务侧报 TLS 握手失败才想起来所以轮换机制比签发本身更值得提前设计。先把证书该放哪、该叫什么名说清楚RustFS 的 TLS 配置非常简单一个环境变量加一对固定命名的文件RUSTFS_TLS_PATH/opt/tls证书文件必须叫rustfs_cert.pem和rustfs_key.pem放在RUSTFS_TLS_PATH指向的目录里路径可以自选。重启后 TLS 同时作用于两个监听端口S3 API9000和 Console9001。Docker 场景下有两个官方明确提到的坑一是要挂载证书路径二是容器以rustfs用户运行证书文件属主必须是rustfs否则启动时会因权限问题读不到私钥。启动之后可以用自带子命令确认解析状态rustfs tls inspect--path/opt/tls这个命令检查目录布局和证书解析是否正常排障时比看日志更直接。证书文件名固定这件事看起来是个小限制实际好处是排障时少了一个变量路径对、文件名对、权限对这三点核对完基本就能定位大部分 TLS 启动失败不用在日志里翻原因。换成新证书时有个动作值得固化成习惯不要直接覆盖写这两个文件。用重定向去写正在被监听的证书进程完全可能在只写入一半的时刻读到它解析失败热重载拿到的是一个残缺的 PEM。正确姿势是先写临时文件再原子重命名install-m0644 new-cert.pem /opt/tls/.rustfs_cert.pem.tmpmv-f/opt/tls/.rustfs_cert.pem.tmp /opt/tls/rustfs_cert.pem同一文件系统内的mv是一次 rename对读者来说只有改名前后两个状态不存在中间态。cp覆盖式写不行那是先截断再写。这条对重启路径同样适用覆盖写留下的半截文件会让启动时读证书这一步失败日志里只留一句看不出所以然的解析错误。私钥按 0600 落盘具体见轮换那节。对照 MinIO 的做法默认目录是$HOME/.minio/certs文件名是public.crt和private.key不支持 PFX 打包格式私钥有密码时需设MINIO_CERT_PASSWD且私钥必须是 PKCS-1 格式。多证书场景下 MinIO 用子目录按主机名分开通过 SNI 自动选证书这套逻辑 RustFS 目前没有对等实现。两条路径其实传递的是同一个思路证书目录里的文件名是契约改了就找不到。RustFS 把契约写得更死固定文件名换来的是排障更简单MinIO 留了更多灵活度多证书 SNI代价是多一层目录结构要维护。自签最快跑通但有一个坑要记自签证书的优点是五分钟就能跑通缺点是客户端默认不信任它除非额外处理。生成自签证书的 openssl 命令通常长这样openssl req-x509-newkeyrsa:4096-sha256-days365\-nodes-keyoutrustfs_key.pem-outrustfs_cert.pem\-subj/CNlocalhost\-addextsubjectAltNameDNS:localhost,IP:127.0.0.1生成完把两个文件放进RUSTFS_TLS_PATH指向的目录属主改成rustfs重启就能跑。SAN 里的 DNS/IP 要覆盖实际访问时用的地址客户端会按 SAN 校验只写 CN 不写 SAN 的话新版客户端会报证书不匹配。RustFS 针对自签场景给了一个专门的开关RUSTFS_TRUST_LEAF_CERT_AS_CA默认false官方描述是「像信任 CA 一样信任叶证书用于自签配置」。另一个相关变量是RUSTFS_TRUST_SYSTEM_CA默认false官方描述里明确写了出站 TLS 连接也会信任操作系统 CA 库。带「出站」这个限定反过来看RUSTFS_TRUST_LEAF_CERT_AS_CA动的是入站那一侧的信任池。这个开关在生产集群上不建议开。官方环境变量参考里没有「仅开发环境」的标注别拿这个当依据问题出在机制上它把一枚本来不具备签发能力的叶证书放进了信任链的位置而自签部署里这份叶证书往往正是分发给应用客户端的那一份。在这个模型下任何持有该证书的人都能被服务端当作可信来源校验。节点互信要靠下一节自建 CA 的完整链路不是靠把叶证书冒充成 CA。MinIO 的处理更自动化一些自签证书的公钥会被自动加到启动时的 CA 信任池所以自签证书不需要额外拷贝到CAs/目录也能工作。但官方明确说这是便利性行为生产环境还是应该把签发 CA 证书放到CAs/目录而不是依赖这个自动机制。自签还有一个容易在轮换时爆的坑下一节单独讲。Let’s Encrypt公网域名才划算Let’s Encrypt 免费且自动续期前提是你要有一个公网可解析的域名。certbot 有两种常见模式差别在于怎么完成域名验证--standalonecertbot 自己监听 80 端口完成验证需要临时停掉占用 80 端口的服务--webroot往已运行 web 服务的.well-known/acme-challenge/目录写验证文件不用停服务对象存储服务本身占用 9000/9001跟 80 端口不冲突所以两种模式都能跑。用--standalone最省事前提是签发那几分钟 80 端口是空闲的。RustFS 官方文档没有给 certbot 的完整流程但在 Kubernetes 部署的 Helm Chart 文档里给了 cert-manager 的集成示例用的是letsencrypt-prod这个 ClusterIssuer 名字通过 ingress 注解cert-manager.io/cluster-issuer触发证书签发。走 K8s 部署的话这条路径是官方支持的。拿到的fullchain.pem和privkey.pem需要重命名成 RustFS 认的那两个文件名做个软链或者签发后脚本改名都可以。用软链确实省事certbot 续期是重写源文件链接本身不动热重载感知到的是目录内容变了。但软链有两个地方会翻车。一是覆盖写会把链接替换掉mv的目标是符号链接时被换掉的是链接而不是链接指向的文件等下次续期往原路径写入RustFS 读到的还是那个已经被变成普通文件的旧链接指向的是一份早已作废的证书。要么用cp -P走链接写入要么续期脚本里先rm再建。二是容器环境bind mount 会把符号链接原样带进容器但链接指向的/etc/letsencrypt/live/...这棵树在容器里通常不存在进去就是个断链。挂载真实文件、或者每次续期后重建链接比依赖软链稳。Let’s Encrypt 证书 90 天有效期自动续期之后记得让存储进程感知到新证书这就引出下一节。内网 CA生产环境的长期解如果对象存储只在内网提供服务Let’s Encrypt 反而不合适域名解析、80 端口暴露都是额外的攻击面这时自己搭一套内部 CA 是更常见的选择。MinIO 官方文档对这条路径的描述最完整值得逐条记把 CA 证书放到CAs/目录所有节点的public.crt都从这个 CA 签发之后只轮换public.crt和private.keyCAs/目录不再动轮换时 MinIO 自动重载无需重启也不影响信任池自己生成的 CA 就够用不必买公共 CARustFS 对应的配置路径是通过 Helm Chart 的 mTLS 支持Chart 会自动创建自签根 CA、namespace Issuer、server 和 client 证书挂载后通过RUSTFS_SERVER_MTLS_ENABLE1和RUSTFS_TLS_PATH/opt/tls启用。这个路径覆盖了内网双向 TLS 场景如果不在 K8s 里跑就需要自己用 openssl 或 cfssl 生成 CA 和叶证书再放到RUSTFS_TLS_PATH指向的目录。mTLS 相关的三个变量各自管的是不同半边混着理解最容易出事。RUSTFS_SERVER_MTLS_ENABLE默认false官方环境变量参考里的描述是「要求服务器监听器提供客户端证书双向 TLS」限定词在监听器上不是只挂在节点互访那一条路径上。Helm Chart 的 mTLS 页是按 Pod 间通信来讲的但同一页末尾专门给了一条外部访问提醒启用之前先确认 Ingress 控制器或别的外部客户端怎么提交证书。两处合起来读结论是普通 S3 客户端一样会被要求提交证书不是「只针对节点之间」。剩下两个RUSTFS_MTLS_CLIENT_CERT、RUSTFS_MTLS_CLIENT_KEY官方描述写的是「节点间 mTLS 连接提供的客户端证书」管的是 RustFS 作为客户端往外连的那一段。多节点部署时这几个要一致配置否则节点间通信会失败。还有一处容易被漏掉Chart 文档里写明健康探针也会用生成的那套客户端证书。手工启用 mTLS 却没配套客户端凭证时最先报错的往往是探针而不是业务请求表现为 Pod 一直起不来或者反复重启很容易误判成证书格式问题。轮换那一下怎么做才不会打挂服务证书过期那天如果没准备好往往比首次配置更狼狈。MinIO 官方文档里有一段专门讲轮换陷阱的原文值得整段抄过来理解AIStor reloads the certificate it presents whenever the certificate files change, but it builds its CA trust pool once, at process start, and never rebuilds it.翻译一下证书文件变化时 MinIO 会自动重载它对外呈现的证书但 CA 信任池只在进程启动时构建一次之后不会重建。这意味着如果你换掉了节点证书但没有重启进程节点之间会突然互不信任这也是自签证书最常踩的坑。官方推荐的解法是把 CA 放进CAs/一次之后只轮换叶证书信任池不用动。替换顺序有讲究先换私钥再换公证书。如果反过来先换公证书会出现一个短暂窗口期证书和私钥不匹配导致 TLS 握手失败。开了热重载之后这个窗口依然存在只是触发条件变了它不再取决于谁先写入而取决于热重载什么时候扫到目录。官方环境变量参考只给了RUSTFS_TLS_RELOAD_ENABLE这个开关和false的默认值轮询间隔那一档没写在参考里源码常量整理里提到默认 30 秒、最小 5 秒这一项建议在自己版本上确认。所以写完新证书不等于立刻生效中间这段时间新私钥配的是旧证书演练时别只看进程状态要实际发起一次 TLS 握手看协商出来的证书有效期对不对。私钥权限也在演练清单里。RustFS 官方 Docker 说明只要求证书文件属主是rustfs用户没规定权限位0600 是通用做法容器里进程以非 root 的 UID 10001 运行权限放开了等于把私钥摊给同主机其他用户。RustFS 侧的对应机制是RUSTFS_TLS_RELOAD_ENABLE默认false显式设为true后会监听证书目录并热重载。开启之后轮换流程变成新证书写进RUSTFS_TLS_PATH→ 存储检测到变化 → 自动加载。不开这个开关的话每次换证书都要重启进程。热重载的可靠性和重启是两回事。重启是一次干净的加载失败会有明确的启动报错热重载加载失败时的表现要盯紧最可能的一种是进程照旧跑着、内存里那份旧证书继续对外服务。这类故障最麻烦的地方恰恰在于它不报警服务 up、健康探针绿、日志里没有异常行只有证书停留在上一个周期一直停到过期那天变成大面积握手失败。所以开了热重载之后第一次轮换要专门观察一次确认新证书真被加载了再把它当稳定机制依赖。巡检要靠命令而不是进程状态。rustfs tls inspect --path DIR是官方 CLI 里查证书目录布局和解析状态的子命令把它放进巡检脚本读输出里的有效期和解析结果比任何「服务是否在跑」的判断都更早发现问题。RustFS 的rustfs diagnose用来分析日志文件、给出 probable failure causes热重载失败留下的线索可以往里丢。证书过期监控也值得提前做。MinIO 暴露minio_system_network_certificate_expires_in指标单位秒官方告警阈值是 30 天 2592000、7 天 604800。RustFS 这边有没有对等指标要看具体版本暂时把rustfs tls inspect的巡检查成定时任务作为过渡手段。三种签发的选择顺序三种路径不是并列选项按场景挑本机开发、内网快速验证自签最省事开启RUSTFS_TRUST_LEAF_CERT_AS_CA或对应客户端的--insecure选项就行公网可访问、有域名Let’s Encrypt 加 certbot用--webroot避免停服务内网生产、多节点集群自建内部 CA一次放进信任池之后只轮换叶证书还有一条混合场景值得单独说内网服务 少量外部访问。这种情况可以走内网 CA 覆盖内部节点同时给对外的一个入口节点用 Let’s Encrypt 签证书两套证书并存。MinIO 通过 SNI 支持多证书并存按主机名自动匹配这套能力在内网为主、对外少量暴露的场景下很有用。RustFS 在 Apache 2.0 许可下开源TLS 配置、环境变量、rustfs tls inspect命令的完整说明都在 docs.rustfs.com。首次配完之后建议立刻跑一次rustfs tls inspect --path确认证书解析正常再进生产。
返回列表