WireGuard privacy concerns

WireGuard privacy concerns

It appears that there is some inaccurate information being spread about WireGuard®'s privacy/security aspects, and a lot of that misinformation is coming from some of the leading VPN providers. There's a collection of quotes from some of those providers at https://restoreprivacy.com/wireguard/. Oddly enough, the most accurate information on that page is in the comments section from a user named "Bugs Bunny", who claims to have shared the article with the official #wireguard IRC channel (the one on freenode). The answers the people in that IRC channel gave are accurate but not very detailed, so we thought we would elaborate, as well as provide more information about our particular WireGuard implementation.

Here's some of the "WireGuard Cons" from that page, and a better explanation of why each statement is true or not.

1) Still under "heavy" development, not ready, not audited

This claim was true whenever this article was first written, but since then WireGuard has been audited. See https://www.wireguard.com/formal-verification/

2) WireGuard privacy concerns and logs

AzireVPN, one of the first VPNs to implement WireGuard, had this to say last year:

At AzireVPN, we care about our no-logging policy, that's why all of our servers are running on diskless hardware and all log files are piped to /dev/null.

But when it comes to WireGuard the default behaviour is to have endpoint and allowed-ip visible in the server interface, which does not really work with our privacy policy. We shouldn't know about your source IP and cannot accept having it visible on our servers.

This is the case with any networking software, be it OpenVPN or WireGuard. In WireGuard, the "endpoint" would be the client's real IP, and the "allowed-ip" would be the internal (10.10.x.x in our setup) IP. The kernel will always need to know both of these in order for it to know where to send packets, and anyone with root access to the server will be able to see these IPs.

AzireVPN attempted to get around these issues by hiring Jason Donenfeld to "write a rootkit-like module that removes the ability of an ordinary system administrator to query endpoint or allowed-ip information about WireGuard peers and disable the ability to run tcpdump".

From AzireVPN's blog:

Of course there are ways around these protections, but none that we actually know about, which means we can't be compelled to carry out something we don't know how to do.

This is silly because even though AzireVPN might not know a way around it, a lot of other people do.
It's simple really, if you can get root access to the live system (which is needed to run `wg` to get the IPs anyways), you can load a second rootkit that bypasses the one Jason wrote.

Something conveniently left out of AzireVPN's article is what Jason himself had this to say about this "rootkit" (to be fair, they did at least provide a link to the page containing this quote):

