Better, Faster, Stronger, Sleepy

The pros and cons of diy'ing my own front door

Tl;DR If you've been seeing the above over the last ~12 hours, you shouldn't anymore. Sorry about the mess, should be smooth sailing from now.

The Full story.

The pitch sounds simple. Put a small VPS in front of your self managed infra, run HAProxy on it, terminate the public internet there, hide your origin IP, get DDoS protection at the network edge. Skip Cloudflare. Skip AWS. Keep your TLS keys on your own hardware. Pay $16 a month instead of negotiating with someone's enterprise sales team for "free" service that scrapes your traffic to train models.

I spent the last 24 hours proving the pitch is simepl. I also spent much of that time learning that the path from "simple" to "actually working" runs down some dark and twisting roads.

The architecture, on paper, is one sentence: a small FreeBSD VPS in Vultr's Johannesburg region runs HAProxy in TCP-passthrough mode, dialing two WireGuard tunnels into my pfSense router over its dual WAN connections, forwarding all :443 traffic through to my origin's HAProxy where TLS terminates and the apps live. Vultr's network-edge DDoS protection absorbs floods. WireGuard hides the origin IPs. Active-active load balancing across both ISPs gives failover. DNS points at the VPS. Easy.

Everything in that sentence required learning what I didn't know I didn't know.

WireGuard cryptokey routing is not a network. I vaguely knew this. I forgot it when I configured two tunnel endpoints with the same destination IP and watched the kernel reject the second route as a duplicate. Two tunnels need two destinations. So I added a virtual IP alias on my routers's WireGuard interface, gave the second tunnel its own /32 to chase, and the routing table calmed down. The lesson generalised: every layer in this stack expects the underlying layer to look "normal," and "normal" is more specific than the docs suggest.

The PROXY protocol giveth and taketh away. Without it, my origin HAProxy sees every connection as coming from one of two WireGuard tunnel IPs — useless for rate-limiting, logging, or anything that cares about real users. With it, those tunnel-side bindings need accept-proxy set, and the public-facing bindings must NOT have it, and if you accidentally flip those bits the entire frontend serves browser-baffling TLS resets that look like a vendor outage but are actually a checkbox in pfSense's web UI. PROXY-v2 is plumbing that's invisible when it works and incomprehensible when it doesn't.

Suricata is not your friend by default. My intrusion detection system happily identified my own proxy's PROXY-v2 traffic as "Applayer Mismatch protocol both directions" and dumped the VPS's IP into a block table. Talk about shooting yourself in the ... router. WireGuard handshakes would succeed for a while, then mysteriously stop receiving bytes, then resume an hour later when the snort2c entry expired naturally. I spent considerable effort theorizing about stacked HAProxy worker processes, MTU mismatches, and connection pool exhaustion before realising my own perimeter defence was systematically betraying me. The fix was a one-line pass-list addition. The diagnostic path was a week.

Stick-tables are surprisingly literal. HAProxy's rate-limiting tracks source IPs, and when 500 internet users all arrive at your origin through one VPS tunnel, they look like one extremely busy client. Move tracking from tcp-request connection to tcp-request session so the PROXY-v2-unwrapped real IP is what gets counted. Now individual users get individual rate limits. But — and this is the part that took an evening of repeating self-inflicted denial, your LAN traffic is NAT'd through your own router's WAN IP before it gets to the VPS and back. So every LAN user, including you debugging at 3 AM, appears at the origin as one shared identity. Trip a rate limit once and you lock out yourself and everyone in the house. Add the WAN IPs to the trusted-source allowlist. Or, better, configure split DNS so LAN traffic doesn't pretend to be internet traffic in the first place.

429 is a 4xx. This sounds obvious until you write a rule that auto-bans clients who generate too many 4xx responses. Now a user who legitimately bursts past your rate limit gets 429s, those 429s count as errors, the error counter trips the auto-ban threshold, and the user is silently dropped for an hour for the crime of clicking around your site. The fix isn't subtle: remove the rule that treats your own rate-limit responses as evidence of malice. The bug was rule design, not edge case.

What this experience clarified is that the building blocks of "I want a load balancer in front of my servers" are all available, well-documented, mature, and individually correct. The hard part is the integration — the gaps between docs where one tool's reasonable default contradicts another tool's reasonable default and your traffic falls into the gap.

What you get for the effort is genuine: hidden origin IPs, real DDoS edge protection, dual-ISP failover with sub-10-second detection, IPv6 reachability for an IPv4-only origin, and your TLS keys still under your own roof. What you save: between $30 and $300 a month versus enterprise alternatives, depending on traffic. What you spend: an unreasonable number of hours learning HAProxy stick-table semantics in the middle of the night, and trying not to lock yourself out of your own infrastructure.

This experience also bears the reality of being a small business, I'm in UTC+2, when I go to sleep and something breaks, unless a kid wakes me up at 2am and I check the site, it won't be fixed till the morning. In general, systems are in place to be robust, and self healing, the problem is when the disaster system itself isn't setup correctly.

The trade is straight forward. The path through it is not. Big-tech CDNs hide all of this complexity for a reason — the reason is that the complexity exists and somebody has to absorb it. If you absorb it yourself, you own both the savings and the experience. The compromise everyone else makes for you costs them nothing and you everything.

#diy #sometimes i hate computers #networking #wireguard #haproxy
RSS