wireguard 协议学习总结
这篇博客主要是 wireguard 学习和使用过程中相关知识点的总结,目前国内对 wireguard 协议的使用和推广比较少,我自己也对 wireguard 白皮书内容做了部分翻译,放在了本篇内容之中。
wireguard 协议的优势
wireguard 协议与 IPsec 协议的对比:
|
IPsec |
Wireguard |
|
代码量庞大 |
Linux 实现只有 4000行 |
|
复杂性高,配置麻烦,容易发生错误,可用性差 |
1.可以使用 ip 和ifconfig 程序对其进行管理 2.密钥交换,连接,断开连接,重新连接,发现等透明,管理员无需担心这些细节 3.简单明了,不容易发生灾难性故障和配置错误。 |
|
使用 Linux transform (“xfrm”) layer 实现,密钥交换层与传输加密层(转换层)分开 |
消除了分层,使用虚拟接口而不是转换层 |
wireguard 协议与 openVPN 协议的对比:
|
OpenVPN |
Wireguard |
|
基于 TLS 协议,使用工作在用户空间的虚拟网络设备 tun/tap,用户空间和内核空间频繁传输数据,需要守护进程,性能差 |
工作在内核空间,性能高 |
|
支持过多的TLS功能,SSL / TLS漏洞持续泛滥 |
故意缺乏密码和协议敏捷性,潜在风险小 |
白皮书第一章节关于 wireguard 协议优势相关内容的翻译:
1. WireGuard消除了这些分层分隔。 WireGuard并没有提供IPsec和xfrm层的复杂性,而只是提供了一个虚拟接口(例如wg0),然后可以使用标准ip(8)和ifconfig(8)实用程序对其进行管理。在为接口配置了私钥(以及可选的预共享的对称密钥,如第5.2节中所述)以及将与之安全通信的对等方的各种公钥后,隧道就可以正常工作。密钥交换,连接,断开连接,重新连接,发现等透明,可靠地在后台进行,并且管理员无需担心这些细节。换句话说,从管理的角度来看,WireGuard接口似乎是无状态的。然后可以使用普通的基础结构为防火墙接口配置防火墙规则,并确保对来自WireGuard接口的数据包进行身份验证和加密。与IPsec相比,WireGuard简单明了,不容易发生灾难性故障和配置错误。需要强调的是,IPsec的分层是正确且合理的。使用IPsec,一切都在正确的地方,以达到学术上的完美。但是,正如抽象的正确性经常发生的那样,它严重缺乏可用性,并且很难实现可验证的安全实现。相反,WireGuard始于有缺陷的违反层的基础,然后尝试使用解决实际问题的实用工程解决方案和密码技术来纠正由这种合并引起的问题。
2. 另一方面,OpenVPN是使用TLS的基于用户空间TUN / TAP的解决方案。由于它位于用户空间中,因此性能非常差-因为必须在内核空间和用户空间之间多次复制数据包-并且需要长寿的守护程序;对于管理员来说,OpenVPN似乎从无状态的发展而来。尽管TUN / TAP接口(例如tun0)具有与上述类似的类似wg0的优势,但OpenVPN也非常复杂,支持全部过多的TLS功能,这将很多代码暴露给潜在的漏洞。 OpenVPN可以在用户空间中实现,因为内核中的ASN.1和x509解析器在历史上一直是个问题(CVE-2008-1673,CVE-2016-2053),而添加TLS堆栈只会使问题变得更糟。 TLS还带来了巨大的状态机,以及源IP地址和公钥之间的关联不够清晰。
3. 对于密钥分发,WireGuard从OpenSSH汲取了灵感,其常见用法包括一种非常简单的密钥管理方法。通过一组不同的带外机制,两个对等点通常交换其静态公钥。有时,它很简单,就像PGP签名的电子邮件一样,而有时,它是使用LDAP和证书颁发机构的复杂密钥分发机制。重要的是,在大多数情况下,OpenSSH密钥分发是完全不可知的。 WireGuard也效仿。两个WireGuard对等方通过某种未指定的机制交换其公钥,之后便可以进行通信。换句话说,WireGuard对待密钥分发的态度是,这是解决特定问题的错误层,因此界面足够简单,因此可以与任何密钥分发解决方案一起使用。另外一个优点是,公共密钥只有32个字节长,并且可以轻松地以44个字符用Base64编码表示,这对于通过各种不同的介质传输密钥很有用。
4. 最后,WireGuard具有密码学依据。它故意缺乏密码和协议敏捷性。如果在基本图元中发现漏洞,则将要求所有端点进行更新。如SSL / TLS漏洞的持续泛滥所示,密码敏捷性极大地增加了复杂性。 WireGuard使用Trevor Perrin的Noise [23]的一种变体(在其开发过程中收到了来自本文作者的大量输入,目的是为了在WireGuard中使用)与Curve25519进行1-RTT密钥交换[5]。对于ECDH,HKDF [15]用于扩展ECDH结果,RFC7539 [17]的ChaCha20 [3]和Poly1305 [8]的结构用于认证加密,而BLAKE2s [2]的哈希处理。它使用新的crypto-cookie机制来实现IP地址可归属性,从而内置了防止拒绝服务攻击的保护措施。
5. 同样认为,WireGuard仅适用于第3层。如下面第2节所述,这是确保数据包真实性和可归属性的最干净的方法。作者认为,第3层是桥接多个IP网络的正确方法,并且将其强加到WireGuard上可以进行许多简化,从而使协议更清洁,更容易实现。它同时支持IPv4和IPv6的第3层,并且可以封装v4-in-v6和v6-in-v4。 WireGuard结合了这些原则,着重于简单性和可审核的代码库,同时仍保持极高的速度并适用于少量环境。通过将密钥交换和第3层传输加密组合为一种机制,并使用虚拟接口而不是转换层,WireGuard确实打破了传统的分层原则,从而寻求了一种既实用又安全的可靠工程解决方案。在此过程中,它采用了几种新颖的密码和系统解决方案来实现其目标。
wireguard 在 linux 服务器上的常规操作命令
首先需要注意的是 wireguard 的运行环境对内核版本有要求,最低版本为
3.10.0-1127.13.1.el7.x86_64
添加虚拟网卡wg0:
ip link add dev wg0 type wireguard
设置wg0的IP和网段:
ip address add dev wg0 192.168.2.1/24
配置wg0:
wg set wg0 listen-port 51820 private-key /path/to/private-key peer ABCDEF... allowed-ips 192.168.88.0/24 endpoint 209.202.254.14:8172
拉起wg0:
ip link set up dev wg0
wireguard 主要报文类型及 wireshark 抓包展示

