{
  "version": "https://jsonfeed.org/version/1",
  "title": "Ian's Digital Garden",
  "home_page_url": "https://ianwwagner.com/",
  "feed_url": "https://ianwwagner.com//tag-networking.json",
  "description": "",
  "items": [
    {
      "id": "https://ianwwagner.com//setting-up-a-wireguard-tunnel-on-freebsd-15.html",
      "url": "https://ianwwagner.com//setting-up-a-wireguard-tunnel-on-freebsd-15.html",
      "title": "Setting up a WireGuard Tunnel on FreeBSD 15",
      "content_html": "<p>It's not like the world needs yet another WireGuard tutorial,\nbut I thought I'd write one since one of the top SEO-ranked ones I stumbled upon was pretty low quality,\nwith several obvious errors and omissions.</p>\n<p>In this post, I'll focus on how you can set up a VPN tunnel\nin the sense that such things were used before shady companies hijacked the term.\nIt's just a way to tunnel traffic between networks.\nFor example, to connect non-internet-facing servers behind a firewall\nto a public host that firewalls and selectively routes traffic over the tunnel.</p>\n<p>I'll assume a pair of FreeBSD servers for the rest of the post,\none that's presumably more accessible (the &quot;server&quot;),\nand a client which is not necessarily routable over the internet.</p>\n<h1><a href=\"#server-setup\" aria-hidden=\"true\" class=\"anchor\" id=\"server-setup\"></a>&quot;Server&quot; setup</h1>\n<p>We'll start with the server setup.\nThis is where your client(s) will connect.\nAt a high level, we'll generate a keypair for the server,\na keypair for the client,\nand generate configuration files for both.\nAnd finally we'll do some basic firewall configuration.</p>\n<h2><a href=\"#wireguard-config\" aria-hidden=\"true\" class=\"anchor\" id=\"wireguard-config\"></a>WireGuard config</h2>\n<p>The following can be run,\neither in a script or line-by-line in a POSIX shell as root.</p>\n<pre><code class=\"language-sh\"># Set this to your server's public IP\nSERVER_PUBLIC_IP=&quot;192.0.2.42&quot;\n\n# We'll be setting up some config files here that we only want to be readable by root.\n# The umask saves us the effort of having to chmod these later.\numask 077\n\n# Wireguard kernel-level support is available in FreeBSD 14+,\n# but this port has a nice service wrapper\npkg install wireguard-tools\n\n# Set up WireGuard config directory\nchmod 770 /usr/local/etc/wireguard\ncd /usr/local/etc/wireguard\n\n# Create a keypair for the server\nSERVER_PRIV_KEY=$(wg genkey)\nSERVER_PUB_KEY=`echo $SERVER_PRIV_KEY | wg pubkey`\n\n# Generate the first section of our WireGuard server config.\n# We'll use 172.16.0.1/24 (no real reason for the choice;\n# it's just somewhat convenient as it doesn't collide with the more common\n# Class A and Class C private networks).\ncat &gt; wg0.conf &lt;&lt;EOF\n[Interface]\nAddress = 172.16.0.1/24\nSaveConfig = true\nListenPort = 51820\nPrivateKey = ${SERVER_PRIV_KEY}\nEOF\n\n# Similarly, we need a client keypair\nCLIENT_PRIV_KEY=$(wg genkey)\nCLIENT_PUB_KEY=`echo $CLIENT_PRIV_KEY | wg pubkey`\n\n# Add peer to the server config.\n# This is what lets your client connect later.\n# The server only stores the client's public key\n# and the private IP that it will connect as.\nCLIENT_IP=&quot;172.16.0.2&quot;\ncat &gt;&gt; wg0.conf &lt;&lt;EOF\n# bsdcube\n[Peer]\nPublicKey = ${CLIENT_PUB_KEY}\nAllowedIps = ${CLIENT_IP}/32\nEOF\n\numask 022 # Revert to normal umask\n\n# Enable the wireguard service\nsysrc wireguard_interfaces=&quot;wg0&quot;\nsysrc wireguard_enable=&quot;YES&quot;\nservice wireguard start\n</code></pre>\n<p><strong>Don't ditch this shell session yet!</strong>\nWe'll come back to the client config later and will need the vars defined above.\nBut first, a brief interlude for packet filtering.</p>\n<h2><a href=\"#pf-setup\" aria-hidden=\"true\" class=\"anchor\" id=\"pf-setup\"></a><code>pf</code> setup</h2>\n<p>We'll use <code>pf</code>, the robust packet filtering (colloquially &quot;firewall&quot;) system\nported from OpenBSD.</p>\n<p>I'm using <code>vtnet0</code> for the external interface,\nsince that's the interface name with my VPS vendor.\nYou may need to change this based on what your main network interface is\n(check <code>ifconfig</code>).</p>\n<p><strong>DISCLAIMER</strong>: This is <em>not</em> necessarily everything you need to launch a production system.\nI've distilled just the parts that are relevant to a minimal WireGuard setup.\nThat said, here's a minimal <code>/etc/pf.conf</code>.</p>\n<pre><code class=\"language-pf\">ext_if = &quot;vtnet0&quot;\nwg_if = &quot;wg0&quot;\n\n# Pass all traffic on the loopback interface\nset skip on lo\n\n# Basic packet cleanup\nscrub in on $ext_if all fragment reassemble\n\n# Allows WireGuard clients to reach the internet.\n# I do not nede this in my config, but noting it here\n# in case your use case is *that* sort of VPN.\n# nat on $ext_if from $wg_if:network to any -&gt; ($ext_if)\n\n# Allow all outbound connections\npass out keep state\n\n# SSH (there's a good chance you need this)\npass in on $ext_if proto tcp from any to ($ext_if) port 22\n\n# Allow inbound WireGuard traffic\npass in on $ext_if proto udp from any to ($ext_if) port 51820\n\n# TODO: Forwarding for the services that YOU need\n# Here's one example demonstrating how you would allow traffic\n# to route directly to one of the WireGuard network IPs (e.g. 172.16.42.1/24 in this example)\n# over port 8080.\n# pass in on $wg_if proto tcp from $wg_if:network to ($wg_if) port 8080\n\n# Allow ICMP\npass in inet proto icmp all\npass in inet6 proto icmp6 all\n</code></pre>\n<p>Next, we enable the service and start it.\nIf you're already running <code>pf</code>, then at least part of this isn't necessary.</p>\n<pre><code class=\"language-sh\"># Allow forwarding of traffic from from WireGuard clients\nsysctl net.inet.ip.forwarding=1\n\n# Enable pf\nsysrc pf_enable=&quot;YES&quot;\nservice pf start\n</code></pre>\n<h1><a href=\"#client-configuration\" aria-hidden=\"true\" class=\"anchor\" id=\"client-configuration\"></a>Client configuration</h1>\n<p>And now we come back to the client configuration.\nThe &quot;client&quot; in this case does not necessarily have to be routable over the internet;\nit just needs to be able to connect to the server.\nYou've still got the same shell session with those variables, right?</p>\n<pre><code class=\"language-sh\">cat &lt;&lt;EOF\n[Interface]\nPrivateKey = ${CLIENT_PRIV_KEY}\nAddress = ${CLIENT_IP}/24\n\n[Peer]\nPublicKey = ${SERVER_PUB_KEY}\nAllowedIPs = 172.16.0.0/24  # Only route private subnet traffic over the tunnel\nEndpoint = ${SERVER_PUBLIC_IP}:51820\nPersistentKeepalive = 30\nEOF\n</code></pre>\n<p>That's it; that's the client config.\nRun through the same initial setup steps for adding the <code>wireguard-tools</code> package\nand creating the directory with the right permissions.\nThen put this config in <code>/usr/local/etc/wireguard/wg0.conf</code>.</p>\n<p>The client will also need a similar <code>pf</code> configuration,\nbut rather than blanket allowing traffic in over <code>$wg_if</code>,\nyou probably want something a bit more granular.\nFor example, allowing traffic in over a specific port (e.g. <code>8080</code>).\nI'll leave that as an exercise to the reader based on the specific scenario.</p>\n",
      "summary": "",
      "date_published": "2026-04-07T00:00:00-00:00",
      "image": "",
      "authors": [
        {
          "name": "Ian Wagner",
          "url": "https://fosstodon.org/@ianthetechie",
          "avatar": "media/avi.jpeg"
        }
      ],
      "tags": [
        "networking",
        "FreeBSD"
      ],
      "language": "en"
    },
    {
      "id": "https://ianwwagner.com//edns-client-subnet-and-geographic-dns.html",
      "url": "https://ianwwagner.com//edns-client-subnet-and-geographic-dns.html",
      "title": "EDNS Client-Subnet and Geographic DNS",
      "content_html": "<p>DNS is a complex beast,\nso it's little surprise when I learn something new.\nToday I learned a bit more about the internals of how DNS works\nat various privacy-centric providers.</p>\n<p>It all started a few weeks ago when someone I follow on Mastodon\n(I'm very sorry I can't remember who)\nmentioned <a href=\"https://quad9.net\">Quad9</a> as a privacy-friendly DNS solution.\nI think the context was that they were looking for alternatives\nto the US &quot;big tech&quot; vendors, like Cloudflare and Google.\nI eventually remembered it and switched by DNS a few weeks ago from 1.1.1.1,\nCloudflare's similar service.</p>\n<p>Fast forward to the present.\nI was frustrated that some page loads were REALLY slow.\nI couldn't figure out a clear pattern, but two sites which I visit a LOT\nare <a href=\"https://stadiamaps.com/\">stadiamaps.com</a> and <a href=\"https://docs.stadiamaps.com/\">docs.stadiamaps.com</a>,\nsince it's kinda my job ;)\nThis had been going on far at least a week or two,\nbut I thought it was just something funky with my ISP,\nor maybe they were throttling me (I'm sure I'm a top 1% bandwidth user).</p>\n<p>I had enough of the slowness this morning and was about to type up a thread in our internal Zulip chat\nasking our network guy to look into it on Monday.\nSo like any decent engineer, I popped up a web inspector and captured a HAR file\nso they could &quot;see what I saw.&quot;</p>\n<p>And what did I see?\nAfter a few minutes looking over it for obvious problems,\nI noticed that our marketing site was loading from our edge server in...\nJohannesburg?!\nAnd our docs site was coming from a server in Texas!\n(We include some HTTP headers which assist in debugging this sort of thing.)</p>\n<p>Well, that's not right...\nI popped up <code>dig</code> in my terminal and verified that, indeed, the A records were resolving to servers\nlocated on the other side of the world.\nAnd then it hit me.\nI had changed my DNS settings recently!\nThat must have something to do with it!</p>\n<p>We use AWS Route53 for our geographically aware DNS resolution needs.\nIt's the best product I've seen in the industry,\nso I assumed it wasn't their fault.\nThen I remembered something I read in the <a href=\"https://quad9.net/support/faq/#edns\">Quad9 FAQ</a>\nabout EDNS Client-Subnet (also known as EDNS0 and ECS).\nThat seems relevant...</p>\n<p>The quick version is that some DNS resolvers can use a truncated version of your IP address\nto improve the quality of the results (giving a server near you).\nAmazon has a great <a href=\"https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-policy-edns0.html\">detailed writeup</a>.</p>\n<p>The trouble is that this info could theoretically be used (with some more details) to identify you,\nso many privacy-focused DNS solutions (including Quad9) disable this by default</p>\n<p>Quad9 operates an alternate server with EDNS Client-Subnet enabled.\nI tried that and it gave the results I expected.\nBut this is not where the story ends!</p>\n<p>It turns out, Cloudflare, which I had been using previously,\nalso gave the &quot;expected&quot; results.\nBut they state very clearly in their <a href=\"https://developers.cloudflare.com/1.1.1.1/faq/#does-1111-send-edns-client-subnet-header\">FAQ</a>\nthat they do not use EDNS Client-Subnet.\nWhat gives?</p>\n<p>At this point I'm speculating,\nbut I think that their network setup is a bit different.\nCloudflare is famous for having an extensive edge network,\nand I have a server very close by.\nMy guess is that they make all upstream queries to authoritative servers\nAND cache any results in the same nearby datacenter.\nThis would easily explain why they can still give a geographically relevant result,\nwithout sending your subnet to the authoritative server.</p>\n<p>Quad9 on the other hand either doesn't have as many servers nearby (for fast routing),\nor perhaps they are sharing cache results globally.</p>\n<p>As I said though, this is all just speculation.\nIf anyone has more knowledge of how Cloudflare and Quad9 operate,\nlet me know and I'll update this post!</p>\n",
      "summary": "",
      "date_published": "2025-05-03T00:00:00-00:00",
      "image": "",
      "authors": [
        {
          "name": "Ian Wagner",
          "url": "https://fosstodon.org/@ianthetechie",
          "avatar": "media/avi.jpeg"
        }
      ],
      "tags": [
        "dns",
        "networking"
      ],
      "language": "en"
    }
  ]
}