Categories
Networking

VPS Passthrough Hosting
Host a large website or application with a modest VPS

Difficulty: Intermediate to High

Have you ever wanted to host a website or other web service without exposing your home IP address or paying for a gigantic VPS (Virtual Private Server)? While VPSs tend to be rather generous on bandwidth they tend to be rather stingy on storage space and general compute power. If I want the VPS with 500GB of storage space to host a clearnet Bitcoin node, I’m going to have to pay a pretty high reoccurring fee. I’m going to explain a method which leverages Firewalld and Wireguard VPN to allow public hosting with a very modestly priced VPS. With this method you’ll be able to leverage your existing home storage and compute resources to do the heavy lifting on your home network while getting the benefit making your application public without exposing your home IP address. This solution will work even if you are stuck behind the dreaded Carrier Grade NAT.

The idea is that you’ll actually run your service from your home network but through the magic of VPN and NAT (Network Address Translation) technology the service can be made available on the public internet. I run a VM (Virtual Machine) host via KVM at my home but this should work equally well with a Raspberry Pi or similar less powerful computer. I would consider this deployment to be of intermediate to high level difficulty depending on your knowledge of Linux and general networking. For this example I’m going to make my BTCPay Server, which I run on my local network, publicly available on the IPv4 internet. I’ll do this without needing an expensive VPS with enough storage for a full Bitcoin node or without exposing my home IP address. See the image above for a high-level overview of what we’ll be configuring.

Here’s what you’ll need to be able to successfully deploy this solution:

  • A website or other service that you want to make available on the public internet. Think of something like a Lightning node that you want to be available on Clearnet rather than Tor only.
  • A Linux VPS to which you have root access with Wireguard and Firewalld installed.
  • Optional – I use the Network Manager utility for configuring a persistent static route on the server at the home location. You can configure this route via another method if you like.
  • A server of some kind on your home network running something you want to host publicly. The server could be a large enterprise machine, a Raspberry Pi, or something in between.

Caveat: Depending on how much bandwidth your application requires, you could run into your ISP data cap. For all of the data your VPS uses to service clients on the internet, your home internet connection will use double that amount. All traffic to the service running on the VPS will travel to your home network and back to the VPS so for all accesses to the VPS you’ll receive and transmit that much traffic on your home ISP connection.

From this point forward I’ll refer to the remote side with the public IPv4 address as the “VPS” and the device running the application on the home network as the “VM” since I run my application on a Virtual Machine. For you this could be a Raspberry Pi or other physical device.

First we’ll do a basic Firewalld configuration on the VPS to allow the Wireguard connection from the home VM. This assumes you already have Firewalld installed and running:

$ sudo systemctl start firewalld 
$ sudo systemctl enable firewalld 

Most likely your public internet connection on the VPS is going to be in the “Public” Zone but we will confirm now. Note the interface name along with the public “inet” address:

$ ip addr 
ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:16:3c:bd:75:9b brd ff:ff:ff:ff:ff:ff
    altname enp0s3
    inet 172.245.91.37/26 brd 172.245.91.63 scope global noprefixroute ens3

Then run this command to confirm that “ens3” is in the Public zone:

$ sudo firewall-cmd --list-all-zones 
public (active) 
  target: default 
  icmp-block-inversion: no 
  interfaces: ens3 
  sources: 
  services: dhcpv6-client ssh 
  ports:
  protocols: 
  forward: yes 
  masquerade: no 
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 

Now I’ll make a permanent change to the Firewalld configuration, reload the firewall so the change takes effect, and then review the configuration to make sure the configuration is as expected:

$ sudo firewall-cmd --zone=public --add-port=51871/udp --permanent 
$ sudo firewall-cmd --reload 
$ sudo firewall-cmd --list-all-zones 
public (active) 
  target: default 
  icmp-block-inversion: no 
  interfaces: ens3 
  sources: 
  services: dhcpv6-client ssh 
  ports: 51871/udp
  protocols: 
  forward: yes 
  masquerade: no 
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 

You can now see that UDP port 51871 is allowed. This is the port on the VPS that will allow the incoming Wireguard connection from the home VM.

Now we’re going to configure a basic Wireguard VPN tunnel. I’m not going to go into detail here. A basic Wireguard configuration is pretty well documented on the internet.

