本文的目的是向读者介绍 Kubernetes 中的网络和管理网络策略的基础知识,以及扩展标准功能的第三方 Calico 插件。 在此过程中,我们将使用我们操作经验中的真实示例来演示配置的简便性和一些功能。
Kubernetes 网络设备快速介绍
无法想象没有网络的 Kubernetes 集群。 我们已经发布了有关其基础知识的材料:“
在本文中,需要注意的是,K8s 本身不负责容器和节点之间的网络连接:为此,各种 CNI 插件 (容器网络接口)。 关于这个概念的更多信息,我们
例如,这些插件中最常见的是
并提供“开箱即用”的方式在 Kubernetes 集群中组织网络策略管理
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
这不是最原始的例子
从逻辑上讲,有两种类型的流量:进入 Pod 的流量(Ingress)和从 Pod 流出的流量(Egress)。
实际上,政治根据运动方向分为这两类。
下一个必需的属性是选择器; 规则适用的人。 这可以是一个 Pod(或一组 Pod)或一个环境(即命名空间)。 一个重要的细节:这两种类型的对象都必须包含一个标签(标签 在 Kubernetes 术语中)——这些是政客们所使用的。
除了由某种标签联合起来的有限数量的选择器之外,还可以以不同的变体编写“允许/拒绝一切/所有人”之类的规则。 为此,使用以下形式的结构:
podSelector: {}
ingress: []
policyTypes:
- Ingress
— 在此示例中,环境中的所有 Pod 都被阻止接收传入流量。 通过以下结构可以实现相反的行为:
podSelector: {}
ingress:
- {}
policyTypes:
- Ingress
同样对于传出:
podSelector: {}
policyTypes:
- Egress
- 将其关闭。 以下是要包括的内容:
podSelector: {}
egress:
- {}
policyTypes:
- Egress
回到集群的 CNI 插件的选择,值得注意的是 并非每个网络插件都支持 NetworkPolicy。 例如已经提到的Flannel不知道如何配置网络策略,这
了解 Calico:理论
Calico 插件可以与 Flannel 集成使用(子项目
使用 K8s“盒装”解决方案和 Calico 的 API 集提供了哪些机会?
以下是 NetworkPolicy 的内置内容:
- 政治家受到环境的限制;
- 策略应用于标有标签的 Pod;
- 规则可以应用于 Pod、环境或子网;
- 规则可以包含协议、命名或符号端口规范。
以下是 Calico 如何扩展这些功能:
- 策略可以应用于任何对象:pod、容器、虚拟机或接口;
- 规则可以包含特定的操作(禁止、许可、记录);
- 规则的目标或源可以是端口、一系列端口、协议、HTTP 或 ICMP 属性、IP 或子网(第四代或第六代)、任何选择器(节点、主机、环境);
- 此外,您可以使用 DNAT 设置和流量转发策略来调节流量的通过。
Calico 存储库中 GitHub 上的首次提交可以追溯到 2016 年 XNUMX 月,一年后该项目在组织 Kubernetes 网络连接方面占据了领先地位 - 例如,调查结果就证明了这一点:
许多使用 K8s 的大型托管解决方案,例如
至于性能,这里一切都很棒。 在测试他们的产品时,Calico 开发团队展示了天文数字般的性能,在 50000 个物理节点上运行了 500 多个容器,每秒创建 20 个容器。 没有发现缩放问题。 这样的结果
该项目发展非常迅速,它支持在流行的解决方案管理的K8s、OpenShift、OpenStack中工作,在部署集群时可以使用Calico
与 Calico 一起练习
在使用普通 Kubernetes 的一般情况下,安装 CNI 归结为使用该文件 calico.yaml
, kubectl apply -f
.
通常,当前版本的插件与 Kubernetes 的最新 2-3 个版本兼容:旧版本中的操作未经测试且无法保证。 据开发人员称,Calico 运行在 3.10 以上的 Linux 内核上,运行 CentOS 7、Ubuntu 16 或 Debian 8,基于 iptables 或 IPVS。
环境内隔离
为了进行一般性理解,让我们看一个简单的案例,以了解 Calico 表示法中的网络策略与标准策略有何不同,以及创建规则的方法如何简化其可读性和配置灵活性:
集群中部署了 2 个 Web 应用程序:Node.js 和 PHP,其中之一使用 Redis。 要阻止从 PHP 访问 Redis,同时保持与 Node.js 的连接,只需应用以下策略:
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: allow-redis-nodejs
spec:
podSelector:
matchLabels:
service: redis
ingress:
- from:
- podSelector:
matchLabels:
service: nodejs
ports:
- protocol: TCP
port: 6379
本质上,我们允许从 Node.js 传入 Redis 端口的流量。 他们显然没有禁止任何其他事情。 NetworkPolicy一出现,其中提到的所有选择器就开始被隔离,除非另有说明。 但是,隔离规则不适用于选择器未涵盖的其他对象。
该示例使用 apiVersion
Kubernetes 开箱即用,但没有什么可以阻止您使用它
apiVersion: crd.projectcalico.org/v1
kind: NetworkPolicy
metadata:
name: allow-redis-nodejs
spec:
selector: service == 'redis'
ingress:
- action: Allow
protocol: TCP
source:
selector: service == 'nodejs'
destination:
ports:
- 6379
上述用于允许或拒绝通过常规 NetworkPolicy API 的所有流量的结构包含带有括号的结构,这些结构难以理解和记忆。 对于 Calico,要将防火墙规则的逻辑更改为相反的逻辑,只需更改 action: Allow
上 action: Deny
.
环境隔离
现在想象这样一种情况:应用程序生成业务指标,以便在 Prometheus 中收集并使用 Grafana 进行进一步分析。 上传的内容可能包含敏感数据,默认情况下这些数据也是公开可见的。 让我们隐藏这些数据以免被窥探:
通常,Prometheus 放置在单独的服务环境中 - 在示例中它将是这样的命名空间:
apiVersion: v1
kind: Namespace
metadata:
labels:
module: prometheus
name: kube-prometheus
领域 metadata.labels
事实证明这并非偶然。 正如刚才提到的, namespaceSelector
(也 podSelector
)与标签一起操作。 因此,要允许从特定端口上的所有 Pod 获取指标,您必须添加某种标签(或从现有标签中获取),然后应用如下配置:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-metrics-prom
spec:
podSelector: {}
ingress:
- from:
- namespaceSelector:
matchLabels:
module: prometheus
ports:
- protocol: TCP
port: 9100
如果您使用 Calico 策略,语法将如下所示:
apiVersion: crd.projectcalico.org/v1
kind: NetworkPolicy
metadata:
name: allow-metrics-prom
spec:
ingress:
- action: Allow
protocol: TCP
source:
namespaceSelector: module == 'prometheus'
destination:
ports:
- 9100
一般来说,通过针对特定需求添加此类策略,可以防止集群中应用程序的运行受到恶意或意外干扰。
根据 Calico 的创建者的说法,最佳实践是“阻止所有内容并显式打开您需要的内容”方法,记录在
使用额外的 Calico 对象
让我提醒您,通过 Calico API 的扩展集,您可以调节节点的可用性,而不仅限于 pod。 在下面的示例中使用 GlobalNetworkPolicy
在集群中传递 ICMP 请求的功能已关闭(例如,从 pod 到节点、pod 之间或从节点到 IP pod 的 ping):
apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
name: block-icmp
spec:
order: 200
selector: all()
types:
- Ingress
- Egress
ingress:
- action: Deny
protocol: ICMP
egress:
- action: Deny
protocol: ICMP
在上述情况下,集群节点仍然可以通过 ICMP 相互“联系”。 这个问题是通过以下方式解决的 GlobalNetworkPolicy
,应用于实体 HostEndpoint
:
apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
name: deny-icmp-kube-02
spec:
selector: "role == 'k8s-node'"
order: 0
ingress:
- action: Allow
protocol: ICMP
egress:
- action: Allow
protocol: ICMP
---
apiVersion: crd.projectcalico.org/v1
kind: HostEndpoint
metadata:
name: kube-02-eth0
labels:
role: k8s-node
spec:
interfaceName: eth0
node: kube-02
expectedIPs: ["192.168.2.2"]
VPN 案例
最后,我将给出一个非常真实的示例,当一组标准策略还不够时,使用 Calico 函数来处理近集群交互的情况。 要访问 Web 应用程序,客户端使用 VPN 隧道,并且此访问受到严格控制并仅限于允许使用的特定服务列表:
客户端通过标准 UDP 端口 1194 连接到 VPN,并在连接后接收到 Pod 和服务的集群子网的路由。 推送整个子网,以免在重新启动和地址更改期间丢失服务。
配置中的端口是标准的,这对配置应用程序并将其传输到 Kubernetes 集群的过程带来了一些细微差别。 例如,去年年底,AWS LoadBalancer for UDP 确实出现在有限的区域列表中,而 NodePort 由于在所有集群节点上转发而无法使用,并且无法扩展服务器实例的数量容错的目的。 另外,您必须更改端口的默认范围......
经过搜索可能的解决方案,选择以下方案:
- 具有 VPN 的 Pod 按节点调度
hostNetwork
,即为实际IP。 - 该服务通过外部发布
ClusterIP
。 端口物理安装在节点上,可以从外部访问,但需要进行少量保留(有条件地存在真实 IP 地址)。 - 确定豆荚升起的节点超出了我们故事的范围。 我只想说,你可以将服务紧紧地“钉”在一个节点上,或者编写一个小型 sidecar 服务来监视 VPN 服务的当前 IP 地址并编辑向客户端注册的 DNS 记录 - 无论谁有足够的想象力。
从路由的角度来看,我们可以通过VPN服务器发布的IP地址来唯一标识VPN客户端。 下面是限制此类客户端访问服务的原始示例,在上述 Redis 上进行了说明:
apiVersion: crd.projectcalico.org/v1
kind: HostEndpoint
metadata:
name: vpnclient-eth0
labels:
role: vpnclient
environment: production
spec:
interfaceName: "*"
node: kube-02
expectedIPs: ["172.176.176.2"]
---
apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
name: vpn-rules
spec:
selector: "role == 'vpnclient'"
order: 0
applyOnForward: true
preDNAT: true
ingress:
- action: Deny
protocol: TCP
destination:
ports: [6379]
- action: Allow
protocol: UDP
destination:
ports: [53, 67]
在这里,严格禁止连接到端口 6379,但同时保留 DNS 服务的操作,在制定规则时,该服务的功能经常受到影响。 因为,如前所述,当选择器出现时,除非另有指定,否则默认拒绝策略将应用于它。
结果
因此,使用 Calico 的高级 API,您可以灵活配置和动态更改集群内部和周围的路由。 一般来说,它的使用看起来就像用大炮射麻雀,并且在平坦网络上的简单 Kubernetes 安装中实现具有 BGP 和 IP-IP 隧道的 L3 网络看起来很可怕......但是,除此之外,该工具看起来非常可行和有用。
隔离集群来满足安全要求可能并不总是可行,这就是 Calico(或类似的解决方案)可以解决的问题。 本文中给出的示例(稍作修改)已用于我们在 AWS 中的多个客户端安装。
PS
另请阅读我们的博客:
- «
面向安全专业人员的 Kubernetes 网络策略简介 “; - “Kubernetes 网络图解指南”:
第 1 部分和第 2 部分(网络模型、覆盖网络) ,第 3 部分(服务和流量处理) ; - «
容器网络接口 (CNI) - Linux 容器的网络接口和标准 “。
来源: habr.com