




















在之前 这篇文章 中有提到,使用 Cloudflare 相互 TLS(mTLS)可以保护网站后台等敏感页面不被公网任意访问,只有在客户端提交证书并经过验证后才可以访问。同样的,这种技术也可以用于 Cloudflare 边缘服务器请求原始服务器的过程,即只有在边缘服务器向原始服务器提交经过验证的证书后,原始服务器才返回内容给边缘服务器。本文将讲述如何生成 mTLS CA 和证书,并在网页服务器和 Cloudflare 中配置验证来源拉取。
注:本次教程中使用的 mTLS 是基于主机名称的验证(Per-hostname Authentication),必须要使用非根域名(例如 www.liups.net 而不是 liups.net,或者也可能是我没找到方法)。如果你使用的域名是根域名,则建议使用区域层级的验证(Zone-level Authentication),稍后会提到。
我们使用 openssl 生成一个 CA 根证书,并用这个根证书创建一个客户端证书。然后,我们会将客户端证书及其私钥上传到 Cloudflare 后台,并且设置了 Cloudflare 边缘节点(作为客户端)在请求原始服务器上的特定域名时,如果要求提交客户端证书,就提交我们上传的证书。
而我们在原始服务器的网页服务器上设置了强制验证和 CA 根证书路径,达成以下效果:
以上解释只考虑了 mTLS 但没考虑 TLS,mTLS 属于 TLS 握手过程的一部分,只有当客户端在 TLS 的第一步确认了服务器证书合法后才会进行第二步的 mTLS 步骤。也就是说,网页服务器上必须配置 HTTPS 才能设置 mTLS。
通过这种方式,可以避免服务器 IP 泄露后,攻击者绕过 Cloudflare 直接访问源站,确保流量来源一定是经过核准的边缘节点。
注:以下操作最好在网页服务所在的服务器上执行,当然如果你电脑就是 Linux 那当我没说
我们在 /etc/certs 文件夹下操作,先创建一个用于 CA 证书的密钥:
sudo mkdir /etc/certs
cd /etc/certs
sudo openssl ecparam -name prime256v1 -genkey -noout -out rootca.key然后基于这个密钥创建 CA 证书(days 后面的 3650 表示 CA 证书过多少天失效):
sudo openssl req -x509 -new -nodes -key rootca.key -sha256 -days 3650 -out rootca.crt执行命令后,会提示输入证书的基本信息,只需要在 Common Name 中填写 根域名,其他栏位填「.」(半角点号),例如:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:.
State or Province Name (full name) [Some-State]:.
Locality Name (eg, city) []:.
Organization Name (eg, company) [Internet Widgits Pty Ltd]:.
Organizational Unit Name (eg, section) []:.
Common Name (e.g. server FQDN or YOUR name) []:liups.net
Email Address []:.然后 CA 根证书(rootca.crt)和密钥(rootca.key)就创建好了。
接下来开始创建要上传到 Cloudflare 的 mTLS 用户端证书,这里先生成密钥和 CSR:
sudo openssl req -new -nodes -out cert.csr -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -keyout cert.key接下来一样要填写信息,Common Name 此时要填写 完整域名,最后的 A challenge password 和 An optional company name 什么都不填,直接回车,其依旧填「.」,例如:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:.
State or Province Name (full name) [Some-State]:.
Locality Name (eg, city) []:.
Organization Name (eg, company) [Internet Widgits Pty Ltd]:.
Organizational Unit Name (eg, section) []:.
Common Name (e.g. server FQDN or YOUR name) []:www.liups.net
Email Address []:.
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:然后生成一个签发用的属性文件:
echo "basicConstraints=CA:FALSE" | sudo tee cert.v3.ext接着使用 CA 证书对 CSR 进行签发(子证书的到期日必须比 CA 根证书短,我这里就减了一天):
sudo openssl x509 -req -in cert.csr -CA rootca.crt -CAkey rootca.key -CAcreateserial -out cert.crt -days 3649 -sha256 -extfile ./cert.v3.ext然后就会生成签发后的子证书(cert.crt),这个待会要和密钥(cert.key)一起上传到 Cloudflare 后台,可以先用 sftp 下载到电脑上。
打开 Cloudflare 仪表板,点击你的域名,转到 SSL/TLS>概观,确认 SSL/TLS 加密的状态是「完整」或「完整(严格)」。如果不是,需要点击右边的「设定」进行修改:选择「自定义 SSL/TLS」,再选择「完整」或「完整(严格)」。注意:请确保你的源站已经配置好了 SSL 证书和 HTTPS 访问才能修改,否则网站将无法访问。

