Skip to content

This is a how-to guide to set up WireGuard VPN as a gateway

License

Notifications You must be signed in to change notification settings

sergibarroso/wireguard-vpn-setup

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Site-2-site WireGuard VPN set up guide

Intro

This is a step-by-step guide on how to set up a WireGuard site-2-site VPN.

This solution connects both sites, secures the connection between both edge's LAN clients, and routes all traffic going to the internet through site Y gateway as we can see in the following diagram.

architecture

Keep in mind that the client site acts as the source of internet connectivity and not the server site as should be expected. The reason is that we have control over the gateway on the site Y but not on the site X.

The solution is perfect for sending the NanoPi client box to any friend anywhere in the world, and have access to his/her internet connection, no set up from their side is required, and you can also control the remote NanoPi from home.

Quite cool, isn't it? :)

Requirements

OS Installation

Copy OS to SD Card

Follow the official Armbian documentation to get the image into the SD card.

Once done, insert it in the NanoPi R2S

Booting NanoPi R2S

  • Plug the ethernet cable
  • Plug the power cable
  • Armbian uses DHCP by default so once you know the IP address assigned from your DHCP server you can SSH into it
  • SSH into the box by ssh root@<IP> and the default password is 1234
  • Immediately after login the first time it will ask:
    • Change root password and create a new regular user account
    • Set up the timezone and language

Common set up

Run the steps below on both NanoPi:

  • Set the hostname (replace <NEW_HOSTNAME> with the name you desire)

    sed -i "s/$HOSTNAME/<NEW_HOSTNAME>/g" /etc/hostname /etc/hosts
  • Upgrade the system to the latest version of all packages by running:

    apt update && apt -y upgrade
  • Install WireGuard

    apt install -y wireguard
  • Install iptables

    apt install -y iptables

