⽬前许多组织都在采⽤Kubernetes来运⾏他们的应⽤程序。以⾄于有些⼈将Kubernetes称为新的数据中⼼操作系统。因此,组织开始将
Kubernetes(通常缩写为k8s)视为关键任务平台,它需要包括⽹络安全在内的成熟业务流程。
负责保护这个新平台的⽹络安全团队可能发现它出奇的不同。例如,默认的Kubernetes策略是允许任何连接。
本⽂提供了⼀些关于Kubernetes⽹络策略⼯作原理的简介,它们如何与传统防⽕墙策略进⾏⽐较以及⼀些可以帮助你保护Kubernetes应⽤
程序的陷阱和最佳实践。
Kubernetes⽹络策略
Kubernetes提供了⼀种称为⽹络策略的机制,可⽤于对部署在平台上的应⽤程序实施第3层分隔。⽹络策略缺乏现代防⽕墙的⾼级功能,
如第7层控制和威胁检测,但是它们确实提供了基本的⽹络安全,这是⼀个很好的起点。
⽹络策略控制Pod的通信
Kubernetes的⼯作负载在pod中运⾏,pod由⼀个或多个部署在⼀起的容器组成。Kubernetes为每个pod分配⼀个IP地址,这个地址可以
从其他所有pod路由,甚⾄可以跨底层服务器。Kubernets⽹络策略指定pod组的访问权限,类似于云服务中的安全组⽤于控制对虚拟机实
例的访问。
编写⽹络策略
与其他Kubernetes资源⼀样,⽹络策略可以使⽤⼀种称为YAML的语⾔定义。下⾯是⼀个简单的例⼦,它允许从负载均衡到postgres的访
问。
- 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
为了编写你⾃⼰的⽹络策略,你需要对yaml有基本的了解。Yaml基于缩进(使⽤的是空格,⽽不是tab)。缩进项属于其上⽅最接近的缩进
项。连字符(破折号)开始⼀个新的列表项。所有其他项都是映射条⽬。你可以在⽹上找到⼤量关于yaml的信息。
编写完策略的YAML⽂件后,使⽤kubectl创建策略:
- Ingress
- kubectl create -f policy.yaml
⽹络策略定义
⽹络策略定义由四个元素组成:
podSelector:将受此策略约束的pod(策略⽬标)。必填
policyType:指定哪些类型的策略包含在这个策略中,ingress或egress。该项是可选的,但建议总是明确的指定它。可选
ingress:允许传⼊⽬标pod的流量。可选
egress:允许从⽬标pod传出的流量。可选
下⾯这个例⼦是从Kubernetes官⽹上改编的(将“role”改为了“app”),它指定了四个元素: - 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
注意,你不必包含所有四个元素。podSelector是必填的,其余三个是可选的。
如果你忽略了policyType,则推断如下:
策略总是被认为指定了⼀个ingress定义。如果你没有明确的定义它,它将被视为“不允许流量“。
egress将由是否存在egress元素诱导。
为了避免错误,建议总是显式的指定policyType。
如果没有提供ingress或egress的定义,并且根据上⾯的逻辑假定它们存在,策略将认为它们是“不允许流量”。
默认策略是允许
当没有定义任何策略时,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
建议显式指定命名空间,除⾮你正在编写的策略要统⼀应⽤在多个命名空间中。
策略中的podSelector元素将从策略所属的命名空间中选择pod(它不能从另⼀个命名空间选择pod)。
ingress和egress元素中的podSelector也会选择相同命名空间中的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
标签
Kubernetes对象,如pod和namespace,可以附加⽤户⾃定义标签。Kubernetes⽹络策略依赖于标签来选择它们应⽤于的pod:
- Ingress
- podSelector:
- matchLabels:
- role: db
或者它们应⽤于的命名空间。下⾯的例⼦中选择匹配标签的命名空间中的所有pod: - namespaceSelector:
- matchLabels:
- project: myproject
需要注意的⼀点是:如果你使⽤namespaceSelector,请确保所选择的命名空间确实具有所使⽤的标签。请记住,像default和kubesystem
这样的内置命名空间没有现成的标签。你可以像这样给命名空间添加⼀个标签: - kubectl label namespace default namespace=default
元数据中的namespace是命名空间的实际名称,⽽不是标签: - apiVersion: networking.k8s.io/v1
- kind: NetworkPolicy
- metadata:
- name: test-network-policy
- namespace: default
- spec:
- …
源和终点
防⽕墙策略由具有源和终点的规则组成。Kubernetes⽹络策略是为⽬标(应⽤策略的⼀组pod)定义的,然后为⽬标指定传⼊或传出流量。再
次使⽤相同的例⼦,你可以看到策略⽬标——默认命名空间中所有具有标签为“db:app”的pod: - 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的传⼊流量。因此,ingress被解释为“源”,⽬标被解释为各⾃的“终点”。同样,egress被解释
为“终点”,⽬标是各⾃的“源”。
Egress和DNS
当执⾏egress时,必须⼩⼼不要阻⽌DNS,Kubernetes使⽤DNS将service的名称解释为其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
要解决它,你必须允许访问DNS服务:
- Egress
- 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
“to”元素为空,它隐式选择了所有命名空间中的所有pod,从⽽允许balance通过Kubernetes的DNS服务执⾏DNS查找,DNS服务通常
位于kube-system命名空间中。
虽然这是有效的,但它过于宽松和不安全——它允许在集群外部进⾏DNS查找。
你可以分阶段锁定它:
1.通过添加⼀个namespaceSelector只允许在集群内进⾏DNS查找:
- Egress
- 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
2.只允许DNS在kube-system命名空间中
为此,你需要为kube-system命名空间添加⼀个标签:
- Egress
- kubectl label namespace kube-system namespace=kube-system
然后⽤namespaceSelector在策略中指定它: - 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
3.偏执狂可能想更进⼀步,将DNS限制为kube-system命名空间中特定的DNS服务。请参考下⾯的“通过命名空间和pod过滤”章节。
另⼀种选择是在命名空间级别允许DNS,这样就不需要为每个服务指定它了:
- Egress
- 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
空的podSelector选择该命名空间中的所有pod。
第⼀个匹配和规则顺序
防⽕墙管理员知道对数据包采取的操作(允许或拒绝)由与其匹配的第⼀个规则决定。但是在Kubernetes中,策略的顺序并不重要。默认的
⾏为是,当没有定义策略时,允许所有通信,因此所有pod之间可以彼此通信。⼀旦定义了策略,每个⾄少被⼀个策略选择的pod,将根据
选择它的策略的并集(逻辑或)进⾏隔离:
没有被任何策略选中的pod继续保持开放。你可以通过定义cleanup规则来改变这个⾏为。
Cleanup规则(拒绝)
防⽕墙策略通常通过any-any-any-deny规则来丢弃所有⾮显式允许的流量。Kubernetes没有拒绝操作,但是你可以使⽤⼀个常规的规则
来实现相同的效果,该策略指定policyTypes=Ingress,但是省略了实际ingress的定义,这被解释为“不允许进⼊”。
- Egress
- apiVersion: networking.k8s.io/v1
- kind: NetworkPolicy
- metadata:
- name: deny-all
- namespace: default
- spec:
- podSelector: {}
- policyTypes:
-
- Ingress
该策略选择命名空间中的所有pod作为源,未定义ingress——这意味着不允许流量进⼊。
类似的,你可以拒绝来⾃⼀个命名空间的所有出站流量:
- Ingress
- apiVersion: networking.k8s.io/v1
- kind: NetworkPolicy
- metadata:
- name: deny-all-egress
- namespace: default
- spec:
- podSelector: {}
- policyTypes:
-
- Egress
请记住,任何其他允许访问命名空间中pod的策略都将优先于此拒绝策略——相当于防⽕墙中将允许策略添加到拒绝策略之上。
Any-Any-Any-Allow
通过使⽤⼀个空的ingress元素修改上⾯的deny-all策略,可以创建⼀个allow-all策略:
- Egress
- apiVersion: networking.k8s.io/v1
- kind: NetworkPolicy
- metadata:
- name: allow-all
- namespace: default
- spec:
- podSelector: {}
- ingress:
-
- {}
- policyTypes:
-
- Ingress
这允许所有命名空间中的所有pod(和IP)到默认命名空间中的任何pod的通信。这是默认⾏为,因此通常不需要这么做。但是,为了查找问
题,暂时覆盖所有其他规则可能很有⽤。
你可以缩⼩此范围,仅允许访问默认命名空间中的⼀组特定的pod:
- Ingress
- apiVersion: networking.k8s.io/v1
- kind: NetworkPolicy
- metadata:
- name: allow-all-to-balance
- namespace: default
- spec:
- podSelector:
- matchLabels:
- app: balance
- ingress:
-
- {}
- policyTypes:
-
- Ingress
下⾯的策略允许任何⼊站和出站流量(包括访问集群外的任何IP):
- Ingress
- apiVersion: networking.k8s.io/v1
- kind: NetworkPolicy
- metadata:
- name: allow-all
- spec:
- podSelector: {}
- ingress:
-
- {}
- egress:
-
- {}
- policyTypes:
-
- Ingress
-
- Egress
组合多个策略
策略在三个级别上使⽤逻辑或进⾏组合。每个pod根据应⽤于它的所有策略的并集决定是否允许通信。
1.在“from”和“to”项中,你可以定义三种类型的项,他们通过“或”进⾏组合:
namespaceSelector——选择⼀整个命名空间
podSelector——选择pod
ipBlock——选择⼀个⼦⽹
你可以在from/to下定义任意数量的项(即使是相同类型也可以定义多条),它们将通过逻辑或组合在⼀起:
- Egress
- 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
2.在⼀个策略中,ingress策略可以有多个“from”项,它们通过逻辑或组合在⼀起。同样,egress策略可以有多个“to”项,它们也通
过逻辑或组合在⼀起:
- Ingress
- 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
3.多个策略也通过逻辑或组合在⼀起。
但是当组合策略时,这⾥有⼀些限制:Kubernetes只能组合policyType(ingress或egress)不同的策略,多个策略都指定ingress(或
egress)将会互相覆盖。
命名空间之间的通信
默认情况下,命名空间间的通信是允许的。你可以使⽤deny-all策略来改变它,以阻⽌从或者到该命名空间的通信。
如果你阻⽌了对某个命名空间的访问,可以使⽤namespaceSelector允许来⾃⼀个特定的命名空间的访问。
- Ingress
- 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
这允许default命名空间中的所有pod访问database命名空间中的postgres pod。但是,如果只想允许default命名空间中特定的pod访问
postgres该怎么办呢?
通过namespace和pod联合过滤
Kubernetes 1.11及以上允许你将namespaceSelector和podSelector通过逻辑与组合在⼀起:
- Ingress
- 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
为什么这被解释为“与”,⽽不是“或”呢?
请注意,podSelector不是以破折号开头的,在yaml中这意味着,podSelector与前⾯的namespaceSelector属于同⼀个列表项,因此它
们通过逻辑与进⾏组合。如果你在podSelector前⾯添加⼀个破折号,将创建⼀个新的列表项,这将与前⾯的namespaceSelector通过逻
辑或组合。要在所有命名空间中选择具有特定标签的pod,可以指定⼀个空的namespaceSelector:
- Ingress
- 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
多个标签通过逻辑与组合
具有多个对象(主机、⽹络、组…)的防⽕墙规则被解释为逻辑或。例如,如果数据包的源匹配HOST_1或HOST_2,则应⽤此规则:
与之相反的是,在Kubernetes中,podSelector和namespaceSelector中的多个标签通过逻辑与进⾏组合。例如,这将选择同时具有标签
role=db和version=v2的pod:
- Ingress
- podSelector:
- matchLabels:
- role: db
- version: v2
相同的逻辑适⽤所有类型的选择器:策略⽬标的选择器、pod的选择器、命名空间的选择器。
⼦⽹和IP地址(ipBlock)
防⽕墙使⽤vlan、ip和⼦⽹来分割⽹络。在Kubernetes中,pod的IP是⾃动分配的,并且可能经常变动,因此⽹络策略使⽤标签来选择pod
和命名空间。⼦⽹(ipBlock)⽤于ingress或egress连接(南北向)。例如,下⾯这个策略允许default命名空间中的所有pod访问⾕歌的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
本例中空的podSelector表⽰“选择该命名空间中的所有pod”。该策略只允许访问8.8.8.8,这意味着它拒绝访问其他任何IP。因此,实际
上,你已经阻⽌了对Kubernetes内部DNS服务的访问。如果你仍然想允许它,需要显式的指定它。
通常ipBlock和podSelector是互斥的,因为通常你在ipBlock中不使⽤内部pod IP。如果你使⽤内部pod IP指定ipBlock,它实际上将允许
与具有这些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
你可以通过排除内部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
端⼝和协议
pod通常只监听⼀个端⼝,这意味着你可以简单的在策略中省略端⼝,默认允许任何端⼝。但是最好将策略设置为受限的,显式的指定端
⼝:
- 10.16.0.0/14
- 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
请注意,端⼝应⽤于它们所在的“to”或“from”⼦句中的所有项。如果你想为不同的项指定不同的端⼝,你可以将ingress或egress拆
分为多个“to”或“from”,每个都有⾃⼰的端⼝:
- Ingress
- 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
端⼝的默认⾏为:
如果完全省略端⼝,则表⽰所有端⼝和所有协议
如果省略协议,则默认为TCP
如果省略端⼝,则默认为所有端⼝
最佳实践:不要依赖默认⾏为,显式的指明。
注意你必须使⽤pod的端⼝,⽽不是service的端⼝。
策略是为pod定义的还是为service定义的?
当⼀个pod访问Kubernetes中另⼀个pod时,它通常要通过service,service是⼀个虚拟的负载均衡器,它将流量转发到实现该服务的pod
上。你可能会以为⽹络策略是控制对service的访问的,但事实并⾮如此。Kubernetes⽹络策略是应⽤于pod的端⼝,⽽⾮service的端
⼝。
例如,如果⼀个service监听80端⼝,但是它将流量转发到监听8080端⼝的pod上,你需要在⽹络策略中指定8080端⼝。
这种设计不是最优的,因为当有⼈更改服务的内部⼯作⽅式时(如pod正在监听的端⼝),你需要更新⽹络策略。
显然,有⼀种解决⽅案可以解决这个问题,即使⽤命名端⼝,⽽不是硬编码的数字端⼝:
- Ingress
- 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的定义中指定这个端⼝的名字:
- port: my-http
- 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解耦了。
Ingress
术语“ingress”在Kubernetes中有两种含义: - ingress⽹络策略允许你控制其他pod或外部IP对pod的访问
- Kubergess的ingress是⼀种配置外部负载均衡器路由流量到集群内部的⽅式
你可以编写⼀个k8s⽹络策略限制来⾃Kubernetes ingress的访问,但在⼤多数情况下这不是很有⽤,因为它只控制负载均衡器的内部
IP。为了控制可以访问集群的外部⼦⽹,你需要在外部实施点(如负载均衡本⾝或负载均衡前⾯的防⽕墙)配置访问控制。
需要同时定义ingress和egress么?
简单的回答是肯定的——为了允许pod A访问pod B,你需要允许pod A通过egress策略创建出站连接,并允许pod B通过ingress策略接
受⼊站连接。
然⽽,实际上,你可能对两个⽅向中的⼀个使⽤默认的允许策略。
如果源pod被⼀个或多个egress策略选中,则将根据策略联合对其进⾏限制,在这种情况下你需要明确允许它连接到⽬标pod。如果pod不
被任何策略选中,则它默认允许所有的出站流量。
同样,被⼀个或多个ingress策略选中的⽬标pod,也会受到策略的联合限制,在这种情况下你必须明确允许它接受来⾃源pod的流量。如
果pod没有被任何策略选中,则它默认允许所有⼊站流量。
hostNetwork陷阱
Kubernetes通常在⾃⼰的隔离⽹络中运⾏pod,然⽽,你可以指定Kubernetes在主机上运⾏pod: - hostNetwork: true
这样会完全绕开⽹路策略,pod像主机上运⾏的其他任何进程⼀样进⾏通信。
流量⽇志
Kubernetes⽹络策略不能⽣成流量⽇志。这很难知道策略是不是像预期⼀样⼯作。这也是安全分析⽅⾯的主要限制。
控制到外部服务的流量
Kubernetes⽹络策略不允许你为egress指定完全限定的域名(DNS)。当试图控制到⽆固定IP的外部站点(如aws.com)的流量时,这是⼀个
限制。
策略验证
防⽕墙将警告甚⾄拒绝接受⽆效的策略。Kubernetes也做了⼀些验证。当使⽤kubectl定义⽹络策略时,Kubernetes可能会告诉你策略⽆
效并且拒绝接受。在其它情况下,Kubernetes将接受策略并修改它缺失的细节,你可以通过以下命令查看它:
kubernetes get networkpolicy -o yaml
请注意,Kubernetes的验证不是⽆懈可击的,它可能允许策略中出现某些类型的错误。
执⾏
Kubernetes本⾝并不执⾏⽹络策略,它只是⼀个API⽹关,它将执⾏的艰巨⼯作传递给⼀个称为容器⽹络接⼝(CNI)的底层系统。在没有合
适CNI的Kubernetes集群中定义策略就像在没有安装防⽕墙的服务器上定义防⽕墙规则⼀样。你必须确保拥有具有安全功能的CNI,或者,
对于托管的Kubernetes平台,你需要显式的启⽤将为你安装CNI的⽹络策略。
请注意,如果CNI不⽀持,你定义了⼀个⽹络策略,Kubernetes不会向你告警。
有状态还是⽆状态?
⽬前所有的Kubernetes CNI都是有状态的,这使得pod能在它发起的TCP连接上接收应答,⽽不必为应答打开⾼端⼝。我不知道有没有
Kubernetes标准保证状态性。
⾼级安全策略管理
以下是⼀些针对Kubernetes实施更⾼级的⽹络策略的⽅法: - 服务⽹格设计模式使⽤sidecar在服务级别提供更⾼级的遥测和流量控制。有关⽰例,请参考Istio。
- ⼀些CNI提供商已经将他们的⼯具扩展到了Kubernetes⽹络策略之外。
- Tufin Orca——⽤于实现Kubernetes⽹络策略的可视化和⾃动化。
总结
Kubernetes⽹络策略为集群划分提供了⼀种很好的⽅法,但是它们不直观并且有许多注意的地⽅。我相信,由于这种复杂性,⼀定有许多
集群在他们的策略中存在“bug”。可能的解决⽅案是⾃动化策略定义或其他分割⽅法。同时,希望本⽂对澄清和解决你可能遇到的问题有
所帮助。