介绍
应用部署到集群后,如果你希望在IP地址或端口层面(OSI 第3层或第4层)控制网络流量,则你可以考虑为集群中特定应用使用 Kubernetes 网络策略(NetworkPolicy)。
3层或者4层是依托于内核能力来实现的,它会解析3层或者4层数据包的包头,并做对应的iptables配置。
NetworkPolicy通过网络插件来实现。要使用NetworkPolicy,你必须使用支持 NetworkPolicy 的网络解决方案。创建一个 NetworkPolicy 资源对象而没有控制器来使它生效的话,是没有任何作用的。目前支持的插件有calico和cilium。
隔离和非隔离的 Pod
默认情况下,Pod 是非隔离的,它们接受任何来源的流量。
Pod 在被某 NetworkPolicy 选中时进入被隔离状态。一旦namespace中有 NetworkPolicy 选择了特定的Pod,该 Pod 会拒绝该 NetworkPolicy 所不允许的连接。
网络策略不会冲突,它们是累积的。如果任何一个或多个策略选择了一个 Pod,则该 Pod 受限于这些策略的入站(Ingress)/出站(Egress)规则的并集。因此评估的顺序并不会影响策略的结果。
NetworkPolicy定义了一个pod的入站和出站防火墙规则。为了允许两个 Pods 之间的网络数据流,源端 Pod 上的出站(Egress)规则和目标端 Pod 上的入站(Ingress)规则都需要允许该流量。如果源端的出站(Egress)规则或目标端的入站 (Ingress)规则拒绝该流量,则流量將被拒绝。
安全策略属性
- spec: NetworkPolicy 规约中包含了在一个名字空间中定义特定网络策略所需的所有信息。
- podSelector:
- 每个 NetworkPolicy 都包括一个 podSelector, 它对该策略所适用的一组 Pod 进行选择。
- 空的 podSelector 选择名字空问下的所有 Pod。
- policyTypes:
- 每个 NetworkPolicy 都包含一个 policyTypes 列表,其中包含 Ingress 或 Egress或两者兼具。
- 如果 NetworkPolicy 未指定 policyTypes 则默认情况下始终设置 Ingress;
- 如果 NetworkPolicy 有任何出口规则的话则设置 Egress。
- Ingress:
- 每个 NetworkPolicy 可包含一个 Ingress 规则的白名单列表。
- 每个规则都允许同时匹配 from 和 ports 部分的流量。
- Egress:
- 每个 NetworkPolicy 可包含一个 Egress规则的白名单列表。
- 每个规则都允许匹配 to 和 port 部分的流量。
NetworkPolicy 默认策略
默认拒绝所有入站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
默认允许所有入站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
spec:
podSelector: {}
ingress:
- {}
policyTypes:
- Ingress
默认允许所有出站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-egress
spec:
podSelector: {}
egress:
- {}
policyTypes:
- Egress
默认拒绝所有入口和所有出站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
一个例子
calico的NetworkPolicy
NetworkPolicy 是命名空间级别资源。规则应用于与标签选择器匹配的 endpoint 的集合。
calico在原生对象的基础上对ingress/egrss做了一些扩展,可以定义具体的流量规则。
GlobalNetworkPolicy
GlobalNetworkPolicy 与 NetworkPolicy 功能一样,是整个集群级别的资源。
GlobalNetworkPolicy 会在集群中所有 Namespace 生效,并且能限制主机(HostEndpoint),即可以限制pod和主机之间的通信。
实例
- 创建calico-demo ns,并创建nginx pod。
cat serverpod.yaml apiVersion: v1 kind: Namespace metadata: name: calico-demo --- apiVersion: apps/v1 kind: Deployment metadata: namespace: calico-demo name: calico-demo labels: app: calico-demo spec: replicas: 1 selector: matchLabels: app: calico-demo template: metadata: labels: app: calico-demo access: "true" spec: containers: - name: calico-demo image: nginx ports: - containerPort: 80
- 在default和calico-demo中创建toolbox客户端(centos),等会测试连接nginx服务端。
cat toolbox.yaml apiVersion: apps/v1 kind: Deployment metadata: # namespace: default name: toolbox spec: replicas: 1 selector: matchLabels: app: toolbox template: metadata: labels: app: toolbox access: "true" spec: containers: - name: toolbox image: centos command: - tail - -f - /dev/null k get po -A -o wide| grep -E 'toolbox|calico-demo'
- 分别连接toolbox,测试ping和curl,都是通的。
k exec -it toolbox-68f79dd5f8-zxzmd -- bash ping 10.100.104.34 curl 10.100.104.34 k exec -it toolbox-68f79dd5f8-vc25n -n calico-demo -- bash ping 10.100.104.34 curl 10.100.104.34
- 创建默认NetworkPolicy,拒绝所有ingress。
cat networkpolicy.yaml kind: NetworkPolicy apiVersion: networking.k8s.io/v1 metadata: name: default-deny namespace: calico-demo spec: podSelector: {} k apply -f networkpolicy.yaml
- 再次测试ping和curl,都不通了,说明防火墙规则生效。
- 创建GlobalNetworkPolicy,允许icmp协议。(注意:如果你是一边用wordpress写博客,一边做实验,一定要在apply之前保存草稿,因为apply后wordpress会无法访问!!!55555)
cat allow-icmp-incluster.yaml apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: allow-ping-in-cluster spec: selector: all() types: - Ingress ingress: - action: Allow protocol: ICMP source: selector: all() icmp: type: 8 # Ping request - action: Allow protocol: ICMPv6 source: selector: all() icmp: type: 128 # Ping request k get gnp
- 测试ping,两个都通了。
- 创建NetworkPolicy,允许default中http80的访问。
cat access-calico-demo.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-http namespace: calico-demo spec: podSelector: {} ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: default ports: - protocol: TCP port: 80
- 再次测试curl。
calico-demo不通。
default通。
calico防火墙规则
在calico-system中的calico-node有NetworkPolicy的控制器,它会监听当前节点上的NetworkPolicy,并在主机上完成防火墙配置。
查看calico防火墙规则
iptables-save | grep cali
# 生成一个cali-INPUT的chain
:cali-INPUT - [0:0]
# 所有入站的流量都要转到cali-INPUT中
-A INPUT -m comment --comment "cali:Cz_u1IQiXIMmKD4c" -j cali-INPUT
# -i cali+ 正则匹配,指的是所有cali开头的网卡执行后面的规则-g cali-wl-to-host
-A cali-INPUT -i cali+ -m comment --comment "cali:VbO71h2UT7k5EQSJ" -g cali-wl-to-host
# 接收到流量请求后首先看connection tracking table连接表,如果这个连接是之前已经建立的,就放行accept
-A cali-tw-cali1bb11b52805 -m comment --comment "cali:piclZH4STH-kg8zX" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# 如果是新的连接就拒绝drop掉
-A cali-tw-cali1bb11b52805 -m comment --comment "cali:NA_KTHLVme9i6l69" -m conntrack --ctstate INVALID -j DROP
# 如果启用了NetworkPolicy,那么calico会把所有的数据包打上0x0/0x20000的标
-A cali-tw-cali8846877f208 -m comment --comment "cali:13XI1h8ZwKJTm9ot" -m comment --comment "Start of policies" -j MARK --set-xmark 0x0/0x20000
# 匹配0x0/0x20000的标,有就丢弃drop,这就是为什么开启NetworkPolicy后网络不通了的原因。
-A cali-tw-cali8846877f208 -m comment --comment "cali:eqqgCJs2qk0AO0ab" -m comment --comment "Drop if no policies passed packet" -m mark --mark 0x0/0x20000 -j DROP
开启icmp的GlobalNetworkPolicy后,防火墙规则如下:
# 所有打了0x0/0x20000标的都转到cali-pi-_1GuvcY/2yw4bHzL8wH
-A cali-tw-cali23a582ef038-m comment --comment "cali:nuuVD6tedS2uGLLB" -m mark --mark 0x0/0x20000 -j cali-pri-_4yi5_iSUAwsU8zMHTk
# 查看协议是否是icmp,如果是的话,就给数据包打上0x10000/0x10000的标
-A cali-pi-_1GuvcY/2yw4bHzL8wH -p icmp -m comment --comment "cali:--CtA5SGB7G86H8e" -m set --match-set cali40s:5y5I3VdRZfDU01O--XXAPx2 src -m icmp --icmp-type 8 -j MARK --set-xmark 0x10000/0x10000
# 如果数据包的标是0x10000/0x10000,就之间return掉,就不往下走了,不会匹配到上面的20000 drop,ping就通了。
-A cali-pi-_1GuvcY/2yw4bHzL8wH- -m comment --comment "cali:1SlOswLbnXZNBbt9" -m mark --mark Ox10000/0x10000 -j RETURN
总结
这个实例中只是单一的icmp协议,实际的NetworkPolicy iptables会很复杂,可以来自不同的源,使用不同的协议,访问不通的端口。
如果需要排查网络层的时候,第一是看iptables规则,filter表;第二是使用tcpdump看数据包的流向;第三如果数据包被丢弃了,系统的dmesg中也可以看到。
实际使用的话,一般是创建一个默认规则拒绝所有,然后再针对性的开墙,保证应用的安全。