跳到主要内容

10.2 IKE (Internet Key Exchange)

上一节我们提到,IPsec 不仅仅是一个内核模块,它是一场跨越用户空间和内核空间的联合行动。内核只负责「执行」——也就是拿到密钥后去加密、解密数据包。但在执行之前,得有人先把密钥商量好,把规则定下来。这个「外交谈判」的任务,就是在这一节要讲的 IKE (Internet Key Exchange)

谈判的主角是用户空间的守护进程。在 Linux 的世界里,主要有几个竞争者:老牌的 Openswan(以及它的衍生品 libreswan)、功能强大的 strongSwan,还有来自 BSD 阵营的 racoon(属于 Kame 项目的一部分)。其中,strongSwan 和 Openswan 是目前最活跃的选择。

安全关联(SA):谈判的目标

无论是用哪个软件,它们最终要做的事只有一件:建立一个 Security Association (SA,安全关联)

你可以把 SA 理解为一份「合同」——它详细规定了这两个 IP 地址之间的通信该怎么加密、用什么算法、密钥是什么、这份合同能管多久。

但在深入合同细节之前,我们要先搞清楚「合同」是怎么被找到的。SA 在内核中由两个参数唯一标识:

  1. 目的地址:发给谁。
  2. SPI (Security Parameter Index,安全参数索引):一个 32 位的编号。

这个 SPI 非常关键。但「SPI 是一个唯一编号」这个类比有一个地方是错的:它并不是像端口号那样全局唯一的,而是必须结合目的地址(加上协议号 ESP/AH)才能唯一标识一个 SA。这就好比同一个写字楼(目的 IP)里有很多家公司(不同的 SA),SPI 是具体的「房间号」,光有房间号找不到人,必须结合「哪栋楼」才能定位。

有了 SA,内核才知道拿到一个加密包后该用哪把钥匙去开锁。如果这个参数对不上,内核就会直接把包丢弃。

密钥是怎么商量出来的?

建立 SA 的过程,本质上就是双方对齐参数的过程:

  • 用什么加密算法?(AES?3DES?)
  • 用什么认证算法?(HMAC-SHA1?HMAC-SHA256?)
  • 密钥本身是多少?
  • 这把钥匙能用多久?(密钥生存期)

有两种方法可以敲定这些参数:

  1. 手工密钥交换:管理员自己在两边敲命令把密钥写死。这很安全(只要你不把密码贴在屏幕上),但很麻烦,而且不支持自动密钥轮换,现在已经很少用了。
  2. IKE 协议:由守护进程自动协商。这是主流做法。

Openswan 的 pluto 守护进程和 strongSwan 的 charon 守护进程,就是专门干这个活的。它们通过 UDP 500 端口与对方对话,谈妥之后,通过 Netlink XFRM 接口把协商好的 SA 和策略写入内核。

回到那个「房间号」的类比:IKE 协议的作用,就是让两栋大楼的管理员坐下来开会,确认「以后我们要互相通信,请去 1001 号房间(SPI=1001)找暗号」。确认无误后,管理员会把这条信息登记在大门保安(内核 XFRM 框架)的小本本上。

IKEv1 vs IKEv2:历史的包袱

虽然很多老系统还在用 IKEv1,但老实说,如果不是为了兼容那些老古董(比如 macOS 自带的客户端基于旧代码),我们早就该全面迁移到 IKEv2 了。

为什么这么说?因为 IKEv1 确实有点「蠢」。

消息交换的效率: IKEv1 太啰嗦了。为了建立一个 IPsec SA,IKEv1 需要发 9 条消息(两个阶段:Main Mode 和 Quick Mode)。而 IKEv2 把这个过程缩减到了 4 条消息

可靠性设计

  • IKEv1:没有确认机制。如果包丢了怎么办?它只能傻傻地重传,而且有个非常讨厌的竞态条件——如果两边同时觉得自己丢了包同时开始重传,可能会产生混乱。
  • IKEv2:吸取了教训。每一条请求都必须有回应。重传的责任完全由发起方承担,这就避免了两边「撞车」的风险。

功能上的巨大差异: IKEv2 不仅仅是把流程简化了,它还顺手解决了很多 IKEv1 处理得很别扭的问题:

  • NAT 穿透:IKEv1 需要额外的补丁来支持 NAT-T(NAT Traversal),而 IKEv2 天生就把这个支持做进去了。
  • 流量选择器:在 IKEv1 里,两边的子网配置必须精确匹配,少一个都不行。IKEv2 支持自动收窄,只要一方的子网是另一方的子集就行,配置起来省心多了。
  • EAP 认证:IKEv1 用 XAUTH 来做用户认证,这玩意儿安全性堪忧。IKEv2 支持 EAP(Extensible Authentication Protocol),你可以先用证书确认服务器身份,然后再用相对弱一点的密码(比如 EAP-MSCHAPv2)做用户认证——这样既保证了连的是真服务器,又允许用户使用简单的密码。

