Calico除了支持BGP网络之外还支持网络策略。

在默认情况下,k8s上的2个不同名称空间下的Pod是可以相互访问的。k8s的名称空间仅用于为资源名称提供隔离机制,而对于不同名称空间下的pod间相互访问并没有进行隔离。而Calico的网络策略则是用来管控Pod间的通信流量。

所谓的网络测略就是专用于调用节点内核上,能够实施流量控制的API从而定义规则来管控Pod间通信的一种机制。

k8s默认网络策略

网络策略是K8S上的一种标准资源。

k8s默认网络策略定义规范

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: networking.k8s.io/v1  # 资源隶属的API群组及版本号
kind: NetworkPolicy # 资源类型的名称,名称空间级别的资源;
metadata: # 资源元数据
name <string> # 资源名称标识
namespace <string> # NetworkPolicy是名称空间级别的资源
spec: # 期望的状态
podSelector <Object> # 当前规则生效的同一名称空间中的一组目标Pod对象,必选字段;
# 空值表示当前名称空间中的所有Pod资源
policyTypes <[]string> # Ingress表示生效ingress字段;Egress表示生效
# egress字段,同时提供表示二者均有效
ingress <[]Object> # 入站流量源端点对象列表,白名单,空值表示“所有”
- from <[]Object> # 具体的端点对象列表,空值表示所有合法端点
- ipBlock <Object> # IP地址块范围内的端点,不能与另外两个字段同时使用
- namespaceSelector <Object> # 匹配的名称空间内的端点
podSelector <Object> # 由Pod标签选择器匹配到的端点,空值表示<none>
ports <[]Object> # 具体的端口对象列表,空值表示所有合法端口
egress <[]Object> # 出站流量目标端点对象列表,白名单,空值表示“所有”
- to <[]Object> # 具体的端点对象列表,空值表示所有合法端点,格式同ingres.from;
ports <[]Object> # 具体的端口对象列表,空值表示所有合法端口

管控入站流量

管控入站流量时有3种方式定义其来源

  1. ipblock
  2. namespaceSelector
  3. podSelector

nameSpaceSelector和podSelector是同时使用的。

示例

1.创建出pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
root@k8s-master01:~/yaml/chapter08# VERSION=v1.0 envsubst < deployment-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-demo
spec:
replicas: 4
selector:
matchLabels:
app: demoapp
release: stable
template:
metadata:
labels:
app: demoapp
release: stable
spec:
containers:
- name: demoapp
image: ikubernetes/demoapp:v1.0
ports:
- containerPort: 80
name: http

# 应用资源清单
root@k8s-master01:~/yaml/chapter08# VERSION=v1.0 envsubst < deployment-demo.yaml | kubectl apply -f - -n dev
deployment.apps/deployment-demo created

2.编写netpol资源清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 此资源清单用来控制入站流量
root@k8s-master01:~/yaml/chapter10# vim netpol-dev-demoapp-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: demoapp-ingress
namespace: dev # 此为该网络策略作用的名称空间
spec:
podSelector: # 表示对dev名称空间下的哪一组Pod生效
matchLabels:
app: demoapp # 表示只对dev名称空间下有用标签名为app: demoapp的pod生效。
policyTypes: ["Ingress"] # 表示管理入站流量
ingress: # 以下为入站流量的管理方法。定义的表示允许,未定义的受制于默认策略
- from: # 此清单有2个from表示有2个规则。
- namespaceSelector: # 名称空间选择器
matchExpressions:
- key: kubernetes.io/metadata.name
operator: In
values: [dev,kube-system,logs,monitoring,kubernetes-dashboard,longhorn-system]
# - ipBlock: # 由于所有pod跑在同一地址段下,一旦大范围放行,将导致拒绝规则不生效
# cidr: 192.168.0.0/16
- from:
- namespaceSelector:
matchExpressions:
- {key: kubernetes.io/metadata.name, operator: NotIn, values: ["default"]}
ports:
- protocol: TCP
port: 80

3.应用资源清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
root@k8s-master01:~/yaml/chapter10# kubectl apply -f netpol-dev-demoapp-ingress.yaml
networkpolicy.networking.k8s.io/demoapp-ingress created