(from https://archive.is/ixN9A):

For clueless operators who wish to become more clueless.
Do not use this code unless you fully understand what it is not designed to do; this is the first sentence of the README for a good reason. In fact, just don't use it. It's mostly snake-oil. There are a million ways to subvert this. It's a fun little toy, but it's not really much beyond a toy.
This whole thing is incredibly stupid, but it is nonetheless an interesting exercise. If you have any sense at all, you won't go near this code and will discard this idea entirely. There are probably several ways to subvert it and a host of other subtle bugs. Some people might think that by hiding things from userspace, they actually hide things, but this could not be further from the truth.
However, if you simply want to be able to claim to people, "we don't have the ability to view internal or external IP addresses of any peers," and you really do lack the know-how to subvert this, then I suppose it might be somewhat useful. It's a strange property: this module only has utility in contexts where you don't know how to subvert it. This means that as you become smarter, this module will need to grow. This implies that either the guy writing it should be more knowledgeable than you are at the moment, or you yourself should be the author, exhausting all the current methods of subversion you can currently think of.

This next statement comes from Perfect Privacy:

Perfect Privacy argued that WireGuard is "not usable without logs" in an interesting post on their blog:

WireGuard has no dynamic address management, the client addresses are fixed. That means we would have to register every active device of our customers and assign the static IP addresses on each of our VPN servers. In addition, we would have to store the last login timestamp for each device in order to reclaim unused IP addresses. Our users would then not be able to connect your devices after a few weeks because the addresses would have been reassigned.

It is particularly important to us that we do not create or store any connection logs at all. Therefore, we cannot store the above registration and login data that would currently be required for WireGuard to operate.

This is partially true. WireGuard does not require logs, but it does require that those internal IPs (10.10.x.x in our setup) are stored somewhere, and that they are paired with the client's public key in a database or configuration file. There is no dynamic address management system built into WireGuard, but the claim that WireGuard needs to store the last login timestamp somewhere is false, as explained at the bottom of this page. 

The next one is from VPN.ac:

VPN.ac is not a no logs VPN provider. Therefore the logging and privacy issues with WireGuard may be less of an issue. Nonetheless, they still raised concerns:

Privacy considerations: by design, WireGuard isn't suitable for none/limited logging policies. Specifically, last public IP of user would be saved on the sever used to connect to and it can't be removed within a day as per our current privacy policy. At a later date we will likely make some tweaks to the source code to sanitize or remove the last used public IP.

This is also false. The last public IP of the user doesn't need to be saved. As explained above, the client's public IP will always need to be known to the kernel though, otherwise packets wouldn't know where to go.
See the bottom of this page for further information.

Next is ExpressVPN:

ExpressVPN raised similar concerns over WireGuard and its implications for privacy on their blog:

One of the challenges WireGuard faces is to ensure anonymity for VPNs. No single user should be statically allocated a single IP address, neither on a public nor a virtual network. A user's internal IP address might be discovered by an adversary (through WebRTC, for example), who might then be able to match it with records acquired from a VPN provider (through theft, sale, or legal seizure). A good VPN must be unable to match such an identifier to a single user. Currently, this setup is not easily achieved with WireGuard.

This statement it mostly true, if the provider were using an authentication system that ties clients to their payment transactions (I.e., a traditional user/pass accounting setup). If a single user is allocated the same internal IP every time they use WireGuard, then it's possible that something like WebRTC can be used in correlation attacks. However, we found a reasonable method that addresses this explained at the bottom of this page.

Last is AirVPN:

In their forums, AirVPN further explained why WireGuard simply does not meet their requirements:

Wireguard lacks dynamic IP address management. The client needs to be assigned in advance a pre-defined VPN IP address uniquely linked to its key on each VPN server. The impact on the anonymity layer is catastrophic;

Wireguard client does not verify the server identity (a feature so essential that it will be surely implemented when Wireguard will be no more an experimental sofware); the impact on security caused by this flaw is very high;

TCP support is missing (third party or anyway additional code is required to use TCP as the tunneling protocol, as you suggest, and that's a horrible regression when compared to OpenVPN);

there is no support to connect Wireguard to a VPN server over some proxy with a variety of authentication methods.

The dynamic IP management issue is addressed above and at the bottom of this page.
As for the claim that WireGuard "does not verify the server identity", this is false.
One of those people on WireGuard's IRC channel said it best:

"Wireguard client does not verify the server identity" is clear and utter bullshit.

The client's config includes the server's public key, which the client uses to verify the server.
The server's config includes the client's public key, which the server uses to verify the client.

Maybe AirVPN meant that WireGuard doesn't include a proper PKI structure for validating the server's TLS certificate against an included CA certificate, as OpenVPN does. That would be true, but WireGuard doesn't use PKI or TLS. You would exchange the client/server configs (which include the keys) using some out-of-band method such as a website, but that's the same with OpenVPN.

If a malicious actor were able to perform a man-in-the-middle attack against that out-of-band method, they could provide the user with a false WireGuard config that points them to a malicious server, and the server verification would succeed because the malicious server's public key would be in that client config (provided that the attacker was also able to add the client's public key to their malicious WireGuard server).
But that's also the case with OpenVPN. If the attacker can MITM whatever method the client uses to download their OpenVPN client config, the attacker could also embed their own CA certificate and point to a malicious OpenVPN server.

The claim that "TCP support is missing" or that there's no way to connect to WireGuard using a proxy is true as well, but that's one of the reasons WireGuard's codebase is so small, because you can do that with 3rd party software.

Those people on IRC say the same thing:

a: As for "there is no support to connect Wireguard to a VPN server over some proxy with a variety of authentication methods", this is obviously possible with additional software, and it's good that it's not part of the wg codebase itself.

a: Same for TCP support. Those people don't seem to understand that wg is not supposed to be a full turn-key VPN solution, but rather a basic building block in accordance with the UNIX philosophy.

b: I just read through that article and the ironic part is that the first thing they mention is wireguard's small, simple codebase compared to openVPN

b: what do they think those extra 600,000 lines in openVPN are doing?

AirVPN stated in their forum:

We will not use our customers as testers.

Nor will we, which is why we started testing WireGuard in 2016, and didn't make our implementation publicly available until April of 2019, and even then still included a disclaimer about the experimental nature of WireGuard.

Our WireGuard Implementation

The dynamic IP management issue

On our https://cryptostorm.is/wireguard page, users provide their client-side public key and their cryptostorm access token, then the page randomly generates a 10.10.x.x IP and a random preshared key for them to use. The page sends a request to a back-end API server, which adds that IP/PSK/key to all of the VPN servers and also stores the IP/PSK/key in a database (explained below).

If a user suspects that their internal 10.10.x.x IP is compromised (via WebRTC/STUN or whatever), they can go to https://cryptostorm.is/wireguard_man and remove that IP/PSK/key from the auth database (and all of our VPN servers), then replace them with new ones.

While this technically isn't a "dynamic IP" setup, it would be possible for a user to have a new 10.10.x.x IP & PSK every time they use WireGuard, if they were willing to remove their old one and replace it each time they wanted to connect with WireGuard.

Our anonymous token authentication system is used to prevent us from knowing the identity of the customers, or their WireGuard keys.

The auth database used by the API server to keep track of whether or not a WireGuard key is valid looks like:

| Field        | Type         | Null | Key | Default | Extra |
| pubkey_0     | varchar(44)  | YES  |     | NULL    |       |
| pubkey_1     | varchar(44)  | YES  |     | NULL    |       |
| pubkey_2     | varchar(44)  | YES  |     | NULL    |       |
| pubkey_3     | varchar(44)  | YES  |     | NULL    |       |
| pubkey_4     | varchar(44)  | YES  |     | NULL    |       |
| pubkey_5     | varchar(44)  | YES  |     | NULL    |       |
| psk_0        | varchar(44)  | YES  |     | NULL    |       |
| psk_1        | varchar(44)  | YES  |     | NULL    |       |
| psk_2        | varchar(44)  | YES  |     | NULL    |       |
| psk_3        | varchar(44)  | YES  |     | NULL    |       |
| psk_4        | varchar(44)  | YES  |     | NULL    |       |
| psk_5        | varchar(44)  | YES  |     | NULL    |       |
| cs_token     | varchar(129) | YES  |     | NULL    |       |
| duration     | int(11)      | YES  |     | NULL    |       |
| activated_at | varchar(20)  | YES  |     | NULL    |       |
| ip_0         | varchar(15)  | YES  |     | NULL    |       |
| ip_1         | varchar(15)  | YES  |     | NULL    |       |
| ip_2         | varchar(15)  | YES  |     | NULL    |       |
| ip_3         | varchar(15)  | YES  |     | NULL    |       |
| ip_4         | varchar(15)  | YES  |     | NULL    |       |
| ip_5         | varchar(15)  | YES  |     | NULL    |       |

The pubkey_* fields are the client's WireGuard public keys.

The cs_token field is the sha512 hash of the client's cryptostorm access token.

duration is the duration of that token in days. It's used to limit the number of WireGuard keys per CS token (1 month tokens can have 1 wg key, lifetime tokens can have 6, etc.).

activated_at is the unix timestamp of when that wg key (or CS token) was first activated. It's used by a cron job that checks whether a CS token has expired, in which case all the associated WireGuard keys are removed. It's also used to activate the CS token if it hasn't already been activated, since otherwise a client could buy a CS token but never use it, instead only using WireGuard, causing both the CS token and WireGuard key to never expire.

The psk_* fields are the preshared keys, and ip_* fields are the internal 10.10.x.x IPs.

The issue of "Endpoint" (real client) IPs

One serious problem we did notice with our implementation is that we were using "SaveConfig = true" in our server-side WireGuard configs.

From https://git.zx2c4.com/WireGuard/about/src/tools/man/wg-quick.8:

SaveConfig — if set to ‘true’, the configuration is saved from the current state of the interface upon shutdown.

The problem with this is that if a client is connected whenever the interface goes down or the server reboots, their real IP ("Endpoint") would get saved to the disk, which should never happen.

We could have changed our setup to not use SaveConfig and instead recreate the interface from scratch when the server came back up using the data from the auth DB, but SaveConfig was much easier to work with.

To address the issue of real IPs being saved to the hard drive, we removed a couple of lines from WireGuard's showconf.c:

*** showconf.c.orig 2019-05-04 20:29:17.314931044 -0500
--- showconf.c  2019-05-03 23:42:38.519785810 -0500
*** 70,96 ****
                printf(", ");
        if (peer->first_allowedip)

-       if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) {
-           char host[4096 + 1];
-           char service[512 + 1];
-           socklen_t addr_len = 0;
-           if (peer->endpoint.addr.sa_family == AF_INET)
-               addr_len = sizeof(struct sockaddr_in);
-           else if (peer->endpoint.addr.sa_family == AF_INET6)
-               addr_len = sizeof(struct sockaddr_in6);
-           if (!getnameinfo(&peer->endpoint.addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) {
-               if (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':'))
-                   printf("Endpoint = [%s]:%s\n", host, service);
-               else
-                   printf("Endpoint = %s:%s\n", host, service);
-           }
-       }
        if (peer->persistent_keepalive_interval)
            printf("PersistentKeepalive = %u\n", peer->persistent_keepalive_interval);

        if (peer->next_peer)
