Posted on Oct 23rd, 2020 by Reuven Harrison

目前,许多组织都在采用Kubernetes来运行他们的应用程序。一些人甚至将Kubernetes称为新的数据中心操作系统。其结果是,很多组织开始将Kubernetes(通常缩写为k8s)作为一个关键任务平台,它需要成熟的业务流程,包括网络安全。

负责保护这个新平台安全的网络安全团队会发现,它有惊人的不同。例如,默认的Kubernetes策略是any-any-any-allow!

本指南提供了一些关于Kubernetes网络策略工作原理的见解,它们与传统防火墙策略的差异,以及一些陷阱和最佳实践,从而帮助您保护Kubernetes应用程序的安全。

About Me

我是安全策略公司Tufin的首席技术官兼联合创始人。在过去的三年里,我一直在研究我们的Kubernetes安全解决方案Tufin SecureCloud。这篇文章是我在整个开发过程中的心得。

欢迎大家在自己的集群上试用 SecureCloud,并分享反馈

Kubernetes Network Policies

Kubernetes提供了一种名为Network Policies(网络策略)的机制,可以用来为部署在平台上的应用强制实施第3层分段。网络策略不具备现代防火墙的高级功能,如第7层控制和威胁检测,但它们确实提供了网络安全的基本级别,这是一个不错的起点。

网络策略控制Pod通信

Kubernetes工作负载在pod中运行,pod由一个或多个部署在一起的容器组成。Kubernetes为每个pod分配一个IP地址,该地址可路由到其他pod,甚至可以跨越底层服务器。Kubernetes网络策略指定了pod组的访问权限,就像云中的安全组控制对VM实例的访问一样。

编写网络策略

和其他Kubernetes资源一样,网络策略可以使用一种叫做YAML的语言来定义。这里有一个简单的例子,它允许从balance到postgress的访问:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.postgres
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: postgres
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: balance
  policyTypes:
  - Ingress

Tufin Orchestration Suite
View

要编写自己的网络策略,您需要对Yaml有基本的了解。Yaml基于缩进(使用空格,而不是制表符!)一个缩进项属于它上面最接近的缩进项。一个连字符(破折号)开始一个新的列表项,所有其他项都是map项。您可以在互联网上找到很多关于yaml的信息。

写好策略yaml后,使用 kubectl 创建策略:

kubectl create -f policy.yaml

网络策略规范

一个网络策略规范由四个元素组成。

  1. podSelector: t将受该策略约束的pod(策略目标) - 这是强制性的
  2. policyTypes: 指定此策略中包含哪些类型的策略,入口和/或出口 - 这是可选的,但我建议始终明确指定它。
  3. ingress: 允许进入目标pod的入站流量 - 可选
  4. egress: 允许从目标 pod 出站的流量 - 可选

这个例子改编自Kubernetes网站(我把“角色”改成了“应用”),指定了所有四个元素:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

Tufin Orchestration Suite

kubernetes policy
View

注意,您不必包含所有的四个元素。主podelector元素是必须的,其他三个元素是可选的。

如果您省略了policyTypes,将按以下方式推导:

  • 始终假定策略指定一个入口定义。如果您没有明确地指定它,它将被认为“不允许流量”。
  • 出口将由出口元素的存在或不存在来推导。

为了避免错误,我建议总是明确指定policyTypes。

如果没有提供入口和/或出口,而根据上面的逻辑,它们被假定为存在,那么策略将把它们视为“不允许流量”(见下面的清理规则)。

默认策略是允许

当没有定义策略时,Kubernetes允许所有的通信。所有pod都可以自由地相互交谈。从安全的角度,这可能听起来有些违反常理,但记住,Kubernetes是由希望应用程序进行通信的开发人员设计的。网络策略是后来作为增强功能加入的。

命名空间

命名空间是Kubernetes的多租户机制,目的是将命名空间环境相互隔离。但是,默认情况下,命名空间之间的通信仍然是允许的。

