Wireguard VPN access

When away from home, it's always nice to be able to access devices, servers, services, on my home network.

In addition to accessing my home network's resources, if I route all of my traffic through my home network, I get the added benifit of avoiding insecure wireless networks.

There have been more than a few adviseries aginst using a hotel's wireless network, at least unencrypted (see References section below for a few links).

Besides the security, and possible data manipulation, there are times when I'm in a hotel I want to access something on my home network, but don't want to punch a hole through my home firewall.

Using a Virtual Private Network (VPN) is a simple way to securly access the internet, though my home's internet connection, and keep prying eyes at bay.

The defacto standard for years was OpenVPN, but Wireguard has taken over as the new top dog, as it's more efficient and performs better.

This can be used anywhere that there is an untrusted internet connection (hotel, airport, Starbucks, etc).

Bonus is that with this setup, I have the ability to access device(s) on my home network while traveling.

Setup is pretty simple, once you've done it a time or two.

High level overview

Read this post for details, but this is the Wireguard VPN setup.

On both the client and server

  • Install Wireguard.

  • Generate private/public key pair.

  • Using the public key of the other side of the connection, configure the tunnel end point (TEP).

On the server

  • Using the client's public key, configure the server to allow connections from the client.

  • Decide on configuration option(s) to use, such as allowed IPs, DNS server settings, etc

Use client to enable Wireguard VPN connection

  • Bring up both TEPs to enable the tunnel.

Example settings

In our example, both the server and the client will generate private/public key pairs:

  • Server's private key: +BfzlVJ6o1n8hRvW4nGhpbhsiyWsCKgIEja6F8alsVg=

  • Server's public key: 5ifRv4mFRho36V65zq3QOc/YUebQzGF6fsRccJWVSnc=

  • Client's private key: +Dq6JSGNqpGE3nqdjXqkpSOgBYEnDiS435/QbqRHQms=

  • Client's public key: Ht9LnRQS33Q4mnyK7D1aN090zWku4KRrWm6hGrVnXVg=

The client will use the server's public key to encrypt data to send to the server. Only the server's private key can decrypt this data.

The server will use the client's public key to encrypt data to send to the client. Only the client's private key can decrypt this data.

Only the private key that was used to generate the public key can decrypt data encrypted with the public key, which is why you never give out your private key.

  • These instructions are listed in three sections:

    • The first section is for the SERVER.

    • The second section is for the CLIENT.

    • The third section is for the SERVER, after the client is configured.

Server setup

Most of the configuration is done on the server side, but neither side is difficult to configure, if you follow the steps correctly.

Enable network forwarding

If we want to connect to devices other than just the Wireguard server, we need to enable IP forwarding.

  • Enable IP forwarding by editing the /etc/sysctl.conf file and uncomment the line:

    net.ipv4.ip_forward=1
    
  • Load the new values for the current terminal session:

    sudo sysctl -p
    net.ipv4.ip_forward = 1
    

Allow tunnel traffic through the Ubuntu Firewall (ufw)

  • Check the ufw status:

    sudo ufw status
    Status: inactive
    
  • If UFW is active, then configure it to allow UDP port 51820 traffic:

    sudo ufw allow 51820/udp
    

Install Wireguard

udo apt-get update
sudo apt install wireguard -y

Generate private/public key pair

  • Use wg to generate the private key, then use the private key to generate the public key:

    wg genkey | sudo tee /etc/wireguard/privatekey | wg pubkey | sudo tee /etc/wireguard/publickey
    5ifRv4mFRho36V65zq3QOc/YUebQzGF6fsRccJWVSnc=
    

    Note that the output of the above command is the public key of the server.

    Copy this key, as we'll need it later, to configure the client.

Configure the server's tunnel interface

  • We are sending the tunnel traffic through the main interface, so to find the public network interface name:

    ip -o -4 route show to default | awk '{print $5}'
    enp1s0
    
  • Using the enp1s0 from the example above, this would be the server's tunnel interface config file /etc/wireguard/wg0.conf:

    [Interface]
    Address = 10.0.0.1/24
    SaveConfig = true
    ListenPort = 51820
    PrivateKey = +BfzlVJ6o1n8hRvW4nGhpbhsiyWsCKgIEja6F8alsVg=
    PostUp = ufw route allow in on wg0 out on enp1s0
    PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o enp1s0 -j MASQUERADE
    PreDown = ufw route delete allow in on wg0 out on enp1s0
    PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o enp1s0 -j MASQUERADE
    
  • The SaveConfig line ensures that when the Wireguard interface is shut down, any changes are saved to the config file.

  • If you don't use ufw, or it's inactive, you can skip the lines in the config file with ufw in them.