然后转到 SSL/TLS>原始服务器>验证来源拉取,如图:

在下方「每个主机名称」的右边,点击「上传凭证」,然后依次上传证书(cert.crt)和密钥(cert.key),再点击「继续」。之后会显示上传的证书的信息,确认 CN 显示的是根域名(而不是子域名),再点击「继续」。在下一个界面,添加适用于这个证书的子域名,再点击「储存」即可。
然后回到验证来源拉取设置的主界面,将全域和区域层级的开关关掉,避免冲突。
然后,转到 Caching>设定,打开开发模式,这样可以即时看到更改,等一切结束后就可以关闭了,如图:

注意:接下来的操作可能会让网站无法访问,请自行斟酌。
对于 Nginx,在站点配置里加入以下两行:
ssl_verify_client on;
ssl_client_certificate /etc/certs/rootca.crt;对于 Apcache,在站点配置里加入以下两行:
SSLVerifyClient require
SSLCACertificateFile /etc/certs/rootca.crt添加好配置后,重启网页服务器,然后在浏览器打开一个隐私窗口访问你的网站,如果能正常打开就行。
但如果你和我一样用的是うまい的 OpenLiteSpeed😋,可以看接下来的配置教程。
操作前,可以先将关键的 rootca.crt 文件移动到 LiteSpeed Web Server 的默认证书目录下,避免权限问题:
sudo mv /etc/cert/rootca.crt /usr/local/lsws/conf/cert/
sudo chown lsadm:lsadm /usr/local/lsws/conf/cert/rootca.crt然后打开 OpenLiteSpeed 面板,点击左侧的「虚拟主机」,然后进入网站对应虚拟主机的设定,如图:

进入虚拟主机设定后,点击菜单栏的「SSL」,点击「SSL 私钥和证书」右边的编辑按钮,然后在 CA 证书文件里填写 conf/cert/rootca.crt(注意是在 CA 证书文件里填写,而不是 CA 证书路径),然后保存。
然后翻到 SSL 选项卡最下面的「客户端验证」部分,点击编辑。将客户端验证设置为 require,验证深度设置为 1,然后保存,最后进行平滑重启即可。然后在浏览器打开一个隐私窗口访问你的网站,如果能正常打开就行。
注意:操作 hosts 文件有风险
如果你想看下是否确实强制 mTLS 是否生效,可以在服务器上,设置 hosts 将网站的完整域名解析到 127.0.0.1:
echo "127.0.0.1 <域名>" | sudo tee -a /etc/hosts # 将解析添加到 hosts 最后一行然后分别执行两个 curl 命令:
curl -k https://<域名>
curl -k --cert /etc/certs/cert.crt --key /etc/certs/cert.key https://<域名>如果第一个命令有报错 tlsv13 alert certificate required, errno 0,而第二个命令正常执行并返回内容,就没问题了。测试完成后,记得复原 hosts 文件:
sudo sed -i '$d' /etc/hosts # 删去 hosts 文件最后一行为了安全性,生成的 cert.crt、cert.key 和 rootca.key 也可以删掉了。
如果有另一个子域名的网站需要设置,其实不需要在原本的 CA 上再签发子证书,直接重新再创建 CA 根证书就可以,因为不同的虚拟主机可以设置验证不同的 CA 根证书。如果证书过期了,同样重新创建 CA 根证书,再签发新的子证书就行。
这样签发一个子证书就更换根证书虽然麻烦,但可以避免泄露的子证书能够通过验证(因为我们更换根证书了,之前泄露的子证书不能通过验证)。正规 PKI 肯定是有吊销列表的,我们也可以配置,但好像更麻烦了。
验证来源拉取更像是一种安全性的增强,但并不是基础。如果你的服务器只连接了 Cloudflare,推荐用防火墙设置只允许边缘服务器的 IP 访问网站的 443 端口(TCP 和 UDP)。
mTLS 是一个非常有用的技术,也可以用来保护网站后台,教程见〈使用 Cloudflare mTLS 用户端凭证保护博客登入页面〉,也欢迎回到 主页 阅读更多文章。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。