Reverse Proxy and Foundry VTT

Originally Posted 12/31/23; Latest update 12/30/24

image of a flag that says we do this not because it is easy, but because we thought it would be easy

Intro

So, I'm putting this under both rant and instructional because, while this does technically fall under instructional, I'm largely writing this just to get the process down in case I ever have to struggle through this again. So while you might be able to glean something from this, and use it in your own setups, I'm not going to be going as in depth, or as step by step, as I'd typically like from an instructional post. Maybe I'll come back and edit this better when I'm not so tapped out, but for now this is the best I got. With that said, let's jump into it.

The Problem

I have 3 services running on a home "server" (Pc running Ubuntu) that I would like to access while not on my local network. The programs are Fittrackee, Freshrss, and FoundryVTT. What started my journey on doing this was installing FoundryVTT, which is a Virtual Tabletop used for playing games like DnD or Lancer. I wanted players to easily access the server, and the easiest way to do that would be giving them a domain name to use, rather than IP address. Before I installed FoundryVTT, the way I was accessing Fittrackee and Freshrss was to use Tailscale to connect to my local network, and then putting in my local IP address for my server pc with the port numbers attached at the end. This works fine when it's just me, but if I want other people to access things on my network (such as Foundry), then I'll need something easier than this.

So, how do we get it so people can connect to a server on our home network from anywhere in the world, as well as making it so we can access Fittrackee and Freshrss as well? This is where DDNS (Dynamic Domain Name System) and a Reverse Proxy comes into play.

What is DDNS

DDNS stands for "Dynamic Domain Name System". To try and keep this simple, your ISP (Internet Service Provider) provides your home internet with an IP address. This is how network traffic can get to you. If you google "What is my ip address", you can see what IP address your ISP has assigned to you. This is also referred to as your public ip address. Now, because of the way ISPs work, your public IP address might occasionally change. This does not matter in your day to day use of the internet. However, say for example you knew your public ip address was 72.72.72.72, and so you either connect to your home server using that address, or have other people use it to connect to a server you're running. Now the ISP changes your public ip address to 73.73.73.73. Now suddenly you and your friends can no longer connect to your server, because while you're typing in 72.72.72.72, it's not going to your server anymore.

