一、集群环境
底层系统为ubuntu18.04,然后在每个node上安装k8s,并构建集群。Master node的IP地址为192.168.26.71/24,两个Worker node的IP地址为192.168.26.72/24、192.168.26.73/24。部署Calico网络插件,保证Node之间能够正常通信。
二、部署网络策略
Network Policy是kubernetes中的一种资源类型,可以理解为一个简单的防火墙,它从属于某个Namespace。其内容从逻辑上看包含两个关键部分,一是pod选择器,基于标签选择相同Namespace下的pod,将其中定义的规则作用于选中的pod。另一个就是规则了,就是网络流量进出pod的规则,其采用的是白名单模式,符合规则的通过,不符合规则的拒绝。
注:相对于网络策略,istio能够做到更细致的网络访问控制。
步骤1:实验环境规划
在我们集群中设置另外一个NameSpace,testns。然后在default的ns中创建2个nginx的pod,和对应的SVC。同时分别使用busybox镜像在两个NS内创建两个testpod。后续,当我们部署网络策略后,可以使用两个testpod来访问pod1和pod2,验证网络策略是否生效。
步骤2:安装metallb服务,以提供网络负载平衡器(Loadbalance)类型的SVC
官网安装步骤:https://metallb.universe.tf/installation/
首先在master上创建命名空间:
kubectl create ns metallb-system
在官网安装步奏中有最新版本的yaml文件。下载metallb.yaml文件,v0.11.0下载地址如下:
https://raw.githubusercontent.com/metallb/metallb/v0.11.0/manifests/metallb.yaml
修改metallb.yaml文件中两个镜像拉取的策略为IfNotPresent。然后再集群环境(所有Node)中下载需要使用的两个镜像:
root@vms71:~/route-policy# grep image metallb.yaml
image: quay.io/metallb/speaker:v0.11.0
imagePullPolicy: IfNotPresent
image: quay.io/metallb/controller:v0.11.0
imagePullPolicy: IfNotPresent
部署metallb:
kubectl apply -f metallb.yaml
使用如下yaml文件创建Loadbalance的地址池,下载地址:https://metallb.universe.tf/configuration/。地址池需要和自己Node在同一网段。
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 192.168.26.240-192.168.26.250
步骤3:部署测试使用的pod和loadbalance类型的SVC
在master上创建测试使用的NameSpace:
kubectl create ns TestNs
在集群设备中下载所需的Nginx和yauritux/busybox-curl镜像:
docker pull nginx
docker pull yauritux/busybox-curl
在default namespace中创建pod1和pod2,yaml文件分别如下:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod1
name: pod1
spec:
terminationGracePeriodSeconds: 0
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod1
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod2
name: pod2
spec:
terminationGracePeriodSeconds: 0
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod2
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
修改pod1主页为11111:
kubectl exec -it pod1 -- sh -c "echo 11111 > /usr/share/nginx/html/index.html"
pod2主页为22222:
kubectl exec -it pod1 -- sh -c "echo 22222 > /usr/share/nginx/html/index.html"
查看pod1和pod2是否正常运行:
root@vms71:~/route-policy# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod1 1/1 Running 0 20m 10.244.5.205 vms72.rhce.cc <none> <none>
pod2 1/1 Running 0 20m 10.244.5.204 vms72.rhce.cc <none> <none>
创建Loadbalance类型的svc1和svc2:
kubectl expose --name=svc1 pod pod1 --port=80 --type=LoadBalancer
kubectl expose --name=svc2 pod pod2 --port=80 --type=LoadBalancer
查看是否创建成功,获取到地址:
root@vms71:~/route-policy# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10d
svc1 LoadBalancer 10.101.126.242 192.168.26.240 80:31482/TCP 2m34s
svc2 LoadBalancer 10.105.233.117 192.168.26.241 80:31075/TCP 2m25s
root@vms71:~/route-policy# curl 192.168.26.240
11111
root@vms71:~/route-policy# curl 192.168.26.241
22222
创建Namespac:testns:
kubectl create ns testns
分别在两个Namespace中创建2个testpod1和testpod2:
kubectl run testpod1 --image=yauritux/busybox-curl -it --rm --image-pull-policy=IfNotPresent -- sh
kubectl run testpod2 -n=testns --image=yauritux/busybox-curl -it --rm --image-pull-policy=IfNotPresent -- sh
测试目前两个testpod是否能够访问pod1和pod2:结果是都可以访问。注意,如果访问不同NameSpace中的SVC,需要在后面指明具体的NameSpace。
/home # curl svc1.default
11111
/home # curl svc2.default
22222
步骤4:创建并测试网络策略
在应用网络策略时需要注意三点:
- 应用到哪个pod上去,也就是去保护哪一个pod;
- 需要设置允许哪些客户端能访问被保护的pod;
- 设置保护的pod能够访问哪些客户端。
在官方网站上可以找到网络策略的模板:https://kubernetes.io/docs/concepts/services-networking/network-policies/。策略的模板同时激活了Ingress和Egress策略。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
role: 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
网络策略首先需要设定作用的Namespace:
namespace: default
其中的podSelector表示作用于namespace中的哪个pod,使用标签的方式进行匹配。如果不写的话,表示作用于namespace中的所有pods。
podSelector:
matchLabels:
role: db
policyType有两种,分别表示匹配入方向或者出方向的流量(以被保护的pod为基础),只有在这里激活了对应的type,后续设置的策略才会生效。
policyTypes:
- Ingress
- Egress
以其中演示的ingress策略为例子(允许谁能够访问被保护的pod):
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
其中有三种设置策略的方式:
- ipBlock:匹配到的IP地址可以访问,except匹配到的无法访问 ;注意, 这些应该是集群外部 IP,因为 Pod IP存在时间短暂的且随机产生。例如,所作用的源IP则可能是LoadBalancer或Pod的node等。
- podSelector:指定匹配到label的pod能够访问被保护的pod;
- namespaceSelector:指定具体命名空间内的pod能够访问被保护的pod。
注意,在 from 数组中仅包含一个元素,只允许来自标有 role=client 的 Pod 且 该 Pod 所在的NameSpace中标有 user=alice 的连接。如下:
ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
podSelector:
matchLabels:
role: client
在 from 数组中包含两个元素,允许来自本地NameSpace标有 role=client 的 Pod 的连接(默认为本NameSpace,也就是此策略所在的NameSpace),或 来自任何名字空间中标有 user=alice 的任何Pod的连接:
ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
- podSelector:
matchLabels:
role: client
egress规则同理。
策略一:在pod1上启用ingress的policy,保证只有192.168.26.0/24且非192.168.26.71/32(master地址)能够访问。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
run: pod1
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
cidr: 192.168.26.0/24
except:
- 192.168.26.71/32
ports:
- protocol: TCP
port: 80
在master和worker上分别做测试:
root@vms71:~/route-policy# curl 192.168.26.240
^Z
[1]+ Stopped curl 192.168.26.240
root@vms72:~# curl 192.168.26.240
11111
测试结果可以看到,master无法访问当前的pod1。
策略二:在pod1上启用ingress的policy,保证只有当前NameSpace的pod能够访问。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
run: pod1
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: default #default空间的lable
ports:
- protocol: TCP
port: 80
如果不写NameSpace的Labels的标签,表示的是任意NameSpace。分别用testpod1和testpod2进行访问。
testpod1上,可以正常访问:
/home # curl svc1
11111
testpod2上,不可以正常访问:
/home # curl svc1.default
curl: (7) Failed connect to svc1.default:80; Connection timed out
如果NameSpace写成如下的形式,则表示所有的NameSpace都可以访问
- namespaceSelector:
matchLabels:
策略三:在testpod2上启用egress的policy,保证只能够访问default空间内的pod2。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: testns
spec:
podSelector:
matchLabels:
run: testpod2
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
run: pod2
namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: default
ports:
- protocol: TCP
port: 80
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
注意写第二个-to策略(每个-to策略之间的关系为或)放行去往kube-system的流量DNS流量。在testpod2中,测试结果如下,说明网络策略运行正常。
/home # curl svc2.default
22222
/home # curl svc1.default
curl: (7) Failed connect to svc1.default:80; Connection timed out
策略四:如果想要保证testpod1能够访问pod1的80端口,testpod2能够访问pod1的8080端口,可以通过写两个from完成。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
run: pod1
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
run: testpod1
ports:
- protocol: TCP
port: 80
- from:
- podSelector:
matchLabels:
run: testpod2
namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: testns
ports:
- protocol: TCP
port: 8080
步骤5:默认策略
默认情况下,如果名字空间中不存在任何策略,则所有进出该名字空间(NameSpace)中 Pod 的流量都被允许。 以下示例使我们可以更改该名字空间中的默认行为。(注意仅作用于策略所在的NameSpace)
1.默认拒绝所有入站流量:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
这样可以确保即使容器没有选择其他任何 NetworkPolicy,也仍然可以被隔离。 此策略不会更改默认的出口隔离行为。
2.默认允许所有入站流量:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
spec:
podSelector: {}
ingress:
- {}
policyTypes:
- Ingress
如果要允许所有流量进入某个名字空间中的所有 Pod(即使添加了导致某些 Pod 被视为 “隔离”的策略),则可以创建一个策略来明确允许该名字空间中的所有流量。
3.默认拒绝所有出站流量:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
spec:
podSelector: {}
policyTypes:
- Egress
此策略可以确保即使没有被其他任何 NetworkPolicy 选择的 Pod 也不会被允许流出流量。 此策略不会更改默认的入站流量隔离行为。
4.默认允许所有出站流量:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-egress
spec:
podSelector: {}
egress:
- {}
policyTypes:
- Egress
如果要允许来自名字空间中所有 Pod 的所有流量(即使添加了导致某些 Pod 被视为“隔离”的策略), 则可以创建一个策略,该策略明确允许该名字空间中的所有出站流量。
5.默认拒绝所有入口和所有出站流量:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
我们以通过在该名字空间中创建以下 NetworkPolicy 来阻止所有入站和出站流量。此策略可以确保即使没有被其他任何 NetworkPolicy 选择的 Pod 也不会被允许入站或出站流量。
三、校验和
为了保证k8s中流量传输过程中没有被更改或者出现传输错误,我们可以使用校验和来检查。
一般来说,当我们下载文件时,在下载页面都附有对应正确的校验和值和所使用的HASH函数。我们当下载完成后,我们可以在Linux系统上重新计算校验和来对比是否有修改。
例如,我们可以使用Linux自带的MD5或者SHA校验软件来校验:
root@vms71:~/route-policy# md5sum metallb.yaml
6313083a000cfeae5403b8bcf96cc7f5 metallb.yaml
root@vms71:~/route-policy# sha512sum metallb.yaml
6ebc53703d23841fe805f2d329da95bc3a4af442eed3823f70c0a6ba9d298e85f8c3f25f0cdea2057d819d7737834a369f4267c597a834913b9c4f91a108b5ff metallb.yaml
如果和官网显示的校验值不同,则删除掉内容被修改或传输出错的文件。
整理资料来源:
K8s官网资料:https://kubernetes.io/zh/docs/concepts/services-networking/network-policies/