Part 3
The Helsinki Gateway: Secure Ingress, Split DNS, and Tailscale Exit Nodes
The last post described the shape of the hybrid architecture. This one is about the part that actually makes it secure: the Helsinki VM isn't just a relay — it's the only member of the whole system that has any business listening on a public port at all.
One reverse proxy, one edge
A single reverse proxy on the Helsinki VM owns every public hostname. It terminates TLS, decides which upstream a request is for, and forwards it — nothing behind it ever needs its own public listener.
# illustrative Caddyfile shape
media.lab.example.com {
reverse_proxy hv-media.tailnet-name.ts.net:8096
}
status.lab.example.com {
reverse_proxy hv-status.tailnet-name.ts.net:3000
}
The upstream targets aren't IPs — they're Tailscale MagicDNS names. The proxy config never has to know or care what the current private address of a service is, because the tailnet resolves that for it.
Certificates for services with no public listener
Standard HTTP-01 certificate validation assumes the server answering the challenge is the same server the domain resolves to. That's fine for the proxy's own hostname, but most of what it fronts has never had an open port 80 in its life. The fix is DNS-01 validation: the proxy proves it controls the domain by creating a TXT record via the DNS provider's API, rather than by serving a file over HTTP. Every hostname behind the gateway gets a real, auto-renewing certificate without ever needing to be reachable on its own.
Split DNS for the tailnet
Every device on the tailnet — laptop, phone, the VM itself — points at a
private resolver rather than a public one. That resolver blocks the usual
ad/tracker noise, but more importantly it's how internal service names
resolve at all: hv-media, hv-status, and friends only mean anything
inside the tailnet. Nothing about them is published to public DNS, because
nothing about them needs to be. Only the handful of hostnames the Helsinki
proxy fronts get a public record, and that record points at Helsinki, never
at home.
Exit nodes, redundantly
Tailscale exit nodes let any device on the tailnet route its general internet traffic through another node instead of its local connection. Two nodes are configured as candidates: a small always-on device on the home network, and the Helsinki VM itself. Either can serve as the exit path, so losing one doesn't strand the other — a deliberate answer to "what happens to routing during a Kazakhstan power blip."
Why the perimeter is one box wide
The firewall posture behind Helsinki is intentionally boring: nothing accepts a new inbound connection unless it arrives over the tailnet interface or loopback. The reverse proxy is the single exception, and it's on a machine that holds no data and can be rebuilt from Terraform in minutes. Every other service — media, monitoring, internal dashboards — is unreachable from the public internet by construction, not by firewall rule someone has to remember to keep correct.
Next post: pointing Claude and Gemini at this same infrastructure and having them write the Terraform and Ansible that provisions it.