November 25th, 2023
Problem: My home server is behind a carrier grade NAT (cgnat) and doesn't have a publically routeable ipv4 address; it does have an ipv6 address. I would like for those without ipv6 to be able to access it.
Resources: I do have a lowend VPS (via tinykvm.com) which has both an ipv4 and ipv6 address. It runs OpenBSD.
One would be inclined to think "just have the VPS transparently forward all the traffic" would be the solution. And it is! Figuring out what tooling was needed and actually pulling the pieces together took a while though; I think mostly due to my own ignorance.
It took me a while to find anyone offering a straight forward explanation to solve this issue. Most of the wisdom the internet was either: just use the VPS to host the stuff or use some kind of private networking solution (ie tailscale, zerotier, etc.).
Neither of these appealed to me. The former because I wanted to run a variety of services, including relatively heavy ones (ie minecraft) and don't want to shell out for a sufficiently powerful VPS when I've got a computer I know fits the bill sitting around. The latter because I want the publicly accessible aspect (for things like this blog and for relative simplicity in giving friends access to the minecraft server). Also there's the data sovereignty/cool factor of having the box offering the service in my own house.
Eventually I saw a serverfault post that addressed my issue. A one liner using a readily available tool: socat. What could be easier!
I elected to go with OpenBSD for the VPS as it's lightweight and I've been rather fascinated by the project of late. Though I elected against using it on my home server, for that I'm using OpenSUSE and running most things in podman containers to simplify setup.
With the tool selected I then found myself against the greatest challenge your average software engineer faces when trudging his way through the world of IT: not really having a very firm grasp on how networking actually works in the real world. I also found that I didn't really have a good grasp of how to configure services in OpenBSD. We'll be talking about both of those things.
The OpenBSD installer is pretty comprehensive at getting a basic system setup if you accept all the defaults. The exception to that being autoconfiguring IPv6 did not occur automatically. Attempting to get OpenBSD to do the autoconfiguration after setup also failed. I believe this has something to do with how the hosting provider has things setup as I did at one point setup an OpenBSD server at home and I don't recall any shenanigans being needed to setup IPv6 access.
So, if you find yourself on OpenBSD 7.4 and have been given an IPv6 range along with a gateway address what do you do?
You need inet6 your-ipv6-address/range
in your
/etc/hostname.interface
file. In my case that's
inet6 2605:8900:1000:1001:000a:0000:018d:5/112
in the
/etc/hostname.vio0
file.
The 5
at the end there took me a while to figure out; it
doesn't actually have to be a five, but I had the audacity to
try using a 0 at first (well actually a "::" but that's the same-diff in
IPv6 land I think. That address is apparently somehow being used
elsewise and so if you run ifconfig you will see that the address is
duplicated in the list of attributes. One might hope that such
a misconfiguration would result in an obvious flag being raised, but I
didn't see one.
The other important piece is the file /etc/mygate
. That
one is easy to setup, the whole file just consists of the ipv6 address
your provider gives you for the gateway.
Also you don't need to run
sysctl net.inet6.ip6.forwarding=1
. I did at one point as I
couldn't determine my issue and chatGPT suggested it as an answer. It
does seem like it could be relevant as that's important if you want to
use OpenBSD as a router. My issue was just the duplicate ip problem
though.
Anyway, all that done you run doas sh /etc/netstart
and
the system should sort things out. Then use ping6 to test that you can
actually access the world with IPv6.
As a test of the actual solution I ran
socat TCP4-LISTEN:443,fork TCP6:[2603:7080:9602:c5d7:a6bb:6dff:fe55:86b3]:443
and then used curl -4 rpicciotti.com
1.
Which was a relief as I'd been debugging the IPv6 issue for a while and
wasn't positive the socat solution would work.
So that's all nice but what we're actually here for is running socat as a service to forward traffic, not debugging IPv62.
Getting the service setup took longer than I'd care to say. This was due to a few confounding factors. Let's look at what actually worked first and then we can look at why it took so long to get something so simple to work. (and also about how it could probably be improved.)
My solution had two files. It could be condensed into one but it got split out while troubleshooting.
/etc/rc.d/socat_redirects
#!/bin/ksh
#
# A personal script to configure socat to make my actual server reachable
daemon="/usr/sbin/socat_redirects.sh"
rc_need="!net net.vio0"
# Sets up defaults in my understanding
. /etc/rc.d/rc.subr
rc_cmd $1
/usr/sbin/socat_redirects.sh
#!/bin/ksh
socat TCP4-LISTEN:80,fork TCP6:[2603:7080:9602:c5d7:a6bb:6dff:fe55:86b3]:80 &
socat TCP4-LISTEN:443,fork TCP6:[2603:7080:9602:c5d7:a6bb:6dff:fe55:86b3]:443 &
Given how simple this is3 why did it take me so long?
Because if you drop the rcneed line it works when you tigger it directly but fails silently at boot. Also OpenRC's documentation explicitly recommends against requiring a network interface.4 To be fair it comes with the caveat that you may need to just thrust responsibillity on the user if it can't be avoided. However the whole effort of getting it working was rather confused because socat doesn't seem to direct an error message to stderr when it can't reach the target ip address. I made multiple errors to get it to log something without success before I tried adding the network interface dependency.
That done the whole setup works (and if you're accessing this page over an IPv4 connection that's exactly what's happening).