A straightforward Minecraft server deployment that taught me new lessons about firewall rules and port forwarding. This project also gave me my first insight into the tools Cloudflare offered via their Dynamic DNS.

The Setup

The server itself runs a Linux virtual machine (VM) provisioned through Proxmox. See the Provisioning a Linux VM on Proxmox write-up for the standard process for VM deployment. Once the VM was online, I installed the Crafty Controller application, which provides me with a web-based management plane for my Minecraft servers. Starting, stopping, server deployment, and mod management all happen through Crafty rather than via the command line.

Port 25565 is as well known to me as any standard port or protocol. This has been the recommended Minecraft port since I started playing the game and self-hosting servers in 2013. In my current setup, I also use port 25566 to allow multiple servers to run simultaneously. However, despite my familiarity, these ports ultimately led to my first hurdle.

Firewall Rule vs Port Forward

Leading up to this project, I had completed a number of other VM deployments on multiple different subnets. The solution for allowing or disallowing communication had thus far been implemented via firewall rules. The Minecraft server, however, was the first VM deployed to the VLAN expressly tasked with isolating traffic originating from any game servers that might be deployed to the network. As a result, new rules needed to be configured. Explicit blocks to sensitive areas of the network needed to be blocked off, and access to DNS needed to be ensured, as the game servers utilize the DNS rewrite functionality for management access. Read more about the DNS rewrite functionality in the journal post covering the AdGuard self-hosted DNS deployment.

Within these firewall rules, I initially thought I would add a rule allowing WAN access to the Crafty server at the previously mentioned ports. This had been my method to grant access to the web for other resources in the past, so I assumed it would work the same way here. However, upon testing, no one could connect.

I realized the problem was that the firewall rules, on their own, do not route traffic. They simply determine whether traffic is allowed to traverse its target route. Without a NAT port forward, the traffic coming into my network had no idea where it should be sent. The public IP address is simply the means by which the sending host locates the receiving server on the Internet. Once the server lands within my perimeter, it relies on the port it is requesting and the port forward to tell it where to send those packets. OPNsense offers a function to add a port forward. By creating the port forward for 25565 and 25566 and directing it to the server running Crafty, I was able to allow my users to connect using my public IP address.

The IP Problem

Now that port forwarding was configured and users were able to connect to the server, my attention turned to the address they were using. Dynamic public IP addresses have been the plague of self-hosted Minecraft server owners for as long as I can remember. When we started playing the game, it was a triumph if our laptops could host a server at all, let alone figure out how to port-forward on an AT&T router from 2013. The user interface (UI), for which I can say does not appear to have changed in those many years. See the journal entry documenting the VLAN migration for more information about configuring an AT&T home internet router. The problem persists: every time your ISP reallocates your public IP address, my users need to be alerted and provided with the updated address.

This problem was solved using Cloudflare's Dynamic DNS tooling. A DDNS client running on OPNsense periodically checks my public IP and updates a Cloudflare DNS A record automatically whenever it changes. This enables me to give my friends a stable, memorable hostname instead of a raw IPv4 address that could shuffle at any moment.

This process exposed me to Cloudflare's capabilities, which I would later leverage to create this site. See the Building a Zero Trust DMZ for a Self-Hosted Web Server post for more insight on solving the dynamic-IP problem.

Key Takeaways

Firewall rules and NAT port forwards serve unique purposes. A firewall rule is there to allow or deny traffic; a NAT rule routes it. Both elements are pieces of the puzzle, but are used for two fundamentally different purposes.

Dynamic IPs are a solvable challenge. Without a static IP from my ISP, DDNS is an effective way to keep a hostname record up to date without frequent manual intervention.

Port forwarding and tunneling solve the same problem differently. DDNS is simple and effective. It gets the job done, but leaves your public IP address exposed. The Cloudflare tunnel, while more complex, completely removes the public IP address from the equation. The recommended approach is dependent on your risk appetite and what you are protecting. DDNS and port forwarding would not be ideal in a true Zero Trust architecture.