在 Azure CNI 中启用 Calico WireGuard
作者:Peter Kelly
译者:Wendi Wang注:本文已取得作者本人的翻译授权!
去年6月,Tigera 宣布首次在 K8s 上支持用于集群内加密传输的开源 VPN - WireGuard 。我们从来不喜欢坐以待毙,所以我们一直在努力为这项技术开发一些令人兴奋的新功能,其中第一个功能是使用 Azure 容器网络接口(CNI)在 Azure Kubernetes 服务(AKS)上支持WireGuard。
首先,这里简单回顾一下什么是 WireGuard 以及我们是如何在 Calico 中使用它的。
WireGuard 是一种 VPN 技术,从 linux 5.6 内核开始默认包含在内核中,它被定位为 IPsec 和 OpenVPN 的替代品。它的目标是更加快速、安全、易于部署和管理。正如不断涌现的 SSL/TLS 的漏洞显示,密码的敏捷性会极大增加复杂性,这与 WireGuard 的目标不符,为此,WireGuard 故意将密码和算法的配置灵活性降低,以减少该技术的可攻击面和可审计性。它的目标是更加简单快速,所以使用标准的 Linux 网络命令便可以很容易的对它进行配置,并且只有约 4000 行代码,使得它的代码可读性高,容易理解、审查。
WireGuard 是一种 VPN 技术,通常被认为是 C/S 架构。它同样能在对等的网格网络架构中配置使用,这就是 Tigera 设计的可以在 Kubernetes 中使用的 WireGuard 解决方案。使用 Calico,将所有启用 WireGuard 的节点相互对等形成一个加密的网格。它甚至支持在同一集群内同时包含启用 WireGuard 的节点与未启用 WireGuard 的节点,并且可以相互通信。

我们选择 WireGuard 并不是一个折中的方案。我们希望提供最简单、最安全、最快速的方式来加密传输 Kubernetes 集群中的数据,mTLS、IPsec 或复杂的配置不是我们想要的。事实上,您可以把 WireGuard 看成是另一个具有加密功能的 overlay。
用户只需一条命令就可以启用 WireGuard,而 Calico 负责完成剩余的工作,包括:
- 在每个节点创建 WireGuard 的网络接口
- 计算编写最优的 MTU
- 为每个节点创建 WireGuard 公钥私钥对
- 向每个节点添加公钥,以便在集群中共享资源
- 将所有节点编辑为对等节点
- 使用防火墙标记(fwmark)编辑 IP route、IP tables和 Routing tables,以此正确处理各自节点上的路由
您仅需指明意图,其他的事情都由集群完成。
使用 WireGuard 时数据包流量的情况
下图显示了启用 WireGuard 后集群中的各种数据包流量情况。

