Reaching Yourself Over the Internet

Updated on

As I’ve started hosting more and more services on my home network, I’ve increasingly found the need to contact myself over the internet.

For example, I host Forgejo locally at git.mami2.moe. The problem is that from my computer, I can’t type git.mami2.moe in the address bar and get on Forgejo, I need something like localhost:8080 on my computer or 10.0.0.2:8080 on my laptop.

The problems become compounded with some services like Cvat, which only allowlist a specific set of domains in the header. This means I can’t even reach Cvat on localhost:8080 from my own computer, as Cvat will deny that request. Additionally, it’s important to make sure your DNS settings are correct, so people on the internet can reach Cvat, but that’s not possible to test on your home network.

In this article I’ll go over my solutions to this problem. Hopefully you learn some networking on the way!

/etc/hosts

The easiest way to go about this is the most basic DNS possible: /etc/hosts. Simply add a line to redirect to the loopback, and your reverse proxy should handle it from there. Systemd-resolved reads /etc/hosts without even needing to reload!

127.0.0.1 git.mami2.moe

SOCKS

SOCKet Secure is a widely-available TCP/UDP proxy, which is even baked into OpenSSH! This means any computer you can SSH into can become your SOCKS proxy. In my case, I found this to be by far the easiest solution.

Start by adding dynamic forwarding to your ~/.ssh/config. Below we specify 9042, but this can be any open port:

Host orca
    IdentitiesOnly=yes
    Hostname 123.4.23.84
    User emiliko
    DynamicForward 8081
    Compression yes
    LogLevel QUIET

Connect to your proxy ssh orca. Now start chromium with this proxy:

chromium --proxy-server='socks5://localhost:8081'

However, I often find I also want to still be able to use a browser without this proxy. In this case, I start up two distinct chromium sessions. For example:

chromium  # Leave this one open for yourself
chromium --proxy-server='socks5://localhost:8081' --user-data-dir=/dev/shm/chromium_socks

From this, my first browser can reach Forgejo only on localhost:8080, while the second one will only reach it on git.mami2.moe, allowing me to develop locally but still test changes from an external user’s perspective!

Cellular Connection

This one is a bit silly, but also the least technical option. If you need to help someone with limited technical expertise, this is the best option to suggest.

You can access your website (git.mami2.moe) from your phone when wifi is turned off! Throw up a hotspot and any computer connected through this hotspot has access too!

The major limitations are that you still can’t access git.mami2.moe on the computer actually hosting git.mami2.moe and you’re now using more expensive data.

Hairpin NAT

If you have access to your router/modem and it has Hairpin NAT support, enable it and everything should just work. Hairpin NAT makes the router analyse where an outbound request is being sent. If the request is trying to reach that router’s IP address, it’ll simply reflect the packet, acting as if it came over the internet.

While this seems like the ideal solution, most residential internet service providers do not provide Hairpin NAT on their modems. I don’t know why… Additionally, most commercial networks have moved away from this solution replacing it instead with Split-horizon DNS.

Split-horizon DNS

Split-horizon DNS allows for multiple layers of DNS resolution to be present on your network. This is the most complicated setup, but also the most flexible, as it allows us to become the DNS resolver for the entire network.

We’ll go through a simple setup, which will function similarly to the /etc/hosts solution, then step it up to work on all devices on this network.

Local Setup

We’ll use dnsmasq as it’s the easiest to deal with. Place the following in /etc/dnsmasq.conf, modifying as needed

# Must match with /etc/resolv.conf
listen-address=127.0.0.1
# Don't look any further in /etc/resolv.conf
no-resolv
 
# DNS Servers
server=9.9.9.9
server=149.112.112.112
server=2620:fe::fe
server=2620:fe::9
 
# Local resolution. Relies on a reverse proxy to choose the port (Caddy)
# Both ipv4/6 just in case
address=/git.mami2.moe/127.0.0.1
address=/git.mami2.moe/::1

There’s a good chance you have systemd-resolved or some other sort of DNS resolver running (NetworkManager does something along these lines too). Kill all competing DNS resolvers:

systemctl disable --now systemd-resolved.service
systemctl enable --now dnsmasq.service

Now you should be able to resolve external domains example.com as well as override the resolution for your local domains git.mami2.moe. Try both in chromium, it should just work.

Full Network

Let’s first modify our /etc/dnsmasq.conf a little bit. We want to replace the loopback with your LAN interface’s IP. Often, eth0 will hold this address. You need both the static IPv4 address and the link-local IPv6. In these examples, my static IPv4 is 10.0.0.2 and my linklocal is fe80::200:ff:fe00:40:

# For the server itself
listen-address=127.0.0.1
# For all other devices on the network contacting the server
interface=eth0
 
# Don't look any further in /etc/resolv.conf
no-resolv
 
# DNS Servers
server=9.9.9.9
server=149.112.112.112
server=2620:fe::fe
server=2620:fe::9
 
# Local resolution. Relies on a reverse proxy to choose the port (Caddy)
# Both ipv4/6 just in case. Notice, you'll need your linklocal
address=/git.mami2.moe/10.0.0.40
address=/git.mami2.moe/fe80::200:ff:fe00:40

You may need to open up your firewall (see firewall-cmd), but it’s often already open to the local network.

Login to your router and set the IPv4/6 DNS servers to point to the IPs of your server. You could also add a fallback DNS server, but I’ve found that one might take precedence, circumventing the whole split-horizon.

DHCP leases are held for several hours usually, so to test it, “forget” the network and reconnect. Try accessing git.mami2.moe and something like example.com, which isn’t in your dnsmasq records.

For devices using systemd-resolved, you must accept DNS records from DHCP. You can do this by explicitly leaving DNS and FallbackDNS blank:

[Resolve]
DNS=
FallbackDNS=9.9.9.9