WireGuard Server setup

  • Create a directory to store the keys and set strict permissions

    mkdir /etc/wireguard/keys
    chmod 700 /etc/wireguard/keys
  • Generate the server's private key by running the following command

    wg genkey > /etc/wireguard/keys/private.key
  • Use the output from the previous command to generate the server's public key

    cat /etc/wireguard/keys/private.key | wg pubkey > /etc/wireguard/keys/public.key
  • Set strict permissions on key files

    chmod 400 /etc/wireguard/keys/*.key
  • Create a WireGuard config file

    nano /etc/wireguard/wg0.conf

    Add the content:

    [Interface]
    # Configuration for the server
    
    # Set the IP subnet that will be used for the WireGuard network.
    # 10.222.0.1 - 10.222.0.255 is a memorable preset that is unlikely to conflict.
    Address = 10.222.0.1/24
    
    # The port that will be used to listen to connections. 51820 is the default
    ListenPort = 51820
    
    # The output of `wg genkey` for the server.
    PrivateKey = <SERVER_PRIVATE_KEY>
    
    # Set DNS resolver to our VPN client, preventing DNS leaks.
    DNS = 10.222.0.2
    
    # Set MTU
    MTU = 1420
    
    # Enable ip forwarding in all interfaces
    PreUp = sysctl -w net.ipv4.ip_forward=1
    
    # Allowing any traffic from <LAN_NETWORK_INTERFACE> (internal) to go over %i (tunnel):
    PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o <LAN_NETWORK_INTERFACE> -j MASQUERADE
    PostUp = iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
    
    PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o <LAN_NETWORK_INTERFACE> -j MASQUERADE
    PostDown = sysctl -w net.ipv4.ip_forward=0
    
    [Peer]
    # Configuration for the server's client Peer
    
    # The output of `echo "client private key" > wg pubkey`.
    PublicKey = <CLIENT_PUBLIC_KEY>
    
    # The IP address that this client is allowed to use.
    AllowedIPs = 0.0.0.0/0
    
    # Ensures that your home router does not kill the tunnel, by sending a ping
    # every 25 seconds.
    PersistentKeepalive = 25
    

    Pay attention to the <CLIENT_PUBLIC_KEY> because we still don't have this. Caution: don't use the one from the server.

    Replace <LAN_NETWORK_INTERFACE> for the name of the interface where the server is connected. On the NanoPi R2S, end0 is the WAN port and lan0 is the LAN port, set the one you're using.

WireGuard Client Setup

  • Create a directory to store the keys and set strict permissions

    mkdir /etc/wireguard/keys
    chmod 700 /etc/wireguard/keys
  • Generate client's private key

    wg genkey > /etc/wireguard/keys/private.key
  • Use the output from the previous command to generate the client's public key

    cat /etc/wireguard/keys/private.key | wg pubkey > /etc/wireguard/keys/public.key

    At this point, you can take the content of the client's public key and add it to the server's WireGuard config on the previous section.

  • Set strict permissions on key files

    chmod 400 /etc/wireguard/keys/*.key
  • Create a WireGuard config file

    nano /etc/wireguard/wg0.conf

    Add the content:

    [Interface]
    # Configuration for the client
    
    # The IP address that this client will have on the WireGuard network.
    Address = 10.222.0.2/24,<LOCAL_IP_NET> # (Such as 192.168.1.0/24)
    
    # Set MTU
    MTU = 1492
    
    # The private key you generated for the client previously.
    PrivateKey = <CLIENT_PRIVATE_KEY>
    
    # Enable traffic to be passed from the server network to the private subnet of the client
    PreUp = sysctl -w net.ipv4.ip_forward=1
    PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o <LAN_NETWORK_INTERFACE> -j MASQUERADE
    PostUp = iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
    
    PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o <LAN_NETWORK_INTERFACE> -j MASQUERADE
    PostDown = sysctl -w net.ipv4.ip_forward=0
    
    [Peer]
    # Configuration for the server to connect to
    
    # The public key you generated for the server previously.
    PublicKey = <SERVER_PUBLIC_KEY>
    
    # The WireGuard server to connect to.
    Endpoint = <SERVER_PUBLIC_ENDPOINT>:<SERVER_PUBLIC_PORT>
    
    # The subnet this WireGuard VPN is in control of.
    AllowedIPs = 0.0.0.0/0
    
    # Ensures that your home router does not kill the tunnel, by sending a ping
    # every 25 seconds.
    PersistentKeepalive = 25
    

    Replace the following values:

    <SERVER_PUBLIC_KEY> with the public key generated in the server machine. <SERVER_PUBLIC_ENDPOINT> with the public DNS name of the WireGuard server. <SERVER_PUBLIC_PORT> with the port exposed on the server network. <LAN_NETWORK_INTERFACE> for the name of the interface where the server is connected. On the NanoPi R2S, end0 is the WAN port and lan0 is the LAN port, set the one you're using.

  • Now that we've set up both server and client we can start WireGuard on both machines:

    wg-quick up wg0

    You should see an output like:

    [#] ip link add wg0 type wireguard
    [#] wg setconf wg0 /dev/fd/63
    [#] ip -4 address add 10.222.0.1/24 dev wg0
    [#] ip link set mtu 1420 up dev wg0

    In case you see an error like the following, please reboot your NanoPi by running reboot:

    [#] ip link add wg0 type wireguard
    Error: Unknown device type.
    Unable to access interface: Protocol not supported
    [#] ip link delete dev wg0
    Cannot find device "wg0"
  • Check WireGuard interface

    # wg show
    interface: wg0
      public key: <SERVER_PUBLIC_KEY>
      private key: (hidden)
      listening port: 51820
    
    peer: <CLIENT_PUBLIC_KEY>
      endpoint: <CLIENT_IP>:36010
      allowed ips: 10.222.0.2/32
      latest handshake: 32 seconds ago
      transfer: 732 B received, 500 B sent
      persistent keepalive: every 25 seconds

    If you don't have any peer definition means that the tunnel didn't work.

    At this point, you should be able to bring up both Wireguard interfaces and ping across both ends by:

    Running this in both ends:

    wg-quick up wg0

    And then try to ping the other host

    ping <REMOTE_WG_IP>
  • Enable SystemD interface

    To make sure that systemd creates the interface every time the system starts, we have to enable it by:

    # systemctl enable wg-quick@wg0
    Created symlink /etc/systemd/system/multi-user.target.wants/wg-quick@wg0.service /lib/systemd/system/wg-quick@.service.

At this point, you should be able to ping the server from the client and through your new VPN.

TIP: In case we do changes in the WireGuard config and we want to apply them without interrupting the actual connection, run: wg syncconf wg0 <(wg-quick strip wg0)

Dynamic DNS

A dynamic DNS server is useful when we can't have static IP addresses on the public network. This solution assumes that we don't have them and we actually don't need them because it is enough to have a dynamic DNS name set up to be good to go. I'm personally using YDNS, but there are hundreds of services available out there.

We have to run this in both boxes with different names (of course).

  • Installing curl

    apt install -y curl
  • Get the YDNS updater

    curl -o /usr/local/bin/updater.sh https://raw.githubusercontent.com/ydns/bash-updater/master/updater.sh
  • Give it execution permissions

    chmod +x /usr/local/bin/updater.sh
  • Edit the file and set your information

    # nano /usr/local/bin/updater.sh
    [...]
    YDNS_USER="<EMAIL>"
    YDNS_PASSWD="<SECRET>"
    YDNS_HOST="<HOST>" # This have to be different on both boxes
    [...]
  • Add the script as a PreUp condition for WireGuard config

    nano /etc/wireguard/wg0.conf

    Add the following content inside the [Interface] section

    With the content:

    PreUp = /usr/local/bin/updater.sh -V
    

Network testing

To test the network bandwidth between the two Wireguard nodes we will use iperf3. Meaning, we will need to install it on both, the Wireguard client and the server:

sudo apt install iperf3

Once we have the tool installed we can proceed by running the following command on the Wireguard client to listed to iperf requests:

iperf3 -s

And on the Wireguard server we run the iperf client

iperf3 -c 10.222.0.2

We should see on both sides something like:

Connecting to host 10.222.0.2, port 5201
[  5] local 10.222.0.1 port 46702 connected to 10.222.0.2 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  16.8 MBytes   140 Mbits/sec    0   7.24 MBytes
[  5]   1.00-2.00   sec  19.5 MBytes   164 Mbits/sec    0   7.30 MBytes
[  5]   2.00-3.00   sec  21.1 MBytes   177 Mbits/sec    0   7.33 MBytes
[  5]   3.00-4.00   sec  19.4 MBytes   163 Mbits/sec    0   7.33 MBytes
[  5]   4.00-5.00   sec  20.9 MBytes   175 Mbits/sec    0   7.33 MBytes
[  5]   5.00-6.00   sec  19.5 MBytes   164 Mbits/sec    0   7.33 MBytes
[  5]   6.00-7.00   sec  20.8 MBytes   174 Mbits/sec    0   7.33 MBytes
[  5]   7.00-8.00   sec  20.6 MBytes   173 Mbits/sec    0   7.33 MBytes
[  5]   8.00-9.00   sec  19.5 MBytes   164 Mbits/sec    0   7.33 MBytes
[  5]   9.00-10.00  sec  20.9 MBytes   175 Mbits/sec    0   7.33 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate           Retr
[  5]   0.00-10.00  sec   199 MBytes   167 Mbits/sec    0     sender
[  5]   0.00-10.17  sec   198 MBytes   163 Mbits/sec          receiver

iperf Done.

Good practices and optional features

Watchdog

What is a watchdog

A watchdog is an electronic timer used for monitoring hardware and software functionality. The software uses a watchdog timer to detect and recover fatal failures.

Why using a watchdog

We use a watchdog to make sure we have a functional VPN. If a problem comes up, the computer should be able to recover itself back to a functional state. We will configure the board to reboot if WireGuard link is down for too long, or a specific process isn’t running anymore.

Set up the watchdog software

  • Install the watchdog software

    apt install watchdog
  • Configure the watchdog to monitor WireGuard network

    nano /etc/watchdog.conf

    Edit the following lines:

    log-dir = /var/log.hdd/watchdog
    
    interface = wg0
    
    ping = <REMOTE_WG_IP>
    
    retry-timeout = 300
    
    interval = 30
    
  • Enable and start the service

    systemctl stop watchdog
    systemctl enable watchdog
    systemctl start watchdog

Unattended security updates

Security updates are crucial to keep our system safe from threats. Even tho we don't have so many services open to the world, one bug is enough to allow attackers to break into our system.

apt install -y unattended-upgrades

The default set up of this package installs security updates for the current release. If you want to update all packages when available, take a look at the /etc/apt/apt.conf.d/50unattended-upgrades.

To test the package behavior, we can run:

unattended-upgrade --debug --dry-run

Remote log server

Nanopi has a very limited amount of store and it's preferable to send logs to a remote log server.

The setup of a remote log server is out of the scope of this how-to but there are plenty of good documentation out there. We assume that you already have a server ready.

Edit /etc/rsyslog.conf and add this at the beginning of the file:

*.- @@<LOG_SERVER_IP>:514

The two @@ symbols indicates the usage of TCP protocol. Use only one symbol in case you need UDP.

Log rotate

Armbian in NanoPi has the logs located in two directories. The first is a ramdisk (/var/log/) which is usually around 50MB size. This is definitely not enough to keep our logs for more than a week, and depending on how much connection we have a day will not even hold 24h of logs before you start getting errors such as:

cannot write to log file '/var/log/xxx.log': No space left on device

The second one is located in the root partition (/var/log.hdd/).

The good practice here would be to save all logs in the disk, or at least safekeeping a compressed copy in the disk for security.

But if you're using this at home and you don't care much about them apart from realtime debugging when errors happen, then you can basically discard all logs after a day using logrotate :)

Let's start by increasing the /var/log ramdisk from 50MB to 100MB.

Edit /etc/default/armbian-ramlog and set SIZE to 100M.

apply the changes by running systemctl restart armbian-ramlog.service

Now, let's move to Logrotate. The main config file is located at /etc/logrotate.conf and then all sort of directory specific Logrotate definitions inside /etc/logrotate.d, let's first edit the default behaviour by:

nano /etc/logrotate.conf

Replace the content of the file for this:

# rotate log files daily
daily

# Old versions are removed
rotate 0

# create new (empty) log files after rotating old ones
create

# uncomment this if you want your log files compressed
compress

# packages drop log rotation information into this directory
include /etc/logrotate.d

Now let's see what we have inside /etc/logrotate.d/ directory:

ls /etc/logrotate.d/
alternatives  apt  armbian-hardware-monitor  btmp  chrony  dpkg  rsyslog  wtmp

And what I'm going to do here is delete everything and create a new config file called nanopi. So let's remove everything:

rm /etc/logrotate.d/*

And now let's create the new config file at /etc/logrotate.d/nanopi with the following content:

/var/log.hdd/*.log /var/log.hdd/*/*.log {
  daily
  rotate 0
  create
  missingok
}

/var/log/*.log /var/log/*/*.log {
  daily
  rotate 0
  create
  missingok
}

What this config is going to do is rotate all log files in /var/log/ and /var/log.hdd as well their child directories.

This can be tested by:

logrotate -d /etc/logrotate.d/nanopi

The -d flag will list each log file it is considering to rotate.

As Logrotate is set up to run daily via CronD we don't have to do any further change.

SSH hardening

These are just some good practices to hardening our SSH daemons, especially when they are publically available.

Add those lines somewhere inside the /etc/ssh/sshd_config file:

# Disable root login
PermitRootLogin no

# Disable password authentication
PasswordAuthentication no

To apply the previous config, just restart the SSH daemon:

systemctl restart ssh

Clean the system

  • Disable unused SystemD services

    systemctl stop wpa_supplicant systemd-rfkill.service systemd-rfkill.socket hostapd
    systemctl disable wpa_supplicant systemd-rfkill.service systemd-rfkill.socket hostapd

References

To build this guide, I've used several references, from blogs, other how-to and man pages.

About

This is a how-to guide to set up WireGuard VPN as a gateway

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published