--- 70,79 ----

The code that's removed is what causes the real IP ("Endpoint") to get saved to the configuration file when the interface goes down.

Without that, the only thing saved is the internal IP, PSK, and client's public key (none of which can be used to identify the client, at least in our setup). 

We were also going to modify the `wg` command's code to redact client IPs from the `wg` output as well, but that would be dumb for the same reasons as using Jason's "rootkit" mentioned above. 
root access is required to run `wg`, and if someone has that they can just recompile WireGuard's `wg` using the original code in order to access those IPs in real-time.

Final thoughts

One last thing that none of the VPN providers above seem to mention is that in OpenVPN, it's still possible to obtain real client IPs without logging (or even packet sniffing) by using OpenVPN's management interface. This is how most VPN providers enforce their abuse policy.
Even though they might have their logs pointed to /dev/null (so they can honestly claim to be a "no-logging VPN"), they can still use that management interface to tie client IPs to accounts or payment transactions.

As explained on our https://cryptostorm.is/privacy page, thanks to our anonymous token authentication system, payment data isn't tied to access tokens.

So even though we can use `wg`, or OpenVPN's management interface, or tcpdump to see real client IPs, we don't have any way to tie those IPs to a client's payment info.
Since our WireGuard auth setup is integrated into our OpenVPN auth setup, neither can be used to identify a customer.

WireGuard is a registered trademark of Jason A. Donenfeld.
Posted on