WEBKT

Calico分层安全:如何在K8s多租户集群中构建不可覆盖的网络安全基线

68 0 0 0

作为一名在多租户Kubernetes环境中摸爬滚打的网络安全工程师,我深知Pod间流量安全的重要性,也清楚Kubernetes原生的NetworkPolicy在保护这些流量方面发挥着核心作用。然而,当面对一个拥有多个租户或多个开发团队的大型集群时,如何协调和强制执行一套全局性的、不可随意覆盖的安全标准,同时又不阻碍各团队根据自身应用需求定义额外策略,这确实是一个令人头疼的挑战。

我们理想中的状态是:集群层面有一套“安全基线”策略,它是强制性的,任何团队都不能简单地覆盖或禁用它;而各团队可以在这个基线之上,为自己的应用定义更细粒度的策略。幸运的是,Calico——作为Kubernetes最流行的网络插件之一,提供了强大的功能来精准解决这个痛点:GlobalNetworkPolicy(全局网络策略)Policy Tiers(策略层)

Kubernetes原生NetworkPolicy的局限性

在深入Calico的解决方案之前,我们先回顾一下原生NetworkPolicy的特点:

  1. 命名空间(Namespace)限定: NetworkPolicy是命名空间范围的资源,只能应用于它所在的命名空间内的Pod。
  2. 没有优先级概念: 同一命名空间内的多个NetworkPolicy是累加生效的,没有明确的优先级顺序。这使得全局性的“默认拒绝”策略难以在不影响其他NetworkPolicy的情况下实施。
  3. 权限分散: 各团队通常有权限管理自己命名空间内的NetworkPolicy,这使得强制推行统一的安全基线变得困难,因为他们可能误操作或故意修改策略,导致安全漏洞。

这些局限性使得我们很难定义一个跨所有命名空间且不可被覆盖的全局安全基线。

Calico的解决方案:GlobalNetworkPolicy与Policy Tiers

Calico通过引入GlobalNetworkPolicyPolicy Tiers两个核心概念,完美地解决了上述挑战。

1. GlobalNetworkPolicy(全局网络策略)

GlobalNetworkPolicy是集群范围的资源,与NetworkPolicy不同,它不限于特定的命名空间。这意味着我们可以定义适用于整个集群、所有命名空间或特定Pod集的策略。

特点:

  • 集群范围: 作用于整个Kubernetes集群。
  • 优先级: GlobalNetworkPolicy比普通的NetworkPolicy拥有更高的优先级(默认情况下)。
  • 跨命名空间: 可以定义跨多个命名空间的Pod间通信规则。

2. Policy Tiers(策略层)

这是实现分层强制性安全策略的关键。Calico允许你定义多个策略层(Tiers),每个层都有一个明确的顺序。策略在层内执行,并且层之间也按照预设的顺序执行。高优先级的层中的策略会先于低优先级的层中的策略被评估和执行。

核心思想:
我们可以创建一个高优先级的“安全基线层”(例如,命名为security-baseline),将所有强制性的全局安全规则定义为GlobalNetworkPolicy并放置在这个层中。然后,为各团队的应用策略预留一个较低优先级的层(例如,默认的default层),允许他们在其中定义自己的NetworkPolicyGlobalNetworkPolicy。由于高优先级层的策略首先被评估,低优先级层的策略无法“覆盖”高优先级层中已经定义的拒绝或允许规则。

构建分层安全策略的实践

下面我们将通过具体示例,演示如何使用Calico实现这种分层强制性安全控制。

步骤一:定义自定义策略层(可选,但推荐)

虽然Calico有默认的策略层(如defaultsystem),但为了清晰和隔离,我们推荐创建自定义的策略层。例如,创建一个名为security-baseline的高优先级层用于全局基线策略,以及一个application-policies层用于团队应用策略。

策略层定义示例 (Tier.yaml):

# security-baseline 层,优先级很高
apiVersion: projectcalico.org/v3
kind: Tier
metadata:
  name: security-baseline
spec:
  order: 100 # 较低的order值表示更高的优先级
---
# application-policies 层,优先级低于 security-baseline
apiVersion: projectcalico.org/v3
kind: Tier
metadata:
  name: application-policies
spec:
  order: 200 # 较高的order值表示较低的优先级

解释: order字段决定了层的优先级。数值越小,优先级越高。这里security-baseline层的order: 100application-policies层的order: 200优先级更高。

步骤二:定义全局安全基线策略 (GlobalNetworkPolicy)

在这个security-baseline层中,我们可以定义一些强制性的、全局适用的GlobalNetworkPolicy