This is where DDNS comes in. On your router, you potentially have the option to sign into a DDNS provider. Somewhat unfortunately, my router only allows me to use no-ip.com or dyn.com for an option. But you buy a domain name from whatever provider, then login to your account on the router for whichever provider (so, on my router I login to my no-ip account), as well as put in the domain name I want to use (I'll be using the example of website.ddns.net for the remainder of this article) and seemingly magically your router will start communicating with the provider about what its public IP address is. Now, when you or your friends want to connect to your home server, you'll be using website.ddns.net instead of 72.72.72.72. And, if your ISP changes your IP address to 73.73.73.73, your router will automatically send that to your DDNS provider, and so when you type in website.ddns.net, you'll still get back to your own server.

Note : I wanted to put this in here quick. Setting up DDNS for your home internet is a RISKY thing to do, as it then makes your home network accessible on the World Wide Web. This can be dangerous and, from the googling I've done at least, seems to be something you shouldn't do lightly.

What is a Reverse Proxy

Now, I want to say this is where my understanding starts becoming a bit hazy, so this might not be entirely correct. My understanding of a reverse proxy is it monitors incoming traffic on a specific port and, depending on how you set it up, sends that traffic somewhere. Either another port or to a specific place or something on your system.

Now you might be asking, what is a port? A port is basically a lane on your router that internet traffic can come in or go out on. Some standard ports are 80 for HTTP traffic, 443 for HTTPS traffic, 25 for email, etc. Different programs will typically monitor different ports for the web traffic they're expecting to get. A popular program is Plex, which uses port 32400 by default.

Some Key Things to Know Up Front

Now, there were some really big gaps in my knowledge when I started out on this project. There are still some pretty big ones left by the end of this. But I wanted to layout some of the things I learned that helped me progress this journey, and not just get stuck in confusion:

  1. For targeting a specific program with a domain name, the format of program.website.ddns.net works for reverse proxy, and website.ddns.net/program does not.

  2. In order to be able to use things such as program.website.ddns.net, enabling "Wildcard" on my domain (at least, for No-ip) was a necessary step. It was not cname record

  3. On Linux (Or, at least on my install of Ubuntu 22.04.3 LTS), regular users can not access ports below 1024 I believe. This means that, for programs (like a reverse proxy) that wants to monitor port 80, this is not possible without running the program as root. This could cause potential security concerns, meaning another way to do this (which seemed to work for me), was to port forward port 80 on my router to a port higher than 1024 (I chose 30100). Then, on the program on my computer, it just watches port 30100 instead.

What is Port Forwarding?

Port forwarding is a setting on routers which basically tells the router to send any traffic coming from an external port, and send it to a different or the same internal port. Ports are closed by default for security, and so port forwarding opens those ports on your router.

Setting up the Reverse Proxy

I chose to use Nginx as my reverse proxy program. When I was googling around on what to use, this seemed to be the one recommended. Apache is another one I've heard about. Anyways, I largely followed the tutorial here on how to setup nginx, and it went largely fine. I did have to change the listening ports to 30100, but the rest of the setup remained largely the same for me.

The problem came in when I was trying to setup HTTPS, or get an SSL certificate. I was trying this because FoundryVTT actually has a built in way to use this. When I was setting that up, I followed Foundry's instructions here which were pretty good! I ran into some permissions issues getting the certificate files, and so had to make it so my login would have access to those files (using the nautilus command). I then moved it into the folder I was saving the Foundry stuff to, updated the configuration settings, and it did end up working for me. If I didn't have other programs I wanted to access, I could've stopped here and everything would've been fine.

Unfortunately, I did not stop there and found out the hell that is getting a wildcard ssl certificate with No-ip. I eventually gave up as it seems near impossible to me (or, at least, impossible to do it easily in a way I'd understand). The certbot used previously can work with wildcard domains, however not seemingly with no-ip domains (the tutorial I had been following can be found here). I eventually gave up on trying to get the HTTPS part setup, and instead fell back on regular HTTP (which is not as secure).

Update: I did end up getting HTTPS working with some help from ChatGPT. I always forget that's a thing now. But by using the command (Again, replace "website.ddns.net" with whatever your domain is). This allowed certbot to have me manually verify that I had ownership over the domain by adding a DNS TXT record to my domain using this tutorial. Overall, pretty straight forward once I got that command. The only downside is Certbot does not have a plugin for noip, meaning (as far as I can tell), there's no way to automatically renew the certification every 90 days. It'll have to be done manually with this command:

Update 4/27/24: I added ports 80 and 443 to listen to so that local traffic would work with the various programs my computer was running.

Update 12/30/24: I changed some things around at the recommendation of a coworker for security. I now access programs by including a port number with the URL, which the router will then bring down to port 443. I did have some issue with Jellyfin with this, but was able to use their forums to fix the issue with the NGINX configuration. The configuration below reflects all of the changes.

This is the final configuration I ended up with for Nginx:

So each "Server" is a different program I want access to. the number after "listen" is the HTTPS port. The server_name is the name I'd like Nginx to recognize for whatever program. Again, I'm using website.ddns.net as my example domain name here, but you would replace it with whatever yours is. Location, and the info beneath it, is telling nginx where to send traffic from the server_name. So, in the example here, any traffic coming from freshrss.website.ddns.net will be sent to the local host (that's what 127.0.0.1 represents) port 8080 (the port FreshRSS uses). The ssl_certificate and ssl_certificate_key are the fullchain.pem and privkey.pem files that certbot creates. Certbot even offers to put it into your code itself if you have the nginx configuration file already setup beforehand. I just manually put those in. The info after "ssl_certificate" is the file location for the pem files.

Note: I did run into permission issues with these files and needed to use the Nautilus command to give my user profile access to the keys themselves, as well as the locations the keys were referencing.

The extra information under Lancer (foundry's) section is from following the tutorial here under "nginx host configuration".

Note 1/27/24: I ended up also wanting to get a jellyfin server setup. It's similar to a Plex server but open source, and a bit less bloat. Also, it has additional functionality for ebooks which Plex does not. I followed the install instructions here for Debuntu and it went well. I then copied the example config for Nginx which can be found here. I had to disable some of their security settings to get it working, but overall wasn't too bad. Also had to tweak a few settings here and there, which you can see above if you compare to their example config. Also, instead of creating the file like they suggested, I just added the config information to the file I already had created for nginx.

Note 4/1/24: I renewed my certificate which went well, however once my original certificate expired, my sites starting coming up as not secure. After googling around a bit, I eventually came across this source which mentions you need to restart nginx once you have new certs. The command for that is as follows:

and that fixed the issue completely and instantly for me.

Note cont.: I wanted to list some commands for the terminal I had forgotten about from originally setting this up, to having to re-edit it, that were necessary, in case it helps anyone else. (Don't include the : when typing these commands

The last server which doesn't have a name is telling Nginx if there is traffic coming from anywhere not listed above (such as just website.ddns.net) to basically throw away the packet. My understanding is it should return a warning of "Connection closed without response", however my web browser (firefox) just says the connection was refreshed. Either way, it doesn't go through. Without this it seems like Nginx just sends traffic to the first program listed (in my case, Fittrackee).

The only thing I did after this was in Foundry I removed the ssl info I had put in earlier, since this was no longer going through HTTPS. In their instructions here, they also mention there's a setting to change when doing a reverse proxy to use its ssl license, rather than foundry's.

Update: I went to add some security to my Ngninx reverse proxy based on this article and ended up messing up Foundry. After many hours of troubleshooting, I ended up having to reinstall docker, meaning I had to setup Fittrackee and Freshrss again. When I did this, I decided to comment out the Fittrackee stuff, as I don't really need access to that on the web. Freshrss I had luckily backed up not too long before, so I could reimport the feeds. For Foundry, somehow the SSL certificates ended up back in its configuration, and I had forgotten to add "Ssl" after the "listen" part of the HTTPS config for Nginx. So I had just "listen 30200" in the config above instead "listen 30200 ssl;". Doing this seemed to fix the issue again for Foundry.

Wrap Up

There was a lot of trial and error and googling and time spent on figuring this out. I think now with the config file figured out, this should be easy to replicate or expand from if need be in the future. I'd love to figure out SSL/ HTTPS, however it seems sort of impossible unless I find a teacher of some kind.

As always, if you have any thoughts or anything feel free to email me at steventanzimedia@gmail.com