像大多数Kubernetes实体一样,网络策略也存在于特定的命名空间中。元数据头告诉Kubernetes该策略属于哪个命名空间:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: my-namespace;
spec:
...

如果您没有明确指定元数据的命名空间,它将应用于kubectl中提供的命名空间(默认是namespace=default):

kubectl apply -n my-namespace -f namespace.yaml

我建议明确指定命名空间,除非您编写的策略需要在多个命名空间中统一应用。

策略中的主podelector元素将从策略所属的同一个命名空间中选择pod(它不能从其他命名空间中选择pod)。

入口和出口元素中的podelector也会选择同一命名空间中的pod,除非您将它们与下文“过滤命名空间和Pod”中描述的namespaceSelector结合。

策略命名约定

策略名称在一个命名空间内是唯一的。您不能在一个命名空间中拥有两个相同名称的策略,但您可以在不同的命名空间中拥有相同名称的策略。当您想在多个命名空间中重复应用某个策略时,这会很方便。

我喜欢的一种命名策略的方法,是将命名空间与目标pod相结合。例如:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.postgres
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: postgres
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: admin
  policyTypes:
  - Ingress


View

视图

Kubernetes对象,如pod和命名空间,可以附加用户定义的标签。标签相当于云中的标签。Kubernetes网络策略依靠标签来选择它们适用的pod:

podSelector:
  matchLabels:
    role: db

或者,它们适用的命名空间。这个例子选择了所有具有匹配标签的命名空间中的pod:

namespaceSelector:
  matchLabels:
    project: myproject

需要注意的是:如果您使用namespaceSelector,请确保您选择的命名空间真的有您正在使用的标签。请记住,像default和kube-system这样的内置命名空间并没有标签。

您可以像这样为一个命名空间添加标签:

kubectl label namespace default namespace=default

相反,元数据部分的命名空间是命名空间的实际名称,而不是标签:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
...

源和目的地

防火墙策略由带有源和目的地的规则组成。Kubernetes网络策略是为一个目标 – 策略适用的一组pod – 定义的,然后为目标指定入口和/或出口流量。再次使用相同的例子,您可以看到策略目标 – 所有在默认命名空间中的pod都有一个标签,键值为“app”、值为“db”:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978


视图

该策略中的ingress项允许入站流量到目标pod。所以,入口被解释为“源”,目标是各自的“目的地”。

同样,出口也被解释为“目的地”,目标是各自的“源”。

目标防火墙
这相当于两个防火墙规则: 入口->目标;目标->出口

出口和DNS(陷阱!)

在执行出口时,您必须小心不要阻止DNS,Kubernetes使用DNS将服务名称解析到其IP地址。例如,这个策略不起作用,因为您没有允许balance执行DNS查询:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.balance
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: balance
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: postgres
  policyTypes:
  - Egress

egress-default
视图

要解决这个问题,您必须允许访问DNS服务:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.balance
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: balance
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: postgres
  - to:
    ports:
    - protocol: UDP
      port: 53
  policyTypes:
  - Egress

egress-default2
视图

高亮显示的“to”元素是空的,因此它隐含地选择了所有命名空间中的所有pod,允许balance针对通常在命名空间kube-system中运行的Kubernetes DNS服务执行DNS查询。

虽然这样做是可行的,但它过于放任,而且不安全 - 它还允许在集群之外进行DNS查询!

您可以用分阶段的方法来锁定它:

 1. 通过添加一个namespaceSelector,只允许集群内DNS:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.balance
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: balance
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: postgres
  - to:
    - namespaceSelector: {}
    ports:
    - protocol: UDP
      port: 53
  policyTypes:
  - Egress

egress-default2
视图

2. 只允许kube-system命名空间中的DNS。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.balance
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: balance
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: postgres
  - to:
    - namespaceSelector:
        matchLabels:
          namespace: kube-system
    ports:
    - protocol: UDP
      port: 53
  policyTypes:
  - Egress

