本文将介绍如何在示例我的项目中应用网络策略,并解释它在 K3s 中的工作原理,从而帮忙用户进步部署的安全性。

对于 K3s 对网络策略的反对存在一个广泛的误会,因为 K3s 默认应用 Flannel CNI,而 Flannel CNI 不反对网络策略。其实,K3s 应用 Kube-router 网络策略控制器为网络策略提供反对,所以网络策略也能够在 K3s 中应用,就像在其余 Kubernetes 发行版中一样。

失常的 Pod 互相通信

默认状况下,在 K3s 中,一个特定命名空间中的所有 services/Pod 都能够被任何其余命名空间中的所有其余 Pod 拜访。

为了举例说明 Pod 如何互相通信,让咱们在两个不同的命名空间(sample1 和 sample2)中应用简略的 nginx deployment 和 service 来测试,如下所示:

nginx.yaml

apiVersion: apps/v1kind: Deploymentmetadata:  name: nginx  labels:    app: nginxspec:  replicas: 1  selector:    matchLabels:      app: nginx  template:    metadata:      labels:        app: nginx    spec:      containers:      - name: nginx        image: nginx        ports:        - containerPort: 80---apiVersion: v1kind: Servicemetadata:  name: ngnix-servicespec:  selector:    app: nginx  type: ClusterIP  ports:  - protocol: TCP    port: 80    targetPort: 80

创立测试命名空间并将 nginx 部署到对应命名空间中:

# 在 sample1 中创立第一个示例利用kubectl create ns sample1kubectl apply -f nginx.yaml -n sample1# 在 sample2 中创立第二个示例利用kubectl create ns sample2kubectl apply -f nginx.yaml -n sample2

接下来能够尝试在 Pod 中应用 curl 来查看 Pod 间的通信。

sample1 中的 pod -> sample2 命名空间中的 service:

# 从 sample1 拜访 sample2kubectl exec -n sample1 $(kubectl get po -n sample1 -l app=nginx -o name) -- curl --max-time 2 http://ngnix-service.sample2.svc.cluster.local# 从 sample1 拜访 sample1kubectl exec -n sample1 $(kubectl get po -n sample1 -l app=nginx -o name) -- curl --max-time 2 http://ngnix-service.sample1.svc.cluster.local:80

sample2 中的 pod -> sample1 命名空间中的 service:

# 从 sample2 拜访 sample1kubectl exec -n sample2 $(kubectl get po -n sample2 -l app=nginx -o name) -- curl --max-time 2 http://ngnix-service.sample1.svc.cluster.local:80# 从 sample2 拜访 sample2kubectl exec -n sample2 $(kubectl get po -n sample2 -l app=nginx -o name) -- curl --max-time 2 http://ngnix-service.sample2.svc.cluster.local:80

能够看到,以上命令均能够拜访到对应目标地址,也就代表互相都能够通信。

应用 NetworkPolicy 限度 Pod 互相通信

