Kubernetes etcd 访问面收敛:验证网络隔离与双向 TLS

枫少@KillBoy
枫少@KillBoy
枫少@KillBoy
管理员
323
文章
0
粉丝
云原生安全1 32字数 3981阅读13分16秒阅读模式
AI智能摘要
多数 Kubernetes 加固者紧盯 Secret 加密,却忽视了 etcd 访问面的隐性敞口。当 2379 与 2380 端口暴露于 0.0.0.0,且缺乏强制双向 TLS 时,静态加密形同虚设,任何能建立连接的主体皆可篡改集群状态。本文揭示静态 Pod 模式下 NetworkPolicy 的局限,解析主机防火墙与安全组在收敛访问面中的决定性作用。通过构建包含快照备份、基线记录与回滚预案的闭环流程,运维人员如何在不中断控制平面 quorum 的前提下,精准剥离冗余网段与宽泛规则,最终实现 etcd 信任链的彻底收敛?
— AI 生成的文章内容摘要

在自建 Kubernetes 集群里,API server 与 etcd 之间的链路承载着全部对象状态、准入结果和控制器协调数据。很多加固清单把注意力放在 Secret 的静态加密上,但另一个更直接的问题往往被忽略:谁能够到达 etcd 的 2379 和 2380 端口,连接建立时是否强制双方出示证书,证书又由哪条信任链签发。只要这三件事没有收敛,静态加密也无法阻止能够连上 etcd 的主体读取或篡改数据。本文面向能够修改控制平面配置的自建集群,按准备、收敛、验证和恢复的顺序展开;托管集群部分只讨论用户可检查的配置项,不假定可以改动云服务商管理的 etcd。

控制平面节点内 API Server 与 etcd 之间的加密访问链路及外部隔离边界示意

先确认访问面:谁在连 etcd,从哪里来

etcd 的访问面通常由三类连接组成。第一类是 kube-apiserver 到 etcd 的客户端连接,默认使用 2379 端口,负责读写集群状态;第二类是 etcd 成员之间的 peer 连接,默认使用 2380 端口,负责日志复制和选主;第三类是运维、备份和监控工具发起的客户端连接。真正的风险往往不在第一类连接本身,而在第二类和第三类连接被放宽到了不必要的网段,或者 etcd 监听在 0.0.0.0 上,使公网、办公网或普通工作负载网段都可能尝试握手。

访问者典型端口目的收敛要点
kube-apiserver2379读写集群状态使用专用客户端证书,限制来源为控制平面节点
etcd 成员2380日志复制、选主启用 peer 双向 TLS,只允许成员之间互访
备份与巡检工具2379快照、健康检查使用独立客户端证书,固定来源地址
监控系统2379采集 metrics只读访问,避免复用 apiserver 证书
普通工作负载2379/2380不应到达通过网络隔离明确拒绝

在 kubeadm 生成的控制平面中,etcd 通常以静态 Pod 运行,并启用 hostNetwork。这意味着它的流量不经过 CNI,Kubernetes 的 NetworkPolicy 通常无法约束 etcd 的监听端口。此时真正起作用的是节点主机防火墙、云安全组或网络 ACL,而不是命名空间里的 NetworkPolicy。如果集群中的 etcd 以普通 Pod 形式运行,或者使用了支持主机端点策略的网络插件,约束方式会不同,需要按实际部署形态判断。Kubernetes 官方安全清单也明确建议 etcd 不应公开暴露在互联网上,并应使用双向 TLS 保护通信。

在获得变更授权后,先读取实际配置,而不是凭经验假设默认值:

grep -E 'listen-(client|peer)-urls|advertise-(client|peer)-urls|client-cert-auth|peer-client-cert-auth|trusted-ca-file|cert-file|key-file' 
  /etc/kubernetes/manifests/etcd.yaml

ss -lntp | grep -E ':(2379|2380)b'

同时检查 kube-apiserver 一侧使用的 etcd 地址和证书:

grep -E 'etcd-(servers|certfile|keyfile|cafile)' 
  /etc/kubernetes/manifests/kube-apiserver.yaml

如果 --listen-client-urls 包含 0.0.0.0、节点公网地址,或者安全组中存在 0.0.0.0/0 到 2379/2380 的入站规则,就应当把这两项列为优先收敛项。注意,监听地址和 advertise-client-urls 不是一回事:前者决定本机在哪些接口上接受连接,后者决定其他成员和客户端被告知去连哪个地址。只改监听地址通常比改 advertise 地址风险更低,但仍要保证 advertise 地址落在监听范围内。

