Page MenuHomeVyOS Platform

Wireguard support
Closed, ResolvedPublicFEATURE REQUEST

Description

It would be really nice to have wireguard VPN (https://git.zx2c4.com/WireGuard) support in vyos for the future. There is already an addon for UBNT/Vyatta, which can be found here:

https://github.com/Lochnair/vyatta-wireguard

Perhaps this could be ported without too much effort straight to VyOS going further.

Details

Version
-

Related Objects

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes

Now, is that suppose to happen automatically ( i can include it in the wireguard.py script), or is it supposed to be setup via 'set static proto ...'? What would be the better way?

If wireguard cannot create routes on its own, I believe we should leave route setup to the user. Doing it automatically when it's not actually supported is a very leaky abstraction.

The wireguard script takes care of loading the kernel module

I tend to agree with @lh on that — unless there's a compelling reason not to load some module, let's just load it on boot. It's not like they take up that much RAM.

If you don't have a wireguard key yet, I generate it when you setup wg for the first time, it is stored under /config/auth/wireguard

I think generating keys should always be left to the user.

@dmbaturin thx for the input, I have added the route setup to the wireguard.py script, because if the interface gets delete from the config, the wg device gets deleted from the OS and all its routes. The config would still have then the routes in the effective config and when you reboot it tries to setup routes on a non existing interface.
The examples on the wireguard website, do it similar, after the tunnel is up, they add the routes. I also couldn't find anything in their code which adds automatically the routes for allowed-ips.

https://git.zx2c4.com/WireGuard/plain/contrib/examples/ncat-client-server/client.sh

I have the kmod check in 5 lines of code, so it can be removed any time or can stay there, I don't mind too much. It checks if the mod is loaded, if not it calls modprobe. It does of course require Kim's packages.

I'm gonna change the generation for the keys, I was thinking this morning as well that the user needs something to delete and regenerate them, it was just way to convenient for me when I was able to create the tunnel just with a few commands :).
Most of my tests were with IPv4, I did just a few with IPv6 so far, but they have been successful as well. So for anyone who plays around with it, IPv6 is suppose to work, if it fails blame me, I have a look then.

Should I place the key-pair generation under 'system wireguard' or 'system options wireguard'?

if the interface gets delete from the config, the wg device gets deleted from the OS and all its routes

This is exactly the reason why "set protocols static ..." commands use zebra instead of pushing routes directly to the kernel with iproute2. Zebra takes care of interfaces going up and down and reinstalls the routes when needed.

Should I place the key-pair generation under 'system wireguard' or 'system options wireguard'?

We already have "run generate vpn" subtree, which is used for IPsec. I think we should put the wireguard command there as well.

Please put key generation as usual under run generate vpn (openvpn key generation uses the same subtree)

Thx guys, learned something new. will implement it that way.

@dmbaturin When I rebuild the iso, I'm currently using the ./configure switch you recently introduced to get the wireguard packages added. How am I supposed to to it for the pull request then? Where would I have to define the modules to load?

Required packages should be added to this file: https://github.com/vyos/vyos-1x/blob/current/debian/control

Regarding module load, you could create a new directory called module-load.d in https://github.com/vyos/vyos-build/tree/current/data/live-build-config/includes.chroot/etc/ which will then be shipped in the iso. See the MAN for details https://www.freedesktop.org/software/systemd/man/modules-load.d.html

@c-po thanks a lot, I was thinking first I have to add it to the rules and do it via configure, the control file is way easier of course. I'm off next week and try to enjoy summer before the rain comes, but try to get the first stages going.

@hagbard just merge your changes and @UnicronNL can continue

@syncer he is following me anyway. I'm almost done, just need to implement the suggested changes from Daniil and Christian.

All right pull request opened. I'm going to enhance a few parts, like the endpoint format check, show status etc., but as mentioned above I won't have much time next week and it seemed everyone needed it quickly.
I've rebuilt the iso, rebuilt vyos-1x and used the follwing config:

host01:

set interfaces wireguard wg01 address '10.2.2.1/24'
set interfaces wireguard wg01 description 'wg02-test'
set interfaces wireguard wg01 listen-port '12345'
set interfaces wireguard wg01 peer 7QQU75St+Kr4+B097E7qzMv0PbBtbvLCyGCpTwRxBEI= allowed-ips '10.1.1.0/24'
set interfaces wireguard wg01 peer 7QQU75St+Kr4+B097E7qzMv0PbBtbvLCyGCpTwRxBEI= endpoint '192.168.0.130:12345'
set protocols static interface-route 10.1.1.0/24 next-hop-interface wg01

host02:
set interfaces wireguard wg01 address '10.1.1.1/24'
set interfaces wireguard wg01 description 'wg01-test'
set interfaces wireguard wg01 listen-port '12345'
set interfaces wireguard wg01 peer z80pwzfFfwfte3p06iIVVBDPfUL+MSH0dL9I33nJzTo= allowed-ips '10.2.2.0/24'
set interfaces wireguard wg01 peer z80pwzfFfwfte3p06iIVVBDPfUL+MSH0dL9I33nJzTo= endpoint '192.168.0.113:12345'
set protocols static interface-route 10.2.2.0/24 next-hop-interface wg01

You'll need:
https://github.com/vyos/vyos-1x/pull/27/files
https://github.com/vyos/vyos-build/pull/22

and the config above. You can add multiple endpoints and peers to a wg device, or multiple wg devices with single or multiple peers. So far all working as long as you set your route. I did basic IPv6 tests as well, ss the main functionality should be working well.
I'm going to open a subtask for documentation, I need to create first an account for the wiki etc.

In T427#17621, @hagbard wrote:

All right pull request opened. I'm going to enhance a few parts, like the endpoint format check, show status etc., but as mentioned above I won't have much time next week and it seemed everyone needed it quickly.
I've rebuilt the iso, rebuilt vyos-1x and used the follwing config:

host01:

set interfaces wireguard wg01 address '10.2.2.1/24'
set interfaces wireguard wg01 description 'wg02-test'
set interfaces wireguard wg01 listen-port '12345'
set interfaces wireguard wg01 peer 7QQU75St+Kr4+B097E7qzMv0PbBtbvLCyGCpTwRxBEI= allowed-ips '10.1.1.0/24'
set interfaces wireguard wg01 peer 7QQU75St+Kr4+B097E7qzMv0PbBtbvLCyGCpTwRxBEI= endpoint '192.168.0.130:12345'
set protocols static interface-route 10.1.1.0/24 next-hop-interface wg01

host02:
set interfaces wireguard wg01 address '10.1.1.1/24'
set interfaces wireguard wg01 description 'wg01-test'
set interfaces wireguard wg01 listen-port '12345'
set interfaces wireguard wg01 peer z80pwzfFfwfte3p06iIVVBDPfUL+MSH0dL9I33nJzTo= allowed-ips '10.2.2.0/24'
set interfaces wireguard wg01 peer z80pwzfFfwfte3p06iIVVBDPfUL+MSH0dL9I33nJzTo= endpoint '192.168.0.113:12345'
set protocols static interface-route 10.2.2.0/24 next-hop-interface wg01

You'll need:
https://github.com/vyos/vyos-1x/pull/27/files
https://github.com/vyos/vyos-build/pull/22

and the config above. You can add multiple endpoints and peers to a wg device, or multiple wg devices with single or multiple peers. So far all working as long as you set your route. I did basic IPv6 tests as well, ss the main functionality should be working well.
I'm going to open a subtask for documentation, I need to create first an account for the wiki etc.

vyos@vyos# commit
[ interfaces wireguard wg01 ]
endpoint required on interface wg01 for peer 7QQU75St+Kr4+B097E7qzMv0PbBtbvLCyGCpTwRxBEI=

interfaces wireguard wg01 failed
Commit failed
[edit]

Endpoint in wireguard peer configuration is optional parameter. For example, the client may be behind NAT. In this case endpoint is specified only on the client side, for “server” peer.

Thx @vas-ast, it's not only when you run via NAT, it's in general if you act as the server. I'm going to fix that, that was the first implementation I was happy to have that release to the public. Your input is always valuable, so if you find more, please don't hesitate to report here.
Thanks again.

@hagbard
This is not critical, but it would be convenient to save the tun interface without specifying a peer. Now validation does not allow this. One of the cases, when we prepared the server, and then we automate the addition and removal of the peer (my case), or we want to do this later for some other reason.

update
Also, it would be nice to add the persistent-keepalive option for peer, for NAT and Firewall Traversal Persistence.

@vast-ast
Already fixed in my branch, since it's a minor change. I have the keepalive option on my plate as well as the show command. I was just on vacation for a week and didn't have much time to contribute. How urgent do you need persistent-keepalive?

T783 and making endpoint optional is currently reviewed by the maintainers.

Found a possible bug when you remove keppalive, the wg device show still as configured with keepalive, I have to check if that is actually true and if so I need to ping upstream and let them know.

suggestion

set interfaces wireguard wg0 listen-port (optional)

set interfaces wireguard wg0 private-key <private key of this host>
set interfaces wireguard wg0 public-key <public key of this host> (derived automatically when setting the private key, or when using "generate-private-key, and is "read-only")
set interfaces wireguard wg0 generate-private-key (generates a new private-key and replaces the old one)

set interfaces wireguard wg0 peer <public key of other host> preshared-key <preshared-key> (optional)
set interfaces wireguard wg0 peer <public key of other host> generate-preshared-key (generates and sets "preshared-key" (optional)

I didn't know you are online...

  • set interfaces wireguard wg0 listen-port (optional) = is only optional if you run as wg as server (others want to connect to you, you can only connect to therm if they initiated a connection successfully)
  • set interfaces wireguard wg0 private-key <private key of this host>
  • set interfaces wireguard wg0 public-key <public key of this host> (derived automatically when setting the private key, or when using "generate-private-key, and is "read-only")
  • set interfaces wireguard wg0 generate-private-key (generates a new private-key and replaces the old one)

That gets automatically generated when you run 'generate wireguard keypair' and automatically picked up when you configure tunnels. That way you just need the pubkey from your client, that's it. (assuming allowed-ips is clear for both parties)

I haven't had a look into preshared-key, it will be only required for stream ciphers I guess, but as I have mentioned, didn't have a look yet. Once I have the status commands in place I go to that section as well.

Is it supposed to not accept "=" ?

vyos@vyos# set interfaces wireguard wg1 peer Q83iXp2vkJcCbMUo/6ItJcdf8vYF1ncbVfX0RxqhiWc=

Configuration path: interfaces wireguard wg1 peer Q83iXp2vkJcCbMUo/6ItJcdf8vYF1ncbVfX0RxqhiWc [=] is not valid

[edit]
vyos@vyos# set interfaces wireguard wg1 peer Q83iXp2vkJcCbMUo/6ItJcdf8vYF1ncbVfX0RxqhiWc
Possible completions:
> <text> Base64 encoded public key

[edit]

What version of Wireguard is it that we are using? Where can I see when it is updated?

@mrjones, @Lochnair yep, that's correct. I'm not really happy with how the packages and kmods made it to us, it's also not the latest version and i think no one is tracking it. I raised that question in https://phabricator.vyos.net/T779, but no one answered yet. So i may have to contact @dmbaturin via email directly.
Do you guys think I should implement a 'show wireguard version' or something similar?

Going to leave this task open for a bit, to track bugs and updates which may come our the next couple of days.

@hagbard,

I think that using the key as a peer identifier makes the configuration unreadable its quite hard to identify each peer when you have more than one of them. I would like to se the peer identifier to be a name/description instead and that key is added as a leafNode instead...

Here is a example:

set interfaces wireguard wg01 address '10.1.1.1/24'
set interfaces wireguard wg01 description 'wg01-test'
set interfaces wireguard wg01 listen-port '12345'
set interfaces wireguard wg01 peer node1 key z80pwzfFfwfte3p06iIVVBDPfUL+MSH0dL9I33nJzTo= 
set interfaces wireguard wg01 peer node1 allowed-ips '10.2.2.0/24'
set interfaces wireguard wg01 peer node1 endpoint '192.168.0.113:12345'
set interfaces wireguard wg01 peer node2 key 7QQU75St+Kr4+B097E7qzMv0PbBtbvLCyGCpTwRxBEI= 
set interfaces wireguard wg01 peer node2 allowed-ips '10.1.1.0/24'
set interfaces wireguard wg01 peer node2 endpoint '192.168.0.130:12345'
set protocols static interface-route 10.2.2.0/24 next-hop-interface wg01

Thats my opinion at least :)

I have the same feelings on that

I agree with syncer and runar.

@runar Thanks a lot for your feedback, yes I was heading into that direction already before I release anything but followed as a guide the implementation here: https://github.com/Lochnair/vyatta-wireguard.

peer node1 would have to be a node.tag, but other than that, I really like your example. I'll have that implemented shortly. Did you functional tests too? With IPv6? I tested IPv6 just a little, works quite well.

Another issue @mrjones mentioned, is the listen port. If you configure wg as client, you won't have to pick a listen port, wg goes with a random ephemeral port, randomly chosen (it checks it's not used).
I do however enforce the setting, shall I make it optional then as well. I was thinking that one wants to control outgoing traffic via iptables,instead of having an open wide rule in place.

I really appreciate anyones opinion, so please give me feedback.

@c-po, @syncer I do appreciate your feedback too of course :).

@hagbard i actually haven't tried it in real life, only looked at the command syntax'es.

about the listen-port, is there defined any "default" wireguard server port anywhere? if there is that could be the default port if nothing is specifies.
if its not and the protocoll is not specific about that it should not have a default port we also could "make a default server port"

?
comments?

@runar I didn't find anything the documentation which mentions a default port. I've made it optional, wg is generating if not set, but I didn't find anything in their code yet, it appears to start at 51820.
Proto is always udp.
If the listen port is not defined, you basically run as client, if it is defined you run as server. The client connects to <endpoint> which is basically IP:port from the server, if the authentication fits, it uses the src port of that client package send sends the answer to taht src ip and src port. I think it have seen it in netlink.c in their src tree. Should be all good now. I'm also almost done with the syntax rewrite, still doing testing, but I expect it to have it ready for release maybe tomorrow.

51820 is the official unofficial port

interface: wg01
public key: QIgKRXNMGm5IM3EwdK3W7oWYrBRh7eDwqi/pGe+sWF4=
private key: (hidden)
listening port: 44971

peer: jW9rYUK/87V5kOM/YdVRcfIXMP1l5UN4+GCqGMKw5mM=
endpoint: 10.1.1.251:12345
allowed ips: 10.2.0.0/24

22:49:18.371379 08:00:27:76:cd:e3 > 08:00:27:a2:e5:88, ethertype IPv4 (0x0800), length 170: 10.1.1.251.12345 > 10.1.1.250.44971: UDP, length 128
22:49:18.371443 08:00:27:a2:e5:88 > 08:00:27:76:cd:e3, ethertype IPv4 (0x0800), length 170: 10.1.1.250.44971 > 10.1.1.251.12345: UDP, length 128

I have currently listen-port optional for the new proposed scheme, so if not set, it's randomly chosen by wg.
I think if it's not a registered port with IANA, we shouldn't maker a default then, if another software is using it..... well would be easy to change.

[another phrase starting with I]
I would like to hear @syncer, @c-po and @dmbaturin opinion on that, defaulting to a port is just an else and one line in the code.

If there is an official unofficial port, ad wg distinguishes if its running in server or client mode, why not use something like:

set interfaces wireguard wg0 mode (server|client)?

@c-po, As far as i can see it does not distinguish between server and client mode.
From the manual:

ListenPort — a 16-bit port for listening. Optional; if not specified, chosen randomly.

All nodes are equally alike, the only difference is that one of the parties needs to have a known UDP port number for other peers to connect to.

Also, the "Endpoint" variable is also only used on first connect, after getting a first response this field is updated with whatever ip the Endpoint sends packets from. it does only care about the crypto key to allow traffic, ip is automatically updated.
dataflow:

1: node 1 have configured endpoint info in config, node 2 does not have endpoint info
2: node 1 loads endpoint info into wg , node 2 starts with a empty endpoint table
3: node 1 sends packet to node 2
4: node 2 gets the packet, checks the key and propagates endpoint info for the peer.

at this point node 1 and node 2 is able to talk to each other.
when one of the nodes changes its ip this will happen:

5: node 1 tries to send packet to node 2 but does not have correct endpoint info
6: node 2 sends packet to node 1 <-- keepalive if enabled, or waits for first transit ip packet if no keepalive
7: node 1 updates endpoint info from node 2.

This happens regardless of witch node changes its ip, until new config is read from the config file.

@hagbard, looking in the manual there are still some parameters not implemented into the parser :
[Interface]

FwMark — a 32-bit fwmark for outgoing packets. If set to 0 or "off", this option is disabled. May be specified in hexadecimal by prepending "0x". Optional.

[peer]

PresharedKey — a base64 preshared key generated by wg genpsk. Optional, and may be omitted. This option adds an additional layer of symmetric-key cryptography to be mixed into the already existing public-key cryptography, for post-quantum resistance.

PersistentKeepalive — a seconds interval, between 1 and 65535 inclusive, of how often to send an authenticated empty packet to the peer for the purpose of keeping a stateful firewall or NAT mapping valid persistently. For example, if the interface very rarely sends traffic, but it might at anytime receive traffic from a peer, and it is behind NAT, the interface might benefit from having a persistent keepalive interval of 25 seconds. If set to 0 or "off", this option is disabled. By default or when unspecified, this option is off. Most users will not need this. Optional.

i dont know if the FwMark setting could be used to something, but specifying the extra extra psk and keepalive messages needs to be implemented.

For quick reference: https://git.zx2c4.com/WireGuard/about/src/tools/man/wg.8

maybe if the listen-port is not specified, it uses the default 51820
the option to chose client/server might be confusing, because it is both

I think the best is to use the default from the protocoll.. (autogenerate port if none is specified) 51820 could be a completion help option on the listen-port command

This comment was removed by hagbard.
In T427#18231, @runar wrote:

All nodes are equally alike, the only difference is that one of the parties needs to have a known UDP port number for other peers to connect to.

yes, I was misleading. Initiator and responder might be a less confusing term.

@hagbard, looking in the manual there are still some parameters not implemented into the parser :
[Interface]

FwMark — a 32-bit fwmark for outgoing packets. If set to 0 or "off", this option is disabled. May be specified in hexadecimal by prepending "0x". Optional.

That is being used for QoS, will implementit as well.

PresharedKey — a base64 preshared key generated by wg genpsk. Optional, and may be omitted. This option adds an additional layer of symmetric-key cryptography to be mixed into the already existing public-key cryptography, for post-quantum resistance.

Ack, will add it. I moved it to the end of my queue since they write it may be omitted.

PersistentKeepalive — a seconds interval, between 1 and 65535 inclusive, of how often to send an authenticated empty packet to the peer for the purpose of keeping a stateful firewall or NAT mapping valid persistently. For example, if the interface very rarely sends traffic, but it might at anytime receive traffic from a peer, and it is behind NAT, the interface might benefit from having a persistent keepalive interval of 25 seconds. If set to 0 or "off", this option is disabled. By default or when unspecified, this option is off. Most users will not need this. Optional.

That has been implemented already and is available in the the rolling releases. I used 1, 99999 sec. To be honest anything above 60 minutes is silly anyway, I never have seen a firewall storing a connection status for more than a few minutes (NAT and conntrack).
It would be only useful for NAT anyway (responder initiates traffic to the initiator).
'set int wireguard wgN peer PEER persistent-keepalive'
(https://github.com/hagbard-01/vyos-1x/commit/f184700bdb0c070be7f3bf9d9b2712581c29e798)

i dont know if the FwMark setting could be used to something, but specifying the extra extra psk and keepalive messages needs to be implemented.

For quick reference: https://git.zx2c4.com/WireGuard/about/src/tools/man/wg.8

Thx, for your input really appreciate it.

Fantastic, thank you for your hard work.
It seems wireguard will handle MTU changes beyond the default 1420 on its interfaces cleanly. Can I suggest that the MTU be made modifiable under set interfaces wireguard wg0 mtu <size>? This would be nice for situations where fragmentation between wireguard peer connections is acceptable.

ICMP Type 3, Code 4 messages are generated by the kernel, pmtud is not a wg feature, but I hear you. I see that often that ICMP is filtered away in general and that can create you a lot of grief. It's been always on my list since I started the implementation.

mtu implementation has been added for review, currently there seem to be a few issues with integration, so if you need it you can rebuilt the package via dpkg-buildbackage -tc -us -uc and download the sources from here: https://github.com/hagbard-01/vyos-1x

Is there still time for a small CLI change?

(20:05) cpo lnx01:~/vyos-build/packages/vyos-1x [current] # grep -r "name=\"port" interface-definitions/ | wc -l
7
(20:05) cpo lnx01:~/vyos-build/packages/vyos-1x [current] # grep -r "name=\"listen-port" interface-definitions/ | wc -l
1

Looking through already ported functionality to XML the usage for a listenport it "port" 7 times and "listen-port" only one time in wireguard.

@hagbard @dmbaturin can we rename set interfaces wireguard wg0 listen-port to set interfaces wireguard wg0 port to have a more consotent CLI?

@c-po yes, Sir not a problem at all, takes less than 30 secs. to implement. Once I know how to proceed with the sonarcloud issues...

I believe /usr/share/vyos/interface-types.json needs to be patched to handle wireguard interfaces so that they show up in show interfaces. Scratch that, it's there, but they don't seem to appear in show interfaces.

I'm gonna have a look asap, currently a little stuck with private live and preshared keys.

@Watcher7 /opt/vyatta/etc/netdevice.... found it, I try

I'm going to leave this task still open for a few days to track any issues, if nothing comes up, I'll close it off this week. Meanwhile I have a look into the documentation part, since more functionality has been added and some paths have changed (peers for instance).

documentation needs to be done.

Seems like the wireguard module is not available in the latest rolling version. (VyOS 1.2.0-rolling+201810261726)

EDIT: It works if you build vyos with kernel 4.18.11

vyos@rt01:~$ uname -a
Linux rt01 4.14.65-amd64-vyos #29 SMP Thu Oct 11 14:26:09 CEST 2018 x86_64 GNU/Linux
vyos@rt01:~$ generate wireguard keypair 
modprobe: FATAL: Module wireguard not found.
Traceback (most recent call last):
  File "/usr/libexec/vyos/op_mode/wireguard.py", line 83, in <module>
    check_kmod()
  File "/usr/libexec/vyos/op_mode/wireguard.py", line 38, in check_kmod
    raise ConfigError("modprobe wireguard failed")
vyos.base.ConfigError: modprobe wireguard failed