Kubernetes旧服务迁移:Calico强制性策略与多层级网络访问控制
在将遗留服务迁移到Kubernetes集群的过程中,网络安全无疑是核心关注点之一。我们不仅需要确保新旧服务之间的安全通信,更需要建立一套健壮、强制性的安全策略,防止任何未经授权的访问。特别是对于敏感资源如数据库集群,必须严格限制其访问源。用户提出的需求是定义一套全集群生效的强制性策略,例如只允许内部IP访问数据库集群,并且任何命名空间下的 NetworkPolicy 都不能“放宽”这个限制。同时,团队又能在这一基线之上,为自己的应用定义更具体的访问规则,例如特定端口的通信。
Calico作为Kubernetes环境中广泛使用的网络插件,其强大的网络策略管理能力,特别是策略优先级(Policy Order)机制,正是解决这一问题的理想方案。
Calico的网络策略模型概览
Calico提供了两种主要的网络策略类型:
GlobalNetworkPolicy(GNP):全局网络策略,不限于特定命名空间,可以作用于整个集群。GNP通常用于定义集群范围内的安全基线或跨命名空间的策略。NetworkPolicy(NP):命名空间网络策略,作用于其所在的命名空间内部。NP通常用于团队或应用开发者为其服务定义细粒度的访问控制。
这两种策略都可以定义 Ingress(入站)和 Egress(出站)规则,通过 selector 匹配目标 Pod。关键在于,Calico允许为这些策略设置优先级,这就是实现层级管理的关键。
如何通过Calico策略优先级实现强制性基线
Calico策略的执行顺序是根据其 order 字段来决定的,order 值越小,优先级越高。策略的 action 字段(Allow、Deny、Log、Pass)决定了流量的处理方式。
要实现“强制性策略不能被放宽”的需求,我们可以遵循以下原则:
- 为强制性基线策略设置更高的优先级(更小的
order值):使用GlobalNetworkPolicy来定义这些不可逾越的规则,并赋予它们一个非常小的order值(例如50或更小)。 - 为命名空间策略设置较低的优先级(更大的
order值):团队定义的NetworkPolicy应该具有比基线策略更大的order值(例如100或更大)。
当流量通过时,Calico会从优先级最高的策略开始评估。一旦某个策略匹配到流量并设置了 action (例如 Deny 或 Allow),则后续优先级较低的策略将不会再对该流量生效。这意味着,如果一个高优先级的 GlobalNetworkPolicy 拒绝了某个流量,那么即使低优先级的 NetworkPolicy 允许该流量,它也会被拒绝。
实践方案:强制性数据库访问限制与团队自定义规则
步骤一:定义强制性数据库访问基线(GlobalNetworkPolicy)
假设我们有一个数据库集群,只允许特定内部网络或特定命名空间(例如 backend-services 命名空间)的Pod访问。所有其他来源的访问都将被拒绝。
强制性策略YAML示例 (GlobalNetworkPolicy):
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: deny-db-access-from-external
spec:
order: 50 # 优先级很高,数字越小优先级越高
selector: app == "my-database" # 匹配数据库Pod的标签
types:
- Ingress
ingress:
- action: Allow # 允许来自特定命名空间的访问
source:
selector: project == "internal-service" # 假设内部服务的Pod都有这个标签
destination:
ports:
- 5432 # 数据库端口,例如PostgreSQL
- action: Allow # 允许来自特定Pod的访问,例如后端服务
source:
selector: app == "backend-app" # 允许标记为后端应用的Pod访问
namespaceSelector: name == "backend-services" # 且在backend-services命名空间中
destination:
ports:
- 5432
- action: Deny # 拒绝所有其他对数据库端口的入站连接
destination:
ports:
- 5432
解释:
order: 50:赋予该策略较高的优先级,确保它在其他NetworkPolicy之前被评估。selector: app == "my-database":此策略应用于所有带有app: my-database标签的Pod。- 第一个
action: Allow:允许所有带有project: internal-service标签的Pod访问数据库的5432端口。这可以是一个集群范围的内部服务标记。 - 第二个
action: Allow:允许backend-services命名空间中带有app: backend-app标签的Pod访问数据库的5432端口。 - 最后一个
action: Deny:这是一个“隐式拒绝”之前的“显式允许”之后的默认拒绝。任何不符合前两条Allow规则的对数据库5432端口的入站流量都将被拒绝。
部署这个 GlobalNetworkPolicy 后,除非明确被该策略的 Allow 规则匹配,否则任何Pod(包括其他命名空间下的Pod)都无法访问数据库的5432端口。
步骤二:团队定义更具体的应用访问规则(NetworkPolicy)
在上述强制性策略生效的基础上,团队可以为其在 backend-services 命名空间中的 backend-app 定义更具体的访问规则。例如,他们可能需要允许来自特定前端服务(frontend-app)的Pod访问其 backend-app 的8080端口。
团队自定义策略YAML示例 (NetworkPolicy):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: backend-services # 此策略仅作用于backend-services命名空间
spec:
podSelector:
matchLabels:
app: backend-app # 匹配backend-services命名空间内的backend-app Pod
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend-app # 允许来自带有app: frontend-app标签的Pod的连接
namespaceSelector:
matchLabels:
name: frontend-services # 且该Pod在frontend-services命名空间中
ports:
- protocol: TCP
port: 8080 # 允许访问backend-app的8080端口
解释:
- 这是一个标准的Kubernetes
NetworkPolicy,它只在backend-services命名空间中生效。 - 它允许
frontend-services命名空间中带有app: frontend-app标签的Pod访问backend-services命名空间中app: backend-app的8080端口。 - 请注意,
NetworkPolicy没有order字段。在Calico中,NetworkPolicy默认的order值通常比GlobalNetworkPolicy的默认值要大(优先级更低)。具体的默认值取决于Calico的配置,但通常GlobalNetworkPolicy的order默认是0,而NetworkPolicy的order默认是1000。我们上面将GNP的order设置为50,远低于NP的默认值,确保其优先级更高。
Calico策略优先级如何工作
当Pod之间发生通信时,Calico会按照以下顺序评估策略:
- Ingress/Egress Pre-DNAT Policy (由
GlobalNetworkPolicy定义,order值非常低,如-1000到-100,在DNAT之前应用)。 - Ingress/Egress Policy (由
GlobalNetworkPolicy和NetworkPolicy定义,order值通常在0到1000之间)。 - Ingress/Egress Post-DNAT Policy (由
GlobalNetworkPolicy定义,order值很高,如1000到2000,在DNAT之后应用)。
对于我们这里讨论的场景,主要关注第二种“Ingress/Egress Policy”。
GlobalNetworkPolicy(deny-db-access-from-external) 的order: 50。NetworkPolicy(allow-frontend-to-backend) 虽然没有显式设置order,但其默认优先级会高于50(通常是1000)。
因此,对数据库的任何访问尝试,都会首先被 deny-db-access-from-external 策略评估。如果该流量被 deny-db-access-from-external 明确拒绝,那么即使存在一个低优先级的 NetworkPolicy 试图允许它,流量也无法通过。
迁移旧服务时的注意事项
- 逐步实施:在全集群强制实施策略前,建议先在测试环境中充分验证。对于旧服务,可以先将其隔离在一个单独的命名空间,并针对该命名空间制定特定的
GlobalNetworkPolicy,确保其只能访问必要的旧系统资源。 - 细粒度标签:确保您的Pod和命名空间都有清晰、一致的标签。这是网络策略能够准确匹配目标的关键。例如,为所有数据库Pod打上
app: my-database标签,为所有内部服务Pod打上project: internal-service标签。 - 监控与审计:部署策略后,务必配置网络流量监控和策略日志(Calico支持
action: Log),以便及时发现任何被意外拒绝的流量或潜在的安全漏洞。 default-deny策略:作为最佳实践,在每个命名空间内部署一个默认拒绝所有入站/出站流量的NetworkPolicy,然后只允许必要的流量。对于GlobalNetworkPolicy,可以设置一个低优先级的Deny All策略作为最终防护。- 示例
default-denyGlobalNetworkPolicy:
请注意:通常不建议直接全局apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: default-deny-all spec: order: 2000 # 较低优先级,作为最终的拒绝规则 selector: all() # 匹配所有Pod types: - Ingress - Egress ingress: - action: Deny egress: - action: DenyDeny All,这会中断所有通信。更常见的做法是为每个命名空间部署NetworkPolicy来实现默认拒绝。Calico的GlobalNetworkPolicy更适合定义集群级的白名单或黑名单例外。对于迁移场景,可能需要更精细的控制,例如仅对旧服务所在命名空间默认拒绝。
- 示例
- 服务发现与DNS:确保强制性策略不会阻断Kubernetes内部的服务发现机制(DNS)。通常,Calico策略默认允许对kube-dns的访问,但仍需验证。
总结
是的,Calico完全能够通过其 GlobalNetworkPolicy 和 NetworkPolicy 的策略优先级机制,实现您所描述的层级管理需求。通过为集群范围内的强制性安全基线(如数据库访问限制)设置高优先级 GlobalNetworkPolicy (使用较小的 order 值),我们可以确保这些规则不会被低优先级的命名空间 NetworkPolicy 所“放宽”或覆盖。这为旧服务迁移提供了一个强大且灵活的网络安全框架,既保证了核心资产的安全性,又允许团队在安全基线之上拥有足够的灵活性来管理其应用的通信。