准备:备份、基线与回滚预案

访问面收敛涉及 etcd 重启、防火墙变更和证书信任调整,任何一步都可能导致 API server 短暂无法连接 etcd。准备阶段的目标不是把变更做得更快,而是让每一步都可验证、可撤回。

备份 etcd 快照

在变更前对每个成员或至少对集群做一次快照,并确认快照可读。etcdctl 的版本应与集群 etcd 版本兼容,证书应使用具备客户端认证用途的证书。以下示例中的地址和路径需要替换为实际值:

ETCDCTL_API=3 etcdctl 
  --endpoints=https://10.0.0.11:2379 
  --cacert=/etc/kubernetes/pki/etcd/ca.crt 
  --cert=/etc/kubernetes/pki/apiserver-etcd-client.crt 
  --key=/etc/kubernetes/pki/apiserver-etcd-client.key 
  snapshot save /var/backups/etcd-$(date +%F-%H%M).db

ETCDCTL_API=3 etcdctl snapshot status /var/backups/etcd-*.db -w table

快照会读取全量数据,建议在低峰期执行,并避免与大规模写入或成员扩缩容重叠。快照文件应复制到控制平面节点之外的位置,否则节点故障时备份会一起丢失。

记录基线

至少记录以下内容,形成可对照的基线表:

  • 每个成员的 endpoint status 和成员列表;
  • etcd 客户端与 peer 的监听地址、advertise 地址;
  • client-cert-auth 和 peer-client-cert-auth 的当前值;
  • etcd CA、server 证书、peer 证书和 apiserver 客户端证书的路径与到期时间;
  • 主机防火墙规则、云安全组入站规则和网络 ACL;
  • kube-apiserver 的 --etcd-servers 列表。
ETCDCTL_API=3 etcdctl 
  --endpoints=https://10.0.0.11:2379,https://10.0.0.12:2379,https://10.0.0.13:2379 
  --cacert=/etc/kubernetes/pki/etcd/ca.crt 
  --cert=/etc/kubernetes/pki/apiserver-etcd-client.crt 
  --key=/etc/kubernetes/pki/apiserver-etcd-client.key 
  endpoint status -w table

明确回滚路径

回滚预案要回答三个问题:API server 不可用时如何登录节点,防火墙规则如何快速恢复,证书配置如何退回上一版本。至少确认以下几点:

  • 具备不依赖 Kubernetes API 的带外访问方式,例如 SSH、串口控制台或云厂商的远程连接;
  • 变更窗口内一次只操作一个成员,确认集群仍保有 quorum 后再继续;
  • 防火墙变更遵循“先加允许、再删宽泛规则”的顺序,避免中间态把管理通道也切断;
  • 旧版静态 Pod 清单、防火墙规则和证书文件已备份到可快速取回的位置;
  • 明确什么情况下停止变更并回滚,而不是继续尝试。

对于三成员 etcd 集群,一次重启一个成员通常仍能维持多数派;两成员集群没有额外容错,任何一个成员不可用都可能影响 quorum,变更前应特别谨慎。

收敛访问面

监听地址最小化

监听地址的目标是只暴露必要的接口。单节点控制平面且 API server 与 etcd 同机时,可以考虑只监听回环地址;多控制平面节点则需要让其他节点的 API server 和 etcd 成员能够访问节点地址或专用网络地址。修改静态 Pod 清单后,kubelet 会检测到文件变化并重建 etcd 容器,因此这是一次真实的 etcd 重启,不是热加载。

# 示意:只保留回环地址和当前节点地址,实际值以集群网络为准
- --listen-client-urls=https://127.0.0.1:2379,https://10.0.0.11:2379
- --listen-peer-urls=https://10.0.0.11:2380

修改前要核对 advertise-client-urls 和 advertise-peer-urls 是否仍落在监听范围内,以及 kube-apiserver 的 --etcd-servers 是否与之一致。若只改监听地址而 advertise 地址指向一个不再被监听的接口,其他成员或 API server 会连接失败。

用主机防火墙和云安全组限制来源

在节点上,将 2379 和 2380 的入站访问限制到控制平面节点网段。以下 iptables 示例仅说明规则顺序,不能直接复制到生产环境;执行前要检查现有规则、默认策略和持久化机制,避免覆盖管理通道。