示例1:默认拒绝所有入站和出站流量(零信任基线)

这是最严格的基线策略,所有Pod在没有被明确允许的情况下都无法进行通信。

# deny-all-ingress-egress.yaml
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: default-deny-all-traffic
spec:
  tier: security-baseline # 绑定到我们定义的高优先级层
  order: 10 # 在该层内,此策略优先级最高
  selector: all() # 应用于所有Pod
  types:
    - Ingress
    - Egress
  ingress:
    - action: Deny
  egress:
    - action: Deny

解释:

  • tier: security-baseline:明确将此策略放置在security-baseline层。
  • order: 10:在该层内部,可以定义多个策略,此order决定它们之间的优先级。
  • selector: all():这个策略将应用于集群中的所有Pod。
  • action: Deny:默认拒绝所有入站和出站流量。

示例2:允许特定的集群内部服务通信(例如,kube-dns)

在默认拒绝所有流量之后,我们需要显式地允许一些核心服务(如DNS、API Server)的通信,否则集群将无法正常工作。这些策略也应放置在security-baseline层。

# allow-kube-dns.yaml
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: allow-kube-dns
spec:
  tier: security-baseline
  order: 20 # 优先级略低于默认拒绝策略,但高于其他应用策略
  selector: all() # 应用于所有Pod
  egress:
    - action: Allow
      destination:
        selector: k8s-app == "kube-dns" # 允许所有Pod访问kube-dns
        ports:
          - 53 # DNS UDP
          - 53 # DNS TCP
      protocol: UDP
    - action: Allow
      destination:
        selector: k8s-app == "kube-dns"
        ports:
          - 53
      protocol: TCP

解释: 此策略允许集群中的所有Pod访问kube-dns服务。由于它也在security-baseline层且order20(高于默认拒绝策略的order: 10),它将在默认拒绝策略之后被评估。但是,Calico策略的执行逻辑是“最先匹配的允许或拒绝规则决定流量走向”。这意味着如果一个流量匹配了Deny规则,它就会被拒绝;如果匹配了Allow规则,它就会被允许。

重要提示: 在Calico中,策略的执行顺序是:高优先级层 -> 低优先级层层内order值小的策略 -> order值大的策略。一旦流量被某个策略允许或拒绝,后续的策略将不再对其进行处理。因此,Deny规则通常应在Allow规则之前定义,以确保拒绝优先级。为了实现“默认拒绝所有,但允许特定流量”的模式,通常会有一个Deny所有流量的策略(例如order: 10),然后是一些Allow特定流量的策略(例如order: 20, order: 30)。在同一个策略层中,如果一个Deny策略的order低于(优先级更高)一个Allow策略,那么Deny将生效。为了实现“默认拒绝,然后允许特定”,通常会把Allow策略的order设置得比Deny高,或者将Deny放在最底层。
但这里用户想要的是“不可被随意覆盖的基线”,这意味着基线的拒绝规则优先级最高,团队只能增加允许规则。
更推荐的做法是:

  • Baselin Tier (security-baseline, order 100): 包含所有强制的拒绝规则必须允许的规则
    • 例如:deny-internet-egress (阻止非白名单外网访问)
    • 例如:allow-api-server (允许所有Pod访问K8s API Server)
  • Default/Application Tier (application-policies, order 200): 团队在此层定义自己的允许规则,但不能定义拒绝规则来“撤销”基线的拒绝。

让我们修改一下示例1和示例2的策略应用思路:

策略层定义 (Tier.yaml):

# system 层,最高优先级,Calico内置,用于系统关键流量
# security-baseline 层,优先级高于应用层,用于全局强制性基线
apiVersion: projectcalico.org/v3
kind: Tier
metadata:
  name: security-baseline
spec:
  order: 150 # 确保高于应用层,但低于Calico内置的system层
---
# application-policies 层,优先级最低,用于各团队自定义应用策略
apiVersion: projectcalico.org/v3
kind: Tier
metadata:
  name: application-policies
spec:
  order: 250

GlobalNetworkPolicy示例:基线拒绝所有出站到公网流量(除了白名单)