NetworkPolicy Editor(https://editor.cilium.io/)是一个很好的 UI 工具,能够用于生成 Networkploicy,上面是一个示例。

apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:  name: test-network-policy  namespace: my-namespacespec:  podSelector:    matchLabels:      role: db <1>  policyTypes:  - Ingress  ingress: <2>  - from:    - namespaceSelector:        matchLabels:          project: myproject    - podSelector:        matchLabels:          role: frontend    ports:    - protocol: TCP      port: 6379

<1> 这会抉择以后命名空间中的特定 Pod 来匹配该策略。<2> 白名单入口规定列表。每个规定都容许与 from 局部匹配的流量。

能够在 ingress from 局部中指定四种选择器:

  • ipBlock: 会抉择特定的 IP CIDR 范畴以容许作为入口源。通常这是集群内部 IP,因为 Pod IP 是长期的。
  • podSelector: 会在与 NetworkPolicy 雷同的命名空间中抉择特定的 Pod,这些 Pod 应该被容许作为入口源或进口目的地。
  • namespaceSelector: 会抉择特定的命名空间,所有 Pod 都应该被容许作为入口源或进口目的地。
  • namespaceSelector 和 podSelector: 指定 namespaceSelector 和 podSelector 的单个 from 条目抉择特定命名空间内的特定 Pod。

应用 NetworkPolicy 配置多租户隔离

当初让咱们应用 NetworkPolicy 来配置隔离。

上面的 yaml 将在不同命名空间的服务之间建设隔离,只容许同一命名空间的 Pod 互相通信,同时也容许从 ingress 和监控 Pod 传入通信:

networkPolicy.yaml

# 阻止所有传入流量kind: NetworkPolicyapiVersion: networking.k8s.io/v1metadata:  name: deny-by-defaultspec:  podSelector: {}  ingress: []---# 容许所有 Pod 在同一命名空间内的所有流量apiVersion: networking.k8s.io/v1kind: NetworkPolicyapiVersion: networking.k8s.io/v1metadata:  name: allow-same-namespacespec:  podSelector: {}  ingress:  - from:    - podSelector: {}---# 容许 ingress Pod “traefik” 与该命名空间中的所有 Pod 通信apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:  name: allow-all-svclbtraefik-ingressspec:  podSelector:    matchLabels:      svccontroller.k3s.cattle.io/svcname: traefik  ingress:  - {}  policyTypes:  - Ingress---# 容许 ingress Pod “traefik” 与该命名空间中的所有 Pod 通信apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:  name: allow-all-traefik-v121-ingressspec:  podSelector:    matchLabels:      app.kubernetes.io/name: traefik  ingress:  - {}  policyTypes:  - Ingress---# 容许监控零碎 Pod 与该命名空间中的所有 Pod 通信(以容许抓取指标)apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:  name: allow-from-cattle-monitoring-systemspec:  ingress:    - from:      - namespaceSelector:          matchLabels:            kubernetes.io/metadata.name: cattle-monitoring-system  podSelector: {}  policyTypes:  - Ingress

当初将策略利用于两个示例命名空间中:

kubectl apply -f networkPolicy.yaml -n sample1kubectl apply -f networkPolicy.yaml -n sample2

利用上述 networkPolicy.yaml 后,你须要设置好 ingress Pod 或 监控 Pod 的白名单。

当初让咱们再次尝试之前的 curl 来查看 Pod 之间的通信:

# 从 sample1 拜访 sample2kubectl exec -n sample1 $(kubectl get po -n sample1 -l app=nginx -o name) -- curl --max-time 2 http://ngnix-service.sample2.svc.cluster.local# 从 sample1 拜访 sample1kubectl exec -n sample1 $(kubectl get po -n sample1 -l app=nginx -o name) -- curl --max-time 2 http://ngnix-service.sample1.svc.cluster.local:80# 从 sample2 拜访 sample1kubectl exec -n sample2 $(kubectl get po -n sample2 -l app=nginx -o name) -- curl --max-time 2 http://ngnix-service.sample1.svc.cluster.local:80# 从 sample2 拜访 sample2kubectl exec -n sample2 $(kubectl get po -n sample2 -l app=nginx -o name) -- curl --max-time 2 http://ngnix-service.sample2.svc.cluster.local:80

当初你应该看到,不同命名空间之间的通信被阻止了,但同一命名空间内是能够失常通信的。

调试 NetworkPolicy 通信

NetworkPolicy 可能看到因为匹配的 NetworkPolicy 而抛弃的数据包。因为网络规定是通过 KUBE-BWPLCY 链中的 iptables 部署的,咱们能够在以后运行 Pod 的节点上看到这些规定。因而,让咱们查看生成的 iptables。

首先,咱们须要查看 Pod 是在哪个节点上运行。

kubectl get po -o wide -n sample1NAMESPACE             NAME                                                  READY   STATUS    RESTARTS      AGE    IP              NODE       NOMINATED NODE   READINESS GATESsample1               nginx-6c8b449b8f-hhwhv                                1/1     Running   0             3d6h   192.168.248.2   node2

登录到 node2,并获取 iptables 的 KUBE-NWPLCY 链:

node2# iptables -L | grep KUBE-NWPLCY -B 2iptables -L | grep KUBE-NWPLCY -B 2target     prot opt source               destinationChain KUBE-NWPLCY-6MLFY7WSIVQ6X74S (1 references)target     prot opt source               destinationChain KUBE-NWPLCY-6ZMDCAWFW6IG7Y65 (0 references)--RETURN     all  --  anywhere             anywhere             /* rule to ACCEPT traffic from all sources to dest pods selected by policy name: allow-all-svclbtraefik-ingress namespace sample1 */ match-set KUBE-DST-AZLS65URBWHIM4LV dst mark match 0x10000/0x10000Chain KUBE-NWPLCY-CMW66LXPRKANGCCT (1 references)--RETURN     all  --  anywhere             anywhere             /* rule to ACCEPT traffic from specified ipBlocks to dest pods selected by policy name: allow-from-cattle-monitoring-system namespace sample1 */ match-set KUBE-SRC-RCIDLRVZOORE5IEC src match-set KUBE-DST-T5UTRUNREWDWGD44 dst mark match 0x10000/0x10000Chain KUBE-NWPLCY-DEFAULT (2 references)--MARK       all  --  anywhere             anywhere             /* rule to mark traffic matching a network policy */ MARK or 0x10000Chain KUBE-NWPLCY-EM64V3NXOUG2TAJZ (1 references)--RETURN     all  --  anywhere             anywhere             /* rule to ACCEPT traffic from specified ipBlocks to dest pods selected by policy name: allow-same-namespace namespace sample1 */ match-set KUBE-SRC-DSEC5V52VOYVVZ4H src match-set KUBE-DST-5TPLTTXGTPDHQ2AH dst mark match 0x10000/0x10000Chain KUBE-NWPLCY-IF5LSB2QJ2HY5MD6 (0 references)--...省略......--ACCEPT     all  --  anywhere             anywhere             /* rule for stateful firewall for pod */ ctstate RELATED,ESTABLISHEDACCEPT     all  --  anywhere             192.168.248.2        /* rule to permit the traffic traffic to pods when source is the pod's local node */ ADDRTYPE match src-type LOCALKUBE-NWPLCY-DEFAULT  all  --  192.168.248.2        anywhere             /* run through default egress network policy  chain */KUBE-NWPLCY-CMW66LXPRKANGCCT  all  --  anywhere             192.168.248.2        /* run through nw policy allow-from-cattle-monitoring-system */KUBE-NWPLCY-EM64V3NXOUG2TAJZ  all  --  anywhere             192.168.248.2        /* run through nw policy allow-same-namespace */KUBE-NWPLCY-RJITOIYNFGLSMNHT  all  --  anywhere             192.168.248.2        /* run through nw policy deny-by-default */

当初咱们察看链 KUBE-NWPLCY-EM64V3NXOUG2TAJZ(在你的环境中会有不同的名称),它是 allow-same-namespace namespace sample1,同时再次运行 curl 测试:

watch -n 2 -d iptables -L KUBE-NWPLCY-EM64V3NXOUG2TAJZ -nvEvery 2.0s: iptables -L KUBE-NWPLCY-EM64V3NXOUG2TAJZ -nv                                                                                                                         node2: Mon Mar  6 20:18:38 2023Chain KUBE-NWPLCY-EM64V3NXOUG2TAJZ (1 references) pkts bytes target     prot opt in     out     source               destination    4   240 MARK       all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* rule to ACCEPT traffic from source pods to dest pods selected by policy name allow-same-namespace namespace sample1 */match-set KUBE-SRC-OPGXQ4TCHJJUUOWB src match-set KUBE-DST-5TPLTTXGTPDHQ2AH dst MARK or 0x10000    4   240 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* rule to ACCEPT traffic from source pods to dest pods selected by policy name allow-same-namespace namespace sample1 */match-set KUBE-SRC-OPGXQ4TCHJJUUOWB src match-set KUBE-DST-5TPLTTXGTPDHQ2AH dst mark match 0x10000/0x10000    0     0 MARK       all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* rule to ACCEPT traffic from specified ipBlocks to dest pods selected by policy name: allow-same-namespace namespace sample1 */ match-set KUBE-SRC-DSEC5V52VOYVVZ4H src match-set KUBE-DST-5TPLTTXGTPDHQ2AH dst MARK or 0x10000    0     0 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* rule to ACCEPT traffic from specified ipBlocks to dest pods selected by policy name: allow-same-namespace namespace sample1 */ match-set KUBE-SRC-DSEC5V52VOYVVZ4H src match-set KUBE-DST-5TPLTTXGTPDHQ2AH dst mark match 0x10000/0x10000

你会看到在运行 curl 测试期间,计数器一直变动,显示已承受和已抛弃的数据包。

被网络策略抛弃的数据包也能够被记录下来。被抛弃的数据包被发送到 iptables NFLOG,它显示了数据包的详细信息,包含阻止它的网络策略。

要将 NFLOG 转换为日志条目,能够装置 ulogd2/ulogd 包并将 [log1] 配置为在 group=100 上读取。而后,重新启动 ulogd2 服务提交新配置。

要将所有这些数据包记录到文件中,ulogd2 须要在 /etc/ulogd.conf 中进行配置,示例如下:

[global]logfile="syslog"loglevel=3plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_inppkt_NFLOG.so"plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_filter_IFINDEX.so"plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_filter_IP2STR.so"plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_filter_IP2BIN.so"plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_filter_PRINTPKT.so"plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_filter_HWHDR.so"plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_raw2packet_BASE.so"plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_output_LOGEMU.so"# 这是一个堆栈,用于记录零碎通过 LOGEMU 发送的数据包stack=log1:NFLOG,base1:BASE,ifi1:IFINDEX,ip2str1:IP2STR,print1:PRINTPKT,emu1:LOGEMU[log1]group=100[emu1]file="/var/log/ulog/syslogemu.log"sync=1

批改配置文件后,重启 ulogd:

systemctl restart ulogd2.service

如果数据包被网络策略规定阻止,日志音讯将呈现在 /var/log/ulog/syslogemu.log 中:

# cat /var/log/ulog/syslogemu.logMar  7 09:35:43 cluster-k3s-masters-a3620efa-5qgpt  IN=cni0 OUT=cni0 MAC=da:f6:6e:6e:f9:ce:ae:66:8d:d5:f8:d1:08:00 SRC=10.42.0.59 DST=10.42.0.60 LEN=60 TOS=00 PREC=0x00 TTL=64 ID=50378 DF PROTO=TCP SPT=47750 DPT=80 SEQ=3773744693 ACK=0 WINDOW=62377 SYN URGP=0 MARK=20000

如果有大量的流量,日志文件可能增长得十分快。为了管制这种状况,通过在相干的网络策略中增加以下正文,适当地设置 "limit "和 "limit-burst "iptables 参数:

# 默认值是limit=10/分钟和limit-burst=10。kube-router.io/netpol-nflog-limit=<LIMIT-VALUE>kube-router.io.io/netpol-nflog-limit-burst=<LIMIT-BURST-VALUE>

参考:

  • Kubernetes Network Policies:https://kubernetes.io/docs/concepts/services-networking/netwo...
  • K3s:https://github.com/k3s-io/k3s/blob/master/README.md
  • 强化指南 – NetworkPolicies:https://docs.k3s.io/security/hardening-guide#networkpolicies
  • K3s Network Policy:https://docs.k3s.io/advanced#additional-network-policy-logging