<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Networking on Backend Engineering Strategy Tools</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/</link><description>Recent content in Networking on Backend Engineering Strategy Tools</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Fri, 22 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/index.xml" rel="self" type="application/rss+xml"/><item><title>Bastion / jump server</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/bastion/</link><pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/bastion/</guid><description>&lt;p&gt;A bastion host (jump server) is a single, hardened machine exposed to the outside that acts as the entry point into a private network. You SSH into the bastion, then hop from there to internal hosts — or configure your SSH client to do it transparently in one step.&lt;/p&gt;
&lt;p&gt;The pattern is simple: minimise the network attack surface to one well-monitored machine, harden that machine specifically, and keep everything else off the public internet.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="basic-jump-manual-two-hop"&gt;Basic jump: manual two-hop
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Step 1: SSH into the bastion&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh user@bastion.example.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Step 2: from bastion, SSH into internal host&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh user@192.168.1.100
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Works, but requires your private key to be on the bastion — which you want to avoid.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="proxyjump-recommended"&gt;ProxyJump (recommended)
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;ProxyJump&lt;/code&gt; tells SSH to tunnel through the bastion transparently. Your key never leaves your local machine.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# One-liner&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh -J user@bastion.example.com user@192.168.1.100
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Or in ~/.ssh/config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Host internal
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; HostName 192.168.1.100
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; User user
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ProxyJump bastion
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Host bastion
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; HostName bastion.example.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; User user
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; IdentityFile ~/.ssh/id_ed25519
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After this, &lt;code&gt;ssh internal&lt;/code&gt; works as a single command with no key on the bastion.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="agent-forwarding-vs-proxyjump"&gt;Agent forwarding vs ProxyJump
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Agent forwarding&lt;/strong&gt; (&lt;code&gt;-A&lt;/code&gt;, &lt;code&gt;ForwardAgent yes&lt;/code&gt;) forwards your SSH agent socket through the bastion so you can authenticate onward with your local key. Older approach — it works but the agent socket is briefly accessible on the bastion host, which is a lateral movement risk if the bastion is compromised.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ProxyJump&lt;/strong&gt; is strictly better for the common case: the connection to the internal host is established from your machine through an SSH tunnel, not from the bastion itself. No agent socket on the bastion.&lt;/p&gt;
&lt;p&gt;Use ProxyJump unless you specifically need agent forwarding for something else.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="hardening-basics"&gt;Hardening basics
&lt;/h2&gt;&lt;p&gt;A bastion is only useful if it&amp;rsquo;s actually hardened. Minimum:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /etc/ssh/sshd_config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PasswordAuthentication no
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PermitRootLogin no
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;AuthorizedKeysFile .ssh/authorized_keys
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;AllowUsers jumpuser
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Key-only authentication — no passwords&lt;/li&gt;
&lt;li&gt;Dedicated user with no shell access to the bastion itself (optional: &lt;code&gt;ForceCommand&lt;/code&gt; to restrict what they can do)&lt;/li&gt;
&lt;li&gt;Non-standard port reduces log noise, not actual security&lt;/li&gt;
&lt;li&gt;fail2ban or equivalent for rate-limiting auth attempts&lt;/li&gt;
&lt;li&gt;Minimal installed software — the bastion should do one thing&lt;/li&gt;
&lt;li&gt;Regular log review (&lt;code&gt;/var/log/auth.log&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="restricting-to-jump-only"&gt;Restricting to jump-only
&lt;/h2&gt;&lt;p&gt;If you want users to be able to jump through the bastion but not get a shell on it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# In authorized_keys, prefix the key with:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;restrict,port-forwarding ssh-ed25519 AAAA...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or use &lt;code&gt;AllowTcpForwarding yes&lt;/code&gt; with &lt;code&gt;ForceCommand /usr/sbin/nologin&lt;/code&gt; — though the interaction between these options is subtle; test carefully.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="related"&gt;Related
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;For making the bastion reachable from outside without a public IP → &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/tunnels/" &gt;Tunnels&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;For exposing internal web services via public URLs → &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/tunneled-reverse-proxy/" &gt;Tunneled reverse proxy platforms&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Dynamic DNS (DDNS)</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/ddns/</link><pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/ddns/</guid><description>&lt;p&gt;Most home internet connections have a dynamic IP — the ISP can reassign it at any time. Dynamic DNS (DDNS) keeps a DNS hostname pointed at whatever IP you currently have, by running a small client that detects changes and updates the DNS record automatically.&lt;/p&gt;
&lt;p&gt;Relevant when using &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/tunnels/" &gt;port forwarding or WireGuard&lt;/a&gt; to reach a private network from outside — you need a stable hostname to connect to.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="how-it-works"&gt;How it works
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;You register a hostname with a DDNS provider (e.g. &lt;code&gt;myhome.duckdns.org&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;An update client runs on your router or a machine on your network&lt;/li&gt;
&lt;li&gt;The client periodically checks your public IP (or watches for changes) and calls the provider&amp;rsquo;s API to update the DNS record&lt;/li&gt;
&lt;li&gt;DNS TTL is kept short (60–300s) so changes propagate quickly&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="providers"&gt;Providers
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Provider&lt;/th&gt;
 &lt;th&gt;Cost&lt;/th&gt;
 &lt;th&gt;Domain&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://www.duckdns.org" target="_blank" rel="noopener"
 &gt;DuckDNS&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Free&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;*.duckdns.org&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Simple, no account required beyond OAuth login&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Cloudflare&lt;/td&gt;
 &lt;td&gt;Free (if you own a domain)&lt;/td&gt;
 &lt;td&gt;Your own domain&lt;/td&gt;
 &lt;td&gt;Best option if you already use Cloudflare for DNS&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;No-IP&lt;/td&gt;
 &lt;td&gt;Free (limited)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;*.ddns.net&lt;/code&gt; etc.&lt;/td&gt;
 &lt;td&gt;Requires manual renewal every 30 days on free tier&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Dynu&lt;/td&gt;
 &lt;td&gt;Free&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;*.dynu.net&lt;/code&gt; etc.&lt;/td&gt;
 &lt;td&gt;More generous free tier than No-IP&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Afraid.org&lt;/td&gt;
 &lt;td&gt;Free&lt;/td&gt;
 &lt;td&gt;Shared subdomains&lt;/td&gt;
 &lt;td&gt;Long-running community service&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Cloudflare&lt;/strong&gt; is the best option if you own a domain — you get a real subdomain (&lt;code&gt;home.yourdomain.com&lt;/code&gt;), the API is reliable, and the client support is universal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DuckDNS&lt;/strong&gt; is the easiest if you don&amp;rsquo;t own a domain — no configuration beyond a token.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="opnsense"&gt;OPNsense
&lt;/h2&gt;&lt;p&gt;OPNsense has a built-in DDNS client under &lt;strong&gt;Services → Dynamic DNS&lt;/strong&gt;. Supports Cloudflare, DuckDNS, No-IP, Route53, and others out of the box.&lt;/p&gt;
&lt;p&gt;Configuration for Cloudflare:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Service: &lt;code&gt;Cloudflare&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Hostname: &lt;code&gt;home&lt;/code&gt; (the subdomain to update)&lt;/li&gt;
&lt;li&gt;Domain: &lt;code&gt;yourdomain.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Username: your Cloudflare account email&lt;/li&gt;
&lt;li&gt;Password: Cloudflare API token with &lt;code&gt;Zone:DNS:Edit&lt;/code&gt; permission for the domain&lt;/li&gt;
&lt;li&gt;Check IP: leave default (uses OPNsense&amp;rsquo;s WAN interface)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OPNsense updates the record whenever the WAN IP changes, detected via interface monitoring.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="linux-update-clients"&gt;Linux update clients
&lt;/h2&gt;&lt;p&gt;If the router doesn&amp;rsquo;t have a built-in client (or you want updates from a specific host):&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ddclient&lt;/strong&gt; — the standard, supports most providers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;apt install ddclient
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /etc/ddclient.conf (Cloudflare example)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;protocol&lt;span style="color:#f92672"&gt;=&lt;/span&gt;cloudflare
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;zone&lt;span style="color:#f92672"&gt;=&lt;/span&gt;yourdomain.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;login&lt;span style="color:#f92672"&gt;=&lt;/span&gt;your@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;password&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&amp;lt;api-token&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ttl&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;home.yourdomain.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;inadyn&lt;/strong&gt; — lighter alternative, similar provider support:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;apt install inadyn
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /etc/inadyn.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;provider cloudflare.com &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; username &lt;span style="color:#f92672"&gt;=&lt;/span&gt; your@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; password &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &amp;lt;api-token&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hostname &lt;span style="color:#f92672"&gt;=&lt;/span&gt; home.yourdomain.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ttl &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; proxied &lt;span style="color:#f92672"&gt;=&lt;/span&gt; false
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="limitations"&gt;Limitations
&lt;/h2&gt;&lt;p&gt;DDNS does not help if your ISP uses CGNAT — if your router&amp;rsquo;s WAN IP is a private address (10.x, 100.64.x, 192.168.x), port forwarding and DDNS will not work. See &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/tunnels/" &gt;Tunnels&lt;/a&gt; for options that work without a public IP.&lt;/p&gt;
&lt;p&gt;DNS propagation delay means there&amp;rsquo;s a brief window after an IP change where connections will fail. Keep TTL at 60–300s to minimise this.&lt;/p&gt;</description></item><item><title>Firewall and router OS options</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/router-os/</link><pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/router-os/</guid><description>&lt;p&gt;Options for running a software-defined firewall or router, from homelab appliances to full routing OS deployments.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-main-split-appliance-vs-routing-os"&gt;The main split: appliance vs routing OS
&lt;/h2&gt;&lt;p&gt;Most options fall into one of two categories:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Firewall appliances&lt;/strong&gt; (OPNsense, pfSense, IPFire) — web UI-first, designed around the perimeter firewall use case. NAT, DHCP, DNS, VPN, IDS/IPS out of the box. Routing is possible but secondary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Routing operating systems&lt;/strong&gt; (VyOS, MikroTik RouterOS, FRRouting) — CLI-first, designed around dynamic routing protocols (BGP, OSPF). Firewall rules exist but feel like an afterthought compared to the routing capabilities.&lt;/p&gt;
&lt;p&gt;For a homelab perimeter gateway: appliance. For BGP peering, complex routing topologies, or network-as-code: routing OS.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="opnsense"&gt;OPNsense
&lt;/h2&gt;&lt;p&gt;Open-source firewall and routing platform based on FreeBSD. Fork of pfSense, with a stronger emphasis on community ownership and more frequent security updates.&lt;/p&gt;
&lt;p&gt;Full gateway function: stateful firewall, NAT, DHCP, DNS (Unbound), TFTP/PXE, VPN (WireGuard, OpenVPN, IPsec), traffic shaping, IDS/IPS (Suricata), DDNS.&lt;/p&gt;
&lt;p&gt;BGP is available via the FRRouting plugin but is not a first-class feature — VyOS is better suited for BGP-heavy setups.&lt;/p&gt;
&lt;p&gt;→ &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/opnsense/" &gt;OPNsense reference&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt;: homelab perimeter gateway, home network, small office. The current actively-maintained community fork of the pfSense lineage.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="pfsense"&gt;pfSense
&lt;/h2&gt;&lt;p&gt;The original FreeBSD-based firewall appliance. Same underlying capabilities as OPNsense — they share a common ancestor (m0n0wall).&lt;/p&gt;
&lt;p&gt;Now owned by Netgate. The &lt;strong&gt;Community Edition (CE)&lt;/strong&gt; remains open source; &lt;strong&gt;pfSense Plus&lt;/strong&gt; is commercial and ships only on Netgate hardware or as a cloud image. Development focus has shifted toward Plus; CE updates have been slower.&lt;/p&gt;
&lt;p&gt;The practical difference between OPNsense and pfSense CE is increasingly small at the feature level. The main reasons to choose one over the other are familiarity, UI preference, and update cadence. OPNsense is the more actively developed option for community use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt;: environments where pfSense is already deployed, or where existing documentation/tooling targets it.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="vyos"&gt;VyOS
&lt;/h2&gt;&lt;p&gt;Open-source network OS built on Debian. Configured via a CLI with a commit/rollback model (similar to Juniper JunOS). Native BGP, OSPF, IS-IS via FRRouting.&lt;/p&gt;
&lt;p&gt;Configuration is declarative and version-controlled — the entire running config is a text file, which makes it automation-friendly (Ansible, Terraform).&lt;/p&gt;
&lt;p&gt;The rolling release is free; LTS releases require a subscription.&lt;/p&gt;
&lt;p&gt;→ &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/vyos/" &gt;VyOS reference&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt;: BGP peering, complex routing topologies, automation-driven network config, VM-based routing inside a cluster.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="mikrotik-routeros"&gt;MikroTik RouterOS
&lt;/h2&gt;&lt;p&gt;Commercial OS that runs on MikroTik hardware and as a VM (CHR — Cloud Hosted Router). Full routing OS with BGP, OSPF, MPLS, and a firewall. Configured via Winbox GUI, web UI, or CLI.&lt;/p&gt;
&lt;p&gt;Very capable at the price point. Hardware is inexpensive. The learning curve is steeper than OPNsense but shallower than VyOS for most tasks. Community is large and documentation is thorough.&lt;/p&gt;
&lt;p&gt;CHR (the VM version) is free for speeds up to 1Mbps; licensed tiers above that. On physical hardware, the license is included.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt;: cost-conscious deployments that need routing features, or environments already using MikroTik hardware.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="ipfire"&gt;IPFire
&lt;/h2&gt;&lt;p&gt;Linux-based firewall focused on simplicity and security hardening. Web UI, stateful firewall, IDS (Snort/Suricata), VPN (OpenVPN, WireGuard, IPsec), proxy.&lt;/p&gt;
&lt;p&gt;Less feature-rich than OPNsense but lighter and more opinionated. No BGP. Easier to get to a secure baseline quickly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt;: simple gateway where you want a small attack surface and don&amp;rsquo;t need advanced routing or a plugin ecosystem.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="untangle--arista-edge-threat-management"&gt;Untangle / Arista Edge Threat Management
&lt;/h2&gt;&lt;p&gt;Commercial product with a free tier (NG Firewall). Web UI, application-layer filtering, content inspection, threat management features. More enterprise-oriented than the others.&lt;/p&gt;
&lt;p&gt;Requires registration. The free tier is limited; the feature set that differentiates it from OPNsense is mostly in the commercial tiers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt;: environments that need application-layer filtering with a managed UI, or commercial support requirements.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="comparison"&gt;Comparison
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;OPNsense&lt;/th&gt;
 &lt;th&gt;pfSense CE&lt;/th&gt;
 &lt;th&gt;VyOS&lt;/th&gt;
 &lt;th&gt;MikroTik RouterOS&lt;/th&gt;
 &lt;th&gt;IPFire&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Base OS&lt;/td&gt;
 &lt;td&gt;FreeBSD&lt;/td&gt;
 &lt;td&gt;FreeBSD&lt;/td&gt;
 &lt;td&gt;Debian&lt;/td&gt;
 &lt;td&gt;Proprietary&lt;/td&gt;
 &lt;td&gt;Linux&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Primary interface&lt;/td&gt;
 &lt;td&gt;Web UI&lt;/td&gt;
 &lt;td&gt;Web UI&lt;/td&gt;
 &lt;td&gt;CLI&lt;/td&gt;
 &lt;td&gt;Winbox / CLI&lt;/td&gt;
 &lt;td&gt;Web UI&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BGP / OSPF&lt;/td&gt;
 &lt;td&gt;Plugin (FRR)&lt;/td&gt;
 &lt;td&gt;Plugin (FRR)&lt;/td&gt;
 &lt;td&gt;Native (FRR)&lt;/td&gt;
 &lt;td&gt;Native&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;IDS/IPS&lt;/td&gt;
 &lt;td&gt;Suricata&lt;/td&gt;
 &lt;td&gt;Snort/Suricata&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;td&gt;Snort/Suricata&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;WireGuard&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Yes (Plus)&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;DDNS&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Via script&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Cost&lt;/td&gt;
 &lt;td&gt;Free&lt;/td&gt;
 &lt;td&gt;Free (CE)&lt;/td&gt;
 &lt;td&gt;Free (rolling)&lt;/td&gt;
 &lt;td&gt;Hardware license&lt;/td&gt;
 &lt;td&gt;Free&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Community&lt;/td&gt;
 &lt;td&gt;Active&lt;/td&gt;
 &lt;td&gt;Slowing (CE)&lt;/td&gt;
 &lt;td&gt;Active&lt;/td&gt;
 &lt;td&gt;Active&lt;/td&gt;
 &lt;td&gt;Active&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="further-reading"&gt;Further reading
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/opnsense/" &gt;OPNsense in the homelab&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/vyos-bgp/" &gt;VyOS + BGP in the homelab&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Tunneled reverse proxy platforms</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/tunneled-reverse-proxy/</link><pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/tunneled-reverse-proxy/</guid><description>&lt;p&gt;A step beyond raw tunnels. These platforms expose services running on a private network as public HTTPS URLs — no open ports, no public IP required. The key difference from a VPN: you don&amp;rsquo;t get network-level access to the remote machine, you get a link to a specific service.&lt;/p&gt;
&lt;p&gt;Typical flow:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Internet → public server (VPS) → tunnel → private network → your service
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The private machine maintains an outbound connection to the public server. Inbound traffic arrives at the public server and is forwarded back through the tunnel to the service.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="pangolin"&gt;Pangolin
&lt;/h2&gt;&lt;p&gt;Self-hosted platform built specifically for this use case. You run the server component on a VPS; client agents (called Newt) run on machines inside your private network and establish outbound tunnels. Services get public HTTPS URLs. Access control is built in — you can gate services behind auth without touching the service itself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Components&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pangolin&lt;/strong&gt; — the server, runs on a VPS, handles routing and auth&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Newt&lt;/strong&gt; — the tunnel client, runs on private machines, connects out to Pangolin&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gerbil&lt;/strong&gt; — WireGuard-based tunnel layer (handled automatically)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Traefik&lt;/strong&gt; — reverse proxy, managed by Pangolin for routing&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# On the VPS (docker-compose)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Pangolin + Traefik + Gerbil run together&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# On a private machine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker run -e SERVER_URL&lt;span style="color:#f92672"&gt;=&lt;/span&gt;https://pangolin.yourdomain.com &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -e TUNNEL_SECRET&lt;span style="color:#f92672"&gt;=&lt;/span&gt;... &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fosrl/newt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Services then appear at subdomains: &lt;code&gt;service.yourdomain.com&lt;/code&gt; — proxied through the tunnel, with optional SSO/auth in front.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Property&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Self-hosted&lt;/td&gt;
 &lt;td&gt;Yes — you own the VPS and the data&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Open ports on private network&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Public IP required&lt;/td&gt;
 &lt;td&gt;VPS only (not home)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Auth built in&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Cost&lt;/td&gt;
 &lt;td&gt;Free, open source&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Maturity&lt;/td&gt;
 &lt;td&gt;Newer project (2024–)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Good fit for homelabs where you want public-facing services but don&amp;rsquo;t want to expose your home IP or open ports on your router.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="ngrok"&gt;ngrok
&lt;/h2&gt;&lt;p&gt;The original in this space. Run a single command, get a public URL. No server to manage — ngrok&amp;rsquo;s infrastructure handles it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ngrok http &lt;span style="color:#ae81ff"&gt;8080&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# → https://abc123.ngrok.io&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The URL changes on every restart unless you&amp;rsquo;re on a paid tier. Custom domains, persistent tunnels, and traffic inspection (useful for debugging webhooks) are paid features.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Property&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Self-hosted&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Open ports on private network&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Auth built in&lt;/td&gt;
 &lt;td&gt;Yes (paid)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Cost&lt;/td&gt;
 &lt;td&gt;Free tier limited; paid for custom domains&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Maturity&lt;/td&gt;
 &lt;td&gt;Established, widely used&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Best for: quick testing, webhook development, demos. Not ideal for a permanent homelab setup due to the dependency and cost at scale.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="frp-fast-reverse-proxy"&gt;frp (Fast Reverse Proxy)
&lt;/h2&gt;&lt;p&gt;Lightweight, self-hosted. You run &lt;code&gt;frps&lt;/code&gt; (server) on a VPS and &lt;code&gt;frpc&lt;/code&gt; (client) on private machines. More DIY than Pangolin — you configure the routing yourself, no built-in auth or dashboard.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# frps.toml (on VPS)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;bindPort&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;7000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# frpc.toml (on private machine)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;serverAddr&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;your-vps-ip&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;serverPort&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;7000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;[[proxies]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;my-service&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;type&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;http&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;localPort&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;8080&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;customDomains&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;[&amp;#34;service.yourdomain.com&amp;#34;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Property&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Self-hosted&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Open ports on private network&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Auth built in&lt;/td&gt;
 &lt;td&gt;Basic (token auth between client/server)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Cost&lt;/td&gt;
 &lt;td&gt;Free, open source&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Maturity&lt;/td&gt;
 &lt;td&gt;Established, widely used in homelab&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Good fit if you want full control and are comfortable wiring things together. No UI — config files only.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="expose-beyondcode"&gt;Expose (BeyondCode)
&lt;/h2&gt;&lt;p&gt;PHP-based self-hosted ngrok alternative. Dashboard included, custom domains, multiple tunnels.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;expose share http://localhost:8080
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Less common than frp or Pangolin, but polished for a self-hosted tool.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="comparison"&gt;Comparison
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;Pangolin&lt;/th&gt;
 &lt;th&gt;ngrok&lt;/th&gt;
 &lt;th&gt;frp&lt;/th&gt;
 &lt;th&gt;Expose&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Self-hosted&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Auth / access control&lt;/td&gt;
 &lt;td&gt;Yes (built-in)&lt;/td&gt;
 &lt;td&gt;Paid&lt;/td&gt;
 &lt;td&gt;No (DIY)&lt;/td&gt;
 &lt;td&gt;Basic&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Dashboard&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Custom domains&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Paid&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Complexity&lt;/td&gt;
 &lt;td&gt;Medium&lt;/td&gt;
 &lt;td&gt;Low&lt;/td&gt;
 &lt;td&gt;Medium&lt;/td&gt;
 &lt;td&gt;Medium&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Maturity&lt;/td&gt;
 &lt;td&gt;Newer&lt;/td&gt;
 &lt;td&gt;Established&lt;/td&gt;
 &lt;td&gt;Established&lt;/td&gt;
 &lt;td&gt;Moderate&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="when-to-use-which"&gt;When to use which
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Pangolin&lt;/strong&gt; if you want a self-hosted, permanent setup with auth and public URLs for homelab services. The right choice if you&amp;rsquo;re already running a VPS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ngrok&lt;/strong&gt; for quick tests, webhook development, or one-off sharing. Not for permanent services.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;frp&lt;/strong&gt; if you want maximum control and minimal overhead, and are comfortable configuring a reverse proxy separately.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="related"&gt;Related
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;For network-level access (not service URLs) → &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/tunnels/" &gt;Tunnels&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;For SSH entry points → &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/bastion/" &gt;Bastion / jump server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Tunnels — remote network access</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/tunnels/</link><pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/tunnels/</guid><description>&lt;p&gt;Approaches for accessing a private network (home lab, office) from outside, when you don&amp;rsquo;t have a static public IP and may be behind NAT or CGNAT.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="first-check-if-youre-behind-cgnat"&gt;First: check if you&amp;rsquo;re behind CGNAT
&lt;/h2&gt;&lt;p&gt;Run on a device inside the network:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl ifconfig.me
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Compare the result to the WAN IP shown in your router. If they match — you have a real public IP (possibly dynamic). If they differ — you&amp;rsquo;re behind CGNAT, and port forwarding will not work regardless of router config.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="tailscale"&gt;Tailscale
&lt;/h2&gt;&lt;p&gt;WireGuard-based mesh VPN. Each device gets a node in a private overlay network coordinated by Tailscale&amp;rsquo;s control plane. Connections are peer-to-peer where possible; relay servers (DERP) handle cases where direct traversal fails.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -fsSL https://tailscale.com/install.sh | sh
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo tailscale up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Subnet routing&lt;/strong&gt;: one node (e.g. a Pi on the LAN) advertises the local subnet. Other Tailscale nodes route through it — the whole LAN becomes reachable, not just that one node.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo tailscale up --advertise-routes&lt;span style="color:#f92672"&gt;=&lt;/span&gt;192.168.1.0/24
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Exit node&lt;/strong&gt;: route all internet traffic through a Tailscale node — useful on untrusted networks.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Property&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Open ports required&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Public IP required&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Works through CGNAT&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Third-party dependency&lt;/td&gt;
 &lt;td&gt;Yes (coordination server)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Free tier&lt;/td&gt;
 &lt;td&gt;100 devices&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Self-hostable&lt;/td&gt;
 &lt;td&gt;Yes — &lt;a class="link" href="https://github.com/juanfont/headscale" target="_blank" rel="noopener"
 &gt;Headscale&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The coordination server must be reachable to establish new connections. Existing sessions survive outages. Headscale replaces the coordination server while reusing the same clients.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="cloudflare-tunnel"&gt;Cloudflare Tunnel
&lt;/h2&gt;&lt;p&gt;The device on your private network runs &lt;code&gt;cloudflared&lt;/code&gt;, which holds an outbound connection to Cloudflare&amp;rsquo;s edge. Cloudflare proxies inbound traffic through it. No open ports, no public IP needed.&lt;/p&gt;
&lt;p&gt;Works for SSH, HTTP, and other TCP services. Native SSH support: access via &lt;code&gt;cloudflared access ssh&lt;/code&gt; or a browser terminal.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cloudflared tunnel login
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cloudflared tunnel create homelab
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cloudflared tunnel route dns homelab ssh.yourdomain.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cloudflared tunnel run homelab
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Property&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Open ports required&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Public IP required&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Works through CGNAT&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Third-party dependency&lt;/td&gt;
 &lt;td&gt;Yes — traffic passes through Cloudflare&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Requires&lt;/td&gt;
 &lt;td&gt;Domain on Cloudflare&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Cost&lt;/td&gt;
 &lt;td&gt;Free (Zero Trust free tier)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Traffic passes through Cloudflare&amp;rsquo;s infrastructure. For SSH this means an encrypted session transiting a third party&amp;rsquo;s edge.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="wireguard-self-hosted"&gt;WireGuard (self-hosted)
&lt;/h2&gt;&lt;p&gt;WireGuard server on a node in the private network. Remote clients connect with a config containing the server&amp;rsquo;s public key and endpoint. Encrypted point-to-point, no intermediary.&lt;/p&gt;
&lt;p&gt;Requires a real public IP at the WAN, UDP port forwarding on the router (default 51820), and DDNS if the IP is dynamic.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /etc/wireguard/wg0.conf (server)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;[Interface]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Address&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;10.0.0.1/24&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ListenPort&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;51820&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;PrivateKey&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;lt;server-private-key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;[Peer]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;PublicKey&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;lt;client-public-key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;AllowedIPs&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;10.0.0.2/32&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Property&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Open ports required&lt;/td&gt;
 &lt;td&gt;Yes (UDP 51820)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Public IP required&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Works through CGNAT&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Third-party dependency&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Complexity&lt;/td&gt;
 &lt;td&gt;Medium&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Full ownership, no external dependency, very fast. Key distribution is manual unless you layer a management tool on top (e.g. wg-easy, Netbird).&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="port-forwarding--ddns"&gt;Port forwarding + DDNS
&lt;/h2&gt;&lt;p&gt;Forward a port on the router to the target host. Use a DDNS provider (DuckDNS, Cloudflare, etc.) to keep a hostname pointed at the dynamic IP.&lt;/p&gt;
&lt;p&gt;Exposes a service directly to the internet. Minimum hardening for SSH:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Key-only auth (&lt;code&gt;PasswordAuthentication no&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Non-standard port&lt;/li&gt;
&lt;li&gt;fail2ban or equivalent&lt;/li&gt;
&lt;li&gt;Regular review of auth logs&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Property&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Open ports required&lt;/td&gt;
 &lt;td&gt;Yes (TCP)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Public IP required&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Works through CGNAT&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Third-party dependency&lt;/td&gt;
 &lt;td&gt;DDNS provider only&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Complexity&lt;/td&gt;
 &lt;td&gt;Low–Medium&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="comparison"&gt;Comparison
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;Tailscale&lt;/th&gt;
 &lt;th&gt;Cloudflare Tunnel&lt;/th&gt;
 &lt;th&gt;WireGuard&lt;/th&gt;
 &lt;th&gt;Port forward&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Open ports&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Public IP&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;CGNAT&lt;/td&gt;
 &lt;td&gt;Works&lt;/td&gt;
 &lt;td&gt;Works&lt;/td&gt;
 &lt;td&gt;Fails&lt;/td&gt;
 &lt;td&gt;Fails&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Traffic via third party&lt;/td&gt;
 &lt;td&gt;Metadata only&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Self-hostable&lt;/td&gt;
 &lt;td&gt;Yes (Headscale)&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Complexity&lt;/td&gt;
 &lt;td&gt;Low&lt;/td&gt;
 &lt;td&gt;Low–Med&lt;/td&gt;
 &lt;td&gt;Medium&lt;/td&gt;
 &lt;td&gt;Low–Med&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="which-to-use"&gt;Which to use
&lt;/h2&gt;&lt;p&gt;Start with &lt;strong&gt;Tailscale&lt;/strong&gt; if you want the least friction and aren&amp;rsquo;t sure whether you&amp;rsquo;re behind CGNAT. Subnet routing covers the entire LAN through a single node.&lt;/p&gt;
&lt;p&gt;Use &lt;strong&gt;Cloudflare Tunnel&lt;/strong&gt; if you have a domain on Cloudflare and want browser-accessible SSH or HTTP without client software.&lt;/p&gt;
&lt;p&gt;Use &lt;strong&gt;WireGuard&lt;/strong&gt; if you have a confirmed public IP and want no third-party in the data path.&lt;/p&gt;
&lt;p&gt;Use &lt;strong&gt;port forwarding&lt;/strong&gt; only if you already have DDNS working and want no new software — it has the highest hardening overhead.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="related"&gt;Related
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;For a hardened SSH entry point → &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/bastion/" &gt;Bastion / jump server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;For exposing internal web services via public URLs → &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/tunneled-reverse-proxy/" &gt;Tunneled reverse proxy platforms&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>BGP</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/bgp/</link><pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/bgp/</guid><description>&lt;p&gt;BGP (Border Gateway Protocol) is the routing protocol that holds the internet together. Every major network operator uses it to advertise which IP prefixes they own and to exchange that information with peers. In a homelab context the scale is different but the mechanics are the same.&lt;/p&gt;
&lt;p&gt;BGP is a path-vector protocol: each router advertises routes along with the path (sequence of ASNs) taken to reach them. Routers choose the best path based on a set of attributes and policy rules, then advertise that path to their peers.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="ebgp-vs-ibgp"&gt;eBGP vs iBGP
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;eBGP&lt;/strong&gt; (external BGP) — sessions between routers in &lt;em&gt;different&lt;/em&gt; autonomous systems. Each party has a different ASN. This is what you configure between VyOS and OPNsense, and between VyOS and MetalLB.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;iBGP&lt;/strong&gt; (internal BGP) — sessions between routers in the &lt;em&gt;same&lt;/em&gt; autonomous system. Used inside large networks to distribute external routes internally. Not relevant for a basic homelab setup.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="asns-for-private-use"&gt;ASNs for private use
&lt;/h2&gt;&lt;p&gt;Autonomous System Numbers in the range &lt;strong&gt;64512–65534&lt;/strong&gt; are reserved for private use (&lt;a class="link" href="https://www.rfc-editor.org/rfc/rfc6996" target="_blank" rel="noopener"
 &gt;RFC 6996&lt;/a&gt;) — the same concept as RFC 1918 private IP addresses. Assign one to each participant in your BGP topology:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Participant&lt;/th&gt;
 &lt;th&gt;Example ASN&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;OPNsense&lt;/td&gt;
 &lt;td&gt;64512&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;VyOS&lt;/td&gt;
 &lt;td&gt;64513&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;MetalLB (Talos cluster)&lt;/td&gt;
 &lt;td&gt;64514&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="why-bgp-for-kubernetes-loadbalancer-ips"&gt;Why BGP for Kubernetes LoadBalancer IPs
&lt;/h2&gt;&lt;p&gt;Kubernetes &lt;code&gt;LoadBalancer&lt;/code&gt; services need something external to the cluster to route traffic to them. In a cloud environment the cloud provider handles this automatically. On bare metal you need to do it yourself.&lt;/p&gt;
&lt;p&gt;Two common approaches with &lt;a class="link" href="https://metallb.universe.tf" target="_blank" rel="noopener"
 &gt;MetalLB&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L2 mode&lt;/strong&gt; — MetalLB uses ARP (IPv4) or NDP (IPv6) to announce service IPs directly on the LAN. Simple to set up. Limitations: only one node handles traffic for each IP at a time (no real load balancing at the network layer), and the service IP must be in the same subnet as the nodes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a class="link" href="https://metallb.universe.tf/concepts/bgp/" target="_blank" rel="noopener"
 &gt;BGP mode&lt;/a&gt;&lt;/strong&gt; — MetalLB establishes a BGP session with an upstream router (VyOS, for example) and announces service IPs as /32 prefixes. The router learns the route and can ECMP across all nodes that are advertising it. More correct: actual load balancing, no subnet constraint, clean separation between cluster and network layer.&lt;/p&gt;
&lt;p&gt;The tradeoff is that BGP mode requires a BGP-capable router in the path, which is why VyOS exists in this topology.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="testing-with-a-real-bgp-network"&gt;Testing with a real BGP network
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://dn42.eu" target="_blank" rel="noopener"
 &gt;DN42&lt;/a&gt; is a community-run experimental network that simulates the real internet using actual BGP, DNS, and whois infrastructure. Participants connect via WireGuard or other tunnels and peer with each other using real BGP sessions and real (private-range) ASNs. A good way to practice BGP outside the homelab without needing a production ASN.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="related"&gt;Related
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/vyos/" &gt;VyOS&lt;/a&gt; — the BGP peer router&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/vyos-bgp/" &gt;VyOS + BGP experiment&lt;/a&gt; — the actual setup in this homelab&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>OPNsense</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/opnsense/</link><pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/opnsense/</guid><description>&lt;p&gt;OPNsense is an open-source firewall and routing platform based on FreeBSD. It is a fork of pfSense, with a stronger emphasis on community ownership, a cleaner UI, and more frequent security updates. Both are descendants of m0n0wall.&lt;/p&gt;
&lt;p&gt;It covers the full gateway function: stateful firewall, NAT, DHCP, DNS, TFTP, VPN, traffic shaping, and IDS/IPS — all through a web UI or via the API.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="feature-overview"&gt;Feature overview
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Feature&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Stateful firewall&lt;/td&gt;
 &lt;td&gt;Zone-based rules, aliases, scheduling&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;NAT&lt;/td&gt;
 &lt;td&gt;Outbound, inbound (port forwarding), 1:1&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;DHCP&lt;/td&gt;
 &lt;td&gt;ISC DHCPv4 and Kea; supports network boot options&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;DNS&lt;/td&gt;
 &lt;td&gt;Unbound resolver with DNSSEC; optional forwarding&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;TFTP&lt;/td&gt;
 &lt;td&gt;Simple server at &lt;code&gt;/usr/local/tftp&lt;/code&gt;; used for PXE boot&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;VPN&lt;/td&gt;
 &lt;td&gt;WireGuard, OpenVPN, IPsec&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;IDS/IPS&lt;/td&gt;
 &lt;td&gt;Suricata integration&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Traffic shaping&lt;/td&gt;
 &lt;td&gt;HFSC, PRIQ, CAKE&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BGP / routing&lt;/td&gt;
 &lt;td&gt;FRRouting plugin available (not enabled by default)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="opnsense-vs-pfsense-vs-vyos"&gt;OPNsense vs pfSense vs VyOS
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;OPNsense&lt;/th&gt;
 &lt;th&gt;pfSense&lt;/th&gt;
 &lt;th&gt;VyOS&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Base&lt;/td&gt;
 &lt;td&gt;FreeBSD&lt;/td&gt;
 &lt;td&gt;FreeBSD&lt;/td&gt;
 &lt;td&gt;Debian Linux&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;License&lt;/td&gt;
 &lt;td&gt;BSD (true FOSS)&lt;/td&gt;
 &lt;td&gt;BSL (mixed)&lt;/td&gt;
 &lt;td&gt;GPL&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Model&lt;/td&gt;
 &lt;td&gt;Firewall appliance&lt;/td&gt;
 &lt;td&gt;Firewall appliance&lt;/td&gt;
 &lt;td&gt;Network OS&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Config interface&lt;/td&gt;
 &lt;td&gt;Web UI + API&lt;/td&gt;
 &lt;td&gt;Web UI&lt;/td&gt;
 &lt;td&gt;CLI (commit/rollback)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BGP&lt;/td&gt;
 &lt;td&gt;Via FRRouting plugin&lt;/td&gt;
 &lt;td&gt;Via FRRouting plugin&lt;/td&gt;
 &lt;td&gt;Native (FRRouting built-in)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Typical use&lt;/td&gt;
 &lt;td&gt;Edge gateway, firewall&lt;/td&gt;
 &lt;td&gt;Edge gateway, firewall&lt;/td&gt;
 &lt;td&gt;Router, BGP peer, lab router VM&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;OPNsense and pfSense are both appliance-style: you configure them through a UI and they manage all the underlying services for you. &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/vyos/" &gt;VyOS&lt;/a&gt; is a network OS in the Juniper/Cisco tradition — CLI-first, commit/rollback, intended for use as a router or BGP peer rather than a full gateway appliance.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="related"&gt;Related
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.opnsense.org/" target="_blank" rel="noopener"
 &gt;OPNsense documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/opnsense/plugins" target="_blank" rel="noopener"
 &gt;OPNsense plugins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/hardware/hardware-provisioning/ipxe-opnsense/" &gt;iPXE + OPNsense&lt;/a&gt; — PXE boot configuration via OPNsense DHCP and TFTP&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/opnsense/" &gt;OPNsense in the homelab&lt;/a&gt; — current setup and planned redo&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>VyOS</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/vyos/</link><pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/vyos/</guid><description>&lt;p&gt;VyOS is an open-source network operating system built on Debian Linux. It runs on bare metal or as a VM, and is configured via a CLI with a commit/rollback model similar to Juniper JunOS. Configuration changes are staged and only take effect when you explicitly &lt;code&gt;commit&lt;/code&gt; — there is no live-editing a running config and hoping nothing breaks.&lt;/p&gt;
&lt;p&gt;It ships FRRouting (FRR) as the routing engine, giving it native support for BGP, OSPF, IS-IS, and other protocols. This is its main distinction from &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/opnsense/" &gt;OPNsense&lt;/a&gt; for homelab use: OPNsense is a firewall appliance that can do some routing; VyOS is a routing OS that can also do firewall.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="configuration-model"&gt;Configuration model
&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;vyos@router# set interfaces ethernet eth0 address &amp;#39;192.168.1.254/24&amp;#39;
vyos@router# set protocols bgp system-as &amp;#39;65001&amp;#39;
vyos@router# commit
vyos@router# save
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;configure&lt;/code&gt; enters configuration mode. &lt;code&gt;set&lt;/code&gt; stages a change. &lt;code&gt;commit&lt;/code&gt; applies it. &lt;code&gt;save&lt;/code&gt; persists it to disk. &lt;code&gt;rollback&lt;/code&gt; reverts to the last committed state if something goes wrong. The separation between staging and applying is genuinely useful when changing routing configuration remotely.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="key-features"&gt;Key features
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Feature&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;BGP&lt;/td&gt;
 &lt;td&gt;Via FRRouting; full eBGP/iBGP support&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;OSPF / IS-IS&lt;/td&gt;
 &lt;td&gt;Also via FRR&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Static routing&lt;/td&gt;
 &lt;td&gt;Standard&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;VLAN&lt;/td&gt;
 &lt;td&gt;802.1Q trunking&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;NAT&lt;/td&gt;
 &lt;td&gt;Source and destination NAT&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Firewall&lt;/td&gt;
 &lt;td&gt;Zone-based, stateful&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;WireGuard&lt;/td&gt;
 &lt;td&gt;Built-in&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;OpenVPN&lt;/td&gt;
 &lt;td&gt;Built-in&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;DHCP server&lt;/td&gt;
 &lt;td&gt;Built-in&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;VXLAN&lt;/td&gt;
 &lt;td&gt;Supported&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="vyos-vs-opnsense"&gt;VyOS vs OPNsense
&lt;/h2&gt;&lt;p&gt;VyOS is the right choice when you want a dedicated BGP peer or a router VM with a clean CLI config model. OPNsense is the right choice when you want a full gateway appliance with a web UI.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="automation"&gt;Automation
&lt;/h2&gt;&lt;p&gt;VyOS is designed to be automated — the commit/rollback model maps cleanly onto infrastructure-as-code workflows.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;REST API&lt;/strong&gt; — built-in HTTP API for retrieving and applying configuration programmatically. Useful for scripting config changes without SSH.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ansible&lt;/strong&gt; — official &lt;code&gt;vyos.vyos&lt;/code&gt; collection on Ansible Galaxy. Modules for interfaces, BGP, firewall rules, and more. Changes go through the normal commit/rollback cycle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Terraform&lt;/strong&gt; — community provider available. Less mature than the Ansible collection but usable for provisioning router config alongside other infrastructure.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="related"&gt;Related
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.vyos.io/" target="_blank" rel="noopener"
 &gt;VyOS documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.vyos.io/en/latest/automation/vyos-api.html" target="_blank" rel="noopener"
 &gt;VyOS REST API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.ansible.com/ansible/latest/collections/vyos/vyos/index.html" target="_blank" rel="noopener"
 &gt;VyOS Ansible collection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://vyos.net/get/" target="_blank" rel="noopener"
 &gt;VyOS rolling release downloads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/bgp/" &gt;BGP&lt;/a&gt; — protocol background&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/networking/opnsense/" &gt;OPNsense&lt;/a&gt; — the complementary edge gateway&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/vyos-bgp/" &gt;VyOS + BGP in the homelab&lt;/a&gt; — the actual setup&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>