egress-default2d
视图

3. 偏执狂还可能想更进一步,将DNS限制在kube-system中的特定DNS服务上。有关执行此操作的说明,请参阅下面的“Filter on Namespaces AND Pod” 。

另一个选择是在命名空间级别允许DNS,这样就无需为每个服务指定它:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.dns
  namespace: default
spec:
  podSelector: {}
  egress:
  - to:
    - namespaceSelector: {}
    ports:
    - protocol: UDP
      port: 53
  policyTypes:
  - Egress

The empty podSelector selects all pods in the namespace.

egress-defaultdns
视图

第一匹配和规则顺序

防火墙管理员知道,对一个数据包采取的行动(“允许”或“拒绝”)是由与之匹配的第一个规则决定的。

在Kubernetes中,策略之间的顺序并不重要。没有定义策略时,默认的行为是允许所有通信,这样所有的pod都可以自由地相互交谈。

当您开始定义策略时。每一个被至少一个策略选中的pod,都会根据选中它的策略的联合(逻辑OR)而被隔离:

pod

未被任何策略选择的pod将继续开放。您可以通过定义清理规则来改变这种行为。

清理规则(拒绝)

防火墙策略通常有一个any-any-deny规则来终止所有非明确允许的流量。

Kubernetes没有“拒绝 ”操作,但您可以通过一个常规(允许)策略来实现同样的效果,该策略指定policyTypes=Ingress,但省略实际的入口定义。这被解释为“不允许进入”:

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

egress-ingressdenyall
视图

该策略选择命名空间中的所有 pod 作为源,并保留 ingress 未定义,这意味着不允许入站流量。

同样,您也可以拒绝所有来自命名空间的出站流量:

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

视图

 

记住,任何允许流量到命名空间中的pod的附加策略都将优先于这个拒绝规则! - 相当于在防火墙中的这个拒绝规则上面添加允许规则。

Any-Any-Any-Allow

可以通过修改上面的“拒绝所有”(deny-all)策略,用一个空的入口元素创建一个“允许所有”(allow-all)策略:

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

egress-ingressdenyall
视图

这允许从所有命名空间(和所有IP)的所有pod与默认命名空间的任何pod进行通信。这是默认的行为,所以您通常不需要定义这个。不过,您可以暂时覆盖任何更具体的允许规则,以诊断问题。

您可以缩小范围,只允许访问默认命名空间中的一组特定的pod(app:balance):

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-to-balance
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: balance
  ingress: 
  - {}
  policyTypes:
  - Ingress

egress-ingressdenyall
视图

以下策略允许所有的入口和出口流量(包括访问集群外的任何IP):

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all
spec:
  podSelector: {}
  ingress:
  - {}
  egress:
  - {}
  policyTypes:
  - Ingress
  - Egress

egress-ingressdenyall

egress-ingressdenyall
视图

允许所有内部通信

您可以通过选择所有命名空间中的所有 pod 来允许所有内部通信(不包括外部 IP)。

例如,此策略允许默认命名空间中的所有 pod 连接到所有命名空间中的所有 pod,但不允许连接到外部 IP 地址:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress-internally
  namespace: default
spec:
  podSelector: {}
  egress:
  - to:
    - namespaceSelector: {}
  policyTypes:
  - Egress

egress allowing all internally
视图

合并多个策略

策略与逻辑OR相结合,允许每个选定的pod根据应用于它的所有策略的联合进行通信。

策略在三个不同的层次上进行组合:

1. 1. 在“from ”和“to”下:

  • namespaceSelector — 选择整个命名空间
  • podSelector — 选择pod
  • ipBlock - 选择一个子网

您可以在from/to下定义任意多的项目(甚至是多个相同的项目) - 它们将与一个逻辑OR结合。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.postgres
  namespace: default
