How to: Roon Mobile over WireGuard on a UniFi USG

With the help of @Aaron_Turner, I managed to get Roon working over WireGuard VPN. Below you can find the steps I took to get it all to work. For more general background info, check this thread.

Some Assumptions

The software in use:

  • wireguard-vyatta-ubnt v1.0.20200908-v1.0.20200827
  • udp-proxy-2020 v0.0.5
  • UniFi Controller v6.0.27

The networks in use:

  • The public WAN ip address of the USG is 12.34.56.78 (eth0)
  • The corporate LAN network of the USG is 10.0.0.1/24 (eth1)
  • The WireGuard VPN network of the USG is 10.0.1.1/24 (wg0)

Authentication:

  • The USG is configured with an SSH key (~/.ssh/id-usg).

Conventions:

  • Command line statements are prefixed with a dollar sign ($). Do not copy/paste the dollar sign!
  • Insert the actual content of the specified file each time you see "(see ).

Installing WireGuard on USG

Download the package ugw3-v1 to your computer:

$ wget https://github.com/WireGuard/wireguard-vyatta-ubnt/releases/download/1.0.20200908-1/ugw3-v1-v1.0.20200908-v1.0.20200827.deb

Upload the package via SSH to the USG:

$ scp -i ~/.ssh/id-usg ugw3-v1-v1.0.20200908-v1.0.20200827.deb admin@10.0.0.1:

Login to your USG over SSH and install the deb package:

$ ssh -i ~/.ssh/id-usg admin@10.0.0.1
$ sudo dpkg -i ugw3-v1-v1.0.20200908-v1.0.20200827.deb

Configure WireGuard on client

Generate a public/private keypair for the client:

$ wg genkey | tee wg.private | wg pubkey >  wg.public

Create a WireGuard config file for the client:

[Interface]
PrivateKey = (see wg.private on client)
Address = 10.0.1.2/32
DNS = 10.0.0.8

[Peer]
PublicKey = (see wg.public on usg)
AllowedIPs = 10.0.0.0/24, 10.0.1.0/24
Endpoint = 12.34.56.78:51820

Configure WireGuard on USG

Generate a public/private keypair for the WireGuard server:

$ wg genkey | tee /config/auth/wg.key | wg pubkey >  wg.public

In a first stage, we’ll just configure the USG over CLI. These instructions do no survive a USG reboot/reprovision, and are solely to test our configuration. On the USG run:

$ configure

# Set up the wg0 interface.
set interfaces wireguard wg0 address 10.0.1.1/24
set interfaces wireguard wg0 listen-port 51820
set interfaces wireguard wg0 route-allowed-ips true
set interfaces wireguard wg0 private-key /config/auth/wg.key

# Set up the allowed peers (ie. clients).
set interfaces wireguard wg0 peer (see wg.public on usg) allowed-ips 10.0.1.2/32

# Setting up the firewall rules
set firewall name WAN_LOCAL rule 20 action accept
set firewall name WAN_LOCAL rule 20 protocol udp
set firewall name WAN_LOCAL rule 20 description 'WireGuard'
set firewall name WAN_LOCAL rule 20 destination port 51820

commit
save
exit

At this stage, you should have a working WireGuard setup. You can verify that a network interface (wg0) has been created and configured with the following commands:

$ sudo wg show
interface: wg0
  public key: (see pg.public on usg)
  private key: (see /config/auth/wg.key on usg)
  listening port: 51820

peer: (see wg.public on client)
  endpoint: (hidden)
  allowed ips: 10.0.1.2/32
  latest handshake: 1 hour, 31 minutes, 49 seconds ago
  transfer: 29.85 MiB received, 381.29 MiB sent
  persistent keepalive: every 25 seconds

$ sudo wg showconf wg0
[Interface]
ListenPort = 51820
PrivateKey = (see /config/auth/wg.key on usg)

[Peer]
PublicKey = (see wg.public on client)
AllowedIPs = 10.0.1.2/32
Endpoint = (hidden)
PersistentKeepalive = 25

Go ahead and try to connect fom the client to your network over WireGuard VPN. (Note that Roon still requires some more configuration.)

Once you verified that everything works, it’s time to make the changes persistent (so you won’t loose them when the USG is rebooted/reprovisioned).

Locate the exact location where the UniFi Controller expects a config.gateway.json for your site. See USG Advanced Configuration Using config.gateway.json for more info.

Create/edit the config.gateway.json. Make sure it contains this section:

{
  "interfaces": {
    "wireguard": {
      "wg0": {
        "address": [
          "10.0.1.1/24" // USG gateway address in wireguard subnet
        ],
        "firewall": {
          "in": {
            "name": "LAN_IN"
          },
          "local": {
            "name": "LAN_LOCAL"
          },
          "out": {
            "name": "LAN_OUT"
          }
        },
        "listen-port": "51820",  //Listen port - can be customised, adjust firewall port accordingly
        "mtu": "1500",
        "peer": [{
          "(see wg.public on usg)": { //Peer 1 Public Key
            "allowed-ips": [
              "10.0.1.2/32" //Peer IP address
            ],
            "persistent-keepalive": 25
          }
        }],
        "private-key": "/config/auth/wg.key",
        "route-allowed-ips": "true"
      }
    }
  }
}

