https://mjg59.dreamwidth.org/72095.html
Account name: [ ] Password [ ] [Log in]
(OpenID?) (Forgot it?) [ ] Remember Me
You're viewing [personal profile] mjg59's journal
Create a Dreamwidth Account Learn More
[ ] [Interest ] [Go]
Reload page in style: site light
Matthew Garrett
Locally hosting an internet-connected server
Locally hosting an internet-connected server
Jun. 16th, 2025 09:20 pm
[personal profile] mjg59
I'm lucky enough to have a weird niche ISP available to me, so I'm
paying $35 a month for around 600MBit symmetric data. Unfortunately
they don't offer static IP addresses to residential customers, and
nor do they allow multiple IP addresses per connection, and I'm the
sort of person who'd like to run a bunch of stuff myself, so I've
been looking for ways to manage this.
What I've ended up doing is renting a cheap VPS from a vendor that
lets me add multiple IP addresses for minimal extra cost. The precise
nature of the VPS isn't relevant - you just want a machine (it
doesn't need much CPU, RAM, or storage) that has multiple world
routeable IPv4 addresses associated with it and has no port blocks on
incoming traffic. Ideally it's geographically local and peers with
your ISP in order to reduce additional latency, but that's a nice to
have rather than a requirement.
By setting that up you now have multiple real-world IP addresses that
people can get to. How do we get them to the machine in your house
you want to be accessible? First we need a connection between that
machine and your VPS, and the easiest approach here is Wireguard. We
only need a point-to-point link, nothing routable, and none of the IP
addresses involved need to have anything to do with any of the rest
of your network. So, on your local machine you want something like:
[Interface]
PrivateKey = privkeyhere
ListenPort = 51820
Address = localaddr/32
[Peer]
Endpoint = VPS:51820
PublicKey = pubkeyhere
AllowedIPs = VPS/0
And on your VPS, something like:
[Interface]
Address = vpswgaddr/32
SaveConfig = true
ListenPort = 51820
PrivateKey = privkeyhere
[Peer]
PublicKey = pubkeyhere
AllowedIPs = localaddr/32
The addresses here are (other than the VPS address) arbitrary - but
they do need to be consistent, otherwise Wireguard is going to be
unhappy and your packets will not have a fun time. Bring that
interface up with wg-quick and make sure the devices can ping each
other. Hurrah! That's the easy bit.
Now you want packets from the outside world to get to your internal
machine. Let's say the external IP address you're going to use for
that machine is 321.985.520.309 and the wireguard address of your
local system is 867.420.696.005. On the VPS, you're going to want to
do:
iptables -t nat -A PREROUTING -p tcp -d 321.985.520.309 -j DNAT
--to-destination 867.420.696.005
Now, all incoming packets for 321.985.520.309 will be rewritten to
head towards 867.420.696.005 instead (make sure you've set
net.ipv4.ip_forward to 1 via sysctl!). Victory! Or is it? Well, no.
What we're doing here is rewriting the destination address of the
packets so instead of heading to an address associated with the VPS,
they're now going to head to your internal system over the Wireguard
link. Which is then going to ignore them, because the AllowedIPs
statement in the config only allows packets coming from your VPS, and
these packets still have their original source IP. We could rewrite
the source IP to match the VPS IP, but then you'd have no idea where
any of these packets were coming from, and that sucks. Let's do
something better. On the local machine, in the peer, let's update
AllowedIps to 0.0.0.0/0 to permit packets form any source to appear
over our Wireguard link. But if we bring the interface up now, it'll
try to route all traffic over the Wireguard link, which isn't what we
want. So we'll add table = off to the interface stanza of the config
to disable that, and now we can bring the interface up without
breaking everything but still allowing packets to reach us. However,
we do still need to tell the kernel how to reach the remote VPN
endpoint, which we can do with ip route add vpswgaddr dev wg0. Add
this to the interface stanza as:
PostUp = ip route add vpswgaddr dev wg0
PreDown = ip route del vpswgaddr dev wg0
That's half the battle. The problem is that they're going to show up
there with the source address still set to the original source IP,
and your internal system is (because Linux) going to notice it has
the ability to just send replies to the outside world via your ISP
rather than via Wireguard and nothing is going to work. Thanks,
Linux. Thinux.
But there's a way to solve this - policy routing. Linux allows you to
have multiple separate routing tables, and define policy that
controls which routing table will be used for a given packet. First,
let's define a new table reference. On the local machine, edit /etc/
iproute2/rt_tables and add a new entry that's something like:
1 wireguard
where "1" is just a standin for a number not otherwise used there.
Now edit your wireguard config and replace table=off with table=
wireguard - Wireguard will now update the wireguard routing table
rather than the global one. Now all we need to do is to tell the
kernel to push packets into the appropriate routing table - we can do
that with ip rule add from localaddr lookup wireguard, which tells
the kernel to take any packet coming from our Wireguard address and
push it via the Wireguard routing table. Add that to your Wireguard
interface config as:
PostUp = ip rule add from localaddr lookup wireguard
PreDown = ip rule del from localaddr lookup wireguard
and now your local system is effectively on the internet.
You can do this for multiple systems - just configure additional
Wireguard interfaces on the VPS and make sure they're all listening
on different ports. If your local IP changes then your local machines
will end up reconnecting to the VPS, but to the outside world their
accessible IP address will remain the same. It's like having a real
IP without the pain of convincing your ISP to give it to you.
Tags:
* advogato,
* fedora
* Previous Entry
* Add Memory
* Share This Entry
* Next Entry
---------------------------------------------------------------------
* 5 comments
* Reply
---------------------------------------------------------------------
Flat | Top-Level Comments Only
no subject
Date: 2025-06-17 08:44 am (UTC)
andrewducker: (Default)
From: [personal profile] andrewducker
I'm curious whether your ISP provides IPv6, and whether that works
without needing the VPS.
* Link
* Reply
* Thread
* Hide 2 comments
* Show 2 comments
no subject
Date: 2025-06-17 09:27 am (UTC)
From: [personal profile] mjg59
Yes, as long as I'm good with the range changing every 48 hours.
Which I'm not, so no.
* Link
* Reply
* Thread from start
* Parent
* Thread
* Hide 1 comment
* Show 1 comment
no subject
Date: 2025-06-17 09:35 am (UTC)
andrewducker: (Default)
From: [personal profile] andrewducker
Ah, so no static v6 either? That's frustrating!
* Link
* Reply
* Thread from start
* Parent
Tailscale
Date: 2025-06-17 10:02 pm (UTC)
From: [personal profile] pheedrus
Why not just use Tailscale?
* Link
* Reply
* Thread
* Hide 1 comment
* Show 1 comment
Re: Tailscale
Date: 2025-06-17 11:03 pm (UTC)
From: [personal profile] mjg59
I want to expose the entire machine, so Funnel doesn't do what I want
here.
* Link
* Reply
* Thread from start
* Parent
* Previous Entry
* Add Memory
* Share This Entry
* Next Entry
* 5 comments
* Reply
Flat | Top-Level Comments Only
Profile
Matthew Garrett
About Matthew
Power management, mobile and firmware developer on Linux. Security
developer at Aurora. Ex-biologist. [personal profile] mjg59 on
Twitter. Content here should not be interpreted as the opinion of my
employer. Also on Mastodon.
Page Summary
* [personal profile] andrewducker - (no subject)
* [personal profile] pheedrus - Tailscale
Expand Cut Tags
No cut tags
Top of page