spec:
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: indexer
    - podSelector:
        matchLabels:
          app: admin
  podSelector:
    matchLabels:
      app: postgres
  policyTypes:
  - Ingress

egress-ingressdenyall
视图

2. 在“入口”或“出口”项下。

在一个策略中,入口策略可以有多个“from”项 - 它们与一个逻辑OR结合。同样,出口策略也可以有多个“to”的项目,这些项目与一个逻辑OR结合。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.postgres
  namespace: default
spec:
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: indexer
  - from:
    - podSelector:
        matchLabels:
          app: admin
  podSelector:
    matchLabels:
      app: postgres
  policyTypes:
  - Ingress

ingress policies

3. 多个策略也与逻辑OR结合

命名空间之间的通信

默认情况下,允许命名空间之间的通信。您可以通过“拒绝所有”(deny-all)策略来改变它,阻止来自您的命名空间和/或到达您的命名空间的通信(参见上面的清理规则)。

如果您阻止了对您的命名空间的访问(见上面的清理规则),您可以通过使用namespaceSelector允许来自特定命名空间的连接,作为拒绝策略的例外情况:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: database.postgres
  namespace: database
spec:
  podSelector:
    matchLabels:
      app: postgres
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          namespace: default
  policyTypes:
  - Ingress

egress-ingressdenyall
视图

这允许默认命名空间中的所有 pod 访问数据库命名空间中的 postgres pod。但如果您只想让默认命名空间中的特定pod访问postgres呢?

命名空间和Pod过滤

Kubernetes 1.11及更新版本允许您将namespaceSelector和podelector用逻辑AND结合起来,如下所示:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: database.postgres
  namespace: database
spec:
  podSelector:
    matchLabels:
      app: postgres
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          namespace: default
      podSelector:
        matchLabels:
          app: admin
  policyTypes:
  - Ingress

egress-ingressdenyall
视图

为什么这被解释为AND,而不是通常的OR?

注意,podelector没有以破折号(连字符)开头,这表示在yaml中,podelector和前面的namespaceSelector都属于同一个列表项目,因此它们在逻辑上是以AND结合的。

如果您在podelector前面加上破折号,就会创建一个新的列表项,这个列表项会和前面的namespaceSelector通过逻辑OR结合在一起。

要在所有命名空间中选择具有特定标签的pod,请指定一个空的namespaceSelector:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: database.postgres
  namespace: database
spec:
  podSelector:
    matchLabels:
      app: postgres
  ingress:
  - from:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          app: admin
  policyTypes:
  - Ingress

egress-ingressdenyall
视图

多个标签与AND结合

具有多个对象(主机、网络、组......)的防火墙规则被解释为逻辑OR。例如,如果数据包源匹配Host_1或Host_2,则将应用此规则。

相反,在Kubernetes中,podelector或namespaceSelector中的多个标签会与一个逻辑AND结合。例如,这将选择同时具有role=db和version=v2两个标签的pod:

podSelector:
  matchLabels:
    role: db
    version: v2

同样的逻辑适用于所有类型的选择器:策略目标的选择器、pod的选择器和命名空间的选择器。

子网和IP地址(IPBlocks)

防火墙使用vlans、IP地址和子网来对网络进行分段。

在Kubernetes中,pod IP是自动分配的,而且可能会经常变化,因此,网络策略使用标签来选择pod和命名空间。

子网(ipBlocks)用于入口或出口连接(北 – 南)。例如这个策略允许默认命名空间中的所有 pod 访问 Google 的 DNS 服务:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: egress-dns
  namespace: default
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 8.8.8.8/32
    ports:
    - protocol: UDP
      port: 53

egress-ingressdenyall
视图

本例中的空pod选择器意味着“选择命名空间中的所有pod”。

这个策略只允许访问8.8.8.8,这意味着它拒绝访问任何其他IP。因此,实际上,您阻止了对Kubernetes内部DNS服务的访问。如果您还想允许它,您需要明确地指定它。