同一主机上的 Pod:
- 数据包被路由到 WireGuard 表。
- 如果目标 IP 是同一主机上的 Pod,则 Calico 将在 WireGuard 路由表中插入一个 “ throw ” 条目,将数据包引导回主路由表。数据包被定向到目标 Pod 的 veth 接口,并且它将在未加密的情况下流动(在图中以绿色显示)。
不同节点上的 Pod:
- 数据包被路由到 WireGuard 表。
- 路由条目与目标 Pod IP 匹配并发送到 WireGuard 组件: cali.wireguard。
- WireGuard 组件加密并封装数据包(在图中以红色显示)并设置 fwmark 以防止路由环路。
- WireGuard 组件使用它与目标 Pod IP(允许的 IP)匹配的对等方的公钥对数据包进行加密,将其封装在 UDP 中,并使用特殊的 fwmark 对其进行标记以防止路由环路。
- 数据包通过 eth0 发送到目标节点并解密。
- 这也适用于主机流量(例如,节点联网的 Pod)。
在以下动画中,您可以看到 3 种流量:
- 同一主机上 Pod 到 Pod 未被加密的流量。
- 不同主机上的 Pod 到 Pod 被加密的流量。
- 主机到主机的流量也会被加密。
注意:绿色表示未加密流量,红色表示加密流量。
WireGuard 在 AKS 中的应用
在 AKS 上使用 Azure CNI 对 WireGuard 的支持带来了一些非常有趣的挑战。
首先,使用 Azure CNI 意味着不使用 Calico IPAM( IP 地址管理)管理 CIDR(无类域间路由)块分配的 Pod IP 。相反,它们是采用节点IP相同的分配方式从底层 VNet 分配的。这对 WireGuard 路由来说是一个有趣的挑战,以往我们可以在 WireGuard 配置中的 Allowed IPs 列表中添加一个 CIDR 块,相比之下,我们现在必须写出该节点所有 Pod IP。这需要 Calico 将 routeSource 的配置设为 workloadIPs。如果您使用的是 AKS 集群进行部署,便无需额外配置。
使用 wireguard-tools 中优秀的工具 wg,可以查看集群内节点允许通过的 IP 列表,其中包括每个节点的 Pod IP 和主机 IP(注意终端 IP也在允许 IP 列表中)。在 AKS 上提供了业务流量加密和主机到主机的加密。
interface: wireguard.cali
public key: bbcKpAY+Q9VpmIRLT+yPaaOALxqnonxBuk5LRlvKClA=
private key: (hidden)
listening port: 51820
fwmark: 0x100000
peer: /r0PzTX6F0ZrW9ExPQE8zou2rh1vb20IU6SrXMiKImw=
endpoint: 10.240.0.64:51820
allowed ips: 10.240.0.64/32, 10.240.0.65/32, 10.240.0.66/32
latest handshake: 11 seconds ago
transfer: 1.17 MiB received, 3.04 MiB sent
peer: QfUXYghyJWDcy+xLW0o+xJVsQhurVNdqtbstTsdOp20=
endpoint: 10.240.0.4:51820
allowed ips: 10.240.0.4/32, 10.240.0.5/32, 10.240.0.6/32
latest handshake: 46 seconds ago
transfer: 83.48 KiB received, 365.77 KiB sent
第二个挑战是正确处理 MTU(最大传输单元)。Azure 设置的 MTU 是 1500,而 WireGuard 在数据包上设置了一个 DF(Don't Fragment)标记。如果没有正确调整 WireGuard MTU,我们会在启用 WireGuard 时发现有丢包和低带宽。我们可以在 AKS 中通过Calico 自动检测并为 WireGuard 的 MTU 设置正确的开销来优化。
我们还可以将节点 IP 本身添加为对等节点允许通信的 IP ,并通过 AKS 中的 WireGuard 处理主机联网的 Pod 和主机到主机通信。主机到主机通信的方法是,当 RPF(反向路径转发)发生时,通过 WireGuard 接口获得路由返回的响应。通过在发送到目的节点的数据包上设置一个标记,然后配置内核以尊守 sysctl 中的 RPF 标记来解决这个问题。
现在您使用AKS时,节点之间的业务流量和主机到主机通信都会被加密。您仅需指明意图,其他的事情都由集群完成。
本文由博客一文多发平台 OpenWrite 发布!
在 Azure CNI 中启用 Calico WireGuard的更多相关文章
- 【Azure Application Insights】在Azure Function中启用Application Insights后,如何配置不输出某些日志到AI 的Trace中
问题描述 基于.NET Core的Function App如果配置了Application Insights之后,每有一个函数被执行,则在Application Insights中的Logs中的tra ...
- 使用Azure Functions 在web 应用中启用自动更新(一)分析基于轮询的 Web 应用的限制
1,引言 上一篇介绍了使用使用 Visual Studio 开发 "Azure Functions" 函数,此篇介绍 “Azure Functions” 的测试以及直接从 Vist ...
- 运行预构建 Linux 映像的 Windows Azure 虚拟机中的交换空间 – 第 1 部分
本文章由 Azure CAT 团队的 Piyush Ranjan (MSFT) 撰写. 随着基础结构服务(虚拟机和虚拟网络)近期在 Windows Azure 上正式发布,越来越多的企业工作负荷正在向 ...
- 探索 Windows Azure 网站中的自动伸缩功能
去年10月,我们发布了若干针对 WindowsAzure平台的更新,其中一项更新是添加了基于日期的自动伸缩调度支持(在不同的日期设置不同的规则). 在这篇博客文章中,我们将了解自动伸缩的概念,并 ...
- 在 Windows Azure 网站中配置动态 IP 地址限制
我们最近对 Windows Azure 网站进行了升级,并启用了IIS8的动态 IP 限制模块.现在,开发人员可以为其网站启用并配置动态 IP 限制功能(或简称 DIPR). 可以通过以下链接查看此 ...
- 如何将Azure DevOps中的代码发布到Azure App Service中
标题:如何将Azure DevOps中的代码发布到Azure App Service中 作者:Lamond Lu 背景 最近做了几个项目一直在用Azure DevOps和Azure App Servi ...
- Azure 应用服务中的 API 应用、ASP.NET 和 Swagger 入门
学习内容: 如何通过 Visual Studio 2015 中的内置工具在 Azure 应用服务中创建和部署 API 应用. 如何使用 Swashbuckle NuGet 包动态生成 Swagger ...
- 在 Azure 虚拟机中配置 Always On 可用性组(经典)
在开始之前,请先假设现在可以在 Azure Resource Manager 模型中完成此任务. 我们建议使用 Azure Resource Manager 模型来进行新的部署. 请参阅 Azure ...
- 在 ASP.NET Core 中启用跨域请求(CORS)
本文介绍如何在 ASP.NET Core 的应用程序中启用 CORS. 浏览器安全可以防止网页向其他域发送请求,而不是为网页提供服务. 此限制称为相同源策略. 同一源策略可防止恶意站点读取另一个站点中 ...
- 【应用程序见解 Application Insights】使用Azure Monitor Application Insights Agent获取Azure VM中监控数据及IIS请求指标等信息
问题情形 为了使用Application Insights也可以监控Azure VM中的相关性能数据,如CPU, Memory,IIS Reuqest等信息,可以在VM中开始一个一个扩展插件: Azu ...
随机推荐
- 训练人形机器人时如何收集人类行为数据 —— 通过人来训练机器人(真人实际演示动作)or 仿真环境自动生成 —— 哪种方式更优、更可行呢
特斯拉的老马,搞的optimus人形机器人就是通过人来训练机器人(真人实际演示动作),但是未来使用仿真环境自动生成数据是否可行呢,NVIDIA的老黄在2024 GTC上是大力推出自家的GROOT平台, ...
- WPF Button自定义样式收集 带有图片的Button
此篇只是收集平时写过的样式~ 带有图片的Button 为Button设定了一些附加属性,用于添加图片到Button. 比如初始化图片和点击后的图片 public static readonly Dep ...
- java中线程的6中状态
1.背景 编写多线程相关的程序,必然会用到线程状态的相关知识点, 那么这篇博客就给大家系统的分析一下多线程的状态, 由于java中线程状态与操作系统中的线程状态划分有区别, 因此这里优先介绍操作系统的 ...
- centos 查看jdk 安装路径
命令行: which java 输出: /usr/bin/java 再次输入: ls -lr /usr/bin/java 输出: lrwxrwxrwx 1 root root 22 4月 26 17: ...
- keycloak~关于社区登录的过程说明
keycloak将第三方登录(社区登录)进行了封装,大体主要会经历以下三个过程: 打开社区认证页面,输入账号密码或者扫码,完成社区上的认证 由社区进行302重定向,回到keycloak页面 keycl ...
- 我当年如何入门Linux的?-zdc的那些往事
一.通信当年的疯狂 还记得09年初, 山寨机横行, 市场上手机主要还是塞班os, 小灵通还没有退出市场, 基于安卓的智能手机陆续推出. 没有王者荣耀,更没有微信, 小米的米聊还在ppt里: 那个时候网 ...
- SSM + Freemarker 开发框架快速搭建
1.项目骨架建立 一.使用开发工具IDEA,构建Maven项目,然后调整Maven项目结构,使其成为一个标准的web项目: 此处不选择Maven骨架,直接Next: 输入项目的相关信息,直接Finis ...
- 阿里云CTF and 其他
RE复现 login_system 这个函数就是判断username,点进去发现是线性方程,用z3解 from z3 import * s=Solver() a=[0]*16 for i in ran ...
- MyBatis日志工厂
目录 日志工厂 标准日志实现 Log4j 使用步骤: 日志工厂 我们在测试SQL的时候,要是能够在控制台输出 SQL 的话,是不是就能够有更快的排错效率? 如果一个 数据库相关的操作出现了问题,我们可 ...
- Java 多个String(字符串)判断是否null(空值)
Java 多个String(字符串)判断是否null(空值) 示例: String s = null; if (str1 != null) { s = str1; } else if (str2 != ...