首页 / 资讯中心 / 文章详情

K8s Nginx Ingress访问不通?四层防火墙逐级排查指南

K8s Nginx Ingress访问不通?四层防火墙逐级排查指南 ★ FEATURED ARTICLE
客户端发起的请求在到Nginx Ingress Controller之前其实要穿过三道甚至四道“安检门”每一道都可能让流量悄无声息地消失。这不是危言耸听我处理过太多“Ingress明明配置没问题但外面就是访问不通”的case最后定位到的问题全都出乎意料地朴素安全组少放了一个端口、iptables规则顺序错了、NetworkPolicy把流量拦在了最后一跳。很多人一提到“防火墙介入Ingress流量路径”第一反应是“那不就是Nginx前面的防火墙设备吗”。实际远不止如此。我把这条路径上的防火墙分成四层边界防火墙硬件设备或云防火墙、云安全组/节点防火墙、集群内的NetworkPolicy、以及Nginx Ingress自身的访问控制。每一层介入的时机、拦截的粒度、配置的方式都完全不同。这篇文章会沿着一条真实的访问链路把这四个环节拆开来讲清楚并给出我在生产环境中实际用过的配置和排障方法。不管是刚接触Kubernetes的开发者还是负责集群稳定性的运维和SRE这篇文章都能帮你少踩几个坑。1. 先画清楚流量路径一次请求从浏览器到Pod的完整旅程1.1 一条HTTP请求的完整链路在讨论防火墙之前必须先建立一张完整的流量路径地图。假设你的业务部署在Kubernetes集群里通过Nginx Ingress Controller对外暴露一个域名比如demo.example.com用户从浏览器发起HTTPS请求流量大致要经过这么一段路客户端浏览器 → DNS解析 → 边界防火墙/WAF可选看部署形态 → 云负载均衡器LoadBalancer类型Service → 节点防火墙安全组/iptables/firewalld → NodePort端口转发 → Nginx Ingress Controller Pod → 根据Ingress规则匹配server/location块 → ClusterIP Service转发 → kube-proxy负载均衡 → 后端业务Pod这里有一个很多人容易理解偏差的地方Ingress Controller本身也是一个Pod它不是直接挂在物理网络上的设备。所以想让外部流量打到它前面必须通过NodePort或LoadBalancer类型的Service把端口暴露出来。在云环境里最常见的形态是创建一个LoadBalancer类型的Service指向Ingress Controller云厂商会在VPC里部署一个负载均衡器把公网流量转发到集群节点的某个NodePort上NodePort再通过iptables/ipvs把流量导到Ingress Controller Pod。搞清楚这条链路后你再看防火墙的问题就会清晰很多流量每经过一个网络边界就可能被一道防火墙拦截。这个“边界”既包括机房出口、VPC安全组也包括节点主机的iptables甚至Kubernetes的NetworkPolicy。每一层都可能在没有任何报错的情况下把包丢掉而你在应用层只会看到超时或502。1.2 防火墙介入的时机与定位基于上面的链路我把防火墙的介入点归纳为四个层级。这个分层结构基本上对应了网络安全里常说的“纵深防御”思想。层级所在位置主要拦截对象典型误配后果边界防火墙/WAF客户端与集群之间的网络边界大流量攻击、恶意IP、七层Web攻击无法访问、连接被重置云安全组/节点防火墙云平台虚拟网卡、节点主机iptables端口、来源IP、主机级访问健康检查失败、间歇性超时集群内NetworkPolicyPod网络层Calico/Cilium实现Pod与Pod之间的访问大量502/503Nginx Ingress自身策略Nginx进程内location阶段IP黑白名单、限流、认证恶意请求直达后端这四个层级不是替代关系而是互补关系。边界防火墙负责把大部分无效流量挡在门外安全组负责在VPC层面做网络隔离NetworkPolicy负责隔离集群内部的东西向流量比如数据库只能让特定服务访问而Nginx Ingress的访问控制则最贴近业务负责在HTTP层做精细的规则过滤。明白这个分层之后再遇到访问不通的问题排查时心里就有一张地图了从外到内一层层确认流量到底是在哪一层断的方向感会清晰很多。2. 防火墙介入的四个关键环节2.1 边界安全设备硬件防火墙与WAF如果集群部署在自建机房出口通常会有硬件防火墙或负载均衡设备这些设备工作在L3/L4层有的还支持七层WAF能力。它们做的事情很直接端口访问控制、IP黑白名单、DDoS攻击缓解、TLS终止部分设备。集群部署在公有云时这个角色由云防火墙、DDoS高防来承担。这一层为什么必须存在因为Ingress Controller的性能是有限的。如果攻击流量直接压到Nginx进程即使Nginx的规则把它们拒掉了CPU和连接数已经被消耗了正常用户的请求也会被拖累。边界防火墙用硬件性能来做第一道粗粒度过滤让Nginx只需要处理真正有效的流量。我在这层踩过的坑主要是会话同步问题。硬件防火墙常做双机热备主备切换的瞬间如果会话表没有同步完成用户会观察到偶发性的TCP连接重置或超时。这个问题非常容易误判成Ingress本身的故障因为我们第一反应是去看Nginx日志但Nginx压根没收到连接。排查到最后才发现是主备切换期间会话丢了。处理方式是调整防火墙的会话同步配置或者把业务FQDN接入时设置合理的会话保持超时时间。另外如果边界设备启用了WAF要特别注意WAF规则和Nginx Ingress规则之间的重复覆盖。比如WAF已经拦截了SQL注入Nginx层再做一遍同样的规则没有任何问题但如果两边的正则规则互相冲突反而可能误伤正常请求。我的经验是边界WAF负责通用攻击防护Nginx层做业务特定的访问控制两者规则尽量不重叠。2.2 云安全组与节点防火墙最常见的隐形拦截者在公有云环境里云安全组本质上是一个分布式的有状态防火墙它挂在每台ECS/虚拟机实例的虚拟网卡上。很多K8s集群的问题都出在安全组配置不完整上。最典型的坑Ingress Controller通过LoadBalancer暴露云负载均衡器把流量转发到集群节点的NodePort。此时安全组需要放行两类流量一是外部用户到NodePort或80/443的流量二是负载均衡器健康检查源IP段到Ingress健康检查端口默认10254的流量。很多人只配了第一类健康检查端口没放行结果负载均衡器把节点标记为不健康流量被转发到其他节点表现出来的症状就是“时好时坏”。还有更隐蔽的Ingress Controller的Pod如果需要访问外部服务比如回调第三方API而节点安全组的出方向规则没放行就会导致Pod里的Nginx可以正常处理入站请求但反向代理到外部时超时。这虽然不直接影响外部到Ingress的主链路但会影响业务功能完整性。节点上的主机防火墙指的是firewalld和iptables。这里有一条铁律在Kubernetes节点上不要轻易动iptables的转发链或执行flush操作。kube-proxy通过iptables或ipvs维护着KUBE-SVC、KUBE-SEP这些链一旦把规则清了整个集群的网络转发就废了。我见过生产事故有人为了“清理防火墙规则”执行了iptables -F结果集群内所有Service的ClusterIP转发全部中断Pod之间互相ping不通。节点的防护应该只做“主机入站”层面的控制也就是控制哪些端口可以从外部或者宿主机网络访问。比如在INPUT链上放行业务端口但不要碰FORWARD链更不要干预kube-proxy管理的那几张表。2.3 集群内NetworkPolicy被忽视的第二层防火墙很多团队对Kubernetes的认知只停留在“对外暴露服务”对集群内部东西向流量几乎零防护。这里说的是NetworkPolicy它是Kubernetes原生的面向Pod的网络访问策略作用范围是Pod与Pod之间、Pod与外部之间的流量。可以把它理解成集群内部的分布式防火墙。默认情况下Kubernetes集群里的Pod是全网互通的没有任何访问限制。只有当你创建了一条NetworkPolicy之后不符合这条策略的流量才会被拒绝。这条规则由实现NetworkPolicy的网络插件比如Calico、Cilium下发到节点在数据路径上强制执行。实际场景很常见后端数据库Pod设置了只允许Ingress Controller所在命名空间的Pod访问其它服务一律拒绝。这样即使某个业务Pod被入侵了攻击者也拿不到数据库连接。在这个架构里Ingress Controller扮演了“唯一可信入口”的角色后端服务只需要信任这一个来源应用层的鉴权压力会小很多。但NetworkPolicy也是一把双刃剑。我遇到过几次事故都是给某个命名空间新增了一条Policy之后Ingress访问后端服务从正常变成了502。原因几乎是同一个Policy里的Label选择器没有匹配到Ingress Controller的Pod。这里要特别提醒Kubernetes的NetworkPolicy在from里不仅支持podSelector还支持namespaceSelector。如果只写了podSelector它默认匹配的是和Policy同命名空间下的Pod。想放行跨命名空间的Pod必须同时使用namespaceSelector来限定来源命名空间或者用ipBlock。配置NetworkPolicy的核心思路是最小权限先拒绝再显式放行。但一次别把策略组全上了建议先把Ingress到各个业务服务的放行策略加载上去观察一段时间再逐步补充其它策略。2.4 Nginx Ingress自身的访问控制最后一道软防火墙当流量穿过上面所有层终于到达Nginx Ingress Controller的进程时Nginx自身也提供了好几道“软防火墙”能力。这是最贴近应用层的一道防线同时也是很多人手里“握着但没用起来”的能力。三个最常用的能力IP白名单、请求限流、Basic Auth。它们都是通过Ingress的annotation配置的最终被翻译成Nginx的配置指令。IP白名单对应的是nginx.ingress.kubernetes.io/whitelist-source-range配置了之后就相当于在Nginx的access阶段写入了allow和deny规则限流对应的是nginx.ingress.kubernetes.io/limit-rps和limit-rpm最终落到limit_req指令上Basic Auth则是通过auth-type和auth-secret实现的。还有一个容易被忽略的细节Nginx Ingress的请求处理流程会经历server块匹配、location块匹配、rewrite阶段、access阶段、proxy_pass转发等环节。whitelist-source-range这类annotation生效在location级别也就是说你可以给不同的path配置不同的白名单。比如/admin路径只允许内网IP访问/api路径允许所有用户访问这就实现了基于路径的精细化访问控制。很多人问既然Nginx自己能做黑白名单为什么还要在前面加防火墙反过来也有一种问题既然有防火墙了Nginx是不是不用再配置这些了答案是都不能偏废。Nginx的过滤是L7层应用级的它对TCP洪水这种攻击无能为力需要前面的防火墙来挡而防火墙如果没有开WAF能力它对SQL注入这种七层攻击也看不见需要Nginx或者应用层来防护。分层防护的意义就在于此。3. 实操层层防火墙规则怎么配才不踩坑3.1 边界防火墙与安全组配置实操这一步我先给出通用安全组配置参考。假设集群在阿里云或AWS上Ingress Controller通过LoadBalancer暴露这种情况下入方向安全组至少要有这么几条规则方向协议端口来源用途入方向TCP10254负载均衡器健康检查源网段Nginx Ingress健康检查入方向TCP80业务来源IP段/公网HTTP流量入方向TCP443业务来源IP段/公网HTTPS流量入方向TCP30000-32767负载均衡器源网段可选NodePort兜底10254这个端口容易被忽视它就是Ingress Controller的healthz和metrics端口。如果它被安全组挡了云负载均衡的健康检查会一直失败节点会被反复摘除用户请求就会间歇性失败。如果你在前面还挂了CDN安全组的来源建议只放行CDN的回源IP段而不是把所有公网IP都放进来这样可以显著减少恶意扫描。边界防火墙的配置同理只放行业务必要的端口其它端口默认拒绝。硬件防火墙上的SYN超时建议设置在30到60秒最大半开连接数也要根据业务规模评估防止TCP半连接攻击拖垮设备。一个实操细节当你修改安全组规则时云平台通常会有“安全组规则的变更在5到10秒内生效”之类的说法但实际上某些平台在规则变更的瞬间可能出现短暂的连接闪断。建议在业务低峰期变更规则并且变更后马上用curl验证。3.2 节点防火墙与NetworkPolicy配置节点防火墙方面的建议很简单能用安全组就用安全组不要在节点上额外跑firewalld。非要跑的话只放行业务端口并做好持久化。# 如果节点用的是firewalld firewall-cmd --permanent --add-port80/tcp firewall-cmd --permanent --add-port443/tcp firewall-cmd --permanent --add-port10254/tcp firewall-cmd --reload如果节点直接管理iptables注意两点。第一确认INPUT链的默认策略很多发行版默认是ACCEPT如果你把它改成DROP一定要把22端口等管理端口也放行否则连远程登录都进不去。第二规则顺序很重要。iptables是顺序匹配的如果你把一条DROP ALL放在最前面后面的ACCEPT规则等于白写。我习惯的写法是先放行回环和已知端口最后再加默认拒绝。# 规则顺序示例 iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT iptables -A INPUT -p tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp --dport 443 -j ACCEPT iptables -A INPUT -j DROP iptables-save /etc/iptables/rules.v4NetworkPolicy的实操我给出一个完整示例。场景是只允许ingress-nginx命名空间下运行Ingress Controller的Pod访问production命名空间里appbackend这个Deployment的Pod。apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-ingress-to-backend namespace: production spec: podSelector: matchLabels: app: backend policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: ingress-nginx podSelector: matchLabels: app.kubernetes.io/name: ingress-nginx我特意加上了namespaceSelector再加podSelector的双重限定。很多人只写podSelector结果Policy在production命名空间下只能匹配production里的同名Pod跨命名空间的Ingress Pod根本不在范围内流量直接被打断。也可以先用--dry-run生成策略再apply减少误操作风险。3.3 Nginx Ingress的限流与黑白名单配置Nginx Ingress的访问控制配置相当简洁全部集中在annotation里。给一个完整的Ingress示例apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: demo-ingress annotations: nginx.ingress.kubernetes.io/whitelist-source-range: 10.0.0.0/8, 192.168.0.0/16 nginx.ingress.kubernetes.io/limit-rps: 20 nginx.ingress.kubernetes.io/limit-rpm: 1200 nginx.ingress.kubernetes.io/auth-type: basic nginx.ingress.kubernetes.io/auth-secret: default/basic-auth-secret nginx.ingress.kubernetes.io/auth-realm: Authentication Required spec: rules: - host: demo.example.com http: paths: - path: / pathType: Prefix backend: service: name: demo-service port: number: 80关于这几个annotation几个值得注意的细节whitelist-source-range是CIDR列表多个网段用英文逗号分隔。这个配置生效后不在列表里的IP访问会直接返回403。limit-rps和limit-rpm可以同时设置。Nginx内部其实是按IP、按请求URI维度统计的。Basic Auth的secret内容必须是htpasswd生成的格式命令参考htpasswd -c auth.txt admin kubectl create secret generic basic-auth-secret --from-fileauthauth.txt如果想让限流时不直接返回503而是返回429可以在Ingress Controller的ConfigMap里加一行配置limit-req-status-code: 429。这样客户端看到的是“请求过多”而非“服务不可用”更符合业务语义。另外还有一个实践技巧Nginx Ingress支持在annotation里配置nginx.ingress.kubernetes.io/server-snippet这样可以在server块里插入自定义Nginx配置。比如针对某个域名加一条地理位置层面的限制。不过server-snippet的灵活性高用户自担风险改错会导致配置加载失败生产环境慎用。4. 常见问题与排障经验4.1 典型故障案例我在实际工作中遇到过的防火墙相关故障几乎都可以归类到下面三种情况里。每个案例都是真实的经验具备很高的参考价值。第一个案例NodePort能通但Ingress域名访问不通。表面上看很诡异手动用节点IP加NodePort端口访问是通的说明Nginx正常、Service正常、Pod正常。但域名就是访问不了。排查发现Nginx Ingress是通过80端口对外提供服务的而云安全组只放行了NodePort端口30080没有放行节点80端口。用户流量经过负载均衡转发到节点80时被安全组直接丢弃。这个案例告诉我们安全组的放行策略必须和实际流量路径完全对齐不能凭印象配。第二个案例Nginx日志里没有任何请求但防火墙日志显示连接已经到达节点。用tcpdump在节点上抓包发现包确实到了网卡但没有进程响应。最后定位是iptables的INPUT链默认策略是DROP而且80端口没有被放行。这种问题在那些“之前手动配置过iptables但没持久化”的节点上尤其容易出现系统重启后规则丢失防火墙恢复默认的严格策略。第三个案例新增NetworkPolicy后出现大面积502。这个前面提过是Label选择器没匹配上Ingress Controller的Pod。排查时看Nginx日志会看到connect() failed (113: No route to host)之类的错误或者更隐蔽的Connection refused。解决办法是调整Policy把Ingress Controller的Pod正确纳入放行范围。4.2 排障工具与思路排障思路说穿了就是“分层追踪、从外到内”。先从客户端发起请求观察现象再到边界设备确认流量是否到达然后到节点上抓包看是否进入主机最后到Ingress日志里确认请求是否被处理。我常用的工具组合和命令# 1. 客户端发起请求观察网络层和HTTP层表现 curl -v https://demo.example.com # 2. 追踪路由路径看是否有黑洞或中间节点丢包 mtr -rwz demo.example.com # 3. 在Ingress节点上抓包确认包是否到达 tcpdump -i eth0 tcp port 443 -w /tmp/ingress.pcap # 4. 确认端口监听是否正常 ss -lntp | grep -E 80|443|10254 # 5. 查看Ingress Controller日志 kubectl logs -f -n ingress-nginx deployment/ingress-nginx-controller --tail100 # 6. 确认Service后端是否有可用Pod kubectl get svc -n demo-service kubectl get endpoints -n production还有一个容易忽略的细节云平台的控制台里看到的“健康检查状态”可能来自不同维度有的检查的是TCP端口有的是HTTP路径。要把负载均衡的健康检查路径默认是/healthz和Ingress的10254端口对齐否则会出现“控制台显示正常但实际流量不通”的怪现象。4.3 分层防护的设计建议最后聊一聊设计层面的经验。我的核心观点是四层防火墙各有职责不要试图把安全功能全部堆在Nginx Ingress上。边界防火墙处理大流量攻击和粗粒度IP封锁安全组处理云平台层面的网络隔离NetworkPolicy处理集群内部的微隔离Nginx只处理应用层的、和业务强相关的访问控制。每层职责清晰配置变更的时候就可以按层评审出了问题也方便定位。关于规则变更我的实践建议是“先增量后收敛”。要新增一个IP白名单先在规则列表末尾追加一条allow规则观察一段时间确认没问题再考虑调整规则顺序或删除旧规则。千万不要一次性重写整张规则表万一某条规则的方向或来源写错影响的就是一片流量。还有一条经验把防火墙规则纳入版本管理。云安全组的Terraform配置、NetworkPolicy的YAML、Nginx Ingress的annotation都应该在Git仓库里留档。我在排障时经常干的一件事就是先git diff看最近一次变更是不是动了安全策略。有了版本记录很多疑难杂症都能快速锁定到“某条规则被某人在某个时间点改掉了”。最后再分享一个小技巧每次调整完防火墙规则我都会顺手在群里发一条验证记录包括改了什么、验证了什么、回滚方案是什么。看起来有点繁琐但真的能在出问题时节省大量沟通成本。希望你看完这篇文章后下次再遇到“Ingress一切正常但就是不通”的诡异故障时能想起这条流量路径上还有四道防火墙需要逐一排查。
阅读完成 · 觉得有帮助?
咨询建站