通常ipBlocks和podelectors是相互排斥的,因为您不会在ipBlocks中使用内部pod IP。如果您指定了ipBlocks与内部pod IP,它将允许与这些IP的pod进行通信,尽管实际上您不知道要使用哪些IP,这就是为什么您不应该使用IP来选择pod。

作为一个反例,这个策略包括所有IP,随后允许访问所有其他pod:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: egress-any
  namespace: default
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0

egress-ingressdenyall
视图

您可以通过排除内部 pod IP,只允许访问外部 IP。例如,如果您的 pod 子网是 10.16.0.0/14:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: egress-any
  namespace: default
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 10.16.0.0/14

egress-ingressdenyall
视图

端口和协议

通常情况下,pod应该监听一个端口,这意味着您可以简单地从您的策略中省略端口,并允许默认的“任意端口”。然而,将策略锁定为具有尽可能多的限制仍然是一个好的做法,这样您就会想要指定端口:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.postgres
  namespace: default
spec:
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: indexer
    - podSelector:
        matchLabels:
          app: admin
    ports:
      - port: 443
        protocol: TCP
      - port: 80
        protocol: TCP
  podSelector:
    matchLabels:
      app: postgres
  policyTypes:
  - Ingress

 

egress-ingressdenyall
视图

 

注意,端口适用于它们出现在同一个“to”或“from”子句中的所有项目。如果您想为不同的项目集指定不同的端口,您可以将入口或出口分解为多个“to”或“from”,每一个都有自己的端口:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.postgres
  namespace: default
spec:
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: indexer
    ports:
     - port: 443
       protocol: TCP
  - from:
    - podSelector:
        matchLabels:
          app: admin
    ports:
     - port: 80
       protocol: TCP
  podSelector:
    matchLabels:
      app: postgres
  policyTypes:
  - Ingress

egress-ingressdenyall
视图

端口默认值:

  • 如果完全省略了端口的定义,就意味着所有的协议和所有的端口。
  • 如果省略协议定义,则默认为TCP
  • 如果省略了端口定义,则默认为所有端口

最佳实践:不要依赖默认值,要明确。

注意,您必须使用 pod 端口,而不是服务端口(更多信息参见下一段)。

策略是为pod还是服务定义的?

当一个pod访问Kubernetes中的另一个pod时,它通常通过服务 – 一个虚拟的负载平衡器,将流量转发到实现该服务的pod。您可能会认为网络策略控制对服务的访问,但事实并非如此。Kubernetes网络策略应用于pod端口,而不是服务端口。

例如,如果一个服务在监听80端口,但在8080端口上转发流量到它的pod,您需要在网络策略中指定8080。

这种设计是次优的,因为这意味着当有人改变服务的内部工作方式(pod在哪个端口监听)时,您需要更新网络策略。

显然有一个解决方案,就是使用命名端口而不是硬编码端口号:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.allow-hello
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: hello
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          run: curl
    ports:
    - port: my-http

您需要在 pod 定义(或其部署)中指定这个端口名称:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: hello
  name: hello
spec:
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
      - image: gcr.io/hello-minikube-zero-install/hello-node
        imagePullPolicy: Always
        name: hello-node
        ports:
        - containerPort: 8080
          name: my-http

这将网络策略和 pod 解耦。

入口

术语“入口”在Kubernetes的上下文中具有两种含义。

  1. 入口网络策略允许您控制pod的入站访问,从其他pod和外部IP。
  2. Kubernetes ingress是一种配置外部负载均衡器以将流量路由到集群的方式。

您可以编写一个k8s网络策略来限制从Kubernetes ingress的访问,但在大多数情况下,它不是很有用,因为它只会控制负载均衡器的内部IP。为了控制可以访问集群的外部子网,您需要在外部执行点上配置访问控制,例如负载均衡器本身或它前面的防火墙。

我需要同时定义入口和出口吗?