# 仅允许控制平面网段访问 etcd 客户端与 peer 端口
iptables -I INPUT -p tcp -s 10.0.0.0/24 --dport 2379 -j ACCEPT
iptables -I INPUT -p tcp -s 10.0.0.0/24 --dport 2380 -j ACCEPT
iptables -A INPUT -p tcp --dport 2379 -j DROP
iptables -A INPUT -p tcp --dport 2380 -j DROP

云环境中,安全组入站规则应只允许控制平面安全组访问 2379/2380,删除 0.0.0.0/0 或过宽的企业网段规则。若 etcd 前面有负载均衡器,需要确认负载均衡器的后端和健康检查来源也被纳入允许列表,否则健康检查失败可能触发不必要的摘除。

网络隔离不能替代双向 TLS。即使启用了客户端证书认证,暴露在公网的 TLS 监听器仍可能被大量未完成握手的连接占用资源。隔离解决“能不能到达”,mTLS 解决“到达后能不能通过认证”,两者需要同时存在。

双向 TLS 与专用证书边界

etcd 的客户端和 peer 通信都支持 TLS 与客户端证书认证。关键参数如下:

配置项作用需要注意
--cert-file / --key-fileetcd 服务端证书SAN 必须包含客户端实际连接的地址
--client-cert-auth=true要求客户端出示受信证书只开 HTTPS 不等于强制客户端认证
--trusted-ca-file验证客户端证书的 CA应与签发客户端证书的 CA 对应
--peer-cert-file / --peer-key-file成员间服务端证书每个成员使用自己的证书
--peer-client-cert-auth=true要求成员间也出示证书避免 peer 端口只做单向 TLS
--peer-trusted-ca-file验证 peer 客户端证书的 CA与 peer 证书签发链一致

kube-apiserver 一侧需要配置 --etcd-certfile、--etcd-keyfile、--etcd-cafile,并确保 --etcd-servers 使用 https://。如果 apiserver 仍使用明文地址,或者关闭了证书校验,双向 TLS 在 etcd 侧配置得再完整也会留下缺口。

证书边界建议按用途拆分:etcd CA 只用于 etcd 服务端、peer 和必要的客户端证书;不要让其他组件复用 etcd CA 签发证书;apiserver 到 etcd 的客户端证书与 peer 证书分开;server 证书的 SAN 覆盖所有 API server 可能连接的地址和成员名称。证书轮换时,先确认新旧 CA 和证书的信任关系能够平滑过渡,再替换文件,避免出现 apiserver 信任旧 CA、etcd 只信任新 CA 的错配。

双向 TLS 解决的是连接对端身份的真实性,不自动提供细粒度授权。etcd 默认未启用认证机制时,客户端证书主要完成 TLS 层的身份校验;如果需要按客户端区分读写权限,还需要启用 etcd 的认证与角色配置。把“能通过 TLS 握手”误认为“已经具备最小权限”,是另一个常见偏差。

验证:连通性、证书与控制平面健康

验证要在每个变更节点上执行,而不是只在第一个节点通过就结束。下面这张流程示意把变更、验证和回滚放在同一条操作链上,避免验证失败后继续变更。

etcd 访问面收敛的变更、验证与回滚流程示意

证书链与 SAN

先确认证书由预期 CA 签发、未过期,并且 SAN 包含实际访问地址:

openssl x509 -in /etc/kubernetes/pki/etcd/server.crt 
  -noout -subject -issuer -dates -ext subjectAltName

openssl verify -CAfile /etc/kubernetes/pki/etcd/ca.crt 
  /etc/kubernetes/pki/etcd/server.crt

对 apiserver 使用的客户端证书和 peer 证书也要做同样检查。SAN 缺失通常不会在文件层面报错,而是在连接时表现为 x509: certificate is valid for ... not ... 或主机名不匹配。

连接测试与 etcd 健康

用 apiserver 实际使用的客户端证书发起 TLS 连接,观察证书校验结果:

openssl s_client -connect 10.0.0.11:2379 
  -CAfile /etc/kubernetes/pki/etcd/ca.crt 
  -cert /etc/kubernetes/pki/apiserver-etcd-client.crt 
  -key /etc/kubernetes/pki/apiserver-etcd-client.key 
  -verify_return_error </dev/null

输出中应出现 Verify return code: 0 (ok)。如果服务端要求客户端证书而客户端未提供,握手会失败;如果客户端提供了证书但 CA 不匹配,也会在校验阶段被拒绝。