handshake inittiation:

handshake response:

transport data:
wireguard 虚拟网卡发送数据包的流程(翻译自白皮书)
1. 明文数据包到达虚拟网卡 wg0;
2. 将数据包的目的IP地址,假设是 192.168.87.21,与 peers 列表中的每个 peer 的 allowed ips 做匹配,发现 与 peer TrMv ... WXX0 匹配,如果没有匹配任何 peer ,则将其丢弃,并通过 ICMP报文 “no route to host” 通知发送方,并将 -ENOKEY 返回给用户空间;
3. 根据 公钥 TrMv ... WXX0,可以派生得到对称加密密钥和随机计数器,然后使用 ChaCha20Poly1305 算法对明文包进行加密;
4. 将必要的一些包头字段信息拼装在加密数据包之前;
5. 将封装好的udp包发送给 公钥 TrMv ... WXX0 关联的endpoint,这个 endpoint 地址要么是预先手动配置好的,要么是事前接收到正确验证的数据包后自动更新的,如果无法确定 endpoint ,则将数据包丢弃,并发送ICMP消息通知发送方,同时将-EHOSTUNREACH返回到用户空间。
wireguard 虚拟网卡接收数据包的流程(翻译自白皮书)
1. 虚拟网 wg0 监听的UDP端口接收到 UDP 包;
2. 根据包头信息,wg0确定接收到的报文和特定的安全会话(secure session)之间的关联, 然后检查消息计数器的有效性,并尝试使用安全会话关联的密钥对报文进行身份验证和解密。如果无法确定和某个安全会话之间的关联,或者身份验证失败,则丢弃数据包;
3. 数据包验证通过后,可以得到接收到的数据包的源IP(公网IP),之后 “update the endpoint for peer TrMv...WXX0”;
4. 解密得到明文包后,如果这不是一个IP包,它将被丢弃,如果是个IP包,wg0 检查明文包的源IP地址(隧道内地址)是否在相应 peer 的 allowed ip 列表中,如果不在,这个包会被丢弃,反之接收;
5. 如果明文数据包没有被丢弃,它将被插入wg0接口的接收队列中。
wireguard 协议如何抵御重放攻击
场景: 在正式加密通信之前,wireguard 双方会进行 handshake 得到对称加密密钥,这一初始握手消息可能被攻击者重放,诱使响应者重新生成临时密钥,从而使合法发起方的会话无效(尽管不会影响任何消息的保密性或真实性)。
解决之道: 在发送方握手消息中包含一个12字节的TAI64N 时间戳,发送方会对其进行加密,响应方会对其进行验证。响应方跟踪每个 peer 到达的最大时间戳,并丢弃时间戳小于等于该时间的数据包。当重放的握手包到达时,会被丢掉。TAI64N 的好处在于它是大端序,当比较两个 12字节的 TAI64N 时间戳时,可以很方便的使用 memcmp() 进行比较。
以下内容是 wireguard 白皮书安全性相关内容的翻译:
1. WireGuard的一个设计目标是避免在身份验证之前存储任何状态,并且不向未经验证的数据包发送任何响应。由于没有为未经验证的数据包存储状态,也没有生成响应,WireGuard对于非法对等方和网络扫描仪是不可见的。通过不允许未经验证的数据包影响任何状态,可以避免几种类型的攻击。
2. 更一般地说,可以以根本不需要动态内存分配的方式来实现WireGuard,即使对于经过身份验证的包也是如此,如第7节所述。但是,此属性要求响应程序接收到的第一条消息来验证发起程序。像这样在第一个数据包中进行身份验证可能会使响应程序对重播攻击打开大门。攻击者可以重放初始握手消息,诱使响应者重新生成其短暂密钥,从而使合法发起方的会话无效(尽管不会影响任何消息的保密性或真实性)。为了防止这种情况,在第一条消息中包含一个12字节的TAI64N[7]时间戳,并对其进行加密和验证。响应程序跟踪每个对等方接收到的最大时间戳,并丢弃包含小于或等于该时间戳的数据包。(事实上,它甚至不必是一个精确的时间戳;它只需是每个对等端单调递增的96位数字。)如果响应程序重新启动并丢失此状态,那就不是问题:即使先前的初始数据包可以重放,它也不可能中断任何正在进行的安全会话,因为响应程序刚刚重新启动,因此没有要中断的活动安全会话。
3. 一旦发起方在重新启动后与响应方重新建立安全会话,则发起方将使用更大的时间戳,使前一个时间戳失效。此时间戳可确保攻击者不会通过重播攻击中断发起方和响应方之间的当前会话。(这也意味着两个不同的对等点不应该共享私钥,因为在这种情况下,发送给其中一个的包可以被重放到另一个,随后的响应将导致发起方在一个对等点之间非自愿地漫游。但是首先不应该共享私钥。)从实现的角度来看,TAI64N[7]非常方便,因为它是big-endian,允许使用标准memcmp()比较两个12字节的时间戳。由于WireGuard不使用签名,为了获得一定程度的可否认性,第一条消息只依赖于两个对等方的静态密钥的Diffie-Hellman结果进行身份验证。这意味着,如果其中一个静态密钥被破坏,攻击者将能够伪造一个启动消息,尽管它无法完成包含最大时间戳值的完整握手,从而阻止所有未来连接的成功。虽然这看起来与传统的密钥泄露模拟漏洞相似,但WireGuard不易受到攻击,但事实上却大相径庭。因为,如果密钥泄露使攻击者能够阻止对等方再次使用他们被泄露的密钥,那么攻击者实际上帮助了对这种泄露的正确响应。如果TIA64N时间戳的精度造成不适当的信息泄漏,则实现可以截断时间戳纳秒部分的24位。
wireguard 协议如何抵御DDos攻击
攻击途径:Curve25519 用于 handshake 消息,它是一个 CPU 密集型算法,当存在大量攻击者构造的握手消息时,可能会把 CPU耗尽,使得正常请求无法被响应。
DTLS 协议 和 IKEv2 协议中也有类似问题,为了防止CPU耗尽,如果响应者处于负载状态,则可以选择不处理握手消息(发起或响应握手消息),而是使用cookie回复消息给发送方。然后,发起者使用此cookie来重新发送消息,并使其被响应者接受, 具体如下(翻译自白皮书):
1. 首先,响应者维护一个秘密的随机值 m ,该值每两分钟更改一次。 响应者计算 cookie:cookie = HMAC (client ip,m); 2. 响应者将 cookie 发送给发送方; 3. 发送者在发送消息的时候,需要同时发送Mac,Mac = HMAC(msg,cookie),这样消息本身其实就和固定的IP绑定了; 响应者收到消息后,验证 mac,选择是否处理该消息。
但是,这种做法有三个主要缺陷:
1. 如5.1节所述,我们希望响应方保持沉默,不要对未认证的消息发送任何回复,在负载状态下不加选择地发送cookie消息会破坏此属性; 2. cookie不应以明文形式发送,因为中间人可能会使用它来发送经过处理的欺诈性消息; 3. 发起者本人可能会被发送欺诈性的cookie,从而遭到拒绝服务攻击,然后,它就无法成功使用它来计算其消息的MAC;
最终解决方案:
WireGuard的cookie机制使用两个MAC(msg.mac1和msg.mac2)解决了这些问题。 //TODO 这里有问题,cookies 的发送逻辑需要再次梳理 1. 对于第一个问题,为了使 responder 在负载状态下也保持沉默,所有发向 responder 的握手报文中都会包含 msg.mac1 字段,这个 mac值(消息验证码) 使用 responder 的公钥计算而来 。无论 responder 是否处在负载的状态下,握手报文中都必须包含合法的 msg.mac1 字段。 2. 对于第二个问题,为了解决以明文形式发送MAC的问题,我们在计算 cookie 时使用了一个随机数,同时将 responder 的公钥用当作对称加密密钥对 cookie 进行加密。 3. 最后,为了解决第三个问题,wireguard 使用AEAD的 " additional data "字段对传输中的cookie进行加密,以认证 msg.mac1,这确保攻击者不能向发起者发送大量无效的 cookie 。
上述三个问题得到解决后,responder 的处理逻辑是这样的: 如果 responder 处于负载状态,它只接受有 msg.mac2 字段的消息,发送方计算 mac2 的方式是 :
mac2 = HMAC(msg, cookie)
总而言之, responder 计算了MAC后,会与消息中收到的MAC进行比较,拒绝含有无效的msg.mac1的消息,当处于负载状态时,会拒绝含有无效的msg.mac2的消息。如果 responder 收到了一个含有有效 msg.mac1 但又含有无效 msg.mac2 的消息,并且处于负载状态,会用 cookie 回复消息来响应。