本文中的域名、IP 地址、路径、Token 等信息均已脱敏处理。
很多人在把自己的服务暴露到公网以后,都会遇到一个问题:
除了账号密码之外,还有没有办法再增加一层保护?
例如:
- 浏览器导航页
- 私人部署的密码库
- 某个服务的管理面板
- NAS
- 各种自建服务等等
这些服务大多都有自己的登录系统。
但只要地址暴露在公网里,理论上任何人都能访问到登录页面。
虽然未必能登录成功,但入口始终是开放的,这就多了一个攻击面多了一层隐患。
于是很多人都会开始寻找一种额外的访问控制方法。
常见方案为什么不一定适合
IP 白名单
最容易想到的方案就是 IP 白名单。
例如:
只允许:203.0.113.10198.51.100.20访问服务。
这种方案简单直接,而且安全性也不错,在早期的互联网环境可用性还是不错的。
但在今天的网络环境里,它已经不再适合。
因为:
- 家宽通常是动态 IP 或干脆是运营商的大内网 IP
- 就算是手机流量,那出口地址也是会不断变化的
- 外出连接其他网络时 IP 则又完全不同
- 就算是 IPv6 前缀也可能发生变化
结果就是:
每换一次网络、刷新一次 IP ,就需要重新维护白名单。
对于经常在不同设备、不同网络之间切换的人来说,这种方式并不方便。
HTTP 认证
另一种常见方案是 HTTP Basic Authentication。
Nginx 就能直接实现。
访问网站时,浏览器会先弹出认证窗口:
Username:Password:输入正确的账号密码后才能继续访问。
这种方案确实有效。
但也存在一些问题。
首先,很多应用本身已经带有登录系统。
于是访问流程会变成:
HTTP 认证↓应用登录两套认证同时存在,访问登录过程繁琐。
其次,并不是所有客户端都对 HTTP 认证支持良好。
大多浏览器通常没有问题。
但移动端 App、桌面客户端、浏览器扩展等场景,体验未必一致,因为软件作者大多都不会适配。
对于一些需要多端访问的服务来说,这会带来额外的兼容性问题。
VPN
还有一种思路。
干脆不开放公网。
把所有服务都放到 VPN 后面。
例如:
- WireGuard
- Tailscale
- ZeroTier
这种方案的安全性很高。
因为不连接 VPN,理论上就无法访问内部服务。
但它的问题在于:
如果只是为了访问某一个服务,就把整套系统的网络流量一起切进 VPN,那代价有点高。
因为实际使用时,设备并不会只做这一件事,你的后台可能还有:
- 下载任务
- 文件同步
- 系统更新
- 消息推送
等各种网络活动。
这时候为了访问某个服务,就让整台设备都挂在 VPN 网络里,那可不太划算。
因此 VPN 更适合构建完整的私有网络。
而不一定适合作为某个单独服务的访问控制方案。
客户端证书是什么
客户端证书提供了另一种思路。
它不是在应用层做认证。
而是在 TLS 握手阶段完成认证。
也就是说:
在网页打开之前。
在登录页面出现之前。
甚至在 HTTP 请求发送之前。
服务器就已经能够判断:
- 客户端是否携带证书
- 证书是否可信
- 是否允许访问
这套机制被称为:
mTLS(Mutual TLS)也就是双向 TLS 认证。
普通 HTTPS 做了什么
理解 mTLS 之前,需要先理解普通 HTTPS。
当浏览器访问:
https://example.com时,首先会进行 TLS 握手。
服务器会把自己的证书发送给浏览器。
浏览器随后验证:
- 证书是否可信
- 域名是否匹配
- 是否在有效期内
- 证书链是否完整
验证通过以后,连接继续建立。
整个过程中:
客户端验证服务器。
服务器并不会验证客户端身份。
因此普通 HTTPS 解决的问题是:
你访问的是否是真正的服务器。
而不是:
访问服务器的人是谁。
mTLS 多做了一步什么
mTLS 在普通 HTTPS 的基础上增加了一步:
服务器要求客户端也出示证书。
TLS 握手过程中,服务器会发送:
Certificate Request要求客户端提供证书。
如果客户端没有证书。
或者证书无法通过验证。
连接会被服务器直接拒绝。
因此:
普通 HTTPS=服务器证明自己
mTLS=服务器证明自己+客户端证明自己这也是 mTLS 名字中“Mutual(双向)”的来源。
mTLS 的证书体系
一个典型的 mTLS 环境通常包含三类证书。
CA 根证书
CA(Certificate Authority)负责签发证书。
在自建环境中,很多人会创建自己的私有 CA(是的,我就是)。
后续所有服务器证书和客户端证书都由它签发。
它相当于整个信任体系的根节点。
服务器证书
服务器证书用于证明服务器身份。
例如:
panel.example.comvault.example.comgit.example.com浏览器访问网站时,会首先验证这张证书。
客户端证书
客户端证书用于证明客户端身份。
它可以签发给:
- 电脑
- 手机
- 平板
等设备。
服务器通过验证客户端证书来决定是否允许访问。
TLS 握手过程中发生了什么
整个过程可以简化为下面几个步骤。
第一步
客户端访问网站:
https://panel.example.com开始 TLS 握手。
第二步
服务器发送自己的证书。
客户端验证:
- 域名
- 有效期
- 签名
- 信任链
这一步与普通 HTTPS 完全相同。
第三步
服务器发送:
Certificate Request要求客户端出示证书。
这是 mTLS 独有的步骤。
第四步
客户端从本地证书库中选择合适的证书。
然后发送给服务器。
通常导入设备的证书格式为:
.p12.pfx第五步
服务器使用自己的 CA 证书验证客户端证书:
- 是否由指定 CA 签发
- 是否过期
- 是否有效
如果验证失败。
连接会被服务器直接拒绝,TLS 握手无法继续完成。
第六步
验证成功后。
TLS 握手完成。
双方开始建立加密连接并传输数据。
Nginx 如何启用 mTLS
在实际部署中。
最常见的做法是由 Nginx 完成客户端证书验证。
配置通常类似:
ssl_client_certificate /etc/nginx/certs/ca.crt;ssl_verify_client on;ssl_verify_depth 2;含义分别是:
ssl_client_certificate
指定用于验证客户端证书的 CA。
ssl_verify_client on
要求客户端必须提供有效证书。
ssl_verify_depth
控制证书链验证深度。
mTLS 适合哪些场景
mTLS 并不适合所有网站。
公开网站通常没有必要这样做。
但对于:
- 浏览器导航页
- 私人部署的密码库
- 某个服务的管理面板
- NAS
- 各种自建服务等等
这类只允许少数或单一用户访问的服务。
它非常合适。
因为它能够把访问控制提前到 TLS 握手阶段。
在应用真正开始处理请求之前,就先完成身份验证。
小结
普通 HTTPS 解决的是:
服务器是不是真的。
而 mTLS 进一步解决的是:
客户端是不是被允许访问。
它不依赖固定 IP。
不需要额外增加一层 HTTP 认证。
也不要求整套网络都接入 VPN。
而是在 TLS 握手阶段完成客户端身份验证。
对于需要将私有服务暴露到公网,但又希望增加额外保护的人来说,mTLS 是一种值得了解的方案。
实战:
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时




























