网络策略

网络策略

NetworkPolicy 是 Kubernetes 的网络安全机制,用于控制 Pod 之间的流量。

核心概念

┌─────────────────────────────────────────┐
│         NetworkPolicy                   │
├─────────────────────────────────────────┤
│  podSelector: 选择要应用策略的 Pod      │
│  policyTypes: [Ingress, Egress]        │
│  ingress: 入站规则                      │
│  egress:  出站规则                      │
└─────────────────────────────────────────┘

关键点

  • NetworkPolicy 是白名单机制
  • 需要 CNI 插件支持(Calico、Cilium、Weave)
  • 多个策略叠加(OR 逻辑)

默认拒绝策略

拒绝所有入站流量

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}  # 选择所有 Pod
  policyTypes:
  - Ingress

拒绝所有出站流量

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Egress

拒绝所有流量(入站+出站)

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

允许特定流量

允许来自特定 Pod 的流量

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-frontend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080

允许来自特定 Namespace 的流量

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-monitoring
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: monitoring
    ports:
    - protocol: TCP
      port: 8080

允许特定 IP 段的流量

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-ip-range
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
  - Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: 10.0.0.0/8
        except:
        - 10.0.1.0/24  # 排除特定子网
    ports:
    - protocol: TCP
      port: 443

出站流量控制

允许访问特定服务

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-egress-to-database
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Egress
  egress:
  # 允许访问数据库
  - to:
    - podSelector:
        matchLabels:
          app: database
    ports:
    - protocol: TCP
      port: 5432
  
  # 允许 DNS 查询(必需!)
  - to:
    - namespaceSelector:
        matchLabels:
          name: kube-system
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53

允许访问外部 API

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-external-api
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
  - Egress
  egress:
  # 允许访问外部 API
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 10.0.0.0/8      # 内网
        - 172.16.0.0/12   # 内网
        - 192.168.0.0/16  # 内网
    ports:
    - protocol: TCP
      port: 443
  
  # 允许 DNS
  - to:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53

跨命名空间访问

场景:允许 Ingress 访问应用

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress-controller
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: ingress-nginx
      podSelector:
        matchLabels:
          app.kubernetes.io/name: ingress-nginx
    ports:
    - protocol: TCP
      port: 8080

场景:允许 Prometheus 采集指标

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-prometheus-scraping
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: monitoring
      podSelector:
        matchLabels:
          app: prometheus
    ports:
    - protocol: TCP
      port: 8080
      # 假设 /metrics 在 8080 端口

完整的三层应用网络策略

Frontend 策略

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: frontend-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      tier: frontend
  policyTypes:
  - Ingress
  - Egress
  
  ingress:
  # 允许来自 Ingress Controller 的流量
  - from:
    - namespaceSelector:
        matchLabels:
          name: ingress-nginx
    ports:
    - protocol: TCP
      port: 80
  
  egress:
  # 允许访问 Backend
  - to:
    - podSelector:
        matchLabels:
          tier: backend
    ports:
    - protocol: TCP
      port: 8080
  
  # 允许 DNS
  - to:
    - namespaceSelector:
        matchLabels:
          name: kube-system
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53

Backend 策略

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      tier: backend
  policyTypes:
  - Ingress
  - Egress
  
  ingress:
  # 只允许来自 Frontend 的流量
  - from:
    - podSelector:
        matchLabels:
          tier: frontend
    ports:
    - protocol: TCP
      port: 8080
  
  # 允许 Prometheus 监控
  - from:
    - namespaceSelector:
        matchLabels:
          name: monitoring
      podSelector:
        matchLabels:
          app: prometheus
    ports:
    - protocol: TCP
      port: 8080
  
  egress:
  # 允许访问 Database
  - to:
    - podSelector:
        matchLabels:
          tier: database
    ports:
    - protocol: TCP
      port: 5432
  
  # 允许访问 Redis
  - to:
    - podSelector:
        matchLabels:
          tier: cache
    ports:
    - protocol: TCP
      port: 6379
  
  # 允许 DNS
  - to:
    - namespaceSelector:
        matchLabels:
          name: kube-system
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53

