在之前的 LAMP 搭建与邮件服务部署中,我们一直面临一个棘手的问题:Let's Encrypt 的证书续签太麻烦了。尤其是申请通配符(泛域名)证书时,Certbot 默认需要手动添加 DNS TXT 记录验证,每三个月就要折腾一次,一旦忘记,服务就会中断。
此外,当我们的站点托管在 Cloudflare(以下简称 CF)后,HTTPS 的链路变得更加复杂。为什么开启了 HTTPS 还有“Unknown”流量?CF 的边缘证书和源服务器证书到底用哪个?
本文将先通俗地厘清 SSL 核心概念,分析 CF 的架构,最后给出一个终极解决方案:使用 acme.sh 配合 CF API 实现全自动证书续签,为后续配置 Dovecot 邮件加密打好基础。
1. 证书、签名、摘要:数字世界的“身份证”
在配置服务器时,我们经常面对 .pem, .crt, .key 这些文件。要理解它们的作用,我们需要理解 HTTPS 握手背后的防伪逻辑。我们可以将数字证书比作一张“防伪钞票”。
1.1 核心组件:A面与B面
我们可以把一个完整的证书文件(如 fullchain.pem)想象成一张钞票,它由两部分组成:
A面(明文正文):包含你的身份信息。
- 内容包括:域名(
example.com)、有效期、颁发机构(CA)、以及最重要的——你的公钥。 - 摘要 (Digest):这是对 A面 内容进行哈希运算(如 SHA-256)后得到的“指纹”。只要 A面 改动一个标点,摘要就会完全改变。
- 内容包括:域名(
B面(数字签名):这是 CA 机构盖的“防伪钢印”。
- CA 机构用CA 自己的私钥,对 A面的“摘要”进行加密,生成的这串密文就是数字签名。
1.2 验证原理:公私钥的“反转”用法
很多同学知道“公钥加密,私钥解密”,这用于数据传输。但在身份验证阶段,逻辑是反过来的:“私钥签名,公钥验证”。
当浏览器(验钞机)收到服务器发来的证书时,会进行如下验证流程:
- 左手算指纹:浏览器读取证书的 A面,用哈希算法算一遍,得到 摘要 X。
- 右手解钢印:浏览器使用操作系统内置的 CA 公钥,去解密证书的 B面(签名),得到 摘要 Y。
- 对比防伪:如果 摘要 X = 摘要 Y,说明证书内容未被篡改,且确实是该 CA 签发的。
1.3 为什么黑客无法伪造证书?
如果黑客拦截了你的请求,试图进行中间人攻击,他必须替换掉证书里的公钥(换成他自己的,以便解密你的数据)。
- 一旦他修改了 A面的公钥,A面的摘要就变了。
- 但他没有 CA 的私钥,无法生成一个新的、能被 CA 公钥解开的 B面(签名)来匹配新的摘要。
- 浏览器验证时会发现摘要不匹配,随即报警拦截。
唯一的例外:如果黑客在你电脑里偷偷植入了一个“恶意的根证书”并设为信任(例如某些流氓软件或公司监控),那么黑客就可以用这个恶意 CA 的私钥堂而皇之地签发假证书。这也是为什么保护系统“受信任根证书列表”至关重要。
2. Cloudflare 环境下的证书架构与流量加密
当我们将域名托管至 Cloudflare(CF)并开启 CDN(橙云)后,原本简单的 用户 <--> 服务器 直连模式,变成了 用户 <--> Cloudflare <--> 服务器 的三方模式。
在这个架构中,Cloudflare 实际上扮演了一个“反向代理”的角色。对于用户来说,CF 是服务器;对于我们的 VPS 来说,CF 则是浏览器(客户端)。
2.1 CF 的三种凭证机制
在 Cloudflare 的 SSL/TLS 后台,我们经常会看到三类容易混淆的证书概念,它们分别负责链路中不同的环节。
2.1.1 边缘凭证 (Edge Certificate)
- 位置:部署在 Cloudflare 的全球 CDN 节点上。
- 作用:负责 用户 $\leftrightarrow$ Cloudflare 这一段的加密。
- 感知:这是用户在浏览器地址栏点开“小锁头”时看到的证书。通常由 Google Trust Services 或 Let's Encrypt 签发,颁发对象是 sni.cloudflaressl.com 或你的域名。
- 管理:完全由 CF 自动申请、续签,无需用户干预。
2.1.2 原点凭证 (Origin Certificate)
- 位置:部署在你的 VPS (Apache/Nginx) 上。
- 作用:负责 Cloudflare $\leftrightarrow$ 你的 VPS 这一段的加密。
选择策略:
方案 A(CF 自签):在 CF 后台生成一张有效期 15 年的证书安装到 VPS。
- 缺点:这张证书只被 CF 信任。如果用户不走 CDN 直接访问 VPS(如邮件客户端),会报证书错误。
方案 B(公共受信,推荐):在 VPS 上使用
acme.sh申请 Let's Encrypt 泛域名证书。- 优点:CF 信任它(只要开启 Full Strict 模式),普通浏览器和邮件客户端也信任它。这是兼顾 Web 加速与邮件服务的最佳方案。
2.1.3 客户端凭证 (Client Certificate / mTLS)
- 位置:安装在访客的设备(浏览器)里。
- 作用:双向认证(门禁系统)。不仅是你检查服务器的证书,服务器也要检查你有没有“通行证”。
误区:很多博主误以为开启 HTTPS 就需要配置这个。错!
- 一旦开启并生效,没有安装特定证书的普通访客将直接被拒绝访问(400 Bad Request)。
- 除非你是银行内网或私有管理系统,否则千万不要在公开博客上配置 mTLS 规则。
2.2 加密模式的选择:Full (Strict)
理解了“CF 是浏览器”这个概念后,SSL 模式的选择就显而易见了。为了安全,我们应该选择 Full (Strict) / 完全(严格) 模式。
流程解析:
- CF 的服务器向你的 VPS 发起连接请求。
- 你的 VPS 出示 Let's Encrypt 证书。
- CF(作为客户端)校验证书的合法性(是否过期、是否由受信任 CA 签发)。
- 验证通过,CF 与 VPS 建立加密通道传输数据。
在这个过程中,CF 不需要“拥有”你的证书私钥,它只需要像普通浏览器一样“验证”你的证书即可。这既保证了安全性,又避免了私钥泄露的风险。
2.3 为什么开启了 TLS 还有 Unknown 流量?
在 CF 后台分析中,你可能会看到 SSL/TLS 版本中有 Unknown 的请求,这通常有以下原因:
- 爬虫/扫描器:很多恶意扫描器只进行 TCP 握手,不完成 SSL 握手,或者使用非标准的协议头。
- 直接 IP 访问:如果攻击者绕过域名,直接扫描你的 VPS IP 的 443 端口,CF 无法记录协议细节(因为流量没走 CF)。
- 旧协议:极老旧的设备尝试使用已废弃的 SSLv3 或 TLS 1.0,如果在握手阶段被拒绝,可能被记录为未知。
3. 终极方案:acme.sh 全自动续签与架构分离
面对“既要网站享受 Cloudflare (CF) 的 CDN 加速与防护,又要邮件服务(Dovecot/Postfix)能被本地客户端直连”的需求,我们需要一套组合拳。
核心矛盾:CF 的免费版 CDN(点亮小黄云)本质上是一个 Web 代理,它只负责代理 HTTP/HTTPS 流量(80/443 端口)。它不认识也不转发邮件协议(SMTP/IMAP 对应的 25, 465, 587, 993 等端口)。
解决策略:域名分流 + 统一证书
网站域名(Web 流量)
- 对象:
www.example.com或@ - 设置:开启代理(橙云 ☁️)。
- 效果:流量经过 CF 清洗和加速。CF 负责与用户加密,并持有边缘凭证。
- 对象:
邮件域名(Mail 流量)
- 对象:
mail.example.com - 设置:仅 DNS 解析(灰云 ☁️)。
- 效果:流量不经过 CF,由用户直连 VPS。此时,用户客户端(如 Outlook)会直接检查 VPS 的证书。
- 对象:
证书选择(关键点)
- 由于邮件域名是直连,客户端不会信任 CF 签发的“原点证书”(那个只被 CF 信任)。
- 因此,VPS 必须持有一张全球受信任的公网证书(Let's Encrypt)。
- 完美闭环:这张 Let's Encrypt 证书既能让邮件客户端信任(直连不报错),也能让 CF 信任(满足 Full Strict 模式)。
操作步骤:为了解决 Let's Encrypt 泛域名证书每 3 个月手动续签的痛点,我们将使用 acme.sh 配合 Cloudflare API 实现全自动化的“申请-验证-部署-重启”。
3.1 获取 Cloudflare API Token
- 登录 CF 后台 -> 右上角头像 -> My Profile -> API Tokens。
- Create Token -> Use template -> Edit zone DNS。
- 选择你的域名,生成 Token,复制保存。
3.2 安装 acme.sh
acme.sh 是一个纯 Shell 脚本,比 Certbot 更轻量,且没有 Python 依赖问题。
# 1. 安装 (将 [email protected] 换成你的邮箱)
curl https://get.acme.sh | sh -s [email protected]
# 2. 让命令生效
source ~/.bashrc3.3 申请泛域名证书
这一步会自动在 CF 添加 DNS TXT 记录进行验证,验证完后自动删除,全程无需人工干预。
# 1. 导入 CF Token 环境变量
export CF_Token="你刚才复制的Token"
export CF_Account_ID="你的AccountID(在域名概览页右下角-账户识别码)"
# 2. 申请证书 (包含主域名和通配符域名)
# --dns dns_cf 代表使用 Cloudflare API
# --server letsencrypt 代表明确使用 Let's Encrypt (默认可能是 ZeroSSL)
acme.sh --issue --dns dns_cf --server letsencrypt -d example.com -d *.example.com3.4 安装证书到 Apache
注意:不要直接使用 .acme.sh 目录下的文件,应使用 --install-cert 命令复制到指定位置,这样 acme.sh 才会记住路径并在续签后自动更新文件并重启服务。
# 1. 创建存放目录
mkdir -p /usr/local/apache/conf/ssl
# 2. 安装证书并指定重载命令
acme.sh --install-cert -d example.com \
--key-file /usr/local/apache/conf/ssl/server.key \
--fullchain-file /usr/local/apache/conf/ssl/server.crt \
--reloadcmd "/usr/local/apache/bin/apachectl -k graceful"3.5 验证与自动续签
acme.sh 会自动添加一个 crontab 定时任务,每天检查证书有效性,到期前自动续签并重启 Apache。你可以通过 crontab -l 查看。
至此,你已经拥有了一个支持泛域名的、自动续签的、被邮件客户端信任的 SSL 证书。下一步,我们就可以愉快地去配置 Postfix 和 Dovecot 的 SSL 加密了!
评论已关闭