Activate the tunnel interface

  • Bring up the tunnel interface wg0:

    sudo wg-quick up wg0
    [#] ip link add wg0 type wireguard
    [#] wg setconf wg0 /dev/fd/63
    [#] ip -4 address add 10.0.0.1/24 dev wg0
    [#] ip link set mtu 1420 up dev wg0
    [#] ufw route allow in on wg0 out on enp1s0
    Rules updated
    Rules updated (v6)
    [#] iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o enp1s0 -j MASQUERADE
    
  • Check the status of the tunnel interface:

    sudo wg show wg0
    interface: wg0
    public key: 5ifRv4mFRho36V65zq3QOc/YUebQzGF6fsRccJWVSnc=
    private key: (hidden)
    listening port: 51820
    
  • Tunnel interface using ifconfig:

    ifconfig wg0
    wg0: flags=209<UP,POINTOPOINT,RUNNING,NOARP>  mtu 1420
            inet 10.0.0.1  netmask 255.255.255.0  destination 10.0.0.1
            unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 1000  (UNSPEC)
            RX packets 0  bytes 0 (0.0 B)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 0  bytes 0 (0.0 B)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    

Client setup

Once the server is configured, we need to generate keys on the client side, and import the server's public key.

Get the server's public key

In case you forgot to write it down...

  • On the server, run one of these commands:

    sudo cat /etc/wireguard/publickey
    5ifRv4mFRho36V65zq3QOc/YUebQzGF6fsRccJWVSnc=
    

    Or, if we want to use Wireguard's wg command:

    sudo wg show wg0 public-key
    5ifRv4mFRho36V65zq3QOc/YUebQzGF6fsRccJWVSnc=
    

Install Wireguard on the client

sudo apt-get update
sudo apt-get install wireguard -y

Generate the private/public key pair on the Linux client

wg genkey | sudo tee /etc/wireguard/privatekey | wg pubkey | sudo tee /etc/wireguard/publickey
Ht9LnRQS33Q4mnyK7D1aN090zWku4KRrWm6hGrVnXVg=

For Ubuntu 20.04 clients, work around a resolve bug

sudo ln -s /usr/bin/resolvectl /usr/local/bin/resolvconf

There is a bug open for Debian (see References section for link)

If you are running an version of Ubuntu before 20.04, the workaround won't work, but this can be solved by installing the openresolv package:

sudo apt install openresolv

Configure the client's tunnel interface

  • Get the client's private key for our client config:

    sudo cat /etc/wireguard/privatekey
    +Dq6JSGNqpGE3nqdjXqkpSOgBYEnDiS435/QbqRHQms=
    
  • In our example:

    • Client's private key: +Dq6JSGNqpGE3nqdjXqkpSOgBYEnDiS435/QbqRHQms=
    • Server's public key: 5ifRv4mFRho36V65zq3QOc/YUebQzGF6fsRccJWVSnc=
  • The resulting client tunnel interace config:

    sudo cat /etc/wireguard/wg0.conf
    [Interface]
    PrivateKey = +Dq6JSGNqpGE3nqdjXqkpSOgBYEnDiS435/QbqRHQms=
    Address = 10.0.0.2/24
    DNS = 192.168.1.5, 192.168.1.6
    
    [Peer]
    PublicKey = 5ifRv4mFRho36V65zq3QOc/YUebQzGF6fsRccJWVSnc=
    AllowedIPs = 0.0.0.0/0
    Endpoint = example.com:51820
    PersistentKeepalive = 25
    
  • I used the PersistentKeepalive = 25 setting to keep the connection open when traversing any firewalls. For more information on why this is necessary, see the Wireguard - Quick Start link in the References section below.

  • If your client's default DNS configuration uses a private DNS server (not accessible from the internet), you will need to add the DNS section of the configuration. If you don't add it, the DNS request will still be sent to the private DNS server, but it will go through the tunnel, so any DNS requests will fail.

Final server setup

Once the client's keys are generated, we need to import the client's public key into the server-side setup.

On the server, add the client to allow access

  • Using the client's public key, add the client to allowed tunnel endpoints:

    sudo wg set wg0 peer Ht9LnRQS33Q4mnyK7D1aN090zWku4KRrWm6hGrVnXVg= allowed-ips 10.0.0.2
    

Save the server config after adding the client

sudo wg-quick save wg0
  • After adding the client to the server with the sudo wg set wg0 peer and saving the configuration with the sudo wg-quick save wg0 commands, under Final server setup, the resulting wg0.conf file on the server side would look like this:

    [Interface]
    Address = 10.0.0.1/24
    SaveConfig = true
    ListenPort = 51820
    PrivateKey = +BfzlVJ6o1n8hRvW4nGhpbhsiyWsCKgIEja6F8alsVg=
    PostUp = ufw route allow in on wg0 out on enp1s0
    PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o enp1s0 -j MASQUERADE
    PreDown = ufw route delete allow in on wg0 out on enp1s0
    PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o enp1s0 -j MASQUERADE
    
    [Peer]
    PublicKey = Ht9LnRQS33Q4mnyK7D1aN090zWku4KRrWm6hGrVnXVg=
    AllowedIPs = 10.0.0.2/32
    

Bring up the tunnel and verify

Now that we (hopefully) have everything configured correctly, time to bring up the Wireguard VPN tunnel

On the client, bring up the tunnel interface

sudo wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.0.0.2/24 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] wg set wg0 fwmark 51820
[#] ip -4 route add 0.0.0.0/0 dev wg0 table 51820
[#] ip -4 rule add not fwmark 51820 table 51820

Note, that with the AllowedIPs = 0.0.0.0/0 in the client config, if we are accessing the Linux box via SSH, we'll get cut off, as we just told the client to send all traffic through the tunnel. We would cut off the branch we're standing on . . . I learned this the hard way . . .

Verify the tunnel on the server side

sudo wg show wg0
interface:: wg0
  public key: 5ifRv4mFRho36V65zq3QOc/YUebQzGF6fsRccJWVSnc=
  private key: (hidden)
  listening port: 51820

peer: Ht9LnRQS33Q4mnyK7D1aN090zWku4KRrWm6hGrVnXVg=
  endpoint: 51.205.110.33:50921
  allowed ips: 10.0.0.2/32
  latest handshake: 1 minute, 34 seconds ago
  transfer: 11.65 KiB received, 600 B sent

Verify the tunnel on the client side

sudo wg show wg0
interface: wg0
  public key: Ht9LnRQS33Q4mnyK7D1aN090zWku4KRrWm6hGrVnXVg=
  private key: (hidden)
  listening port: 50921
  fwmark: 0xca6c

peer: 5ifRv4mFRho36V65zq3QOc/YUebQzGF6fsRccJWVSnc=
  endpoint: 162.143.28.61:51820
  allowed ips: 0.0.0.0/0
  latest handshake: 1 minute, 4 seconds ago
  transfer: 10.93 KiB received, 25.16 KiB sent
  persistent keepalive: every 25 seconds

All traffic will now flow through the tunnel.

Simple test verifying DNS and ICMP

  • Send a single ICMP request from the client to google.com

    ping -c1 www.google.com
    
  • On the server, use tcpdump to capture the traffic, to show it traversing the tunnel:

    sudo tcpdump -nni any host 10.0.0.2 or icmp or port 53
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on any, link-type LINUX_SLL (Linux cooked v1), capture size 262144 bytes
    12:43:35.749283 IP 10.0.0.2.42582 > 192.168.1.5.53: 18428+ [1au] A? www.google.com. (43)
    12:43:35.749287 IP 192.168.1.182.42582 > 192.168.1.5.53: 18428+ [1au] A? www.google.com. (43)
    12:43:35.749302 IP 10.0.0.2.59636 > 192.168.1.5.53: 40314+ [1au] AAAA? www.google.com. (43)
    12:43:35.749315 IP 192.168.1.182.59636 > 192.168.1.5.53: 40314+ [1au] AAAA? www.google.com. (43)
    
    12:43:35.749595 IP 192.168.1.5.53 > 192.168.1.182.42582: 18428 1/0/1 A 142.251.32.164 (59)
    12:43:35.749617 IP 192.168.1.5.53 > 10.0.0.2.42582: 18428 1/0/1 A 142.251.32.164 (59)
    12:43:35.782867 IP 192.168.1.5.53 > 192.168.1.182.59636: 40314 1/0/1 AAAA 2607:f8b0:4000:818::2004 (71)
    12:43:35.782890 IP 192.168.1.5.53 > 10.0.0.2.59636: 40314 1/0/1 AAAA 2607:f8b0:4000:818::2004 (71)
    
    12:43:35.898450 IP 10.0.0.2 > 142.251.32.164: ICMP echo request, id 7, seq 1, length 64
    12:43:35.898511 IP 192.168.1.182 > 142.251.32.164: ICMP echo request, id 7, seq 1, length 64
    12:43:35.925275 IP 142.251.32.164 > 192.168.1.182: ICMP echo reply, id 7, seq 1, length 64
    12:43:35.925342 IP 142.251.32.164 > 10.0.0.2: ICMP echo reply, id 7, seq 1, length 64
    
  • In lines 4-7, we see the DNS requests (IPv4/A and IPv6/AAAA) come in through the tunnel, get NAT'd from the client's IP (10.0.0.2) to the Wireguard server's IP (192.168.1.182) and sent to the configured DNS server of 192.168.1.5.

  • Lines 9-12 are the DNS replies for google.com

  • Finally we see the ICMP request/replies in lines 14-17 traversing the tunnel.

Web browser test

Besides just accessing the internet through my home connection, I can also access my local (192.168.1.0/24) network:

From my hotel room, accessing the web interface for one of my Pihole instances:

web GUI of Pihole

Configuring multiple clients

  • Follow the same client instructions to generate a key pair.

  • On the server side, add the additional client(s) the same way you added the first client.

Do not use the same key pair on multiple clients. Technically this will work, but from an accountability and security standpoint . . . don't do it.

  • Example /etc/wireguard/wg0.conf server file, with multiple clients configured:

    [Interface]
      Address = 10.0.0.1/24
    SaveConfig = true
    ListenPort = 51820
    PrivateKey = +BfzlVJ6o1n8hRvW4nGhpbhsiyWsCKgIEja6F8alsVg=
    PostUp = ufw route allow in on wg0 out on enp1s0
    PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o enp1s0 -j MASQUERADE
    PreDown = ufw route delete allow in on wg0 out on enp1s0
    PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o enp1s0 -j MASQUERADE
    
    [Peer]
    PublicKey = Ht9LnRQS33Q4mnyK7D1aN090zWku4KRrWm6hGrVnXVg=
    AllowedIPs = 10.0.0.2/32
    
    [Peer]
    PublicKey = CHCvYVqTcBsb/AD/Y6wuliQ8TDFFmcQaMqxsGN6mU2M=
    AllowedIPs = 10.0.0.3/32
    

Notes

  • I would highly recommend using a port other than 51820, as most of the Wireguard documentation refers to it, so it's a semi-well known port. Anyone trying to hack your network will quickly realize port 51820 is most likely Wireguard, so use something else. Security through obscurity . . .

    • In OSX, iOS, Android, Windows, etc (GUI-based), editing the config is easy, just change the port from 51820 to the new port using the GUI.

    • On a Linux client, there is the wg-quick script that is used, so that will need to be edited, or it will still use port 51820.

      • Using sed, change the port from 51820 to 51821:

        sed -i 's/51820/51821/g' /usr/bin/wg-quick
        
    • Either use the port forwarding feature of your router to change the port for the client traffic, keeping the default 51820 port on the server OR

    • Change the port Wireguard listens on, in the server configuration file /etc/wireguard/wg0.conf.

  • As with any private/public key pair, never, never, never give out your server's private key. Treat your private key as you would a highly embarrassing and compromising photo of yourself . . . . don't let anyone ever see it!

  • The client private key should be kept private by the client, but there are some situations where that wouldn't be the case, namely if you were setting up Wireguard for someone not technical, to give them access to your system/network (aka, family members).

  • To remove a client peer from the server's config, using the original example:

    sudo wg set wg0 peer Ht9LnRQS33Q4mnyK7D1aN090zWku4KRrWm6hGrVnXVg= remove
    
    sudo wg-quick save wg0
    

References

Wireguard - Fast, Modern, Secure VPN Tunnel https://wireguard.com/

How to Set Up WireGuard VPN on Ubuntu 20.04 https://linuxize.com/post/how-to-set-up-wireguard-vpn-on-ubuntu-20-04/

How To Set Up WireGuard on Ubuntu 20.04 https://www.digitalocean.com/community/tutorials/how-to-set-up-wireguard-on-ubuntu-20-04

Wireguard - Quick Start - NAT and Firewall Traversal Persistence https://www.wireguard.com/quickstart/#nat-and-firewall-traversal-persistence

Debian Bug report logs - #939904 - systemd should ship resolvconf symlink in some package https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=939904

Wi-Fi security: FBI warns of risks of using wireless hotel networks https://www.techrepublic.com/article/wi-fi-security-fbi-warns-of-risks-of-using-wireless-hotel-networks/

The Hotel Hackers Are Hiding in the Remote Control Curtains https://www.bloomberg.com/news/features/2019-06-26/the-hotel-hackers-are-hiding-in-the-remote-control-curtains

Federal Bureau of Investigations - A COVID 19-Driven Increase in Telework from Hotels Could Pose a Cyber Security Risk for Guests https://www.ic3.gov/Media/Y2020/PSA201006