08
2019
05
10:22:03

Route-based VPN on Linux with WireGuard

Vincent Bernat 

https://vincent.bernat.ch/en/blog/2018-route-based-vpn-wireguard

In a previous article, I described an implementation of redundant site-to-site VPNs using IPsec (with strongSwan as an IKE daemon) and BGP (with BIRD) to achieve this: 🦑

Redundant VPNs between 3 sites
Three sites using redundant IPsec VPNs to protect some subnets.

The two strengths of such a setup are:

  1. Routing daemons distribute routes to be protected by the VPNs. They provide high availability and decrease the administrative burden when many subnets are present on each side.

  2. Encapsulation and decapsulation are executed in a different network namespace. This enables a clean separation between a private routing instance (where VPN users are) and a public routing instance (where VPN endpoints are).

As an alternative to IPsec, WireGuard is an extremely simple (less than 5,000 lines of code) yet fast and modern VPN that utilizes state-of-the-art and opinionated cryptography (Curve25519ChaCha20Poly1305) and whose protocol, based on Noise, has been formally verified. It is currently available as an out-of-tree module for Linux but is likely to be merged when the protocol is not subject to change anymore. Compared to IPsec, its major weakness is its lack of interoperability.

It can easily replace strongSwan in our site-to-site setup. On Linux, it already acts as a route-based VPN. As a first step, for each VPN, we create a private key and extract the associated public key:

$ wg genkeyoM3PZ1Htc7FnACoIZGhCyrfeR+Y8Yh34WzDaulNEjGs=$ echo oM3PZ1Htc7FnACoIZGhCyrfeR+Y8Yh34WzDaulNEjGs= | wg pubkeyhV1StKWfcC6Yx21xhFvoiXnWONjGHN1dFeibN737Wnc=

Then, for each remote VPN, we create a short configuration file:1

[Interface]PrivateKey = oM3PZ1Htc7FnACoIZGhCyrfeR+Y8Yh34WzDaulNEjGs=ListenPort = 5803[Peer]PublicKey  = Jixsag44W8CFkKCIvlLSZF86/Q/4BovkpqdB9Vps5Sk=EndPoint   = [2001:db8:2::1]:5801AllowedIPs = 0.0.0.0/0,::/0

A new ListenPort value should be used for each remote VPNWireGuard can multiplex several peers over the same UDP port but this is not applicable here, as the routing is dynamic. The AllowedIPs directive tells to accept and send any traffic.

The next step is to create and configure the tunnel interface for each remote VPN:

$ ip link add dev wg3 type wireguard$ wg setconf wg3 wg3.conf

WireGuard initiates a handshake to establish symmetric keys:

$ wg show wg3interface: wg3  public key: hV1StKWfcC6Yx21xhFvoiXnWONjGHN1dFeibN737Wnc=  private key: (hidden)  listening port: 5803peer: Jixsag44W8CFkKCIvlLSZF86/Q/4BovkpqdB9Vps5Sk=  endpoint: [2001:db8:2::1]:5801  allowed ips: 0.0.0.0/0, ::/0  latest handshake: 55 seconds ago  transfer: 49.84 KiB received, 49.89 KiB sent

Like VTI interfaces, WireGuard tunnel interfaces are namespace-aware: once created, they can be moved into another network namespace where clear traffic is encapsulated and decapsulated. Encrypted traffic is routed in its original namespace. Let’s move each interface into the private namespace and assign it a point-to-point IP address:

$ ip link set netns private dev wg3$ ip -n private addr add 2001:db8:ff::/127 dev wg3$ ip -n private link set wg3 up

The remote end uses 2001:db8:ff::1/127. Once everything is setup, from one VPN, we should be able to ping each remote host:

$ ip netns exec private fping 2001:db8:ff::{1,3,5,7}2001:db8:ff::1 is alive2001:db8:ff::3 is alive2001:db8:ff::5 is alive2001:db8:ff::7 is alive

BIRD configuration is unmodified compared to our previous setup and the BGP sessions should establish quickly:

$ birdc6 -s /run/bird6.private.ctl show proto | grep IBGP_IBGP_V2_1 BGP      master   up     20:16:31    EstablishedIBGP_V2_2 BGP      master   up     20:16:31    EstablishedIBGP_V3_1 BGP      master   up     20:16:31    EstablishedIBGP_V3_2 BGP      master   up     20:16:29    Established

Remote routes are learnt over the different tunnel interfaces:

$ ip -6 -n private route show proto bird2001:db8:a1::/64 via fe80::5254:33ff:fe00:13 dev eth2 metric 1024 pref medium2001:db8:a2::/64 metric 1024        nexthop via 2001:db8:ff::1 dev wg3 weight 1        nexthop via 2001:db8:ff::3 dev wg4 weight 12001:db8:a3::/64 metric 1024        nexthop via 2001:db8:ff::5 dev wg5 weight 1        nexthop via 2001:db8:ff::7 dev wg6 weight 1

From one site, you can ping an host on the other site through the VPNs:

$ ping -c 2 2001:db8:a3::1PING 2001:db8:a3::1(2001:db8:a3::1) 56 data bytes64 bytes from 2001:db8:a3::1: icmp_seq=1 ttl=62 time=1.54 ms64 bytes from 2001:db8:a3::1: icmp_seq=2 ttl=62 time=1.67 ms--- 2001:db8:a3::1 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 1001msrtt min/avg/max/mdev = 1.542/1.607/1.672/0.065 ms

As with the strongSwan setup, you can easily snoop unencrypted traffic with tcpdump:

$ ip netns exec private tcpdump -c3 -pni wg5 icmp6tcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on wg5, link-type RAW (Raw IP), capture size 262144 bytes08:34:34 IP6 2001:db8:a3::1 > 2001:db8:a1::1: ICMP6, echo reply, seq 4008:34:35 IP6 2001:db8:a3::1 > 2001:db8:a1::1: ICMP6, echo reply, seq 4108:34:36 IP6 2001:db8:a3::1 > 2001:db8:a1::1: ICMP6, echo reply, seq 423 packets captured3 packets received by filter0 packets dropped by kernel

You can find all the configuration files for this example on GitHub.

Update (2018.11)

It is also possible to transport IPv4 on top of IPv6 WireGuard tunnels. The lab has been updated to support such a scenario.




推荐本站淘宝优惠价购买喜欢的宝贝:

image.png

本文链接:https://hqyman.cn/post/652.html 非本站原创文章欢迎转载,原创文章需保留本站地址!

分享到:
打赏





休息一下~~


« 上一篇 下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

请先 登录 再评论,若不是会员请先 注册

您的IP地址是: