Repository Reading Site
第十五课:HTTPS、TLS、SNI、证书信任与 Ingress 终止原理
上一课我们已经把北南向流量的 HTTP 入口讲清楚了: 但只讲 HTTP 还远远不够。 真实生产里你真正面对的通常是: 也就是说,入口层真正难的部分,往往不是“路由到哪里”,而是: 如果这条线不通,你会频繁卡在下面这些问题: 1. 为什么 HTTPS 能访问业务,但证书却是错的 2. 为什么换了 `Host` 头,证书还是没变 3. 为什么 curl 报 `
第十五课:HTTPS、TLS、SNI、证书信任与 Ingress 终止原理
为什么这一课必须接在 Ingress 之后
上一课我们已经把北南向流量的 HTTP 入口讲清楚了:
- NodePort 怎样把流量引进集群
- ingress-nginx 怎样根据
Host和Path做七层路由 404、503大概分别意味着哪一层出了问题- Ingress 规则最终怎样落到 Nginx 配置里
但只讲 HTTP 还远远不够。
真实生产里你真正面对的通常是:
https://...- 证书
- 浏览器信任
- TLS 握手
- SNI
- 证书续期
也就是说,入口层真正难的部分,往往不是“路由到哪里”,而是:
请求在还没有进入 HTTP 路由之前,TLS 握手时到底发生了什么。
如果这条线不通,你会频繁卡在下面这些问题:
- 为什么 HTTPS 能访问业务,但证书却是错的
- 为什么换了
Host头,证书还是没变 - 为什么 curl 报
self-signed certificate - 为什么浏览器说“不安全”,但流量其实已经加密了
- 为什么错误主机名时常见表现是“假证书 + 404”
- 为什么 Ingress 里加了
tls:之后,后端应用并不一定需要自己支持 HTTPS
这一课的目标,就是把:
- TLS 是什么
- 证书是什么
- 信任链是什么
- SNI 是什么
- Ingress 为什么能做 TLS termination
全部讲透。
先把四个最容易混淆的概念拆开
这是整节课最重要的基础。
第一:HTTP
HTTP 是应用层协议。
它默认是明文。
也就是说,如果只有 HTTP:
- 中间人可以看到请求内容
- 密码、Cookie、Token 都可能被截获
第二:TLS
TLS 解决的是:
传输过程中的加密、完整性保护、以及身份校验框架。
它不是“证书本身”,也不是“浏览器信任本身”。
TLS 握手里会协商:
- 使用什么密码套件
- 使用什么证书
- 会话密钥是什么
第三:证书
证书的核心作用是:
把某个公钥和某个身份绑定起来。
这个身份通常体现在:
CNSAN
尤其现代 TLS 主机名校验,真正更关键的是:
SAN,也就是 Subject Alternative Name
第四:信任
很多初学者会把“有证书”和“客户端信任”当成一回事。
不是。
你完全可以有一张证书,但客户端仍然不信任它。
比如:
- 自签证书
它依然能提供加密,但默认不被系统信任。
这句话你以后必须能脱口而出:
加密不等于信任,证书存在不等于证书被信任。
先建立 HTTPS 的时序认知:TLS 发生在 HTTP 之前
这一点极其关键。
很多人会本能地觉得:
- 服务器先看到
Host - 再决定用哪张证书
真实顺序不是这样。
HTTPS 请求大致是:
- TCP 建立连接
- TLS ClientHello 发出
- 客户端在握手阶段通过 SNI 告诉服务端“我想访问哪个主机名”
- 服务端根据 SNI 选择证书
- TLS 握手完成
- 才开始发送 HTTP 请求
- HTTP 请求头里才出现
Host
所以你必须牢牢记住:
证书选择发生在 HTTP 头到来之前。
这也是为什么:
- 对 HTTPS 来说,只改
Host头不一定足够 - 真正影响证书选择的是 SNI
Host 和 SNI 到底有什么区别
这两个词经常被混用,但它们不在同一层。
Host
属于:
- HTTP 层
作用:
- 告诉反向代理或应用,我想访问哪个虚拟主机
SNI
属于:
- TLS 握手层
作用:
- 告诉服务端,在握手阶段应该返回哪张证书
为什么要分清这两者
因为很多入口问题表面看起来像“同一个域名问题”,但其实层次不同:
- 证书不对,往往先看 SNI
- 路由不对,往往再看
Host
所以你以后看到:
- 证书是默认假证书
- 但 HTTP 路由其实能到正确服务
不要惊讶,这完全可能发生。
这套集群里 HTTPS 的真实底座是什么
这一课我先验证了几件事实。
1. ingress-nginx-controller 对外暴露了 HTTPS 入口
它的 Service 是:
NodePort80 -> 30080443 -> 30443
也就是说:
30080是 HTTP 入口30443是 HTTPS 入口
2. controller 自己有默认假证书机制
在上一课查看 nginx.conf 时,我已经看到:
default-fake-certificate.pem
这说明 ingress-nginx 在没有找到更合适证书时,会回退到一张默认假证书。
3. 这套集群当前没有 cert-manager
我实际验证了:
- 没有
cert-managernamespace kubectl get certificates,issuers,clusterissuers -A直接报资源类型不存在
这意味着:
- 这套集群现在没有自动证书签发控制面
所以这节课我故意用:
- 手工自签证书
来做最小可验证闭环。
这反而更利于教学,因为你能先看清:
- TLS Secret 到底是什么
- Ingress
tls:到底做了什么
然后再去理解:
- 为什么生产里还需要
cert-manager
本次实验为什么这样设计
我专门设计了一条很有教学价值的路径:
阶段 1:只创建 HTTP 版 Ingress
对象:
tls-lab/secure-web
规则:
Host = secure.k8s-lab.local- 路由到
secure-web-svc
但故意不写:
spec.tls
目的就是验证:
HTTPS 在没有
tls:时,是否也可能访问成功。
阶段 2:生成一个自签 TLS Secret
我没有把私钥硬编码进仓库,而是写了一个脚本:
它会:
- 本地临时生成 key/cert
- 创建
kubernetes.io/tlsSecret - 打印证书主题、签发者、有效期和 SAN
为什么要这样做?
因为这符合正确工程习惯:
教学可以用自签证书,但不应该把私钥当静态明文材料长期放进仓库。
阶段 3:把同一个 Ingress 切到 TLS 版本
然后我应用带 tls: 的版本:
secretName: secure-web-tlshosts: secure.k8s-lab.local
这时我们就可以直接比较:
- 加
tls:前,证书是什么 - 加
tls:后,证书变成什么 - 访问行为是否变化
- 错误主机名时会发生什么
第一组核心现象:没有 tls:,HTTPS 也可能“能通”
这是本课最反直觉、也最重要的现象。
我在只应用 HTTP 版 Ingress 后,做了两组请求。
HTTP 请求
我在节点上执行:
curl -H 'Host: secure.k8s-lab.local' http://127.0.0.1:30080/
返回:
200 OK- 命中了
secure-web
这很正常。
HTTPS 请求
我再执行:
curl -k --resolve secure.k8s-lab.local:30443:127.0.0.1 \
https://secure.k8s-lab.local:30443/
结果居然也是:
HTTP/2 200- 同样返回了
secure-web内容
但证书是什么
我用:
openssl s_client -connect 127.0.0.1:30443 \
-servername secure.k8s-lab.local
取证书,看到的是:
CN = Kubernetes Ingress Controller Fake CertificateSAN = DNS:ingress.local
这说明什么
这说明:
路由成功和证书正确,是两条不同维度的判断。
也就是说:
- ingress-nginx 能先用默认假证书完成 TLS
- 握手完成后,HTTP 请求依然可能命中正确的
Host路由 - 所以业务内容能返回
- 但证书身份是错的
这正是很多生产现场最容易误判的地方:
- “页面都打开了,所以 HTTPS 应该没问题”
不对。
可能只是:
- 路由没问题
- 但证书完全不对
第二组核心现象:加了 tls: 后,证书换成自定义 Secret
我用脚本生成了:
Secret/tls-lab/secure-web-tls
并确认它的结构是:
type: kubernetes.io/tls- data keys:
tls.crttls.key
这说明 Kubernetes 里 TLS Secret 的本质就是:
一对证书和私钥的封装。
然后我把 Ingress 更新成:
spec.tls.hosts = [secure.k8s-lab.local]spec.tls.secretName = secure-web-tls
之后再次执行:
openssl s_client -connect 127.0.0.1:30443 \
-servername secure.k8s-lab.local
这次看到的证书变成:
subject = CN = secure.k8s-lab.localissuer = CN = secure.k8s-lab.localSAN = DNS:secure.k8s-lab.local
也就是说:
- ingress-nginx 已经不再回退默认假证书
- 它成功根据 SNI 和
tls:配置选中了我们自签的那张证书
但客户端为什么还是报错
我再执行:
curl --resolve secure.k8s-lab.local:30443:127.0.0.1 \
https://secure.k8s-lab.local:30443/
得到的是:
curl: (60) SSL certificate problem: self-signed certificate
这一步就是在给你做一个最核心的概念分离训练:
证书已经对了,不等于客户端就会信任它。
因为它仍然是:
- 自签证书
所以默认 trust store 不认。
加 -k 之后为什么又能通
我再执行:
curl -k --resolve secure.k8s-lab.local:30443:127.0.0.1 \
https://secure.k8s-lab.local:30443/
这次是:
HTTP/2 200
这说明:
- TLS 握手本身是成立的
- 加密也是成立的
- 只是默认信任校验没过
这一步对专家思维非常重要。
以后别人说“HTTPS 不通”,你第一反应要追问:
- 是握手失败?
- 是证书名不匹配?
- 是证书不受信任?
- 还是业务路由失败?
它们不是一回事。
第三组核心现象:错误主机名时,证书和路由会一起退回默认状态
我还做了一个非常关键的反向实验。
我用错误主机名发起 HTTPS:
openssl s_client -connect 127.0.0.1:30443 \
-servername wrong.k8s-lab.local
拿到的又变回:
Kubernetes Ingress Controller Fake Certificate
然后我再请求:
curl -k --resolve wrong.k8s-lab.local:30443:127.0.0.1 \
https://wrong.k8s-lab.local:30443/
得到的是:
HTTP/2 404
这两件事必须连起来理解。
证书为什么回退成假证书
因为在 TLS 层:
- SNI =
wrong.k8s-lab.local - 但没有任何
tls.hosts对它负责
所以 controller 只能回退默认假证书。
为什么后面又是 404
因为 TLS 握手完成后进入 HTTP 层:
Host = wrong.k8s-lab.local- 也没有任何 Ingress 规则匹配它
所以 HTTP 层返回默认 404
这正好把:
- SNI 证书选择
- HTTP Host 路由
两层彻底拆开了。
这节课最重要的一句话:证书选择和路由匹配是相关但独立的两个阶段
你以后必须能把这句话讲得非常顺。
TLS 阶段
服务端根据:
- SNI
spec.tls.hostssecretName
来决定用哪张证书。
HTTP 阶段
服务端根据:
HostPath
来决定把请求转给哪个后端。
所以你完全可能遇到:
- 证书错,但路由对
- 证书对,但路由错
- 证书和路由都对
- 证书和路由都错
本课里我已经分别做出了真实样本:
证书错,但路由对
- 没有
tls:时的secure.k8s-lab.local
证书对,但默认客户端不信任
- 有
tls:后的自签证书
证书错,路由也错
wrong.k8s-lab.local
这就是为什么入口 TLS 问题一定不能只看“页面能不能打开”。
Ingress TLS Termination 到底是什么意思
这是生产里非常重要的架构概念。
本课里,从客户端角度看:
- 我请求的是
https://secure.k8s-lab.local:30443/ - 客户端拿到的是
HTTP/2 200
但 ingress-nginx 的访问日志里,上游仍然显示:
10.244.147.112:8080
这说明什么?
说明:
TLS 在 ingress-nginx 这一层被终止了。
也就是:
- 客户端到 ingress-nginx:HTTPS / TLS
- ingress-nginx 到后端 Pod:普通 HTTP
这就叫:
- TLS termination
- SSL termination
这有什么好处
好处是:
- 后端服务不必每个都自己处理证书
- 证书集中管理
- 统一入口做 TLS、HSTS、重定向、限流、WAF 等更方便
有什么代价
代价是:
- ingress 到后端这一段如果需要“全链路加密”,还得额外做 re-encrypt 或 mTLS
所以以后你要分清两种场景:
入口终止 TLS
最常见、最实用。
端到端都加密
更偏高安全要求场景。
为什么我专门用了 curl --resolve
这条命令非常值得你学会。
我用的是:
curl --resolve secure.k8s-lab.local:30443:127.0.0.1 \
https://secure.k8s-lab.local:30443/
它同时做了三件事:
- 把
secure.k8s-lab.local临时解析到127.0.0.1 - URL 主机名仍然是
secure.k8s-lab.local - 因此 curl 会把这个主机名同时用于:
- TLS 的 SNI
- HTTP 的
Host
这就是为什么它特别适合调试 HTTPS 入口。
如果你只是:
curl -H 'Host: secure.k8s-lab.local' https://127.0.0.1:30443/
那 TLS 阶段看到的主机名可能仍然是:
127.0.0.1
从而根本没法准确模拟真实 SNI 场景。
这就是“会用命令”和“理解命令”的区别。
这次实验里还有一个很值得学习的控制器细节
我在并行执行:
- 创建 TLS Secret
- 更新带
tls:的 Ingress
之后,controller 日志里出现了短暂现象:
Error getting SSL certificate ... secure-web-tls was not found. Using default certificate
随后又马上出现:
Secret was added ...Adding secret to local store
这说明:
- controller 是基于 watch / 本地缓存工作的
- 在短时间窗口里,Ingress 可能先被处理,而 Secret 还没进入 controller 本地 store
- 这时它会先回退默认证书
- 等 Secret 缓存到位后,再切换到正确证书
这件事很有平台工程味道。
它提醒你:
Kubernetes 控制器不是事务数据库式的“全局同步瞬时完成”,而是多个控制循环逐步收敛。
这也是为什么真实生产里要有“短暂过渡态”的认知。
作为专家,你应该怎样排 HTTPS / 证书问题
建议按下面顺序来。
第一步:先分层判断是 TLS 问题还是 HTTP 路由问题
先问自己:
- 握手有没有成功
- 证书是不是对的
- 客户端是不是信任它
- 路由是不是命中了
第二步:看 Ingress 是否声明了 TLS
kubectl get ingress -n <ns> <name> -o yaml
重点看:
spec.tls.hostsspec.tls.secretName
第三步:看 Secret 是否存在且类型正确
kubectl get secret -n <ns> <name> -o yaml
重点看:
type: kubernetes.io/tls- 是否包含
tls.crt - 是否包含
tls.key
第四步:直接取服务端证书,不猜
openssl s_client -connect <ip>:443 -servername <host>
看:
subjectissuerSAN- 有效期
第五步:用 curl --resolve 测真实 SNI 行为
curl --resolve host:443:ip https://host/
这比只改 Host 头更准确。
第六步:看 controller 日志
kubectl -n ingress-nginx logs deploy/ingress-nginx-controller
重点找:
Using default certificateError getting SSL certificateSecret was added- 上游请求日志
学完这一课你必须真正记住的结论
- HTTPS 不只是“加一个证书”,它先有 TLS 握手,再有 HTTP 路由。
- SNI 决定服务端在握手阶段选哪张证书,
Host决定 HTTP 阶段命中哪条路由。 - 没有
tls:时,Ingress 也可能用默认假证书完成 TLS,然后把请求路由到正确后端。 - 证书正确不等于客户端信任它;自签证书默认仍会触发校验失败。
kubernetes.io/tlsSecret 本质上就是tls.crt + tls.key。- Ingress TLS termination 的意思是:客户端到入口加密,入口到后端未必加密。
- 错误主机名时常见组合现象是:默认假证书 + HTTP 404。
- 证书与路由问题必须分层排查,不能混成一句“HTTPS 不通”。
你现在具备了什么能力
学完这一课,你已经不只是“会把证书挂进 Ingress”了。
你开始具备的是:
- 能把加密、证书、信任、路由四个概念彻底拆开的能力
- 能解释 SNI 与
Host的分工 - 能判断“证书错但路由对”这类复杂入口现象
- 能直接用
openssl s_client和curl --resolve做 HTTPS 实证排障
这已经是从“Kubernetes 使用者”走向“入口与安全架构负责人”的关键一步。