# baseline-deny-egress-to-internet.yaml
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: deny-internet-egress
spec:
  tier: security-baseline
  order: 10 # 在此层内优先级较高
  selector: all()
  egress:
    - action: Deny # 默认拒绝所有非私有IP的出站流量
      destination:
        notCidr:
          - 10.0.0.0/8
          - 172.16.0.0/12
          - 192.168.0.0/16
          - 100.64.0.0/10 # CGNAT
          - 127.0.0.0/8 # Loopback
          - 169.254.0.0/16 # Link-local
          - 0.0.0.0/0 # 如果需要严格控制,这个可以根据实际情况调整
    - action: Allow # 允许私有IP通信
      destination:
        cidr:
          - 10.0.0.0/8
          - 172.16.0.0/12
          - 192.168.0.0/16
          - 100.64.0.0/10
          - 127.0.0.0/8
          - 169.254.0.0/16
    - action: Allow # 允许访问API Server
      destination:
        selector: k8s-app == "kube-apiserver" # 假设API Server有这个标签
        ports:
          - 443

解释: 这个策略在security-baseline层中,首先拒绝了所有非RFC1918地址的出站流量,相当于默认禁止了对公网的访问。然后才允许对私有IP段和K8s API Server的访问。由于这个策略优先级高,团队定义的任何NetworkPolicy都无法允许绕过这个公网访问限制。

步骤三:各团队定义应用级策略 (NetworkPolicy或GlobalNetworkPolicy)

各团队可以在application-policies层或直接使用默认的default层来定义自己的NetworkPolicy。由于application-policies层的优先级低于security-baseline层,团队的策略只能在不违反基线策略的前提下,进一步允许流量。他们无法定义一个Deny规则来推翻基线层的Allow规则,也无法定义一个Allow规则来绕过基线层的Deny规则。

示例:团队应用级的NetworkPolicy

假设team-abackend服务需要允许来自frontend服务的入站流量,并且需要访问一个数据库。

# team-a-backend-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: team-a-backend-access
  namespace: team-a # 仅限于team-a命名空间
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: database
          namespaceSelector: # 如果数据库在其他命名空间
            matchLabels:
              env: prod
      ports:
        - protocol: TCP
          port: 5432

解释: 这个NetworkPolicy将自动在defaultapplication-policies层生效(取决于Calico配置,通常NetworkPolicydefault层),优先级低于security-baseline层。它允许team-a命名空间中的backend Pod接收来自frontend Pod的8080端口流量,并允许访问数据库。这些规则只有在不违反security-baseline层中的任何拒绝规则时才会生效。例如,如果baseline-deny-egress-to-internet策略拒绝了所有非内网出站,那么backend Pod即便尝试访问公网IP的数据库,也会被基线策略拒绝。

实施步骤总结

  1. 安装Calico: 确保你的Kubernetes集群已经安装并配置了Calico作为CNI插件。
  2. 定义策略层: 创建Tier资源来定义你的security-baselineapplication-policies层,并设置好它们的order以确定优先级。
  3. 创建全局安全基线策略: 编写GlobalNetworkPolicy清单,将其tier字段指向security-baseline。这些策略应包含默认的拒绝规则以及必要的允许规则(如API Server、DNS)。
  4. 部署基线策略: 将这些TierGlobalNetworkPolicy部署到集群中。这些通常由集群管理员或安全团队管理,并通过RBAC限制其他团队的修改权限。
  5. 指导团队创建应用策略: 告知开发团队他们可以在自己的命名空间或预设的application-policies层中创建NetworkPolicyGlobalNetworkPolicy。强调这些策略不能覆盖基线策略。

最佳实践与注意事项

  • RBAC控制: 严格控制哪些用户或Service Account有权限创建和修改TierGlobalNetworkPolicy资源。这通常是集群管理员和安全团队的职责。普通开发人员应该只能管理自己命名空间内的NetworkPolicy
  • 策略粒度: 尝试从最粗粒度的拒绝策略开始(如默认拒绝所有入站/出站),然后逐步添加允许特定流量的策略。
  • 日志和监控: 启用Calico的策略日志功能,可以帮助你在策略生效后监控和审计网络流量,及时发现潜在的安全问题或配置错误。
  • 测试: 在生产环境部署前,务必在预生产环境中充分测试所有策略,确保没有意外的流量中断或安全漏洞。
  • 文档: 详细记录所有策略的意图、生效范围和优先级,方便团队理解和维护。

结语

通过Calico的GlobalNetworkPolicyPolicy Tiers,我们得以在复杂的Kubernetes多租户环境中实现一套强大且灵活的分层网络安全控制机制。它不仅能帮助网络安全工程师强制执行统一的集群安全标准,设立不可被随意覆盖的“安全基线”,还能赋予各开发团队在基线之上自行管理应用网络策略的灵活性,极大地简化了安全管理工作,并显著提升了整个集群的安全性。拥抱这些高级功能,让你的Kubernetes集群网络真正做到安全可控。

安全运维君 KubernetesCalico网络安全

评论点评