Rathole

· Shell Yeah!

Expose Services from Behind CG-NAT with Rathole

Stuck behind pesky CG-NAT? Don't have access to the router to forward ports? Maybe you travel and your laptop is your homelab. Or maybe you simply don't want to reveal your home IP to visitors. rathole will expose your services to the internet - no port forwarding required!

Say hi to rathole #

rathole (lack of capitalization is intentional) is a secure, stable and high-performance reverse proxy for NAT traversal, written in Rust. It can help to expose the service on the device behind the NAT to the Internet, via a server with a public IP.

Prerequisites #

rathole overview

Setting up rathole #

Download the latest version of rathole on the local machine and the VPS #

1wget https://github.com/rapiz1/rathole/releases/latest/download/rathole-x86_64-unknown-linux-gnu.zip
2unzip rathole-x86_64-unknown-linux-gnu.zip
3chmod +x ./rathole
4mv rathole /usr/local/bin/
5rm rathole-x86_64-unknown-linux-gnu.zip

ℹ️

Download URL might be different when you read this. Use the correct URL for your architecture from the GitHub repo.

Keygen #

Run rathole --genkey x25519 to generate keypairs on both the local machine and server. We'll copy them from the terminal history later.

## server side
Private Key:
uO3qkGhfId7josk54IkksbbDn3iLysq77ty2bPOiFXA=

Public Key:
bnXjhbLKrzKR4gjTr+DdYlalu7mvqT/QQtCur6DEnyo=

## client side
Private Key:
CJHbyVBCnIFXW9zz4kTvrQQWJfo0257MDYpq0KqaPlI=

Public Key:
sKDWEizIaVtZb+6VVcrab5KRgFuTbOnMJgnNSj/e4TM=

⚠️

Do not share private keys between devices.

Create a directory to store config files on both the local machine and server:

mkdir /etc/rathole

Configure the server #

In this example we'll proxy WireGuard server and Home Assistant. I have WireGuard listening on port 59509 and Home Assistant on port 8123. This is what my rathole config would look like on the VPS:

 1[server]
 2bind_addr = "123.123.123.123:1234"
 3default_token = "iJT5nWLo6fYI8zMxHPeDE7ok"
 4
 5[server.transport]
 6type = "noise"
 7
 8[server.transport.noise]
 9pattern = "Noise_KK_25519_ChaChaPoly_BLAKE2s"
10local_private_key = "uO3qkGhfId7josk54IkksbbDn3iLysq77ty2bPOiFXA="
11remote_public_key = "sKDWEizIaVtZb+6VVcrab5KRgFuTbOnMJgnNSj/e4TM="
12
13[server.services.hassio]
14bind_addr = "127.0.0.1:32625"
15
16[server.services.wg]
17bind_addr = "0.0.0.0:28827"
18type = "udp"

The local_private_key is the private key generated on the VPS and remote_public_key is the public key generated on the local machine.

Replace 123.123.123.123 with the IP address of the VPS and 1234 with a random port number.

We're binding Home Assistant to 127.0.0.1 because we want it accessible only through Caddy.

The default type is tcp so it does not have to be explicitly mentioned for HTTP(S) traffic. For WireGuard we do have to mention it.

We're binding WireGuard on all interfaces (0.0.0.0) so it's accessible directly via the IP of the VPS.

The service ports don't have to be the same between the VPS and local network.

default_token can be any random string. This will be shared between client and server.

Save your config at /etc/rathole/rathole.toml.

Now let's test it: rathole -s /etc/rathole/rathole.toml

If you see no errors, we can move on to creating a service file so it automatically runs at boot.

Service file #

We'll create a service file at /etc/systemd/system/rathole.service with the following content:

[Unit]
Description=Rathole Server Service
After=network.target

[Service]
Type=simple
Restart=on-failure
RestartSec=5s
ExecStart=/usr/local/bin/rathole -s /etc/rathole/rathole.toml
LimitNOFILE=1048576

[Install]
WantedBy=multi-user.target

We'll then reload systemd: systemctl daemon-reload.

Start rathole service: systemctl start rathole.service.

And enable it: systemctl enable rathole.service.

Configure the local machine #

Corresponding client-side config would look like this:

 1[client]
 2remote_addr = "123.123.123.123:1234"
 3default_token = "iJT5nWLo6fYI8zMxHPeDE7ok"
 4
 5[client.transport]
 6type = "noise"
 7
 8[client.transport.noise]
 9pattern = "Noise_KK_25519_ChaChaPoly_BLAKE2s"
10local_private_key = "CJHbyVBCnIFXW9zz4kTvrQQWJfo0257MDYpq0KqaPlI="
11remote_public_key = "bnXjhbLKrzKR4gjTr+DdYlalu7mvqT/QQtCur6DEnyo="
12
13[client.services.hassio]
14local_addr = "localhost:8123"
15
16[client.services.wg]
17local_addr = "localhost:59509"
18type = "udp"

The local_private_key is the private key generated on the local machine and remote_public_key is the public key generated on the VPS.

Save your config at /etc/rathole/rathole.toml. Then test it: rathole -c /etc/rathole/rathole.toml.

If you see Control channel established, you've configured everything correctly so far. Let's create the service file.

Service file #

Create a service file at /etc/systemd/system/rathole.service with the following content:

[Unit]
Description=Rathole Client Service
After=network.target

[Service]
Type=simple
DynamicUser=yes
Restart=on-failure
RestartSec=5s
ExecStart=/usr/local/bin/rathole -c /etc/rathole/rathole.toml
LimitNOFILE=1048576

[Install]
WantedBy=multi-user.target

Reload systemd, start service and enable service the same way as on the VPS.

Both the server and client rathole services will automatically start on boot.

Setting up Caddy #

Configuration #

Install Caddy on the VPS.

Edit configuration file at /etc/caddy/Caddyfile:

1hass.doma.in {
2    reverse_proxy localhost:32625
3}

Use your own sub.doma.in.

Restart Caddy: systemctl restart caddy.service.

Home Assistant should now be accessible at hass.doma.in with valid TLS certificates and WireGuard should be accessible at the IP of the VPS (or any subdomain pointed at VPS) at port 28827.

Conclusion #

To add more services simply use the Home Assistant section as a template and replace the port number with the correct one and change the service name; hassio with any unique name (without spaces).

Happy tunneling!