随后用 etcdctl 检查所有成员的端点和健康状态:

ETCDCTL_API=3 etcdctl 
  --endpoints=https://10.0.0.11:2379,https://10.0.0.12:2379,https://10.0.0.13:2379 
  --cacert=/etc/kubernetes/pki/etcd/ca.crt 
  --cert=/etc/kubernetes/pki/apiserver-etcd-client.crt 
  --key=/etc/kubernetes/pki/apiserver-etcd-client.key 
  endpoint health -w table

控制平面与观察点

API server 是否真正可用,不能只看 etcd 端口是否监听。用 kubectl 的就绪检查确认 etcd 依赖项通过:

kubectl get --raw='/readyz?verbose'

输出中应包含 etcd 相关检查项且状态正常。随后执行一次读操作和一次写操作,例如列出节点、创建一个测试命名空间再删除,确认 API server 到 etcd 的写路径没有被孤立地限制。

验证项命令或观察点通过标准失败时先做什么
监听地址ss -lntp、etcd 清单只出现在预期接口检查静态 Pod 是否已重建
来源限制主机防火墙、安全组仅控制平面网段可达恢复最近一次允许规则
证书链openssl verify返回 OK核对 CA 与证书路径
SANopenssl x509 -ext subjectAltName包含访问地址重新签发或改用匹配地址
客户端认证openssl s_client双向握手成功检查 client-cert-auth 与客户端证书
etcd 健康endpoint health所有成员 healthy检查成员日志与 quorum
API server/readyz?verboseetcd 检查通过查看 apiserver 容器日志

监控侧可以关注 etcd_server_has_leader、etcd_server_leader_changes_seen_total 以及 API server 日志中的 etcd 连接错误。变更后如果 leader 频繁变化或 apiserver 出现大量超时,应暂停后续节点操作,先判断是网络规则过严、证书不匹配,还是 etcd 成员本身未恢复。

故障恢复与复盘

误拦截的典型表现是:kubectl 请求超时或连接被拒绝,kube-apiserver 容器反复重启或就绪检查失败,etcd 日志中出现 TLS 握手错误或连接被重置。此时不要急着重启整个控制平面,按影响面从大到小恢复:

  1. 先恢复主机防火墙或安全组中的允许规则,确认 SSH 管理通道仍然可用;
  2. 检查 kube-apiserver 容器状态和日志,确认失败原因是连接拒绝、证书校验还是超时;
  3. 如果是证书配置错误,回滚到变更前的证书和 CA 配置,再逐节点重建静态 Pod;
  4. 只有在数据目录损坏或成员无法恢复时,才考虑从快照恢复,并按成员逐个执行,避免多个成员同时以旧数据启动造成状态混乱。

快照恢复不是普通的“重启修复”。恢复操作会生成新的数据目录和成员标识,需要与 initial-cluster 配置匹配;在多成员集群中,错误地同时恢复多个成员可能丢失变更后的日志。执行前应确认恢复目标和成员角色,必要时先在隔离环境验证恢复步骤。

复盘时把临时判断变成可复核的记录:允许来源网段、证书到期时间、监听地址、防火墙规则版本、变更时间和验证命令的输出。节点扩缩容、控制平面升级和证书轮换都会改变访问面,今天收敛过的配置在下次变更后可能重新放宽。

托管集群的检查边界

主流托管 Kubernetes 服务通常由云厂商管理 etcd,用户看不到 etcd 静态 Pod 清单,也无法直接修改其监听地址或证书。能够检查的通常是 API server 端点是否私有、是否配置了授权网段、审计日志是否开启并可用、控制平面日志是否可导出,以及云厂商是否声明对 etcd 通信和静态数据做了加密。把这些可观测项纳入基线即可,不要假设可以通过节点上的防火墙规则去“修正”厂商管理的 etcd,也不要为了验证隔离而扫描不属于授权范围的端点。

访问面收敛不是一次性开关。它更像一份随集群生命周期维护的清单:谁应该连、从哪个网段连、用什么证书连、失败时如何退回。把这三件事写进变更记录,并在每次控制平面变更后重新跑一遍验证命令,比单纯记住一组加固参数更有用。

 
枫少@KillBoy
    • 绿植控
      绿植控 2

      NetworkPolicy 管不到 hostNetwork 确实很容易被忽略

    匿名

    发表评论

    匿名网友

    拖动滑块以完成验证