# 查看netpol的描述信息
root@k8s-master01:~/yaml/chapter10# kubectl describe netpol -n dev demoapp-ingress
Name: demoapp-ingress
Namespace: dev
Created on: 2021-08-09 08:15:42 +0000 UTC
Labels: <none>
Annotations: <none>
Spec:
PodSelector: app=demoapp
Allowing ingress traffic:
To Port: <any> (traffic allowed to all ports) # 表示到app=demoapp这组pod的任意端口
From:
NamespaceSelector: kubernetes.io/metadata.name in (dev,kube-system,kubernetes-dashboard,logs,monitoring)
----------
To Port: 80/TCP
From:
NamespaceSelector: kubernetes.io/metadata.name notin (default)
Not affecting egress traffic
Policy Types: Ingress

4.在default名称空间下使用pod进行访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 当前名称空间下存在的pod
root@k8s-master01:~/yaml/chapter10# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deployment-demo-fb544c5d8-8jd62 1/1 Running 0 3d 192.168.96.1 k8s-node02 <none> <none>
deployment-demo-fb544c5d8-wcdms 1/1 Running 0 3d 192.168.131.3 k8s-node01 <none> <none>
deployment-demo-fb544c5d8-xvsm7 1/1 Running 0 3d 192.168.30.2 k8s-node03 <none> <none>
deployment-demo-fb544c5d8-zwbhd 1/1 Running 0 3d 192.168.30.1 k8s-node03 <none> <none>
root@k8s-master01:~/yaml/chapter10# kubectl get pods -o wide -n dev
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deployment-demo-fb544c5d8-frmr7 1/1 Running 0 13m 192.168.30.3 k8s-node03 <none> <none>
deployment-demo-fb544c5d8-lxnvz 1/1 Running 0 13m 192.168.96.2 k8s-node02 <none> <none>
deployment-demo-fb544c5d8-vqqbt 1/1 Running 0 13m 192.168.131.4 k8s-node01 <none> <none>
deployment-demo-fb544c5d8-z9wfc 1/1 Running 0 13m 192.168.96.3 k8s-node02 <none> <none>

# 使用default名称空间下的pod对dev名称空间下的pod进行访问
root@k8s-master01:~/yaml/chapter10# kubectl exec deployment-demo-fb544c5d8-8jd62 -- curl 192.168.131.4

注意事项:

  1. 在使用规则时,名称空间必须要存在相应的标签,否则规则将无法匹配导致不生效
  2. 定义规则时必须生效顺序,若允许的规则范围太大将会导致拒绝规则不生效。

管控出站流量

由于默认的出战规则是放行所有,所以需要先将所有的入站和出栈规则做拒绝,才能进行管控。

1.创建资源清单,拒绝所有出入站

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@k8s-master01:~/yaml/chapter10# vim netpol-dev-denyall.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: dev
spec:
podSelector: {}
policyTypes: ["Ingress","Egress"]
egress:
- to:
- podSelector: {}
ingress:
- from:
- podSelector: {}

2.应用配置清单

1
2
3
4
5
6
7
8
9
10
root@k8s-master01:~/yaml/chapter10# kubectl apply -f netpol-dev-denyall.yaml
networkpolicy.networking.k8s.io/deny-all-ingress created

# 在dev名称空间下使用pod进行名称解析
root@k8s-master01:~/yaml/chapter10# kubectl exec deployment-demo-fb544c5d8-frmr7 -n dev -- nslookup www.baidu.com
;; connection timed out; no servers could be reached

command terminated with exit code 1

# 由于拒绝所有,目前无法进行访问kube-dns的服务

3.定义egress的放行规则,编写资源清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
root@k8s-master01:~/yaml/chapter10# vim netpol-dev-demoapp-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: demoapp-egress
namespace: dev
spec:
podSelector:
matchLabels:
app: demoapp
policyTypes: ["Egress"]
egress:
- to:
ports:
- protocol: UDP
port: 53
- to:
- podSelector:
matchLabels:
app: redis
ports:
- protocol: TCP
port: 6379
- to:
- podSelector:
matchLabels:
app: demoapp
ports:
- protocol: TCP
port: 80

4.应用资源清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
root@k8s-master01:~/yaml/chapter10# kubectl apply -f netpol-dev-demoapp-egress.yaml
networkpolicy.networking.k8s.io/demoapp-egress created

# 在dev名称空间下使用pod进行名称解析
root@k8s-master01:~/yaml/chapter10# kubectl exec deployment-demo-fb544c5d8-frmr7 -n dev -- nslookup www.baidu.com
Server: 10.96.0.10
Address: 10.96.0.10#53

Non-authoritative answer:
www.baidu.com canonical name = www.a.shifen.com.
Name: www.a.shifen.com
Address: 180.101.49.12
Name: www.a.shifen.com
Address: 180.101.49.11

隔离名称空间