However, first there is one important WARNING. As part of the Wireguard configuration we are going to be sending ALL traffic (as in 0.0.0.0/0) from the home VM to the public VPS. If your home VM is running on a different subnet, like a DMZ for instance, than the machine you are using to configure the VM will lose access when you enable the Wireguard connection.

We’ll work around this by configuring a static route to the network of the configuration machine. I use the very fancy “nmtui” utility in Network Manager to accomplish this:

Now on to the Wireguard configuration:

Public VPS side:

[Interface] 
Address = 192.168.231.1/24 
ListenPort = 51871 
PrivateKey = [REDACTED]

[Peer] 
PublicKey = [REDACTED]
AllowedIPs = 192.168.231.0/24 

Home VM side:

[Interface] 
Address = 192.168.231.2/24 
ListenPort = 51993 
PrivateKey = [REDACTED]

[Peer] 
PublicKey = [REDACTED]
AllowedIPs = 192.168.231.0/24, 0.0.0.0/0 
Endpoint = 172.245.91.37:51871 
PersistentKeepalive = 25 

The really important part of this configuration is the 0.0.0.0/0 “AllowedIPs” on the Home VM side. This allows my BTCPay Server running on my home network to return traffic from web requests coming to the public IP address of the VPS. In fact, all traffic originating on the home VM will go toward the VPS Wireguard interface EXCEPT for any static routes you have set (like we did with “nmtui” above).

Another important thing to note is that we don’t specify an endpoint on the VPS side of the Wireguard VPN configuration. The Wireguard VPN tunnel has to be initiated from the home VM side but this provides a large benefit in that the home VM side doesn’t require a public IP. This configuration will work even if your home network is stuck behind Carrier Grade Double NAT!

Don’t forget to start and enable your wireguard service with Systemd on both the VPS and VM sides of the tunnel. The name of my Wireguard service is “btcpay”:

$ sudo systemctl start wg-quick@btcpay 
$ sudo systemctl enable wg-quick@btcpay 

Make sure that your Wireguard tunnel is working correctly by pinging between the home VM and the VPS on the 192.168.231.0/24 subnet. You can run “sudo wg” to see that traffic has been both transmitted and received:

Before we get into the interesting part we will enable IP routing on the VPS. This will be required to route traffic from the home VM, through the BTCPay VM, and out to the internet as well as in the other direction.

On my Arch Linux system I will now enable IP Routing by creating the file “/etc/sysctl.d/99-sysctl.conf” and adding the single line “net.ipv4.ip_forward = 1”. Now reboot the VPS to confirm that IP routing is enabled in a persistent fashion. Run the following command to confirm that IP Routing is persistently enabled. We are hoping to get a “1” as the result:

$ sudo sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

The way that you enable IPv4 Routing will vary depending on your Linux distribution but IP Routing must be enabled for this setup to work.

Now we get to the interesting part. We are going to configure the VPS to route traffic from the home VM out to the internet and port forward traffic from the internet back to the BTCPay VM.

Before we start, let’s enable a constant ping from our VM to 1.1.1.1. Hopefully you will get a result of “Packet filtered” because the firewalld instance on the VPS is dropping the traffic. We are going to change that now!

At first thought you might consider enabling masquerade on the public Firewalld zone and that will work… until you have more than one connection at a time. This would probably be fine for my very lightly used BTCPay Server but if you run a server that receives even a modest amount of traffic you will run into problems. Instead we will use a Firewalld feature that is rarely mentioned and there seems to be little documentation on its actual usage. We are going to use Firewalld Policies. If you are familiar with the concept of a Zone Based Firewall then the Firewalld Policy configuration will make sense to you. The Firewalld Policy tied together with the port forwarding on the VPS public zone will give us the desired result of traffic flowing between our home VM and the internet like it has the public IPv4 address directly tied to an interface.

This is a useful page detailing Firewalld policy commands if you want to learn more about this useful feature:

https://firewalld.org/documentation/man-pages/firewall-cmd.html

The Firewalld configuration will take place entirely on the VPS side. The first step is assigning the Wireguard interface we just created to the internal zone, reloading Firewalld for the change to go into effect, and then verifying that the configuration is as expected:

$ sudo firewall-cmd --permanent --zone=internal --add-interface=btcpay
$ sudo firewall-cmd --reload
$ sudo firewall-cmd --list-all-zones
internal (active) 
  target: default 
  icmp-block-inversion: no 
  interfaces: btcpay 

We will now add a “rich rule” so that Firewalld will NAT the traffic from our home VM (all traffic from 192.168.231.2) out to the internet through our VPS:

$ sudo firewall-cmd --permanent --zone=public --add-rich-rule='family="ipv4" source address="192.168.231.2/32" masquerade' 
$ sudo firewall-cmd --reload 

We will also disable intra zone forwarding as we don’t need this feature and disabling it is better for security:

$ sudo firewall-cmd --zone=public --permanent --remove-forward 
$ sudo firewall-cmd --reload 

We are going to create a new policy called “outbound”:

$ sudo firewall-cmd --new-policy=outbound --permanent 

Then we will add interfaces ingress and egress zones to the policy:

$ sudo firewall-cmd --permanent --policy=outbound --add-ingress-zone=internal 

$ sudo firewall-cmd --permanent --policy=outbound --add-egress-zone=public 

We will then set the target to ACCEPT meaning that all traffic received from the internal zone will be forwarded to the public zone. After making the change we’ll reload Firewalld for the change to go into effect:

$ sudo firewall-cmd --permanent --policy=outbound --set-target=ACCEPT 
$ sudo firewall-cmd --reload 

I had to stop my constant ping and then restart it but I’m now able to ping 1.1.1.1 through my VPS! You can stop the Wireguard service on the home VM for a moment to notice the difference in latency when routing through the VPS vs just going directly to 1.1.1.1:

$ ping 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=55 time=21.3 ms
64 bytes from 1.1.1.1: icmp_seq=2 ttl=55 time=21.1 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=55 time=21.2 ms

$ sudo systemctl stop wg-quick@btcpay

S ping 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=54 time=11.5 ms
64 bytes from 1.1.1.1: icmp_seq=2 ttl=54 time=7.70 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=54 time=8.36 ms

Make sure to restart Wireguard after this test:

$ sudo systemctl start wg-quick@btcpay

The final step is to allow the traffic to our external ports and forward that traffic from the VPS to the home VM. In the case of BTCPay we’ll forward http (80/tcp) and https (443/tcp). On the VPS we’ll run several commands. These allow the traffic into the public zone:

$ sudo firewall-cmd --permanent --zone=public --add-service=http 
$ sudo firewall-cmd --permanent --zone=public --add-service=https 

Now we will port forward these to our VM:

$ sudo firewall-cmd --permanent --zone=public --add-forward-port=port=80:proto=tcp:toport=80:toaddr=192.168.231.2
$ sudo firewall-cmd --permanent --zone=public --add-forward-port=port=443:proto=tcp:toport=443:toaddr=192.168.231.2

Finally we’ll reload Firewalld one last time to make the changes take effect:

sudo firewall-cmd --reload 

You can now access the login page of my BTCPay Server that is being run via this method right now! The VM running BTCPay Server as well as my Bitcoin full node are running on my home network and this inexpensive VPS is allowing you to access it!

https://btcpay.bitcoinlizard.net

If you found this guide useful you can throw a couple Sats my way via this BTCPay Server instance using the donation widget below.

3 replies on “VPS Passthrough Hosting
Host a large website or application with a modest VPS

Access to various payment gateways was one of the greatest issues we observed from
multiple countries. Instead of limiting
our customers, we offer different payment options,
so they don’t have to worry about having a specific payment type on hand.
In certain places of the world, traditional payment
gateways may restrict customers. You can use Bitcoin payments to solve this problem.
Many businesses worldwide are starting to accept Bitcoin as a payment method like they do credit cards.
Bitcoin is known as “the secure money of the Internet.” To handle bitcoin payments, bitcoin payment services
function as a mediator between the sender of the money and the recipient.
We offer support for receiving bitcoin payments.
So don’t worry.
Due to lower transaction costs than a conventional credit card payment,
bitcoin payments are more cost-effective than standard credit card transactions.
So it is advisable to use Bitcoin payments instead of the traditional ways, but we are not trying to force you into anything; you are free to choose whatever
payment method you want.

Leave a Reply

Your email address will not be published.