⚠️ 注意 尽量别用 IKEv1 的 Aggressive Mode(激进模式)。虽然配置起来方便(不用输入 ID),但它在网络上是明文传输响应哈希的,很容易被离线字典攻击破解。strongSwan 甚至戏称使用该模式的守护进程为 weakSwan

IKE 的两个阶段

既然协议还在运行,我们就得看看它是怎么工作的。这里主要以 IKEv1 为主(因为它的阶段划分更清晰,理解了它,IKEv2 的合并模式也就很好懂了)。

第一阶段:Main Mode(主模式) 这是建立信任的阶段。双方在这里互相确认身份(你是谁?),并通过 Diffie-Hellman 算法算出一个共享的会话密钥。

  • 认证可以基于 RSA/ECDSA 证书,也可以基于 PSK (Pre-Shared Key,预共享密钥)。PSK 虽然简单,但既然是「预共享」,意味着如果有人改了密码却没同步,或者密码太弱,整个链路就完了。
  • 如果第一阶段成功,双方就建立了一个 ISAKMP SA(也叫 IKE SA)。这是一条管理通道,后续的协商都要走这条通道,受它保护。

第二阶段:Quick Mode(快速模式) 有了管理通道,现在开始干正事。在这个阶段,双方商定具体用来传输数据的 IPsec SA 参数(加密算法、密钥等)。

  • 在 IKEv2 中,这个阶段被合并到了 IKE_AUTH 交换中,建立了第一个 CHILD_SA

之所以 IKEv1 需要 9 条消息(6 条在第一阶段,3 条在第二阶段),是因为它既要在这个不安全的网络上偷偷摸摸地商量密钥(DH 交换),又要互相验明正身(认证),还要防止中间人攻击(MITM)。每一步都得小心翼翼。


IPsec and Cryptography

当守护进程在用户空间忙着握手的时候,内核这边其实也在做一件极其繁重的工作——加密解密。这背后的支撑是 Linux Kernel Crypto API

两个时代的栈

在 Linux 内核的历史上,有过两个主要的 IPsec 实现:

  1. KLIPS:这是上古时代的实现,早于 netfilter。它支持 OCF (Open Cryptography Framework),一个优势在于它支持异步调用。
  2. Netkey (XFRM):这是从 2.6 内核开始引入的「原生」实现,由 Alexey Kuznetsov 和 David S. Miller 开发。它直接使用内核的 Crypto API。

现在的内核默认都在用 Netkey(即 XFRM 框架)。虽然早期的 Crypto API 大多是同步调用(在那儿死等加密结果),但现在的实现已经非常高级了。

异步加密与硬件加速

加密是个 CPU 密集型的活儿。如果你在处理千兆流量的 VPN,CPU 光是算哈希就能跑满。这时候,硬件加速卡就派上用场了。

硬件设备通常不能阻塞等待——它发起了加密请求就得去干别的事,等硬件算好了再中断通知 CPU。因此,它们必须使用 异步 API

  • acrypto:内核里有一层异步加密层专门处理这事。
  • pcrypt:这是一个多核环境下的神器。它可以把加密请求并行分发到多个 CPU 核心上处理,同时又保证不乱序(这一点对 IPsec 至关重要,因为 IPsec 有防重放攻击机制,乱序会导致丢包)。在某些场景下,开启 pcrypt 能让 IPsec 性能提升数倍。

AEAD:效率的巅峰

从 Linux 2.6.25 内核(2008年)开始,XFRM 框架开始支持 AEAD (Authenticated Encryption with Associated Data) 算法,最典型的代表就是 AES-GCM

这为什么重要? 在以前,加密和认证是分开算的。你先算一遍 AES 加密,再算一遍 HMAC-SHA1 认证。这意味着数据要被扫描两遍。 而 AES-GCM 这种 AEAD 算法,一次操作就能同时完成加密和完整性校验。如果你的 CPU 支持 AES-NI 指令集(Intel 和 AMD 的服务器级 CPU 基本都有),这种操作几乎是「免费」的——速度极快,CPU 占用极低。

如果你在搭建高性能 VPN,一定要检查你的算法配置。如果还在用 3DES + HMAC-SHA1,那就相当于开法拉利加 92 号汽油,完全浪费了内核的能力。


下一节,我们将深入内核,看看用户空间的守护进程把密钥交出来之后,内核的 XFRM 框架是如何接管场面的。