Database 策略

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: database-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      tier: database
  policyTypes:
  - Ingress
  - Egress
  
  ingress:
  # 只允许来自 Backend 的流量
  - from:
    - podSelector:
        matchLabels:
          tier: backend
    ports:
    - protocol: TCP
      port: 5432
  
  egress:
  # 数据库通常不需要出站流量
  # 但可能需要访问外部备份服务
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
    ports:
    - protocol: TCP
      port: 443
  
  # DNS(如果需要)
  - to:
    - namespaceSelector:
        matchLabels:
          name: kube-system
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53

高级用法

使用多个选择器(AND 逻辑)

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-specific-namespace-and-pod
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
  - Ingress
  ingress:
  - from:
    # 必须同时满足两个条件
    - namespaceSelector:
        matchLabels:
          name: monitoring
      podSelector:
        matchLabels:
          app: prometheus
    ports:
    - protocol: TCP
      port: 8080

使用多个规则(OR 逻辑)

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-multiple-sources
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
  - Ingress
  ingress:
  # 规则1:来自 frontend
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080
  
  # 规则2:来自 admin namespace
  - from:
    - namespaceSelector:
        matchLabels:
          name: admin
    ports:
    - protocol: TCP
      port: 8080

调试 NetworkPolicy

检查策略是否生效

# 查看 NetworkPolicy
kubectl get networkpolicy -n production

# 查看详细信息
kubectl describe networkpolicy <policy-name> -n production

# 检查 Pod 标签
kubectl get pods --show-labels -n production

测试连通性

# 从 Pod A 测试访问 Pod B
kubectl exec -it pod-a -n production -- curl http://pod-b-ip:8080

# 使用 netshoot 调试
kubectl run netshoot --rm -it --image=nicolaka/netshoot -n production -- /bin/bash

# 在 netshoot 中测试
curl http://backend-service:8080
nc -zv backend-service 8080

常见问题

问题 1:策略不生效

# 检查 CNI 插件是否支持 NetworkPolicy
kubectl get pods -n kube-system | grep -E 'calico|cilium|weave'

# 如果使用 Flannel,需要额外配置
# Flannel 默认不支持 NetworkPolicy

问题 2:DNS 无法解析

# 确保允许 DNS 流量
egress:
- to:
  - namespaceSelector:
      matchLabels:
        name: kube-system
    podSelector:
      matchLabels:
        k8s-app: kube-dns
  ports:
  - protocol: UDP
    port: 53
  - protocol: TCP
    port: 53  # 某些情况下也需要 TCP

问题 3:忘记允许内部服务发现

# 允许访问 Kubernetes API(Service 发现)
egress:
- to:
  - namespaceSelector: {}
  ports:
  - protocol: TCP
    port: 443

NetworkPolicy 生成器

使用工具自动生成策略:

# 使用 kubepug 生成建议
kubectl run policy-advisor --rm -it --image=quay.io/fairwinds/polaris:latest -- sh

# 使用 Cilium Editor
# https://editor.cilium.io/

# 使用 Network Policy Editor
# https://networkpolicy.io/

最佳实践

1. 默认拒绝 + 白名单

# 第一步:默认拒绝所有
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

---
# 第二步:逐个开放必要的流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-specific-traffic
spec:
  podSelector:
    matchLabels:
      app: myapp
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend

2. 分层隔离

┌─────────────────┐
│    Frontend     │ ← 允许来自 Ingress
└────────┬────────┘
         │
┌────────▼────────┐
│     Backend     │ ← 只允许来自 Frontend
└────────┬────────┘
         │
┌────────▼────────┐
│    Database     │ ← 只允许来自 Backend
└─────────────────┘

3. 测试驱动

# 1. 先部署应用(不加 NetworkPolicy)
# 2. 确认应用正常工作
# 3. 添加默认拒绝策略
# 4. 测试(预期失败)
# 5. 逐步添加允许规则
# 6. 每次添加后测试

4. 文档化

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-policy
  namespace: production
  annotations:
    description: "Backend 层网络策略"
    purpose: "只允许来自 Frontend 的流量访问 Backend"
    owner: "platform-team"
    reviewed: "2024-01-08"

小结

本节介绍了网络策略:

默认拒绝:默认拒绝所有流量,白名单机制
入站控制:podSelector、namespaceSelector、ipBlock
出站控制:DNS、数据库、外部 API
跨命名空间:Ingress、Prometheus 访问
三层架构:Frontend、Backend、Database 完整策略
调试方法:测试连通性、常见问题
最佳实践:分层隔离、测试驱动

下一节:Pod 和镜像安全。