答案很简单:“需要”。为了允许pod A与pod B对话,您需要允许pod A通过出口策略创建出站连接,pod B通过入口策略接受入站连接。

但实际上,您可以依靠默认的允许策略来实现两个方向中的一个。

如果源 pod 被一个或多个出口策略选择,它将根据策略的联盟受到限制,在这种情况下,您需要明确允许它连接到目标 pod。如果 pod 没有被任何策略选择,则默认允许所有出口流量。

同样,被一个或多个入口策略选择的目标 pod 将会根据策略的联盟受到限制,在这种情况下,您需要明确允许它从源 pod 接收流量。如果 pod 没有被任何策略选择,则默认允许所有入口流量。

另请参阅下面的“有状态或无状态”。

hostNetwork Gotcha

Kubernetes通常在自己的隔离网络中运行您的pod,但是,您可以通过指定Kubernetes在主机网络上运行您的pod。

hostNetwork: true
hostNetwork: true

这样做可以完全绕过网络策略。您的 pod 将能够像主机上运行的任何其他进程一样进行通信。

流量日志

Kubernetes网络策略不能生成流量日志。这就很难知道一个策略是否如预期地那样工作。这也是安全分析的一大限制。

Tufin SecureCloud 可以为Kubernetes集群生成流量日志。

控制外部服务的流量

Kubernetes网络策略不允许您为出口指定一个完全限定的域名(DNS)。当试图限制流量到未与固定IP地址(如aws.com)相关联的外部端点时,这是一个主要的限制。

Tufin SecureCloud 可以用动态IP地址控制对外部服务的访问。

控制到Kubernetes API服务器的流量

我专门针对该用例写了一篇文章,欢迎阅读。.

策略验证

防火墙会警告您,甚至拒绝接受无效的策略。Kubernetes也会进行一些验证。当使用kubectl定义一个网络策略时,Kubernetes可能会告诉您策略定义无效,并拒绝接受它。在其他情况下,Kubernetes会接受策略并修改它,同时提供缺失的细节,您可以通过以下路径查看:

kubernetes get networkpolicy <policy-name> -o yaml

kubernetes get networkpolicy <policy-name> -o yaml

要知道,Kubernetes验证并不是万无一失的,它可能允许策略中出现某些类型的错误。

执行

Kubernetes本身并不执行网络策略,它只是一个API网关,它将“执行”这项艰巨的任务传递给一个叫做容器网络接口(CNI) 的底层系统。在没有合适的CNI的情况下在Kubernetes集群上定义策略,就像在没有安装防火墙的情况下在防火墙管理服务器上定义策略一样。您必须确保您有一个具有安全能力的CNI,或者,对于托管的Kubernetes平台,您需要明确启用网络策略,它将为您安装CNI。

注意,如果您在没有支持CNI的情况下定义网络策略,Kubernetes不会警告您。

有状态还是无状态?

我见过的所有Kubernetes CNI都是有状态的(例如Calico使用Linux conntrack)。这样,一个pod能够在它发起的TCP连接上接收回复,而不必为了回复打开高端口。我不知道有什么Kubernetes标准可以保证有状态。

高级安全策略管理

以下是一些为Kubernetes执行更高级网络策略的方法:

  1. servicemesh设计模式使用sidecars在服务层面提供高级遥测和流量控制。参见Istio的例子。
  2. 一些CNI提供商扩展了他们的工具,超越了Kubernetes的网络策略
  3. 使用Tufin SecureCloud监控实时流量,并自动执行Kubernetes网络策略

Tufin SecureCloud
Tufin SecureCloud

其他信息

总结

Kubernetes网络策略为分段集群提供了一个很好的方法,但它们并不直观,并且有很多注意事项。因此,许多集群都暴露在最小或没有网络分段的环境中。一种解决方案是自动化策略定义或使用其他分段方法。

我希望本指南能帮助您了解和解决您遇到的一些问题。

对了,我是安全策略管理解决方案提供商Tufin的首席技术官兼联合创始人。