之前的denyall资源清单,使用后虽然能够实现隔离名称空间的效果,但是其直接拒绝了所有。而后ingress和egress的资源清单实现了流量控制的机制,但是定义网络规则时,如果定义在多个资源内很有可能资源和资源间的规则会冲突和覆盖导致无法达成目标,所以建议所有的入站和出战规则写入同一个文件中。

示例

名称空间级别出入站同一文件示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
root@k8s-master01:~/yaml/chapter10# vim netpool-stage-default.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default
namespace: stage
spec:
podSelector: {}
policyTypes: ["Ingress","Egress"]
ingress:
- from:
- namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: In
values: [stage,kube-system,logs,monitoring,kubernetes-dashboard]
egress:
- to:
ports:
- protocol: UDP
port: 53
- to:
- namespaceSelector:
matchLabels:
name: kube-apiserver
podSelector:
matchLabels:
component: kube-apiserver
ports:
- protocol: TCP
port: 443
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: stage

k8s自身的networkPolicy每个名称空间都需要以当前名称空间为中心设置networkPolicy,系统上存在几个namespace就需要几个networkPolicy去应用,这管理起来十分复杂。因此Calico提供了另一种解决方案

Calico的网络策略

尽管k8s功能上日渐丰富,但k8s自己的NetworkPolicy资源仍然具有相当的局限性,例如它没有明确的拒绝规则、缺乏对选择器高级表达式的支持、不支持应用层规则,以及没有集群范围的网络策略等。为了解决这些限制,Calico等提供了自有的策略CRD,包括NetworkPolicyGlobalNetworkPolicy等,其中的NetworkPolicy CRDKubernetes NetworkPolicy API提供了更大的功能集,包括支持拒绝规则、规则解析级别以及应用层规则等,但相关的规则需要由calicoctl创建。

1
2
3
root@k8s-master01:~/yaml/chapter10# kubectl api-resources | grep calico | grep networkpolicies
globalnetworkpolicies crd.projectcalico.org/v1 false GlobalNetworkPolicy
networkpolicies crd.projectcalico.org/v1 true NetworkPolicy

Calico全局管理

GlobalNetworkPolicy支持使用selectorserviceAccountSelectornamespaceSelector来选定网络策略的生效范围,默认为all(),即集群上的所有端点。下面的配置清单示例(globalnetworkpolicy-demo.yaml)为非系统类名称空间(本示例假设有kube-system、kubernetes-dashboard、logs和monitoring这4个)定义了一个通用的网络策略。

示例

定义资源清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
root@k8s-master01:~/yaml/chapter10# vim globalnetworkpolicy-demo.yaml
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy # 资源名称,使用此资源可以为所有名称空间施加默认策略
metadata:
name: namespace-default
spec:
order: 0.0 # 定义次序,用来定义规则的优先级,0.0表示级别最高
namespaceSelector: name not in {"kube-system","kubernetes-dashboard","logs","monitoring"} # 挑选名称空间
types: ["Ingress","Egress"]
ingress:
- action: Allow # 施加动作可以为Allow也可以Deny
source:
namespaceSelector: name in {"kube-system","kubernetes-dashboard","logs","monitoring"}
egress:
- action: Allow

# 以上规则的意思是,
# 入站规则:非"kube-system","kubernetes-dashboard","logs","monitoring"名称空间的pod,
# 允许"kube-system","kubernetes-dashboard","logs","monitoring"这些名称空间的pod来访问。
# 出站规则:全都允许

# 规则的定义方法一般放行所有的出站流量,限制入站的流量。
# 只要限制住入站流量,出站流量即使不限制也无法访问到其他限制了入站流量的pod。

应用资源清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 使用kubectl-calico命令进行生效
root@k8s-master01:~/yaml/chapter10# kubectl-calico apply -f globalnetworkpolicy-demo.yaml
Successfully applied 1 'GlobalNetworkPolicy' resource(s)

# 获取globalnetworkpolicy资源
root@k8s-master01:~/yaml/chapter10# kubectl-calico get globalnetworkpolicy namespace-default -o yaml
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
creationTimestamp: "2021-08-10T03:16:58Z"
name: namespace-default
resourceVersion: "510265"
uid: b38164a9-5d6f-441d-970a-a493d03fb868
spec:
egress:
- action: Allow
destination: {}
source: {}
ingress:
- action: Allow
destination: {}
source:
namespaceSelector: name in {"kube-system","kubernetes-dashboard","logs","monitoring"}
namespaceSelector: name not in {"kube-system","kubernetes-dashboard","logs","monitoring"}
order: 0
types:
- Ingress
- Egress