To avoid any issues with your USG, make sure the config.gateway.json is a valid JSON file. You can use an online validator to check (e.g. https://jsonlint.com). Do not skip this step or hell might break loose.

Routing network packages across subnets / interfaces

Download the package udp-proxy-2020 for mips arch to your computer:

wget https://github.com/synfinatic/udp-proxy-2020/releases/download/v0.0.5/udp-proxy-2020-0.0.5-linux-mips64-static

Upload the package via SSH to the USG:

scp -i ~/.ssh/id-usg udp-proxy-2020-0.0.5-linux-mips64-static admin@10.0.0.1:

Login to your USG over SSH and make it executable:

$ ssh -i ~/.ssh/id-usg admin@10.0.0.1
$ mkdir -p ~/bin/
$ mv udp-proxy-2020-0.0.5-linux-mips64-static p ~/bin/udp-proxy-2020
$ chmod +x ~/bin/udp-proxy-2020

Now it’s time to start udp-proxy-2020. We basically want to route packages from 10.0.0.0/24 (eth1) to 10.0.1.0/24 (wg0) and vice versa. Given that Roon broadcasts on port 9003 we can execute:

sudo ./udp-proxy-2020 --port 9003 --interface eth1,wg0

As a last step, we need to make sure that udp-proxy-2020 runs whenever the USG reboots. This can be done by creating a new script at /config/scripts/post-config.d/udp-proxy-2020.sh:

#!/usr/bin/env bash

/home/admin/.bin/udp-proxy-2020 --port 9003 --interface eth1,wg0 &

Now change the owner to root and make it executable:

$ sudo chown root:root /config/scripts/post-config.d/udp-proxy-2020.sh
$ sudo chmod +x /config/scripts/post-config.d/udp-proxy-2020.sh

That’s it! We’re all set! Now for the moment we’ve all been waiting for, it’s finally time to see whether Roon works over WireGuard VPN:

  1. Disconnect the client from the local network (e.g disable wifi on your phone)
  2. Turn on WireGuard VPN on the client (e.g. flip the VPN switch on your phone)
  3. Open up Roon Remote and see whether it can find your Core and stream to the client

Pics or it didn’t happen:


Changelog

2020/10/17:

  • Add instructions on how to start udp-proxy-2020 after each boot of the USG

2020/10/17:

  • Initial version
8 Likes

Amazing work, much, much appreciated. Thanks very much.

About to give it ago, however you may have one extra “.” at the end here -

# Set up the allowed peers (ie. clients).
set interfaces wireguard wg0 peer (see wg.public on usg) allowed-ips 10.0.1.2./32
1 Like

Keen eye you have there. Just fixed it in my original post. Let me know if you spot any other mistakes.

Updated initial post with instructions on how to start udp-proxy-2020 after each boot of the USG.

Great writeup @Nepherte!

Btw, I ended up adding a link to this writeup on in the readme for udp-proxy-2020. Thanks again for taking the time to explain this in such detail!

1 Like

Appreciate the reference / credits (FYI you did spell the name wrong though, though you’re definitely not the first one :wink: )

Doh. Sorry. Fixed. Thanks for the heads up.

One small thing, you can’t run Roon app on a phone without WiFi even if you enable VPN, can’t on Android that’s for sure it just moans it has no WiFi and won’t get any further. So how does this work over cellular?

It’s not so much as totally disable WiFi, but rather make sure you’re not connected to a WiFi access point. In iOS that would look something like this (cfr. the white WiFi icon instead of blue):

On iOS the Roon app will work as long as you have a network connection, whether that’s 4G/5G or WiFi doesn’t matter. Not sure how Android works, perhaps others can pitch in here.

Hope that clarifies things

love that playing track! :wink:

If your not connected to WiFi it won’t even try to connect on Android its infuriating. Only way around it is to use another device as a hotspot and connect wirelessly to that. So if it’s just a phone your out of luck.

I see. That sucks for those using Android. I can confirm it works on iOS though.

I’m a big fan of Chase & Status (gotta love British drum ‘n’ bass) and only recently discovered their latest album RTRN II JUNGLE :slight_smile:

1 Like

Hi! I have tried using udp-proxy-2020 on a pfSense install. It works fine from one local interface/subnet to another, but not over a site2site OpenVPN. The error message I get is:

DEBU[0009]/home/vagrant/udp-proxy-2020/cmd/listen.go:191 main.(*Listen).sendPackets() ovpns1: Unable to send packet; no discovered clients

Has anyone experienced this? Any solutions?

Thanks :slight_smile:

You may want to check in the dedicated openvpn thread: How I got Roon working over OpenVPN (hard for me, easy for you)

Site to site is something I haven’t tested. are you running udp-proxy-2020 on only one end or both ends of the tunnel?

Hi, I am running it on one end at the moment. Looks like it is checking if there are connected “road warrior” clients?

I tested it on both client- and server-side (one end at a time). Any suggestions, Aaron?

I posted in the correct thread now: