Kubernetes——网络策略
网络策略
网络策略(Network Policy)是用于控制分组的 Pod 资源彼此之间如何进行通信,以及分组的 Pod 资源如何与其他网络端点进行通信的规范。它用于为 Kubernetes 实现更为精细的流量控制,实现租户隔离机制。
Kubernetes 使用标准的资源对象 "NetworkPolicy" 供管理员按需定义网络访问控制策略。
一、网络策略概述
Kubernetes 的网络策略功能由其所使用的网络插件实现,因此,仅在使用那些支持网络策略功能的网络插件时才能够配置网络策略,如 Calico、Canal 及 kube-router 等。每种解决方案各有其特定的网络策略实现方式,它们的实现或依赖于节点自身的功能,或借助于 Hypervisor 的特性,也可能是网络自身的功能。Calico 的 calico/kube-controller 即为 Calico 项目中用于将用户定义的网络策略予以实现组件,它主要依赖于节点的 iptables 来实现访问控制功能。
策略控制器用于监控创建 Pod 时所生成的新 API 端点,并按需为其附加网络策略。当发生需要配置策略的事件时,侦听器会监视到变化,控制器随即响应以进行接口配置和策略应用。
Pod 的网络流量包含 "流入"(Ingress) 和 "流出"(Egress)两种方法,每种方向的控制策略则包含 "允许" 和 "禁止" 两种。
默认情况下,Pod 处于非隔离状态,它们的流量可以自由来去。一旦有策略通过选择器规则则将策略应用于 Pod,那么所有未经明确允许的流量都将被网络策略拒绝,不过,其他未被选择器匹配的 Pod 不受影响。
二、部署 Canal 提供网络策略
Canal 代表了针对云原生应用程序的最佳策略网络解决方案,旨在让用户轻松地将 Calico 和 flannel 网络部署在一起作为统一的网络解决方案,将 Calico 的网络策略执行与 Calico 和 flannel 叠加以及非叠加网络连接选项的丰富功能相组合。
换句话说,Calico 项目既能够独立地为 Kubernetes 集群提供网络解决方案和网络策略,也能与 flannel 结合一起,由 flannel 提供网络解决方案,而 Calico 此时仅用于提供网络策略,这时我们也可以将 Calico 称为 Canal。Calico 将数据存储于 etcd 中,它支持选择使用专用的 etcd 存储,也能够以 Kubernetes API Server 作为后端存储,这里选择以第二种方式进行。
结合 flannel 工作时,Calico 提供的默认配置清单中是以 flannel 默认使用的 10.244.0.0/16 作为 Pod 网络,因此,请确保 kube-controller-manager 程序在启动时通过 --cluster-cidr 选项设置使用了此网络地址,并且 --allocate-node-cidrs 的值应设置为 true。
--cluster-cidr=<your-pod-cidr>
--allocate-node-cidrs=true
部署前,要在启用了 RBAC 的 Kubernetes 集群中设置必要的相关资源:
kubectl apply -f https://docs.projectcalico.org/v3.2/getting-started/kubernetes//installation/hosted/canal/rbac.yaml
# {{site.prodname}} Roles
# Reference {{site.url}}/{{page.version}}/getting-started/kubernetes/installation/hosted/rbac-kdd.yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: calico
rules:
- apiGroups: [""]
resources:
- namespaces
- serviceaccounts
verbs:
- get
- list
- watch
- apiGroups: [""]
resources:
- pods/status
verbs:
- update
- apiGroups: [""]
resources:
- pods
verbs:
- get
- list
- watch
- patch
- apiGroups: [""]
resources:
- services
verbs:
- get
- apiGroups: [""]
resources:
- endpoints
verbs:
- get
- apiGroups: [""]
resources:
- nodes
verbs:
- get
- list
- update
- watch
- apiGroups: ["networking.k8s.io"]
resources:
- networkpolicies
verbs:
- get
- list
- watch
- apiGroups: ["crd.projectcalico.org"]
resources:
- globalfelixconfigs
- felixconfigurations
- bgppeers
- globalbgpconfigs
- globalnetworksets
- hostendpoints
- bgpconfigurations
- ippools
- globalnetworkpolicies
- networkpolicies
- clusterinformations
verbs:
- create
- get
- list
- update
- watch
---
# Flannel roles
# Pulled from https://github.com/coreos/flannel/blob/master/Documentation/kube-flannel-rbac.yml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: flannel
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- apiGroups:
- ""
resources:
- nodes
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- nodes/status
verbs:
- patch
---
# Bind the flannel ClusterRole to the canal ServiceAccount.
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: canal-flannel
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: flannel
subjects:
- kind: ServiceAccount
name: canal
namespace: kube-system
---
# Bind the {{site.prodname}} ClusterRole to the canal ServiceAccount.
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: canal-calico
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: calico
subjects:
- kind: ServiceAccount
name: canal
namespace: kube-system
接下来即可部署 Canal 提供网络策略:
https://projectcalico.docs.tigera.io/v3.2/getting-started/kubernetes/installation/hosted/canal/canal.yaml
# Canal Version v3.2.8
# https://docs.projectcalico.org/v3.2/releases#v3.2.8
# This manifest includes the following component versions:
# calico/node:v3.2.8
# calico/cni:v3.2.8
# coreos/flannel:v0.9.1
# This ConfigMap is used to configure a self-hosted Canal installation.
kind: ConfigMap
apiVersion: v1
metadata:
name: canal-config
namespace: kube-system
data:
# The interface used by canal for host <-> host communication.
# If left blank, then the interface is chosen using the node's
# default route.
canal_iface: ""
# Whether or not to masquerade traffic to destinations not within
# the pod network.
masquerade: "true"
# The CNI network configuration to install on each node. The special
# values in this config will be automatically populated.
cni_network_config: |-
{
"name": "k8s-pod-network",
"cniVersion": "0.3.0",
"plugins": [
{
"type": "calico",
"log_level": "info",
"datastore_type": "kubernetes",
"nodename": "__KUBERNETES_NODE_NAME__",
"ipam": {
"type": "host-local",
"subnet": "usePodCidr"
},
"policy": {
"type": "k8s"
},
"kubernetes": {
"kubeconfig": "__KUBECONFIG_FILEPATH__"
}
},
{
"type": "portmap",
"snat": true,
"capabilities": {"portMappings": true}
}
]
}
# Flannel network configuration. Mounted into the flannel container.
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
---
# This manifest installs the calico/node container, as well
# as the Calico CNI plugins and network config on
# each master and worker node in a Kubernetes cluster.
kind: DaemonSet
apiVersion: extensions/v1beta1
metadata:
name: canal
namespace: kube-system
labels:
k8s-app: canal
spec:
selector:
matchLabels:
k8s-app: canal
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
template:
metadata:
labels:
k8s-app: canal
annotations:
# This, along with the CriticalAddonsOnly toleration below,
# marks the pod as a critical add-on, ensuring it gets
# priority scheduling and that its resources are reserved
# if it ever gets evicted.
scheduler.alpha.kubernetes.io/critical-pod: ''
spec:
nodeSelector:
beta.kubernetes.io/os: linux
hostNetwork: true
tolerations:
# Make sure canal gets scheduled on all nodes.
- effect: NoSchedule
operator: Exists
# Mark the pod as a critical add-on for rescheduling.
- key: CriticalAddonsOnly
operator: Exists
- effect: NoExecute
operator: Exists
serviceAccountName: canal
# Minimize downtime during a rolling upgrade or deletion; tell Kubernetes to do a "force
# deletion": https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods.
terminationGracePeriodSeconds: 0
containers:
# Runs calico/node container on each Kubernetes node. This
# container programs network policy and routes on each
# host.
- name: calico-node
image: quay.io/calico/node:v3.2.8
env:
# Use Kubernetes API as the backing datastore.
- name: DATASTORE_TYPE
value: "kubernetes"
# Wait for the datastore.
- name: WAIT_FOR_DATASTORE
value: "true"
# Set based on the k8s node name.
- name: NODENAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
# Don't enable BGP.
- name: CALICO_NETWORKING_BACKEND
value: "none"
# Cluster type to identify the deployment type
- name: CLUSTER_TYPE
value: "k8s,canal"
# Period, in seconds, at which felix re-applies all iptables state
- name: FELIX_IPTABLESREFRESHINTERVAL
value: "60"
# No IP address needed.
- name: IP
value: ""
# The default IPv4 pool to create on startup if none exists. Pod IPs will be
# chosen from this range. Changing this value after installation will have
# no effect. This should fall within `--cluster-cidr`.
- name: CALICO_IPV4POOL_CIDR
value: "192.168.0.0/16"
# Disable file logging so `kubectl logs` works.
- name: CALICO_DISABLE_FILE_LOGGING
value: "true"
# Set Felix endpoint to host default action to ACCEPT.
- name: FELIX_DEFAULTENDPOINTTOHOSTACTION
value: "ACCEPT"
# Disable IPv6 on Kubernetes.
- name: FELIX_IPV6SUPPORT
value: "false"
# Set Felix logging to "info"
- name: FELIX_LOGSEVERITYSCREEN
value: "info"
- name: FELIX_HEALTHENABLED
value: "true"
securityContext:
privileged: true
resources:
requests:
cpu: 250m
livenessProbe:
httpGet:
path: /liveness
port: 9099
host: localhost
periodSeconds: 10
initialDelaySeconds: 10
failureThreshold: 6
readinessProbe:
httpGet:
path: /readiness
port: 9099
host: localhost
periodSeconds: 10
volumeMounts:
- mountPath: /lib/modules
name: lib-modules
readOnly: true
- mountPath: /var/run/calico
name: var-run-calico
readOnly: false
- mountPath: /var/lib/calico
name: var-lib-calico
readOnly: false
# This container installs the Calico CNI binaries
# and CNI network config file on each node.
- name: install-cni
image: quay.io/calico/cni:v3.2.8
command: ["/install-cni.sh"]
env:
# Name of the CNI config file to create.
- name: CNI_CONF_NAME
value: "10-canal.conflist"
# Set the hostname based on the k8s node name.
- name: KUBERNETES_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
# The CNI network config to install on each node.
- name: CNI_NETWORK_CONFIG
valueFrom:
configMapKeyRef:
name: canal-config
key: cni_network_config
volumeMounts:
- mountPath: /host/opt/cni/bin
name: cni-bin-dir
- mountPath: /host/etc/cni/net.d
name: cni-net-dir
# This container runs flannel using the kube-subnet-mgr backend
# for allocating subnets.
- name: kube-flannel
image: quay.io/coreos/flannel:v0.9.1
command: [ "/opt/bin/flanneld", "--ip-masq", "--kube-subnet-mgr" ]
securityContext:
privileged: true
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: FLANNELD_IFACE
valueFrom:
configMapKeyRef:
name: canal-config
key: canal_iface
- name: FLANNELD_IP_MASQ
valueFrom:
configMapKeyRef:
name: canal-config
key: masquerade
volumeMounts:
- name: run
mountPath: /run
- name: flannel-cfg
mountPath: /etc/kube-flannel/
volumes:
# Used by calico/node.
- name: lib-modules
hostPath:
path: /lib/modules
- name: var-run-calico
hostPath:
path: /var/run/calico
- name: var-lib-calico
hostPath:
path: /var/lib/calico
# Used by flannel.
- name: run
hostPath:
path: /run
- name: flannel-cfg
configMap:
name: canal-config
# Used to install CNI.
- name: cni-bin-dir
hostPath:
path: /opt/cni/bin
- name: cni-net-dir
hostPath:
path: /etc/cni/net.d
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: canal
namespace: kube-system
---
# Create all the CustomResourceDefinitions needed for
# Calico policy and networking mode.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: felixconfigurations.crd.projectcalico.org
spec:
scope: Cluster
group: crd.projectcalico.org
version: v1
names:
kind: FelixConfiguration
plural: felixconfigurations
singular: felixconfiguration
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: bgpconfigurations.crd.projectcalico.org
spec:
scope: Cluster
group: crd.projectcalico.org
version: v1
names:
kind: BGPConfiguration
plural: bgpconfigurations
singular: bgpconfiguration
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: ippools.crd.projectcalico.org
spec:
scope: Cluster
group: crd.projectcalico.org
version: v1
names:
kind: IPPool
plural: ippools
singular: ippool
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: hostendpoints.crd.projectcalico.org
spec:
scope: Cluster
group: crd.projectcalico.org
version: v1
names:
kind: HostEndpoint
plural: hostendpoints
singular: hostendpoint
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: clusterinformations.crd.projectcalico.org
spec:
scope: Cluster
group: crd.projectcalico.org
version: v1
names:
kind: ClusterInformation
plural: clusterinformations
singular: clusterinformation
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: globalnetworkpolicies.crd.projectcalico.org
spec:
scope: Cluster
group: crd.projectcalico.org
version: v1
names:
kind: GlobalNetworkPolicy
plural: globalnetworkpolicies
singular: globalnetworkpolicy
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: globalnetworksets.crd.projectcalico.org
spec:
scope: Cluster
group: crd.projectcalico.org
version: v1
names:
kind: GlobalNetworkSet
plural: globalnetworksets
singular: globalnetworkset
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: networkpolicies.crd.projectcalico.org
spec:
scope: Namespaced
group: crd.projectcalico.org
version: v1
names:
kind: NetworkPolicy
plural: networkpolicies
singular: networkpolicy
三、配置网络策略
Kubernetes 系统中,报文流入和流出的核心组件是 Pod 资源,因此它们也是网络策略功能生效的主要目标,因此,NetworkPolicy 对象也要使用标签选择器事先选择出一组 Pod 资源作为控制对象。
一般来说,NetworkPolicy 是定义在一组 Pod 资源上的用于管控入栈流量的 "Ingress规则",或者说是管理出站流量的 "Egress规则",也可以是两者组合定义,而仅部分生效还是全部生效则需要由 spec.policyTypes 予以定义。
默认情况下,Pod 对象既可以接受来自任何来源的流量,也能够向外部发出期望的的所有流量。而附加网络策略机制后,Pod 对象会因为 NetworkPolicy 对象的选定而被隔离。一旦名称空间中有任何 NetworkPolicy 对象匹配了某特定的 Pod 对象,则该 Pod 将拒绝 NetworkPolicy 所不允许的一切连接请求,而那些未被任何 NetworkPolicy 对象匹配到的其他 Pod 对象仍可接受所有流量。
因此,就特定的 Pod 集合来说,入站 和 出站流量默认均处于放行状态,除非有规则能够明确匹配到它。一旦在 spec.policyTypes 中指定了生效的规则类型,却在 networkpolicy.spec 字段中嵌套定义了没有任何规则的 Ingress 或 Egress 字段时,则表示拒绝相关方向上的一切流量。
定义网络策略时常用到的术语及说明具体如下:
-
- Pod 组:由网络策略通过 Pod 选择器选定的一组 Pod 的集合,它们是规则生效的目标 Pod;可由 NetworkPolicy 对象通过 macthLabel 或 matchExpression 选定。
- Ingress:入站流量,即由其他网络端点发往特定 Pod 组的流量,通常由流量发出的源站点(from)和流量的目标端口所定义。
- Egress:出站流量,即由特定的 Pod 组发往其他网络端点的流量,通常由流量的目标网络端点(to)和端口(ports)来进行定义。
- 端点(to,from):流量目标和流量源相关的组件,它可以是 CIDR 格式的IP地址块(ipBlock)、网络名称空间选择器(namespaceSelector)匹配的名称空间,或 Pod 选择器(podSelector)匹配的 Pod 组。
- 端口(ports):TCP 或 UDP 的端口号。
无论是 Ingress 还是 Egress 流量,与选定的某 Pod 组通信的另一方都可使用“网络端点”予以描述,它通常是某名称空间中的一个或一组 Pod 资源,由 namespaceSelector 选定名称空间后,经由 ipBlock 或 podSelector 进行指定。
在 Ingress 规则中,网络端点也称为“源端点”,它们用 from 字段进行标识,而在 Egress 规则中,网络端点也称为“目标端点”,它们用 to 字段进行标识。不过,在未定义 Ingress 或 Egress 规则时,相关方向的流量均为“允许”,即默认为非隔离状态。而一旦在 networkpolicy.spec 中明确给出了 Ingress 或 Egress 字段,则它们的 from 或 to 字段的值就成了白名单列表,而空值意味着所有端点,即不限制访问。
四、管控入站流量
以提供服务问主要目的的 Pod 对象通常是请求流量的目标对象,但它们的服务未必应该为所有网络端点所访问,这就有必要对它们的访问许可施加控制。
networkpolicy.spec 中嵌套的 Ingress 字段用于定义入站流量规则,就特定的 Pod 集合来说,入站流量默认处于放行装填,除非在所有入站策略中,至少有一条规则能够明确匹配到它。Ingress 字段的值是一个对象列表,它主要由以下两个字段组成:
- from<[]Object>:可访问当前策略匹配到的Pod对象的源地址对象列表,多个项目之间的逻辑关系为“逻辑或”的关系;若未设置此字段或其值为空,则匹配一切源地址(默认的访问策略为不限制);如果此字段至少有一个值,那么它将成为放行的源地址白名单,仅来源于此地址列表中的流量允许通过。
- ports<[]Object>:当前策略匹配到的Pod集合的可被访问的端口对象列表,多个项目之间的逻辑关系为“逻辑或”的关系;若未设置此字段或其值为空,则匹配Pod集合上的所有端口(默认的访问策略为不限制);如果此字段至少有一个值,那么它将成为允许被访问的Pod端口白名单列表,仅入站流量的目标端口处于此列表中方才准许通过。
需要注意的是,NetworkPolicy 资源属于名称空间级别,它的有效作用范围为其所属的名称空间。
1)设置默认的 Ingress 策略
必要时,用户可以创建一个 NetworkPolicy 来为名称空间设置一个 "默认" 的隔离策略,该策略选择所有的 Pod 对象,而后允许或拒绝任何到达这些 Pod 的入站流量。
例如下面的策略示例,其通过 policyTypes 字段指明要生效 Ingress 类型的规则,但未定义任何 Ingress 字段,因此不能匹配到任一源端点,从而拒绝所有入站流量:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress # 没有设置命令空间则默认命令空间为default
spec:
podSelector: {} # 空的 podSelector 表示选择命名空间下的所有pod
policyTypes: ["Ingress"] # 入口网络策略
# 未设置进入条件,也就是不允许任何进入
若要将默认策略设置为允许所有的入站流量,则只需要显式定义 Ingress 字段,并将其值设置为空以匹配所有源端点即可。如下面示例中的定义,没有为入站流量定义任何规则时,本身的默认规则即为允许访问,因此允许所有相关的入站流量时,本身无须定义默认规则,下面的示例只是为说明规则的定义格式:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-all-ingress spec: podSelector: {} policyTypes: ["Ingress"] ingress: - {}
实践中,通常将默认策略设置为拒绝所有的入站流量,而后显式放行允许的源端点的入站流量。
2)放行特定的入站流量
在 Ingress 规则中嵌套 from 和 ports 字段即可匹配特定的入站流量,仅定义 from 字段时将隐含本地 Pod 资源组的所有端口,而仅定义 ports 字段时则表示隐含所有的源端点。from 和 ports 同时定义时表示隐含 "逻辑与" 关系,它将匹配到那些同时满足 from 和 ports 的定义的入站流量,即那些来自 from 指定的源端点,访问由当前 NetworkPolicy 的 podSelector 匹配的当前名称空间的 Pod 资源组上所指定的 ports 请求,如下图所示:
from 字段的值是一个对象列表,它可嵌套使用 ipBlock、namespaceSelector 和 podSelector 字段来定义流量来源,此三个字段匹配 Pod 资源的方式各有不同,同时使用两个或以上的字段,彼此之间隐含 "逻辑或" 关系:
-
- ipBlock <Object>: 根据 IP 地址或网络地址选择流量源端点。
- namespaceSelector <Object>: 基于集群级别的标签挑选名称空间,它将匹配由此标签选择器选出的所有名称空间内的所有 Pod 对象;赋予字段以空值来表示挑选所有的名称空间,即源点为所有名称空间内的所有 Pod 对象。
- podSelector <Object>: 于 NetworkPolicy 所在的当前名称空间内基于标签选择器挑选 Pod 资源,赋予字段以空值来表示挑选当前名称空间内的所有 Pod 对象。
- ports: 字段的值也是一个对象列表,它嵌套 port 和 protocol 来定义流量的目标端口,即由 NetworkPolicy 匹配到的当前名称空间内的所有 Pod 资源上的端口。
- port:端口号或在 Container 上定义的端口名称,未定义时匹配所有端口。
- protocol: 传输层协议的名称,TCP 或 UDP,默认为 TCP。
下面配置清单中的网络策略示例定义了如何开放 myapp pod 资源给相应的源站点访问:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-myapp-ingress
namespace: default
spec:
podSelector:
matchLabels:
app: myapp
policyTypes: ["Ingress"]
ingress:
- from:
- ipBlock:
cidr: 10.244.0.0/16
except:
- 10.244.3.0/24
- podSelector:
matchLabels:
app: myapp
ports:
- protocol: TCP
port: 80
它将 default 名称空间拥有标签 "app=myapp" 的 Pod 资源的 80/TCP 端口开放给 10.244.0.0/16 网络内除 10.244.3.0/24 子网中的所有源端点,以及当前名称空间中拥有标签 "app=myapp" 的所有 Pod 资源访问,其他未匹配到的源端点的流量则取决于其他网络策略的定义,若没有任何匹配策略,则默认允许访问。
五、管控出站流量
通常应该将出站流量的默认策略设置为准许通过。但如果有必要对其实施精细管理,仅放行那些有对外请求需要的 Pod 对象的出站流量,则也可先为名称空间设置“禁止所有”默认策略,而后定义明确的“准许”策略。
networkpolicy.spec 中嵌套的 Egress 字段用于定义入站流量规则,就特定的 Pod 集合来说,出站流量一样默认处于放行状态,除非在所有入站策略中至少有一条规则能够明确匹配到它。
Egress字段的值是一个字段列表,它主要由以下两个字段组成。
-
- to<[]Object> :由当前策略匹配到的 Pod 资源发起的出站流量的目标地址列表,多个项目之间为“或”(OR)关系;若未定义或字段值为空则意味着应用于所有目标地址(默认为不限制);若明确给出了主机地址列表,则只有目标地址匹配列表中的主机地址的出站流量被放行。
- ports<[]Object> :出站流量的目标端口列表,多个端口之间为“或”(OR)关系;若未定义或字段值为空则意味着应用于所有端口(默认为不限制);若明确给出了端口列表,则只有目标端口匹配列表中的端口的出站流量被放行。
Egress规则中,to 和 ports 字段的值都是对象列表格式,它们可内嵌的字段分别与 Ingress 规则中的 from 和 ports 相同,区别仅是作用到的流量方向相反。
1)设置默认 Egress 策略
通过创建一个 NetworkPolicy 对象来为名称空间设置一个默认的隔离策略,该策略选择所有的 Pod 对象,而后允许或拒绝由这些 Pod 发出的所有出站流量。
通过 policyTypes 字段指明要生效 Egress 类型的规则,但未定义任何 Egress 字段,因此不能匹配到任何目标端点,从而拒绝所有的出站流量:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-egress
spec:
podSelector: {} # 该命名空间下的所有pod
policyTypes: ["Egress"] # 出口网络策略
# 未设置出口条件,也就是不允许任何出口流量
2.放行特定的出站流量
示例中定义了一个Egress规则,它对来自拥有“app=tomcat”的Pod对象的,到达标签为“app=nginx”的Pod对象的80端口,以及到达标签为“app=mysql”的Pod对象的3306端口的流量给予放行:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-tomcat-egress
namespace: default
spec:
podSelector:
matchLabels:
app: tomcat
policyTypes: ["Egress"]
egress:
- to:
- podSelector:
matchLabels:
app: nginx
ports:
- protocol: TCP
port: 80
- to:
- podSelector:
matchLabels:
app: mysql
ports:
- protocol: TCP
port: 3306
需要注意的,此配置清单中仅定义了出站规则,将入站流量的默认规则设置为拒绝所有时,还应该为具有标签 "app=tomcat" 的 Pod 对象放行入站流量。
六、隔离名称空间
彼此隔离所有的名称空间,但应该允许它们都能够与 kube-system 名称空间中的 Pod 资源进行流量交换,以实现监控和名称解析等各种管理功能。
下面示例为 default 名称空间定义了相关的规则,在出站和入站流量默认均为拒绝的情况下,它用于放行名称空间内部的各 Pod 对象之间的通信,以及与 kube-system 名称空间内各 Pod 间的通信:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: namespace-deny-all
namespace: default
spec:
policyTypes: ["Ingress","Egress"]
podSelector: {}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: namespace-deny-all
namespace: default
spec:
policyTypes: ["Ingress","Egress"]
podSelector: {}
ingress:
- from:
- namespaceSelector:
matchExpressions:
- key: name
operator: In
values: ["default","kube-system"]
egress:
- to:
- namespaceSelector:
matchExpressions:
- key: name
operator: In
values: ["default","kube-system"]
需要注意的是,有些管理员可能会把一些系统附件部署到专有的名称空间中,例如把 Prometheus 监控系统部署到 prom 名称空间中等,所有这类的具有管理功能的附件所在的名称空间与每一个特定名称空间的出入流量都应该被放行。
七、网络策略应用案例
假设有名为testing的名称空间内运行着一组nginx Pod和一组myapp Pod。要求实现如下目标。
1)myapp Pod仅允许来自nginx Pod的流量访问其80/TCP端口,但可以向nginx Pod的所有端口发出出站流量。
2)nginx Pod允许任何源端点对其80/TCP端口的访问,并能够向任意端点发出出站流量。
3)myapp Pod和nginx Pod都可与kube-system名称空间的任意Pod进行任何类型的通信,以便于可以使用由kube-dns提供的名称解析服务等
如下图所示,出站和⼊站的默认策略是均为“禁⽌”。
下面是测试实现步骤:
第一步:
- 创建 testing 名称空间,并于其内基于 Deployment 控制器创建用于测试用的 nginx Pod 和 myapp Pod 各一个,创建相关Pod资源时顺便为其创建与 Deployment 控制器同名的 Service 资源:
kubectl create namespace testing kubectl run nginx --image=nginx:alpine --replicas=1 --namespace=testing --port 80 --expose --labels app=nginx kubectl run myapp --image=ikubernetes/myapp:v1 --replicas=1 --namespace=testing --port 80 --expose --labels app=myapp
-
为了便于在网络策略规则中引⽤ kube-system 名称空间,这⾥为其添加标签 “ns=kube-system”:
kubectl label namespace kube-system ns=kube-system
-
待相关资源创建完成后,即可通过与其相关的Service资源的名称访问相关的服务。例如,另外启动一个终端,使用 kubectl 命令在 default 名称空间中创建一个用于测试的临时交互式客户端:
kubectl run cirros-$RANDOM --namespace=default --rm -it --image=cirros --sh
- 而后基于此客户端分别测试访问 nginx 和 myapp 的服务,名称空间默认网络策略为准允许,接下来确认其访问请求可正常通过即可。
curl nginx.testing curl myapp.testing
- 创建 testing 名称空间,并于其内基于 Deployment 控制器创建用于测试用的 nginx Pod 和 myapp Pod 各一个,创建相关Pod资源时顺便为其创建与 Deployment 控制器同名的 Service 资源:
第二步:
- 定义网络策略清单文件(testing-netpol-denally.yaml),将testing名称空间的入站及出站的默认策略修改为拒绝访问,并再一次进行访问测试:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all-traffic namespace: testing spec: podSelector: {} policyTypes: - Ingress - Egress
- 接下来,将 testing-netpol-denyall.yaml 定义的默认策略 deny-all-traffic 予以应用,为 testing 名称空间设置默认的网络策略:
kubectl apply -f testing-netpol-denyall.yaml
- 回到第一步创建的交互式客户端,再次对 nginx 和 myapp 发起访问测试。为了避免长时间等待,这里为 curl 命令添加 --connect-timeout 选项为其定义连接超时时长。由下面的命令可知,此时无法再访问到 nginx 和 myapp 的相关服务:
curl --connect-timeout 2 nginx.testing curl --connect-timeout 2 myapp.testing
- 定义网络策略清单文件(testing-netpol-denally.yaml),将testing名称空间的入站及出站的默认策略修改为拒绝访问,并再一次进行访问测试:
第三步:
- 定义流量放行规则配置清单 nginx-allow-all.yaml,放行 nginx Pod 之上 80/TCP 端口的所有流量:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: nginx-allow-all namespace: testing spec: podSelector: matchLabels: app: nginx ingress: - ports: - port: 80 - from: - namespaceSelector: matchLabels: ns: kube-system egress: - to: policyTypes: - Ingress - Egress
- 将上述清单文件中定义的网络策略应用至集群中以创建相应的网络策略:
kubectl apply -f nginx-allow-all.yaml
- 而后再次回到交互式测试客户端发起访问请求进行测试,由下面的命令结果可知,nginx 已经能够正常访问,这同时也意味着由 kube-system 名称空间中的 kube-dns 进行的名称解析服务也为可用状态:
curl --connect-timeout 2 nginx.testing …… <title> Welcome to nginx! <title> ……
- 定义流量放行规则配置清单 nginx-allow-all.yaml,放行 nginx Pod 之上 80/TCP 端口的所有流量:
第四步:
- 定义网络策略配置清单 myapp-allow.yaml,放行 testing 名称空间中来自 nginx Pod 的发往 myapp Pod 的 80/TCP 的访问流量,以及 myapp Pod 发往 nginx Pod 的所有流量。另外,允许 myapp Pod 与 kube-system 名称空间的任何Pod进行交互的所有流量:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: myapp-allow namespace: testing spec: podSelector: matchLabels: app: myapp ingress: - from: - podSelector: matchLabels: app: nginx ports: - port: 80 - from: - namespaceSelector: matchLabels: ns: kube-system egress: - to: - podSelector: matchLabels: app: nginx - to: - namespaceSelector: matchLabels: ns: kube-system policyTypes: - Ingress - Egress
- 接下来首先创建清单中定义的网络策略:
kubectl apply -f myapp-allow.yaml
- 然后切换至此前在专用终端中创建的临时使用的交互式 Pod,对 myapp Pod 发起访问请求。由下面的命令结果可知,其访问被拒绝:
curl --connect-timeout 2 http://myapp.testing
-
myapp Pod 仅允许来自 nginx Pod 对其 80/TCP 端口的访问,于是,这里进入 testing 名称空间中的是 nginx Pod 的交互式接口,使用 wget 命令对 myapp Pod 发起访问请求:
~]$ kubectl exec -it nginx-b477df957-jb2nf -n testing -- /bin/sh / # wget http://myapp.testing -O - -q Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a> / #
- 定义网络策略配置清单 myapp-allow.yaml,放行 testing 名称空间中来自 nginx Pod 的发往 myapp Pod 的 80/TCP 的访问流量,以及 myapp Pod 发往 nginx Pod 的所有流量。另外,允许 myapp Pod 与 kube-system 名称空间的任何Pod进行交互的所有流量: