<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><title>IT Notes - vpn</title><link>https://it-notes.dragas.net/categories/vpn/</link><description>Articles in category vpn</description><atom:link href="https://it-notes.dragas.net/categories/vpn/feed.xml" rel="self"/><language>en</language><lastBuildDate>Tue, 03 Sep 2024 01:41:00 +0200</lastBuildDate><atom:link href="https://it-notes.dragas.net/categories/vpn/feed.xml" rel="self" type="application/rss+xml"/><item><title>Make Your Own CDN with NetBSD</title><link>https://it-notes.dragas.net/2024/09/03/make-your-own-cdn-netbsd/</link><description>&lt;p&gt;&lt;img src="https://it-notes.dragas.net/featured/embedded.jpg" alt="Make Your Own CDN with NetBSD"&gt;&lt;/p&gt;&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;This article is a spin-off from &lt;a href="https://it-notes.dragas.net/2024/08/29/make-your-own-cdn-openbsd/"&gt;a previous post on how to create a self-hosted CDN&lt;/a&gt;, based on OpenBSD, but this time we'll focus on using &lt;a href="https://www.netbsd.org/"&gt;NetBSD&lt;/a&gt;. The idea is to &lt;a href="https://it-notes.dragas.net/2024/08/26/building-a-self-hosted-cdn-for-bsd-cafe-media/"&gt;create reverse proxies with local caching&lt;/a&gt;. These proxies would cache the content on the first request and serve it directly afterward. The proxies would be distributed across different regions, and the DNS would route requests to the nearest proxy based on the caller’s location. All this is achieved without relying on external CDNs, using self-managed tools instead. &lt;/p&gt;
&lt;p&gt;NetBSD is a lightweight, stable, and secure operating system that supports a wide range of hardware, making it an excellent choice for a caching reverse proxy. Devices that other operating systems may soon abandon, such as early Raspberry Pi models or i386 architecture, are still fully supported by NetBSD and will continue to be so. Additionally, NetBSD is an outstanding platform for virtualization (using &lt;a href="https://wiki.netbsd.org/ports/xen/howto/"&gt;Xen&lt;/a&gt; or &lt;a href="https://www.netbsd.org/docs/guide/en/chap-virt.html"&gt;qemu/nvmm&lt;/a&gt;) and deserves more attention than it currently receives.&lt;/p&gt;
&lt;p&gt;The choice of Varnish is based on several factors, with the main ones being the ability to keep the cache in RAM (which means it can run on read-only systems) and the ability to flush the cache remotely. For example, with each change to my blog, I can choose whether to perform an immediate flush (such as for a new article or an error) or wait for the cache's "natural" expiration (such as for a typo or minor, non-critical changes).&lt;/p&gt;
&lt;p&gt;While I won't detail the installation process for NetBSD, as it depends heavily on the hardware you have available, I will guide you through setting up a self-hosted CDN using NetBSD, Varnish, nginx, and the acme.sh or lego tool for SSL certificate management.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;During the installation of NetBSD, ensure that you enable support for binary package management. This will install &lt;code&gt;pkgin&lt;/code&gt;, &lt;a href="https://pkgin.net/"&gt;a tool that simplifies package management&lt;/a&gt; on NetBSD. If you skip this step during installation, you can still install pkgin later, but it's easier to let the installer handle it.&lt;/p&gt;
&lt;p&gt;Once your system is up and running, use pkgin to install the necessary packages: Varnish, nginx, and, depending on your preference, either acme.sh or Go (if you plan to compile lego). Although lego is not available as a precompiled package, you can easily compile it locally using Go, but for simplicity, I recommend using acme.sh.&lt;/p&gt;
&lt;p&gt;For this setup, I will present two methods for generating and renewing certificates: using acme.sh or compiling lego - to reach the same final outcome as the &lt;a href="https://it-notes.dragas.net/2024/08/29/make-your-own-cdn-openbsd/"&gt;OpenBSD article&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Option 1: Using acme.sh (Recommended)&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/acmesh-official/acme.sh"&gt;acme.sh&lt;/a&gt; is a simple, yet powerful, shell script that handles certificate generation and renewal with ease. To install acme.sh:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;pkgin in acmesh varnish nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once acme.sh is installed, you can proceed to configure it for certificate management. It supports DNS authentication and integrates with many DNS providers, making it a flexible choice.&lt;/p&gt;
&lt;h3&gt;Option 2: Compiling Lego&lt;/h3&gt;
&lt;p&gt;If you prefer to use lego, you will need to compile it manually, as it is not available as a precompiled package for NetBSD. First, install Go and other necessary packages:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;pkgin in go varnish nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To compile lego, you’ll need some disk space. Since the &lt;code&gt;/tmp&lt;/code&gt; directory in NetBSD is often mounted as &lt;code&gt;tmpfs&lt;/code&gt; (using RAM), you may run out of space during compilation if your system has limited memory. You can temporarily disable &lt;code&gt;tmpfs&lt;/code&gt; by editing &lt;code&gt;/etc/fstab&lt;/code&gt; and commenting out the relevant line:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;#tmpfs           /tmp    tmpfs   rw,-m=1777,-s=ram%25
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After rebooting, compile lego:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;export GO111MODULE=on
go122 install github.com/go-acme/lego/v4/cmd/lego@latest

cp go/bin/lego /usr/pkg/bin/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once lego is compiled and installed, you can uncomment the &lt;code&gt;/tmp&lt;/code&gt; line in &lt;code&gt;/etc/fstab&lt;/code&gt; and reboot again.&lt;/p&gt;
&lt;h2&gt;Configuring Varnish and nginx&lt;/h2&gt;
&lt;p&gt;First, copy the necessary &lt;code&gt;rc.d&lt;/code&gt; scripts for nginx and Varnish:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;cp /usr/pkg/share/examples/rc.d/nginx /etc/rc.d/
cp /usr/pkg/share/examples/rc.d/varnishd /etc/rc.d/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, add the following to &lt;code&gt;/etc/rc.conf&lt;/code&gt; to enable and configure nginx and Varnish:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;nginx=YES
varnishd=YES
varnishd_flags=&amp;quot;-f /usr/pkg/etc/varnish/default.vcl -T localhost:9999 -a &amp;quot;/var/run/varnish.sock&amp;quot;,user=nginx,group=varnish,mode=660 -s default,500m&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In this configuration, Varnish listens on a Unix socket, and nginx connects to it. This approach is more efficient and helps avoid some issues that may arise when exposing Varnish over an IP/port.&lt;/p&gt;
&lt;h3&gt;Creating the Varnish VCL Configuration&lt;/h3&gt;
&lt;p&gt;Next, create the VCL configuration file for Varnish at &lt;code&gt;/usr/pkg/etc/varnish/default.vcl&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;vcl 4.1;
import std;

# Backend - it-notes.dragas.net
backend it_notes {
    .host = &amp;quot;myBackendIP&amp;quot;;
    .port = &amp;quot;80&amp;quot;;
}

# ACL - purge - it-notes.dragas.net
acl purge_it_notes {
    &amp;quot;allowedToPurge_IP&amp;quot;;
}

sub vcl_recv {
    # it-notes.dragas.net
    if (req.http.Host == &amp;quot;it-notes.dragas.net&amp;quot;) {
        set req.backend_hint = it_notes;
        set req.http.Host = &amp;quot;it-notes.dragas.net&amp;quot;;

        # PURGE - it-notes.dragas.net
        if (req.method == &amp;quot;PURGE&amp;quot;) {
            std.log(&amp;quot;Purge request received for &amp;quot; + req.url);

            if (!std.ip(req.http.X-Forwarded-For, &amp;quot;0.0.0.0&amp;quot;) ~ purge_it_notes) {
                return (synth(405, &amp;quot;Not allowed.&amp;quot;));
            }

            if (req.url == &amp;quot;/&amp;quot; || req.url == &amp;quot;/*&amp;quot;) {
                ban(&amp;quot;req.http.host == &amp;quot; + req.http.host);
                return(synth(200, &amp;quot;Entire cache has been cleared.&amp;quot;));
            }
            return (purge);
        }

    } else {
        # Other domains - 404
        return (synth(404, &amp;quot;Domain not found&amp;quot;));
    }

    if (req.method != &amp;quot;GET&amp;quot; &amp;amp;&amp;amp; req.method != &amp;quot;HEAD&amp;quot;) {
        return (pipe);
    }

    return (hash);
}

sub vcl_backend_response {
    # TTL - it-notes.dragas.net
    if (bereq.http.host == &amp;quot;it-notes.dragas.net&amp;quot;) {
        if (bereq.url ~ &amp;quot;\.(gif|jpg|jpeg|png|webp|ico|css|js)$&amp;quot;) {
            set beresp.ttl = 1w;
            set beresp.grace = 1d;
            set beresp.keep = 7d;
            unset beresp.http.Set-Cookie;
            unset beresp.http.Cache-Control;
            set beresp.http.Cache-Control = &amp;quot;public, max-age=604800&amp;quot;;
        } else {
            set beresp.ttl = 15m;
            set beresp.grace = 48h;
            set beresp.keep = 7d;
        }
    }

    # Remove some headers
    unset beresp.http.Server;
    unset beresp.http.X-Powered-By;
    unset beresp.http.Via;

    return (deliver);
}

sub vcl_deliver {
    # Add X-Cache header
    if (obj.hits &amp;gt; 0) {
        set resp.http.X-Cache = &amp;quot;HIT&amp;quot;;
    } else {
        set resp.http.X-Cache = &amp;quot;MISS&amp;quot;;
    }

    std.log(&amp;quot;Delivering content for &amp;quot; + req.url + &amp;quot; - Cache: &amp;quot; + resp.http.X-Cache);

    # Remove Varnish headers
    unset resp.http.Via;
    unset resp.http.X-Varnish;

    return (deliver);
}

sub vcl_hash {
    hash_data(req.url);
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }
    return (lookup);
}

sub vcl_hit {
    return (deliver);
}

sub vcl_miss {
    return (fetch);
}

sub vcl_purge {
    std.log(&amp;quot;Purge executed for &amp;quot; + req.url);
    return (synth(200, &amp;quot;Purge successful&amp;quot;));
}

sub vcl_synth {
    set resp.http.Content-Type = &amp;quot;text/html; charset=utf-8&amp;quot;;
    set resp.http.Retry-After = &amp;quot;5&amp;quot;;
    synthetic({&amp;quot;&amp;lt;!DOCTYPE html&amp;gt;
        &amp;lt;html&amp;gt;
            &amp;lt;head&amp;gt;
                &amp;lt;title&amp;gt;&amp;quot;} + resp.status + &amp;quot; &amp;quot; + resp.reason + {&amp;quot;&amp;lt;/title&amp;gt;
            &amp;lt;/head&amp;gt;
            &amp;lt;body&amp;gt;
                &amp;lt;h1&amp;gt;Status &amp;quot;} + resp.status + &amp;quot; &amp;quot; + resp.reason + {&amp;quot;&amp;lt;/h1&amp;gt;
                &amp;lt;p&amp;gt;&amp;quot;} + resp.reason + {&amp;quot;&amp;lt;/p&amp;gt;
                &amp;lt;h3&amp;gt;Guru Meditation:&amp;lt;/h3&amp;gt;
                &amp;lt;p&amp;gt;XID: &amp;quot;} + req.xid + {&amp;quot;&amp;lt;/p&amp;gt;
                &amp;lt;hr&amp;gt;
                &amp;lt;p&amp;gt;Varnish cache server&amp;lt;/p&amp;gt;
            &amp;lt;/body&amp;gt;
        &amp;lt;/html&amp;gt;
    &amp;quot;});
    return (deliver);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Configuring nginx&lt;/h3&gt;
&lt;p&gt;Now, modify the nginx configuration file at &lt;code&gt;/usr/pkg/etc/nginx/nginx.conf&lt;/code&gt;. Set the number of worker processes to "auto" to take advantage of all server cores, and configure the reverse proxy for your site(s). Here's an example configuration:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;[...]
worker_processes  auto;
[...]

server {
    server_name it-notes.dragas.net;

    location / {
        proxy_method $request_method;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_pass http://unix:/var/run/varnish.sock;
    }

    access_log /var/log/nginx/access.it-notes.dragas.net.log;
    error_log /var/log/nginx/error.it-notes.dragas.net.log;

    listen [::]:443 ssl;
    listen 443 ssl;
    http2 on;
    # If you're using acme.sh, just change the location of the certificates
    ssl_certificate /root/.lego/certificates/it-notes.dragas.net.crt;
    ssl_certificate_key /root/.lego/certificates/it-notes.dragas.net.key;
}

server {
    if ($host = it-notes.dragas.net) {
        return 301 https://$host$request_uri;
    }
    server_name it-notes.dragas.net;
    listen 80;
    listen [::]:80;
    return 404;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Starting Varnish and nginx&lt;/h3&gt;
&lt;p&gt;Finally, start the Varnish and nginx services:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;service varnishd start
service nginx start
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If everything is configured correctly, both Varnish and nginx will be up and running, ready to handle incoming connections.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Congratulations, you have successfully set up your own CDN on NetBSD. This solution is lightweight, stable, and fully under your control, allowing you to break free from the constraints of major service providers. With NetBSD's broad hardware support and minimal overhead, this setup can run on a wide variety of devices, making it a versatile choice for self-hosted solutions.&lt;/p&gt;
&lt;p&gt;I'm using it as a test and as a read-only root filesystem with a RAM-only local cache for my blog on a &lt;a href="https://www.raspberrypi.com/products/raspberry-pi-zero-w/"&gt;Raspberry Pi Zero W (first edition)&lt;/a&gt;, and as soon as I get the new FTTH, I'll probably make it accessible via IPv6 for Italy, putting it physically into production.&lt;/p&gt;
&lt;p&gt;If your goal is geo-replication, you can use DNS providers that offer location-based routing or set up your own DNS infrastructure to manage and resolve requests according to the user’s location. With multiple reverse proxies, separate DNS servers, and a well-configured cache, you can achieve a highly resilient system with minimal risk of a single point of failure.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Stefano Marinelli</dc:creator><pubDate>Tue, 03 Sep 2024 01:41:00 +0200</pubDate><guid isPermaLink="true">https://it-notes.dragas.net/2024/09/03/make-your-own-cdn-netbsd/</guid><category>netbsd</category><category>server</category><category>hosting</category><category>tutorial</category><category>ownyourdata</category><category>vpn</category><category>ha</category><category>wireguard</category><category>web</category><category>cdn</category><category>bsdcafe</category><category>varnish</category><category>series</category></item><item><title>Make Your Own CDN with OpenBSD Base and Just 2 Packages</title><link>https://it-notes.dragas.net/2024/08/29/make-your-own-cdn-openbsd/</link><description>&lt;p&gt;&lt;img src="https://it-notes.dragas.net/featured/web_text.webp" alt="Make Your Own CDN with OpenBSD Base and Just 2 Packages"&gt;&lt;/p&gt;&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;This article is a "spin-off" from the previous post "&lt;a href="https://it-notes.dragas.net/2024/08/26/building-a-self-hosted-cdn-for-bsd-cafe-media/"&gt;Building a Self-Hosted CDN for BSD Cafe Media&lt;/a&gt;," which I recommend reading, based on FreeBSD. In that article, I showed how I addressed the issue of geo-replication and geo-distribution of BSD Cafe media content. If you prefer a NetBSD-based setup, &lt;a href="https://it-notes.dragas.net/2024/09/03/make-your-own-cdn-netbsd/"&gt;there's an article that describes how to do it&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The internet today relies TOO MUCH on just a few big players. When one of them stops working, half the world is impacted because too many services, in my opinion, depend on them. “Too big to fail,” some might say. 
“Single Point of Failure,” I respond."&lt;/p&gt;
&lt;p&gt;The strength of the internet has always been its extreme decentralization, which is now less evident due to this phenomenon.&lt;/p&gt;
&lt;p&gt;In this article, I want to show how easy it is to create a self-hosted CDN using OpenBSD and just two external packages: &lt;a href="https://varnish-cache.org/"&gt;Varnish&lt;/a&gt; and &lt;a href="https://github.com/go-acme/lego"&gt;Lego&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Actually, only one package is truly needed (Varnish), and SSL certificates could be generated using the built-in &lt;a href="https://man.openbsd.org/acme-client.1"&gt;acme-client&lt;/a&gt; in OpenBSD. However, this might be limiting since acme-client handles certificate generation via traditional methods (using a file in &lt;code&gt;.well-known&lt;/code&gt;), but when dealing with a CDN and several reverse proxies listening, you don’t have perfect control over which one will receive the certificate generation validation request.&lt;/p&gt;
&lt;p&gt;The choice of Varnish is based on several factors, with the main ones being the ability to keep the cache in RAM (which means it can run on read-only systems) and the ability to flush the cache remotely. For example, with each change to my blog, I can choose whether to perform an immediate flush (such as for a new article or an error) or wait for the cache's "natural" expiration (such as for a typo or minor, non-critical changes).&lt;/p&gt;
&lt;p&gt;For convenience and practicality, I’ll use the excellent Lego tool-a Go application that supports many DNS authentication methods, including PowerDNS.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;The steps are quite simple. After installing and updating OpenBSD (using the &lt;a href="https://man.openbsd.org/syspatch"&gt;syspatch&lt;/a&gt; command), start by installing the two packages:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-sh"&gt;obcdn# pkg_add lego varnish
quirks-7.14:updatedb-0p0: ok
quirks-7.14 signed on 2024-08-24T13:35:23Z
quirks-7.14: ok
lego-4.16.1: ok
varnish-7.4.2:bzip2-1.0.8p0: ok
varnish-7.4.2:pcre2-10.37p2: ok
useradd: Warning: home directory `/var/varnish' doesn't exist, and -m was not specified
varnish-7.4.2: ok
The following new rcscripts were installed: /etc/rc.d/varnishd
See rcctl(8) for details.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The next step is to enable Varnish:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;rcctl enable varnishd
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Varnish has a generic startup script, but it's best to customize the startup options. To do this, modify the &lt;code&gt;/etc/rc.conf.local&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;varnishd_flags=&amp;quot;-j unix,user=_varnish,ccgroup=_varnish -f /etc/varnish/default.vcl -T localhost:9999 -a localhost:8080 -s default,500m&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This configuration sets Varnish with a 500 MB cache and listens on localhost, port 8080.&lt;/p&gt;
&lt;p&gt;Next, rename the default VCL file to prepare for your own content:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;mv /etc/varnish/default.vcl /etc/varnish/default.vcl.distrib
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Create a new &lt;code&gt;default.vcl&lt;/code&gt; file. Below is an example based on this blog (at the time of writing). You’ll need to adapt it according to your needs, especially if there are cookies or other dynamic content. Note that Varnish will fetch data from a specific backend accessed in http. If privacy is needed, consider creating a VPN between the backend and Varnish, &lt;a href="https://it-notes.dragas.net/2024/08/26/building-a-self-hosted-cdn-for-bsd-cafe-media/"&gt;as briefly mentioned in the previous article&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-vcl"&gt;vcl 4.1;
import std;

# Backend - it-notes.dragas.net
backend it_notes {
    .host = &amp;quot;myOriginalServer&amp;quot;;
    .port = &amp;quot;80&amp;quot;;
}

# ACL - purge - it-notes.dragas.net
acl purge_it_notes {
    &amp;quot;authorizedIPForCachePurge&amp;quot;;
}

sub vcl_recv {
    # it-notes.dragas.net
    if (req.http.Host == &amp;quot;it-notes.dragas.net&amp;quot;) {
        set req.backend_hint = it_notes;
        set req.http.Host = &amp;quot;it-notes.dragas.net&amp;quot;;

        # PURGE - it-notes.dragas.net
        if (req.method == &amp;quot;PURGE&amp;quot;) {
            std.log(&amp;quot;Purge request received for &amp;quot; + req.url);

            if (!std.ip(req.http.X-Forwarded-For, &amp;quot;0.0.0.0&amp;quot;) ~ purge_it_notes) {
                return (synth(405, &amp;quot;Not allowed.&amp;quot;));
            }

            if (req.url == &amp;quot;/&amp;quot; || req.url == &amp;quot;/*&amp;quot;) {
                ban(&amp;quot;req.http.host == &amp;quot; + req.http.host);
                return(synth(200, &amp;quot;Entire cache has been cleared.&amp;quot;));
            }
            return (purge);
        }

    } else {
        # Other domains - 404
        return (synth(404, &amp;quot;Domain not found&amp;quot;));
    }

    if (req.method != &amp;quot;GET&amp;quot; &amp;amp;&amp;amp; req.method != &amp;quot;HEAD&amp;quot;) {
        return (pipe);
    }

    return (hash);
}

sub vcl_backend_response {
    # TTL - it-notes.dragas.net
    if (bereq.http.host == &amp;quot;it-notes.dragas.net&amp;quot;) {
        if (bereq.url ~ &amp;quot;\.(gif|jpg|jpeg|png|webp|ico|css|js)$&amp;quot;) {
            set beresp.ttl = 1w;
            set beresp.grace = 1d;
            set beresp.keep = 7d;
            unset beresp.http.Set-Cookie;
            unset beresp.http.Cache-Control;
            set beresp.http.Cache-Control = &amp;quot;public, max-age=604800&amp;quot;;
        } else {
            set beresp.ttl = 15m;
            set beresp.grace = 48h;
            set beresp.keep = 7d;
        }
    }

    # Remove some headers
    unset beresp.http.Server;
    unset beresp.http.X-Powered-By;
    unset beresp.http.Via;

    return (deliver);
}

sub vcl_deliver {
    # Add X-Cache header
    if (obj.hits &amp;gt; 0) {
        set resp.http.X-Cache = &amp;quot;HIT&amp;quot;;
    } else {
        set resp.http.X-Cache = &amp;quot;MISS&amp;quot;;
    }

    std.log(&amp;quot;Delivering content for &amp;quot; + req.url + &amp;quot; - Cache: &amp;quot; + resp.http.X-Cache);

    # Remove Varnish headers
    unset resp.http.Via;
    unset resp.http.X-Varnish;

    return (deliver);
}

sub vcl_hash {
    hash_data(req.url);
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }
    return (lookup);
}

sub vcl_hit {
    return (deliver);
}

sub vcl_miss {
    return (fetch);
}

sub vcl_purge {
    std.log(&amp;quot;Purge executed for &amp;quot; + req.url);
    return (synth(200, &amp;quot;Purge successful&amp;quot;));
}

sub vcl_synth {
    set resp.http.Content-Type = &amp;quot;text/html; charset=utf-8&amp;quot;;
    set resp.http.Retry-After = &amp;quot;5&amp;quot;;
    synthetic({&amp;quot;&amp;lt;!DOCTYPE html&amp;gt;
        &amp;lt;html&amp;gt;
            &amp;lt;head&amp;gt;
                &amp;lt;title&amp;gt;&amp;quot;} + resp.status + &amp;quot; &amp;quot; + resp.reason + {&amp;quot;&amp;lt;/title&amp;gt;
            &amp;lt;/head&amp;gt;
            &amp;lt;body&amp;gt;
                &amp;lt;h1&amp;gt;Status &amp;quot;} + resp.status + &amp;quot; &amp;quot; + resp.reason + {&amp;quot;&amp;lt;/h1&amp;gt;
                &amp;lt;p&amp;gt;&amp;quot;} + resp.reason + {&amp;quot;&amp;lt;/p&amp;gt;
                &amp;lt;h3&amp;gt;Guru Meditation:&amp;lt;/h3&amp;gt;
                &amp;lt;p&amp;gt;XID: &amp;quot;} + req.xid + {&amp;quot;&amp;lt;/p&amp;gt;
                &amp;lt;hr&amp;gt;
                &amp;lt;p&amp;gt;Varnish cache server&amp;lt;/p&amp;gt;
            &amp;lt;/body&amp;gt;
        &amp;lt;/html&amp;gt;
    &amp;quot;});
    return (deliver);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Start Varnish and check if it starts correctly:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;rcctl start varnishd
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Generating SSL Certificates&lt;/h2&gt;
&lt;p&gt;Before configuring &lt;a href="https://man.openbsd.org/relayd.conf.5"&gt;relayd&lt;/a&gt;, you’ll need to generate SSL certificates. Lego supports many DNS providers and provides clear and comprehensive examples, so I suggest reading its &lt;a href="https://github.com/go-acme/lego?tab=readme-ov-file"&gt;README file&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Certificates generated by Lego are not directly compatible with relayd, so you must generate them in the correct format. Add the &lt;code&gt;-k rsa4096&lt;/code&gt; flag to the Lego command to obtain certificates compatible with relayd.&lt;/p&gt;
&lt;p&gt;Once generated, the certificates will be in a subdirectory of the directory from which the command was launched. For example, if the command is run as root (which is unnecessary, but just for the example), the certificates will be in &lt;code&gt;/root/.lego/certificates/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;relayd expects certificates in a specific location. Copy them to the appropriate directories. In my example:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;obcdn# cp /root/.lego/certificates/it-notes.dragas.net.crt /etc/ssl/
obcdn# cp /root/.lego/certificates/it-notes.dragas.net.key /etc/ssl/private/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Remember to copy the files to the correct directories when renewing. You can also create symbolic links, this will make your life easier but it’s less secure.&lt;/p&gt;
&lt;h2&gt;Configuring relayd&lt;/h2&gt;
&lt;p&gt;It’s time to configure relayd. The file is &lt;code&gt;/etc/relayd.conf&lt;/code&gt;, and here’s an example configuration:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;log state changes
prefork 10

table &amp;lt;itnotes&amp;gt; { 127.0.0.1 }

http protocol &amp;quot;http&amp;quot; {
    match request header append &amp;quot;X-Forwarded-For&amp;quot; value &amp;quot;$REMOTE_ADDR&amp;quot;
    match request header append &amp;quot;X-Forwarded-By&amp;quot; value &amp;quot;$SERVER_ADDR:$SERVER_PORT&amp;quot;

    match request path &amp;quot;/*.atom&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.css&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.gif&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.html&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.ico&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.jpg&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.webp&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.js&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.png&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.rss&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.svg&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.xml&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.ttf&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.woff2&amp;quot; tag &amp;quot;CACHE&amp;quot;

    match response tagged &amp;quot;CACHE&amp;quot; header set &amp;quot;Cache-Control&amp;quot; value &amp;quot;public, max-age=604800&amp;quot;

    pass request header &amp;quot;Host&amp;quot; value &amp;quot;it-notes.dragas.net&amp;quot; forward to &amp;lt;itnotes&amp;gt;
}

http protocol &amp;quot;https&amp;quot; {
    match request header append &amp;quot;X-Forwarded-For&amp;quot; value &amp;quot;$REMOTE_ADDR&amp;quot;
    match request header append &amp;quot;X-Forwarded-By&amp;quot; value &amp;quot;$SERVER_ADDR:$SERVER_PORT&amp;quot;

    match response header set &amp;quot;Referrer-Policy&amp;quot; value &amp;quot;no-referrer&amp;quot;
    match response header set &amp;quot;X-Content-Type-Options&amp;quot; value &amp;quot;nosniff&amp;quot;
    match response header set &amp;quot;X-Download-Options&amp;quot; value &amp;quot;noopen&amp;quot;
    match response header set &amp;quot;X-Frame-Options&amp;quot; value &amp;quot;SAMEORIGIN&amp;quot;
    match response header set &amp;quot;X-Permitted-Cross-Domain-Policies&amp;quot; value &amp;quot;none&amp;quot;
    match response header set &amp;quot;X-XSS-Protection&amp;quot; value &amp;quot;1; mode=block&amp;quot;
    match response header set &amp;quot;Strict-Transport-Security&amp;quot; value &amp;quot;max-age=15552000; includeSubDomains; preload&amp;quot;

    match request path &amp;quot;/*.atom&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.css&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.gif&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.html&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.ico&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.jpg&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.webp&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.js&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.png&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.rss&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.svg&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.xml&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.ttf&amp;quot; tag &amp;quot;CACHE&amp;quot;
    match request path &amp;quot;/*.woff2&amp;quot; tag &amp;quot;CACHE&amp;quot;

    match response tagged &amp;quot;CACHE&amp;quot; header set &amp;quot;Cache-Control&amp;quot; value &amp;quot;public, max-age=604800&amp;quot;
    tcp { nodelay, sack, socket buffer 65536, backlog 100 }

    tls { keypair &amp;quot;it-notes.dragas.net&amp;quot; }

    pass request header &amp;quot;Host&amp;quot; value &amp;quot;it-notes.dragas.net&amp;quot; forward to &amp;lt;itnotes&amp;gt;
}

relay &amp;quot;http&amp;quot; {
    listen on vio0 port 80
    protocol &amp;quot;http&amp;quot;

    forward to &amp;lt;itnotes&amp;gt; port 8080
}

relay &amp;quot;https&amp;quot; {
    listen on vio0 port 443 tls
    protocol &amp;quot;https&amp;quot;

    forward to &amp;lt;itnotes&amp;gt; port 8080
}

relay &amp;quot;https6&amp;quot; {
    listen on my:ip:v6:address::1 port 443 tls
    protocol &amp;quot;https&amp;quot;

    forward to &amp;lt;itnotes&amp;gt; port 8080
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The content of the file is quite self-explanatory. Note that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;vio0&lt;/code&gt; is the interface name-it should be modified based on the interface where relayd needs to listen.&lt;/li&gt;
&lt;li&gt;I’ve configured relayd to listen on port 80 as well.&lt;/li&gt;
&lt;li&gt;IPv4 and IPv6 listeners are separated. If IPv6 is not configured, simply comment out that part. Please, if you can, use IPv6. In a fairer world, everyone would have the right to at least one class of public addresses without having to pay exorbitant fees.&lt;/li&gt;
&lt;li&gt;The "keypair" must correspond to the certificate and key names in &lt;code&gt;/etc/ssl&lt;/code&gt; and &lt;code&gt;/etc/ssl/private&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Test the configuration, enable, and start relayd:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;obcdn# relayd -n
configuration OK
obcdn# rcctl enable relayd
obcdn# rcctl start relayd
relayd(ok)
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Final Checks&lt;/h2&gt;
&lt;p&gt;The stack is ready. A &lt;code&gt;ps&lt;/code&gt; command will show the process status:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;obcdn# ps aux
USER       PID %CPU %MEM   VSZ   RSS TT  STAT   STARTED       TIME COMMAND
root         1  0.0  0.0   920   276 ??  I       7:10PM    0:00.01 /sbin/init
[...]
_varnish 44999  0.0  0.2  2256  2424 ??  S       8:27PM    0:00.05 varnishd: Varnish-Mgt -i obcdn.my.domain (varnishd)
_varnish 54481  0.0  8.8 34500 88876 ??  S       8:27PM    0:00.60 varnishd: Varnish-Child -i obcdn.my.domain (varnishd)
root     57753  0.0  0.5  4080  4820 ??  IU      8:32PM    0:00.03 /usr/sbin/relayd
_relayd  95940  0.0  0.4  2152  3892 ??  Spc     8:32PM    0:00.01 relayd: pfe (relayd)
_relayd  81032  0.0  0.4  2156  3716 ??  Spc     8:32PM    0:00.01 relayd: hce (relayd)
_relayd  80312  0.0  0.5  2748  5280 ??  Ipc     8:32PM    0:00.03 relayd: relay (relayd)
_relayd  88919  0.0  0.5  2748  5268 ??  Ipc     8:32PM    0:00.04 relayd: relay (relayd)
_relayd  90186  0.0  0.5  2752  5272 ??  Ipc     8:32PM    0:00.03 relayd: relay (relayd)
_relayd  32851  0.0  0.5  2736  5284 ??  Ipc     8:32PM    0:00.03 relayd: relay (relayd)
_relayd  20270  0.0  0.5  2744  5252 ??  Ipc     8:32PM    0:00.03 relayd: relay (relayd)
_relayd  98826  0.0  0.5  2752  5264 ??  Ipc     8:32PM    0:00.04 relayd: relay (relayd)
_relayd   6454  0.0  0.5  2744  5264 ??  Ipc     8:32PM    0:00.03 relayd: relay (relayd)
_relayd  90102  0.0  0.5  2740  5276 ??  Ipc     8:32PM    0:00.03 relayd: relay (relayd)
_relayd  60314  0.0  0.5  2748  5336 ??  Ipc     8:32PM    0:00.03 relayd: relay (relayd)
_relayd  27246  0.0  0.5  2744  5284 ??  Ipc     8:32PM    0:00.03 relayd: relay (relayd)
_relayd  87082  0.0  0.4  2100  4508 ??  Ipc     8:32PM    0:00.02 relayd: ca (relayd)
_relayd  53308  0.0  0.4  2096  4496 ??  Ipc     8:32PM    0:00.02 relayd: ca (relayd)
_relayd  11697  0.0  0.4  2092  4492 ??  Ipc     8:32PM    0:00.02 relayd: ca (relayd)
_relayd  83636  0.0  0.4  2092  4500 ??  Ipc     8:32PM    0:00.02 relayd: ca (relayd)
_relayd  74563  0.0  0.4  2096  4484 ??  Ipc     8:32PM    0:00.02 relayd: ca (relayd)
_relayd  35910  0.0  0.4  2100  4508 ??  Ipc     8:32PM    0:00.02 relayd: ca (relayd)
_relayd  24911  0.0  0.4  2096  4504 ??  Ipc     8:32PM    0:00.02 relayd: ca (relayd)
_relayd  78649  0.0  0.4  2096  4496 ??  Ipc     8:32PM    0:00.02 relayd: ca (relayd)
_relayd  89586  0.0  0.4  2096  4500 ??  Ipc     8:32PM    0:00.02 relayd: ca (relayd)
_relayd  11989  0.0  0.4  2100  4500 ??  Ipc     8:32PM    0:00.02 relayd: ca (relayd)
[...]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In this case, both relayd and Varnish are running correctly.&lt;/p&gt;
&lt;h2&gt;Automating Certificate Renewal&lt;/h2&gt;
&lt;p&gt;As a final step, remember to create a script to renew the certificates, copy them to &lt;code&gt;/etc/ssl&lt;/code&gt; and &lt;code&gt;/etc/ssl/private&lt;/code&gt;, and restart relayd.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Congratulations, you now have your own free CDN, with your data, fully under your control. Portable, extendable, controllable, and outside of the big providers' grip.&lt;/p&gt;
&lt;p&gt;If your goal is geo-replication, you should use one of the available methods. Some DNS providers allow selection based on the caller's location (like Bunny.net), or, as I prefer, install your own DNS and use tools to manage operations and resolutions, &lt;a href="https://it-notes.dragas.net/2024/08/26/building-a-self-hosted-cdn-for-bsd-cafe-media/"&gt;as briefly described in the previous article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With multiple separate reverse proxies, separate DNS servers (on different providers and possibly different countries or continents) capable of checking if the reverse proxies are operational, you can achieve an extremely low likelihood of encountering a Single Point of Failure, as all components, once the cache is filled, will be nearly autonomous even in the event of a (temporary) backend outage - i.e., the original node.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Stefano Marinelli</dc:creator><pubDate>Thu, 29 Aug 2024 01:41:00 +0200</pubDate><guid isPermaLink="true">https://it-notes.dragas.net/2024/08/29/make-your-own-cdn-openbsd/</guid><category>openbsd</category><category>server</category><category>hosting</category><category>tutorial</category><category>ownyourdata</category><category>vpn</category><category>ha</category><category>wireguard</category><category>web</category><category>cdn</category><category>bsdcafe</category><category>varnish</category><category>series</category></item><item><title>Building a Self-Hosted CDN for BSD Cafe Media</title><link>https://it-notes.dragas.net/2024/08/26/building-a-self-hosted-cdn-for-bsd-cafe-media/</link><description>&lt;p&gt;&lt;img src="https://it-notes.dragas.net/featured/network_lights.webp" alt="Building a Self-Hosted CDN for BSD Cafe Media"&gt;&lt;/p&gt;&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;For just over a year, &lt;a href="https://wiki.bsd.cafe"&gt;BSD Cafe&lt;/a&gt;'s media was hosted on a FreeBSD physical server jail with an outgoing bandwidth of 250 Mbit/sec. To mitigate bandwidth congestion, I initially integrated Cloudflare with a tunnel to serve media (and only media) through Cloudflare. The goal was to georeplicate the media and reduce the load on my server. To do this, the media server had to be on a separate domain managed by Cloudflare since the DNS for the primary bsd.cafe domain was managed by Bunny.net.&lt;/p&gt;
&lt;p&gt;The first step (mainly because I discovered that Bunny's DNS does not support IPv6) was to bring the DNS back in-house using two FreeBSD jails (running on different VPS providers), both powered by PowerDNS. PowerDNS supports LUA records, which would come in handy later.&lt;/p&gt;
&lt;p&gt;In line with the principles of &lt;a href="https://it-notes.dragas.net/tags/ownyourdata"&gt;self-hosting and data ownership&lt;/a&gt;, I decided to remove Cloudflare. I created a dedicated subdomain (media.bsd.cafe) and configured the reverse proxy in front of the jail running Minio to respond to that domain. I also reconfigured Mastodon for the new address, and after some fine-tuning, everything worked seamlessly. However, this led to some bandwidth congestion when media was posted, resulting in slower download speeds for users, especially during peak times. This is because, once content is published and federated servers are notified, they all attempt to download the newly published content - media included - almost simultaneously.&lt;/p&gt;
&lt;p&gt;Not wanting to abandon my media server (a dedicated jail with spinning disks, offering 4 TB of storage), I opted for a different approach that I’ll describe here, as it might be useful for similar setups.&lt;/p&gt;
&lt;p&gt;While this setup was implemented on FreeBSD, the configuration and tools - Nginx, Varnish, WireGuard and PowerDNS - are compatible with many operating systems, including Linux, with only minor adjustments required.&lt;/p&gt;
&lt;h2&gt;The Approach: Building a Self-Hosted CDN&lt;/h2&gt;
&lt;p&gt;The idea is to create reverse proxies with local caching. These proxies would cache the content on the first request and serve it directly afterward. The proxies would be distributed across different regions, and the DNS would route requests to the nearest proxy based on the caller’s location. All this is achieved without relying on external CDNs, using self-managed tools instead.&lt;/p&gt;
&lt;p&gt;To establish a direct connection between Minio and the reverse proxies, I configured WireGuard inside the jail. The reverse proxies connect via WireGuard, allowing them to access Minio securely as if they were on the same LAN.&lt;/p&gt;
&lt;p&gt;No further changes were needed on the media jail itself.&lt;/p&gt;
&lt;h2&gt;Setting Up the Reverse Proxies&lt;/h2&gt;
&lt;p&gt;I began configuring the reverse proxies (also running FreeBSD jails, OpenBSD (&lt;a href="https://it-notes.dragas.net/2024/08/29/make-your-own-cdn-openbsd/"&gt;setup described in another post&lt;/a&gt;) and NetBSD (also &lt;a href="https://it-notes.dragas.net/2024/09/03/make-your-own-cdn-netbsd/"&gt;described in another post&lt;/a&gt;), hosted on different providers). The choice of Varnish is based on several factors, with the main ones being the ability to keep the cache in RAM (which means it can run on read-only systems) and the ability to flush the cache remotely. For example, with each change to my blog, I can choose whether to perform an immediate flush (such as for a new article or an error) or wait for the cache's "natural" expiration (such as for a typo or minor, non-critical changes). &lt;/p&gt;
&lt;p&gt;First, I connected them via WireGuard to the Minio jail (I won’t detail the steps here; I’ve &lt;a href="https://it-notes.dragas.net/tags/wireguard"&gt;covered similar setups in other posts&lt;/a&gt;). Then, I installed Nginx and Varnish. A more granular setup would have Varnish on a separate jail, but this way, I can move the reverse proxy jails to different hosts with minimal hassle. Currently, these reverse proxies also serve this blog.&lt;/p&gt;
&lt;p&gt;Next, I installed and configured Varnish inside the jail:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;pkg install varnish7
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I created the directory &lt;code&gt;/usr/local/etc/varnish&lt;/code&gt; and wrote a custom VCL file to manage this setup, named &lt;code&gt;default.vcl&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;vcl 4.1;
import std;

# Backend - it-notes.dragas.net
backend it_notes {
    .host = &amp;quot;itnotesip&amp;quot;;
    .port = &amp;quot;itnotesport&amp;quot;;
}

# Backend - media.bsd.cafe
backend media_bsd {
    .host = &amp;quot;minioWGip&amp;quot;;
    .port = &amp;quot;minioport&amp;quot;;
}

# ACL - IPs allowed to purge - it-notes.dragas.net
acl purge_it_notes {
    &amp;quot;a.b.c.d&amp;quot;;
}

# ACL - IPs allowed to purge - media.bsd.cafe
acl purge_media_bsd {
    &amp;quot;e.f.g.h&amp;quot;;
}

sub vcl_recv {

    # it-notes.dragas.net
    if (req.http.Host == &amp;quot;it-notes.dragas.net&amp;quot;) {
        set req.backend_hint = it_notes;
        set req.http.Host = &amp;quot;it-notes.dragas.net&amp;quot;;

        # PURGE - it-notes.dragas.net
        if (req.method == &amp;quot;PURGE&amp;quot;) {

            std.log(&amp;quot;Purge request received for &amp;quot; + req.url);

            if (!std.ip(req.http.X-Real-IP, &amp;quot;0.0.0.0&amp;quot;) ~ purge_it_notes) {
                return (synth(405, &amp;quot;Not allowed.&amp;quot;));
            }

        if (req.url == &amp;quot;/&amp;quot; || req.url == &amp;quot;/*&amp;quot;) {
                ban(&amp;quot;req.http.host == &amp;quot; + req.http.host);
                return(synth(200, &amp;quot;Entire cache has been cleared.&amp;quot;));
        }
            return (purge);
        }

    # media.bsd.cafe
    } elsif (req.http.Host == &amp;quot;media.bsd.cafe&amp;quot;) {
        set req.backend_hint = media_bsd;
        set req.http.Host = &amp;quot;media.bsd.cafe&amp;quot;;

        # PURGE - media.bsd.cafe
        if (req.method == &amp;quot;PURGE&amp;quot;) {
            if (!std.ip(req.http.X-Real-IP, &amp;quot;0.0.0.0&amp;quot;) ~ purge_media_bsd) {
                return (synth(405, &amp;quot;Not allowed.&amp;quot;));
            }
            if (req.url == &amp;quot;/&amp;quot; || req.url == &amp;quot;/*&amp;quot;) {
                ban(&amp;quot;req.http.host == &amp;quot; + req.http.host);
                return(synth(200, &amp;quot;Entire cache has been cleared.&amp;quot;));
            }
            return (purge);
        }

    } else {
        # Other domains - 404
        return (synth(404, &amp;quot;Domain not found&amp;quot;));
    }

    if (req.method != &amp;quot;GET&amp;quot; &amp;amp;&amp;amp; req.method != &amp;quot;HEAD&amp;quot;) {
        return (pipe);
    }

    return (hash);
}

sub vcl_backend_response {
    # TTL - it-notes.dragas.net
    if (bereq.http.host == &amp;quot;it-notes.dragas.net&amp;quot;) {
        if (bereq.url ~ &amp;quot;\.(gif|jpg|jpeg|png|ico|css|js)$&amp;quot;) {
            set beresp.ttl = 1w;
            set beresp.grace = 1d;
            set beresp.keep = 7d;
            unset beresp.http.Set-Cookie;
            unset beresp.http.Cache-Control;
            set beresp.http.Cache-Control = &amp;quot;public, max-age=604800&amp;quot;;
        } else {
            set beresp.ttl = 15m;
            set beresp.grace = 48h;
            set beresp.keep = 7d;
        }

    # TTL - media.bsd.cafe
    } elsif (bereq.http.host == &amp;quot;media.bsd.cafe&amp;quot;) {
        if (bereq.url ~ &amp;quot;\.(mp4|mp3|wav|flac|ogg)$&amp;quot;) {
            set beresp.ttl = 1d;
            set beresp.grace = 6h;
            set beresp.keep = 3d;
            unset beresp.http.Set-Cookie;
            unset beresp.http.Cache-Control;
            set beresp.http.Cache-Control = &amp;quot;public, max-age=86400&amp;quot;;
        } else {
            set beresp.ttl = 30m;
            set beresp.grace = 12h;
            set beresp.keep = 3d;
        }
    }

    # Remove some headers
    unset beresp.http.Server;
    unset beresp.http.X-Powered-By;
    unset beresp.http.Via;

    return (deliver);
}

sub vcl_deliver {
    # ADD header X-Cache
    if (obj.hits &amp;gt; 0) {
        set resp.http.X-Cache = &amp;quot;HIT&amp;quot;;
    } else {
        set resp.http.X-Cache = &amp;quot;MISS&amp;quot;;
    }

  std.log(&amp;quot;Delivering content for &amp;quot; + req.url + &amp;quot; - Cache: &amp;quot; + resp.http.X-Cache);


    # Remove Varnish headers
    unset resp.http.Via;
    unset resp.http.X-Varnish;

    return (deliver);
}

sub vcl_hash {
    hash_data(req.url);
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }
    return (lookup);
}

sub vcl_hit {
    return (deliver);
}

sub vcl_miss {
    return (fetch);
}

sub vcl_purge {
    std.log(&amp;quot;Purge executed for &amp;quot; + req.url);
    return (synth(200, &amp;quot;Purge successful&amp;quot;));
}

sub vcl_synth {
    set resp.http.Content-Type = &amp;quot;text/html; charset=utf-8&amp;quot;;
    set resp.http.Retry-After = &amp;quot;5&amp;quot;;
    synthetic( {&amp;quot;&amp;lt;!DOCTYPE html&amp;gt;
        &amp;lt;html&amp;gt;
            &amp;lt;head&amp;gt;
                &amp;lt;title&amp;gt;&amp;quot;} + resp.status + &amp;quot; &amp;quot; + resp.reason + {&amp;quot;&amp;lt;/title&amp;gt;
            &amp;lt;/head&amp;gt;
            &amp;lt;body&amp;gt;
                &amp;lt;h1&amp;gt;Status &amp;quot;} + resp.status + &amp;quot; &amp;quot; + resp.reason + {&amp;quot;&amp;lt;/h1&amp;gt;
                &amp;lt;p&amp;gt;&amp;quot;} + resp.reason + {&amp;quot;&amp;lt;/p&amp;gt;
                &amp;lt;h3&amp;gt;Guru Meditation:&amp;lt;/h3&amp;gt;
                &amp;lt;p&amp;gt;XID: &amp;quot;} + req.xid + {&amp;quot;&amp;lt;/p&amp;gt;
                &amp;lt;hr&amp;gt;
                &amp;lt;p&amp;gt;Varnish cache server&amp;lt;/p&amp;gt;
            &amp;lt;/body&amp;gt;
        &amp;lt;/html&amp;gt;
    &amp;quot;} );
    return (deliver);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This setup allows Varnish to handle both domains with distinct configurations but within the same cache.&lt;/p&gt;
&lt;p&gt;To enable Varnish, I updated the &lt;code&gt;/etc/rc.conf&lt;/code&gt; file with the following lines, setting a maximum cache size of 2GB:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;varnishd_enable=&amp;quot;YES&amp;quot;
varnishd_listen=&amp;quot;127.0.0.1:8080&amp;quot;
varnishd_config=&amp;quot;/usr/local/etc/varnish/default.vcl&amp;quot;
varnishd_storage=&amp;quot;default,2000M&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can now start Varnish:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;service varnishd start
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The next step is to create two virtual hosts on Nginx (one for it-notes.dragas.net and one for media.bsd.cafe) that will listen on both IPv4 and IPv6 for HTTP and HTTPS. HTTP connections will be redirected to HTTPS, and incoming HTTPS traffic will be passed to Varnish, which will either return cached data or fetch it from the original server (Minio via WireGuard, for media.bsd.cafe). Let's see the media.bsd.cafe part:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;server {
   server_name  media.bsd.cafe;

   [...]

   location / {
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    proxy_connect_timeout 300;
    proxy_http_version 1.1;
    proxy_set_header Connection &amp;quot;&amp;quot;;
    chunked_transfer_encoding off;

    expires 12h;
    add_header Cache-Control public;

    add_header X-Cache-Status $upstream_cache_status;
    add_header X-Content-Type-Options nosniff;

    add_header Strict-Transport-Security &amp;quot;max-age=31536000; includeSubDomains; preload&amp;quot; always;
    add_header Referrer-Policy &amp;quot;no-referrer-when-downgrade&amp;quot;;
    add_header Permissions-Policy &amp;quot;geolocation=(), microphone=(), camera=()&amp;quot;;

    proxy_pass http://127.0.0.1:8080;

    [...]

}

[...]

}

server {
    if ($host = media.bsd.cafe) {
        return 301 https://$host$request_uri;
    }
   listen       80;
   listen  [::]:80;
   server_name  media.bsd.cafe;
    return 404;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This configuration isn’t complete, but it provides a good idea of how to set up Nginx - each setup will vary. TTL and caching sizes will also differ based on the characteristics of each reverse proxy. For example, one of the proxies has an 8GB cache since I have ample resources there.&lt;/p&gt;
&lt;p&gt;Generating certificates is another important aspect. In this case, as the reverse proxies are distributed, they all need to respond to the same addresses. One approach is to generate the certificate on one proxy and distribute it to the others. In my case, I opted to use &lt;a href="https://github.com/go-acme/lego"&gt;lego&lt;/a&gt;, which, through PowerDNS’s API, adds a DNS record for validation. This way, each reverse proxy can independently generate and renew its certificates when needed.&lt;/p&gt;
&lt;h2&gt;Configuring DNS for Optimal Routing&lt;/h2&gt;
&lt;p&gt;Once everything is set up, it’s important to ensure that DNS responds correctly. In my case, I implemented a strategy like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Track reverse proxies that respond on port 443 (further refinements are possible and will be done later).&lt;/li&gt;
&lt;li&gt;Return the closest reverse proxy based on the client’s IP address.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unfortunately, PowerDNS on FreeBSD does not include GeoIP support by default, but I have my poudriere ready to compile and install the necessary packages. Alternatively, you could compile it within the jail using the port system.&lt;/p&gt;
&lt;p&gt;After that, I installed the &lt;code&gt;geoipupdate&lt;/code&gt; package (which requires a free license from MaxMind), updated the IP list, and configured PowerDNS to use the GeoIP database. I added the GeoIP backend alongside the existing SQLite3 backend and specified the database to use:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-code"&gt;launch=gsqlite3,geoip
geoip-database-files=mmdb:/usr/local/share/GeoIP/GeoLite2-City.mmdb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, I created a LUA record to return the correct address:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;testclosest.bsd.cafe   60      IN      LUA     A &amp;quot;ifportup(443, {'proxy1ip', 'proxy2ip','proxy3ip'}&amp;quot; &amp;quot;, {selector='pickclosest'})&amp;quot;
testclosest.bsd.cafe   60      IN      LUA     AAAA &amp;quot;ifportup(443, {'proxy1ip6', 'proxy2ip6','proxy3ip6'}&amp;quot; &amp;quot;, {selector='pickclosest'})&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And voilà! We now have a small, self-hosted CDN, keeping full control and ownership of our data. Adding a new reverse proxy is straightforward - simply clone an existing proxy, update the WireGuard configuration (adding a peer on the Minio jail and changing the keys on the new proxy), and add it to the DNS.&lt;/p&gt;
&lt;p&gt;Happy caching!&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Stefano Marinelli</dc:creator><pubDate>Mon, 26 Aug 2024 08:41:00 +0200</pubDate><guid isPermaLink="true">https://it-notes.dragas.net/2024/08/26/building-a-self-hosted-cdn-for-bsd-cafe-media/</guid><category>freebsd</category><category>server</category><category>hosting</category><category>tutorial</category><category>jail</category><category>ownyourdata</category><category>vpn</category><category>ha</category><category>wireguard</category><category>web</category><category>cdn</category><category>bsdcafe</category><category>varnish</category><category>series</category></item><item><title>Evolving the BSD Cafe Network Setup: From Bridging to Routing with FreeBSD</title><link>https://it-notes.dragas.net/2024/08/01/evolving-bsd-cafe-from-bridging-to-routing/</link><description>&lt;p&gt;&lt;img src="https://it-notes.dragas.net/featured/server_rack.webp" alt="Evolving the BSD Cafe Network Setup: From Bridging to Routing with FreeBSD"&gt;&lt;/p&gt;&lt;p&gt;In the ever-changing landscape of system administration, network configurations often need to grow and evolve to meet new challenges and requirements. This post details my journey from a simple VPS setup to a complex, multi-node network using FreeBSD, jails, VPNs, and advanced routing techniques. Along the way, I'll explore the reasons behind each change and delve into why certain solutions, while functional, may not always be ideal in the long run.&lt;/p&gt;
&lt;h2&gt;Initial Setup: The Single VPS&lt;/h2&gt;
&lt;p&gt;My story begins with a single VPS (let's call it VPSSmall) hosted on Hetzner, running FreeBSD. This initial configuration was straightforward and served its purpose well for a time:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Created an internal bridge (&lt;code&gt;bridge0&lt;/code&gt;) with IP 192.168.123.1&lt;/li&gt;
&lt;li&gt;Used BastilleBSD to create VNET jails with IPs in the 192.168.123.X range&lt;/li&gt;
&lt;li&gt;Set up port forwarding to the jails using pf's rdr rules&lt;/li&gt;
&lt;li&gt;Utilized a /64 IPv6 block, subdivided into /72 subnets for the bridge and jails&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This setup allowed for easy management of multiple services within isolated jails, all sharing the same network namespace.&lt;/p&gt;
&lt;h2&gt;Growing Pains: Adding a Second VPS&lt;/h2&gt;
&lt;p&gt;As is often the case in system administration, my needs grew over time. The number of jails increased, and their resource requirements expanded. To address this, I added a second VPS (VPSBig) hosted on a Proxmox server. This VPS isn't directly exposed, and relies on NAT to connect to the outside world. This introduced a new challenge: how to maintain flexibility in moving jails between VPSs without changing their network configurations?&lt;/p&gt;
&lt;p&gt;To solve this, I implemented the following setup:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Installed &lt;a href="https://www.zerotier.com/"&gt;ZeroTier&lt;/a&gt; on both VPSs in bridge mode&lt;/li&gt;
&lt;li&gt;Created &lt;code&gt;bridge0&lt;/code&gt; on VPSBig (without an IP)&lt;/li&gt;
&lt;li&gt;Added ZeroTier interfaces to both bridges&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This configuration allowed for seamless movement of jails between nodes by simply transferring the ZFS dataset. The jails could retain their IP addresses regardless of which physical VPS they were running on.&lt;/p&gt;
&lt;p&gt;While this setup was functional, it had some drawbacks that I'll discuss in the next section.&lt;/p&gt;
&lt;h2&gt;The Limitations of Bridging&lt;/h2&gt;
&lt;p&gt;The bridged setup using ZeroTier, while effective, wasn't without its issues. Here's why I found bridging, in this case, wasn't an ideal long-term solution:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance Overhead&lt;/strong&gt;: Bridging all traffic between VPSs can introduce additional latency and processing overhead, especially when dealing with high-volume traffic.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scalability Concerns&lt;/strong&gt;: As the number of VPSs and jails grows, managing a large bridged network becomes increasingly complex. Each new node added to the network increases the potential for broadcast storms and can lead to unnecessary traffic across the entire network.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Security Implications&lt;/strong&gt;: In a bridged network, all nodes essentially exist on the same network segment. This can potentially allow for lateral movement between jails or VPSs if not carefully managed, increasing the attack surface.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Dependency on ZeroTier&lt;/strong&gt;: While ZeroTier is a powerful tool, relying on a third-party service for critical infrastructure introduces an external point of failure and potential security considerations.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Limited Control&lt;/strong&gt;: Bridging provides less granular control over traffic flow compared to routing. This can make it harder to implement complex network policies or optimize traffic paths.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Broadcast Domain Size&lt;/strong&gt;: Large bridged networks can result in expansive broadcast domains, which can lead to increased network congestion and reduced overall performance.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These limitations prompted me to seek a more robust, scalable, and controllable solution, leading to the next evolution of my network setup.&lt;/p&gt;
&lt;h2&gt;Refining the Setup: Wireguard and VXLAN&lt;/h2&gt;
&lt;p&gt;To address the limitations of the bridged setup, I implemented a new configuration involving &lt;a href="https://www.wireguard.com/"&gt;Wireguard&lt;/a&gt; and VXLAN:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Created a Wireguard VPN between VPSSmall and VPSBig&lt;/li&gt;
&lt;li&gt;Implemented a VXLAN over the Wireguard tunnel&lt;/li&gt;
&lt;li&gt;Replaced ZeroTier with the VXLAN for inter-VPS communication&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This setup, described in detail &lt;a href="https://it-notes.dragas.net/2024/07/15/bridging-networks-across-vps-wireguard-vxlan-freebsd/"&gt;here&lt;/a&gt;, offered several advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Improved Security&lt;/strong&gt;: Wireguard provided a secure, encrypted tunnel between the VPSs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better Performance&lt;/strong&gt;: Direct Wireguard VPN connection often results in lower latency compared to ZeroTier.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Greater Control&lt;/strong&gt;: By managing all the components of the VPN myself, I have more control over the network configuration and problems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reduced Dependency&lt;/strong&gt;: Eliminating ZeroTier removed a third-party dependency from my critical infrastructure.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While this setup was a significant improvement, it still relied on bridging via VXLAN, which didn't fully address all the scalability and control issues. This realization led to the final evolution of my network.&lt;/p&gt;
&lt;h2&gt;The Final Evolution: Routing Instead of Bridging&lt;/h2&gt;
&lt;p&gt;The last step in my network's evolution was to move from a bridged to a routed setup. This change offered even more flexibility and scalability. The new configuration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VPSSmall: Uses 192.168.122.x/24 and a /72 IPv6 subnet&lt;/li&gt;
&lt;li&gt;VPSBig: Uses 192.168.123.x/24 and its original /72 IPv6 subnet&lt;/li&gt;
&lt;li&gt;Future nodes can use new private IPv4 ranges and /72 IPv6 subnets as needed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This routed setup provides several key benefits:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Improved Scalability&lt;/strong&gt;: Each VPS or future node can have its own subnet, making it easier to add new nodes without reconfiguring the entire network.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better Traffic Control&lt;/strong&gt;: Routing allows for more granular control over traffic flow, enabling complex network policies and optimizations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enhanced Security&lt;/strong&gt;: With distinct subnets, it's easier to implement security policies and control inter-subnet communication.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reduced Broadcast Domain&lt;/strong&gt;: Each subnet forms its own broadcast domain, reducing unnecessary network traffic and improving overall performance.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To ensure jails on VPSBig use the Wireguard tunnel for outgoing traffic while VPSBig itself uses its default gateway, I leveraged FreeBSD's &lt;a href="https://man.freebsd.org/cgi/man.cgi?setfib"&gt;FIB&lt;/a&gt; feature. This allows for separate routing tables, providing even more flexibility in managing network traffic.&lt;/p&gt;
&lt;h3&gt;Implementing Multiple FIBs&lt;/h3&gt;
&lt;p&gt;Edit &lt;code&gt;/etc/sysctl.conf&lt;/code&gt; to enable multiple FIBs:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;net.fibs=2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This allows me to use two separate routing tables.&lt;/p&gt;
&lt;h4&gt;VPSBig Wireguard Configuration&lt;/h4&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-ini"&gt;[Interface]
PrivateKey = *VPSBigPrivateKey*
Address = 10.77.0.2/32,oneOfMyIpv6/128

Table = off
PostUp = route -q -n add -inet 0.0.0.0/0 -interface wg0 -fib 1
PostUp = route -q -n add -inet6 ::/1 -interface wg0 -fib 1
PostUp = route -q -n add -inet6 8000::/1 -interface wg0 -fib 1

[Peer]
PublicKey = *VPSSmallPublicKey*
AllowedIPs = 0.0.0.0/0,::0/0
Endpoint = *endpointip:port*
PresharedKey = *presharedkey*
PersistentKeepalive = 30
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let's break down this configuration:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Table = off&lt;/code&gt;: This disables Wireguard's automatic routing table management. We're doing this because we want to manually configure the routing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;PostUp&lt;/code&gt; commands are crucial for our manual routing setup:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;route -q -n add -inet 0.0.0.0/0 -interface wg0 -fib 1&lt;/code&gt;: This adds a default route for IPv4 traffic through the Wireguard interface (&lt;code&gt;wg0&lt;/code&gt;) in the alternate routing table (FIB 1).&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The next two commands do the same for IPv6 traffic, covering the entire IPv6 address space (&lt;code&gt;::/1&lt;/code&gt; and &lt;code&gt;8000::/1&lt;/code&gt; together cover all IPv6 addresses).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;AllowedIPs = 0.0.0.0/0,::0/0&lt;/code&gt;: This tells Wireguard to route all traffic through this peer. However, because we've set &lt;code&gt;Table = off&lt;/code&gt;, Wireguard won't actually create these routes - we're doing it manually with our &lt;code&gt;PostUp&lt;/code&gt; commands.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;PersistentKeepalive = 30&lt;/code&gt;: This sends a keepalive packet every 30 seconds, which is necessary because VPSBig is behind NAT and needs to keep the NAT session alive.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;VPSSmall Wireguard Configuration&lt;/h4&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-ini"&gt;[Interface]
PrivateKey = *VPSSmallPrivateKey*
ListenPort = *port*
Address = 10.77.0.1/24,oneOfMyIpv6/128

[Peer]
PublicKey = *VPSSmallPublicKey*
PresharedKey = *presharedkey*
AllowedIPs = 10.77.0.2/32, 192.168.123.0/24, *theRemote/72ipv6class*/72
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In this configuration:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Address = 10.77.0.1/24,oneOfMyIpv6/128&lt;/code&gt;: This sets up the Wireguard interface with an IPv4 and IPv6 address.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;AllowedIPs = 10.77.0.2/32, 192.168.123.0/24, *theRemote/72ipv6class*/72&lt;/code&gt;: This tells Wireguard to route traffic for VPSBig's Wireguard IP (10.77.0.2), VPSBig's local subnet (192.168.123.0/24), and VPSBig's IPv6 subnet through this peer.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;The Role of FIB in Routing&lt;/h4&gt;
&lt;p&gt;The use of FIB (Forwarding Information Base) 1 in the VPSBig configuration is key to our setup. By adding routes to FIB 1, we're creating a separate routing table that can be used by our jails. This allows us to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Keep the main system (VPSBig itself) routing through its default gateway.&lt;/li&gt;
&lt;li&gt;Route all traffic from the jails through the Wireguard tunnel to VPSSmall.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We achieve this by configuring the jails to use FIB 1:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;exec.prestart += &amp;quot;ifconfig epairXa fib 1&amp;quot;;
exec.prestart += &amp;quot;ifconfig epairXb mtu 1380&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This ensures that each jail uses the alternate routing table (FIB 1) instead of the default routing table, effectively sending all its traffic through the Wireguard tunnel.&lt;/p&gt;
&lt;p&gt;If the WireGuard MTU is 1420, setting the jail's MTU to 1380 should be safe enough.&lt;/p&gt;
&lt;h4&gt;NAT Configuration on VPSSmall&lt;/h4&gt;
&lt;p&gt;To complete the setup, we need to configure NAT on VPSSmall to allow the jails on VPSBig to access the internet:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;nat on vtnet0 from 192.168.123.0/24 to ! &amp;lt;private&amp;gt; -&amp;gt; vtnet0:0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This NAT rule translates the source IP of packets coming from VPSBig's subnet (192.168.123.0/24) to VPSSmall's public IP when they're destined for non-private IP addresses.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;My journey from a simple single-VPS setup to this complex, multi-node network illustrates the power and flexibility of FreeBSD. By transitioning from a bridged to a routed setup, I've created a solution that offers improved scalability, security, and control.&lt;/p&gt;
&lt;p&gt;Key takeaways from this evolution include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Adaptability is Crucial&lt;/strong&gt;: As your needs grow, be prepared to evolve your network architecture.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Understand the Tradeoffs&lt;/strong&gt;: Each networking approach (bridging, VPNs, routing) has its pros and cons. Choose the one that best fits your current and future needs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leverage Advanced Features&lt;/strong&gt;: FreeBSD's features like jails, FIBs, and pf allow for powerful and flexible network configurations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security is Paramount&lt;/strong&gt;: Always consider the security implications of your network design, especially when dealing with multiple nodes and public-facing services.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentation is Key&lt;/strong&gt;: Keep detailed notes of your network evolution. It helps in troubleshooting and future planning.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Whether you're managing a small personal server or a large-scale infrastructure, the techniques described here can help you build a robust and adaptable network. Remember, network design is an iterative process. Don't be afraid to evolve your setup as your needs change.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Stefano Marinelli</dc:creator><pubDate>Thu, 01 Aug 2024 13:05:00 +0200</pubDate><guid isPermaLink="true">https://it-notes.dragas.net/2024/08/01/evolving-bsd-cafe-from-bridging-to-routing/</guid><category>freebsd</category><category>wireguard</category><category>vxlan</category><category>networking</category><category>vps</category><category>virtualization</category><category>security</category><category>hosting</category><category>vpn</category><category>firewall</category><category>zerotier</category><category>pf</category><category>fib</category><category>routing</category><category>bsdcafe</category></item><item><title>Bridging Networks Across VPS with Wireguard and VXLAN on FreeBSD</title><link>https://it-notes.dragas.net/2024/07/15/bridging-networks-across-vps-wireguard-vxlan-freebsd/</link><description>&lt;p&gt;&lt;img src="https://it-notes.dragas.net/featured/server_rack.webp" alt="Bridging Networks Across VPS with Wireguard and VXLAN on FreeBSD"&gt;&lt;/p&gt;&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;In today's interconnected world, system administrators often face the challenge of managing services across multiple Virtual Private Servers (VPS). This article describes an advanced networking setup that allows you to bridge networks between two VPS instances using Wireguard and &lt;a href="https://man.freebsd.org/cgi/man.cgi?query=vxlan&amp;amp;sektion=4"&gt;VXLAN&lt;/a&gt; on FreeBSD. This configuration is particularly useful when you need to distribute services across different providers or when you want to leverage the strengths of multiple hosting environments.&lt;/p&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;At &lt;a href="https://bsd.cafe"&gt;BSD Cafe&lt;/a&gt;, we utilize various VPS instances to provide our services. The two main ones are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A publicly accessible VPS that hosts the reverse proxy and all firewall rules for packet routing.&lt;/li&gt;
&lt;li&gt;A larger VPS on a physical host I own, which is not directly exposed to the internet and doesn't have a public IP address.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most of the &lt;a href="https://wiki.bsd.cafe/bsdcafe-technical-details"&gt;jails hosting BSD Cafe services&lt;/a&gt; are distributed between these two VPS instances. Occasionally, I need to move services between them for performance reasons or to manage updates efficiently.&lt;/p&gt;
&lt;p&gt;To facilitate this flexibility, I've always maintained a bridge on each VPS. Initially, I used Zerotier to establish a connection between these bridges, allowing them to communicate as if they were part of a single, large network.&lt;/p&gt;
&lt;h2&gt;The New Setup: Wireguard and VXLAN&lt;/h2&gt;
&lt;p&gt;While the Zerotier setup worked, I decided to switch to a more streamlined solution using Wireguard and VXLAN. Here's why:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Wireguard offers excellent performance with low overhead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simplicity&lt;/strong&gt;: The configuration is straightforward and easy to maintain.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: Wireguard provides strong, modern cryptography.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I had already prepared a Wireguard connection between the two servers from the beginning. Since only one of the servers is publicly accessible, I set up one to only accept connections and the other to connect directly to the public IP of the first, with a 20-second keepalive (which is generally not necessary due to the high traffic between the jails).&lt;/p&gt;
&lt;p&gt;To complete the setup, I added two VXLAN interfaces on the VPS instances, added these interfaces to the local bridges, and immediately, packets started flowing between the networks.&lt;/p&gt;
&lt;h2&gt;Step-by-Step Implementation&lt;/h2&gt;
&lt;p&gt;Follow these instructions to create a bridge between two different networks using Wireguard and VXLAN on FreeBSD. While I use this setup to connect jails at BSD Cafe, you can use it for various purposes, such as bridging different VM (bhyve) instances across providers.&lt;/p&gt;
&lt;h3&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;Wireguard is now an integral part of FreeBSD, so you no longer need to compile a module or use the Go version. However, we'll use the "wireguard-tools" scripts as they provide the useful "wg-quick" command.&lt;/p&gt;
&lt;p&gt;Start by installing the wireguard-tools package on both servers:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;pkg install wireguard-tools
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Configuration&lt;/h3&gt;
&lt;h4&gt;Server 1 (Public IP)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Generate the Wireguard keys:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;wg genkey | tee /dev/stderr | wg pubkey | grep --label PUBLIC -H .
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This command will output a private key and a public key. Note down the public key as you'll need it to configure the client.&lt;/p&gt;
&lt;p&gt;Let's also add a PSK; it's optional but will increase the security of the entire setup.&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;wg genpsk
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Create a new file &lt;code&gt;/usr/local/etc/wireguard/wg0.conf&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-ini"&gt;[Interface]
## Default port is 51820 - feel free to change it
PrivateKey = &amp;lt;the private key from the previous command&amp;gt;
ListenPort = 43671
Address = 10.77.0.1/24

PostUp = /sbin/ifconfig vxlan create vxlanid 42 vxlanlocal 10.77.0.1 vxlanremote 10.77.0.2 inet 10.77.1.1/24
PostUp = /sbin/ifconfig bridge0 addm vxlan0 up
PostDown = /sbin/ifconfig vxlan0 destroy

[Peer]
PublicKey = &amp;lt;the other peer's public key&amp;gt;
#If publicly exposed, you can specify the peer ip address/port
#Endpoint = &amp;lt;public_ip&amp;gt;:&amp;lt;port&amp;gt;
AllowedIPs = 10.77.0.2/32
PresharedKey = &amp;lt;the PSK from the previous command&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Modify &lt;code&gt;/etc/rc.conf&lt;/code&gt; and add:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;wireguard_interfaces=&amp;quot;wg0&amp;quot;
wireguard_enable=&amp;quot;YES&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Start Wireguard and the VXLAN endpoint:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;wg-quick up wg0
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Server 2 (Behind NAT)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Generate the Wireguard keys as before.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create &lt;code&gt;/usr/local/etc/wireguard/wg0.conf&lt;/code&gt;:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-ini"&gt;[Interface]
PrivateKey = &amp;lt;the private key from the previous command&amp;gt;
Address = 10.77.0.2/24

PostUp = /sbin/ifconfig vxlan create vxlanid 42 vxlanlocal 10.77.0.2 vxlanremote 10.77.0.1 inet 10.77.1.2/24
PostUp = /sbin/ifconfig bridge0 addm vxlan0 up
PostDown = /sbin/ifconfig vxlan0 destroy

[Peer]
PublicKey = &amp;lt;the other peer's public key&amp;gt;
Endpoint = &amp;lt;public_ip&amp;gt;:&amp;lt;port&amp;gt;
AllowedIPs = 10.77.0.1/32
PresharedKey = &amp;lt;the PSK from the previous command&amp;gt;
PersistentKeepalive = 20
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Modify &lt;code&gt;/etc/rc.conf&lt;/code&gt; as before.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Start Wireguard and the VXLAN endpoint:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;wg-quick up wg0
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Verifying the Connection&lt;/h3&gt;
&lt;p&gt;To check if the connection is established, run the &lt;code&gt;wg&lt;/code&gt; command on either host. This will show you the connection status, the last handshake, and the data transferred.&lt;/p&gt;
&lt;p&gt;You can also try pinging the other host's Wireguard and VXLan interface IP address (in this example, 10.77.0.1 or 10.77.0.2 and 10.77.1.1 or 10.77.1.2).&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This setup allows the two VXLAN interfaces, inserted into the local bridge, to enable packet transit through Wireguard. This facilitates free passage between the two hosts, effectively creating a single, unified network across your VPS instances.&lt;/p&gt;
&lt;p&gt;This configuration is particularly useful for:
- Distributing services across different providers
- Leveraging both public-facing and private VPS instances
- Creating flexible, scalable network architectures&lt;/p&gt;
&lt;p&gt;By using Wireguard and VXLAN, you get the benefits of strong encryption, high performance, and the ability to create complex network topologies across physically separate servers.&lt;/p&gt;
&lt;p&gt;Remember to always keep your systems updated and regularly review your network configuration to ensure it meets your evolving needs and security requirements.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Stefano Marinelli</dc:creator><pubDate>Mon, 15 Jul 2024 08:41:00 +0200</pubDate><guid isPermaLink="true">https://it-notes.dragas.net/2024/07/15/bridging-networks-across-vps-wireguard-vxlan-freebsd/</guid><category>freebsd</category><category>wireguard</category><category>vxlan</category><category>networking</category><category>vps</category><category>virtualization</category><category>security</category><category>hosting</category><category>vpn</category><category>firewall</category></item><item><title>Migrating from an Old Linux Server to a New FreeBSD Machine</title><link>https://it-notes.dragas.net/2023/10/25/migrating-from-an-old-linux-server-to-a-new-freebsd-machine/</link><description>&lt;p&gt;&lt;img src="https://it-notes.dragas.net/content/images/2023/10/3500d14b-cb7e-4de6-af9e-0ca135985b41.webp" alt="Migrating from an Old Linux Server to a New FreeBSD Machine"&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Preamble:&lt;/em&gt; I believe it's time to bid farewell to this venerable Linux server.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt;  &lt;em&gt;The article chronicles the journey of transitioning from an outdated Linux server, running for 1690 days without updates, to a modern FreeBSD machine. This migration involved using tools like mfsBSD, BastilleBSD, Borg Backup, and bhyve. Despite initial hesitations due to the Linux server's impeccable performance, the transition was smooth, resulting in improved manageability and efficiency. The piece emphasizes the importance of regular system updates and anticipates revisiting the topic in the future with new uptime achievements and updates.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This server loyally served for years as a secondary backup server while also providing a few minor services to users. As it often happens, it remained in operation, neglected and without updates for years. Stable operating systems have the "flaw" of being forgotten, giving the false impression that they don't need maintenance or updates. This machine continued its service without oversight for years. When approached for a service request (not due to malfunctions), I advised the client to upgrade the whole system. A mere update would not suffice, so I suggested starting afresh on new hardware with FreeBSD as the primary OS.&lt;/p&gt;
&lt;p&gt;The client was understandably hesitant given the uptime stats:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;08:58:43 up 1690 days, 21:32, 4 users, load average: 9.57, 10.15, 8.76&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Not a single error, not a single hiccup. From his perspective, a similar setup to what was installed many years ago and still working flawlessly was preferred. Nevertheless, he trusted my expertise and let me proceed.&lt;/p&gt;
&lt;p&gt;This server had a plethora of duties, among which:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One of the pivotal tasks was running &lt;a href="https://www.proxmox.com/en/proxmox-backup-server/overview"&gt;Proxmox Backup Server&lt;/a&gt; via Docker. Proxmox Backup Server requires Debian, but this server was running on Ubuntu 16.04 (previously upgraded from Ubuntu 14.04 – yes, ancient!). Hence, Proxmox Backup Server was still at version 1.x.&lt;/li&gt;
&lt;li&gt;Another critical function was storing backups made through &lt;a href="https://www.borgbackup.org/"&gt;BorgBackup&lt;/a&gt; on its file system. The /home directory used a mirrored btrfs file system, and each backed-up server had its user on this system. Clients could backup (using a push method) only via VPN and only during specific windows when the server permitted (by adding specific firewall rules via Jenkins. Jenkins also managed connection protocols, snapshots, backups, etc.).&lt;/li&gt;
&lt;li&gt;Among the lesser tasks, the server ran a few Docker containers with HandBrake on various presets. The client processed video conversions by uploading the original files via sftp, and after some hours, fetched the converted files from the destination directory. This will not be replicated on the new FreeBSD server since they now handle this operation locally on their high-performance MacBook Pro with Apple Silicon. However, a future restoration isn't off the table.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first thing I did was install FreeBSD on the new hardware. Given that it's a physical server on Hetzner (an auction pick due to disk space needs over power), and FreeBSD wasn't an option, I used &lt;a href="https://mfsbsd.vx.sk/"&gt;mfsBSD&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After booting the physical server in Linux rescue, I copied mfsBSD onto the disks using &lt;code&gt;dd&lt;/code&gt; and restarted. On boot, I SSHed into mfsBSD and executed the installation using &lt;code&gt;bsdinstall&lt;/code&gt;-a robust and efficient method.&lt;/p&gt;
&lt;p&gt;I set up a raidz1 with all four 6 TB disks, resulting in a final storage space of 21.8T, ample for now without the video files.&lt;/p&gt;
&lt;p&gt;To ensure continuity, I kept a setup similar to the old one. The clients would essentially continue with their usual backup procedure without necessitating drastic changes to backup scripts. To avoid storing these backups directly in the physical machine's /home and to leave the door open for future services, I installed &lt;a href="https://bastillebsd.org/"&gt;BastilleBSD&lt;/a&gt; and began setting up several jails. I replaced the old Linux machine's behavior with a VNET FreeBSD jail. In past scenarios, I've created Linux jails (thanks to BastilleBSD) and transferred the old server into the jail using rsync, making minor configuration tweaks. While this usually works, it doesn't address the underlying issue of an obsolete setup. Given the opportunity, I opted for a modern toolset.&lt;/p&gt;
&lt;p&gt;Thus, I copied every home directory (along with their historic backups) in its entirety, installed BorgBackup, and re-established the VPN. With a VNET jail, I can craft networking devices and fine-tune configurations. After recreating user accounts, inputting the various SSH &lt;code&gt;authorized_keys&lt;/code&gt;, and checking all clients, I set up a snapshot plan on the host. This ensures that if a client is compromised with the potential (however remote) for breach and backup deletion, a ZFS snapshot of the entire jail remains available.&lt;/p&gt;
&lt;p&gt;As mentioned, one of the core tools on the old server was Proxmox Backup Server. It's not natively installable on FreeBSD, necessitating a VM. Enter the fantastic &lt;code&gt;bhyve&lt;/code&gt;, supported by &lt;code&gt;vm-bhyve&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, one issue arose: backups would consume vast amounts of space, and I wanted to avoid housing an enormous disk image (or a zvol) with both VMs and backups. So, I opted for a slightly less performant but more flexible solution: installing Debian 12 and Proxmox Backup Server on the VM while placing backups on a separate ZFS dataset on the physical machine, exported via NFS and mounted on the VM.&lt;/p&gt;
&lt;p&gt;Given that the physical server has an internal bridge "vm-public" with IP &lt;code&gt;192.168.124.1&lt;/code&gt; and the VM is at &lt;code&gt;192.168.124.2&lt;/code&gt;, I just created a dataset named &lt;code&gt;zroot/PBS&lt;/code&gt; and added the following line to &lt;code&gt;/etc/exports&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/zroot/PBS -alldirs -maproot=root -network 192.168.124.2/32&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;To enable NFS, insert into &lt;code&gt;/etc/rc.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;rpcbind_enable=&amp;quot;YES&amp;quot; 
nfs_server_enable=&amp;quot;YES&amp;quot; 
mountd_flags=&amp;quot;-r&amp;quot; 
rpc_lockd_enable=&amp;quot;YES&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Within the VM, create &lt;code&gt;/PBS&lt;/code&gt; and include in &lt;code&gt;/etc/fstab&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;192.168.124.1:/zroot/PBS /PBS nfs rw,async,soft,intr,noexec 0 0&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;After Proxmox Backup Server's installation, simply set up the datastores in &lt;code&gt;/PBS/&lt;/code&gt;, and they'll directly store on the physical machine's ZFS dataset.&lt;/p&gt;
&lt;p&gt;For firewall configurations, I exposed port 8007, redirecting it towards the VM, and everything started functioning smoothly. I then set a Proxmox Backup Server replica from the old to the new server. After completion, I changed the Proxmox Backup Server IP on all Proxmox hosts to point to the new server. Smooth sailing.&lt;/p&gt;
&lt;p&gt;The old Ubuntu server also managed other minor services, which have become obsolete and weren't replicated.&lt;/p&gt;
&lt;p&gt;The transition was seamless, the client is pleased, and I'm content since each service is now neatly segregated into its jail or VM. The machine's load is minimal, which might pave the way for other tasks, via VPN. Everything now rests on ZFS, and the icing on the cake: I made the client promise not to reach another 1690 days of uptime but to timely update as required.&lt;/p&gt;
&lt;p&gt;I'm not entirely convinced the promise will hold-meaning, in a few years, I might yet be discussing this "new" server, highlighting another impressive uptime and another upgrade journey.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Stefano Marinelli</dc:creator><pubDate>Wed, 25 Oct 2023 16:39:37 +0000</pubDate><guid isPermaLink="true">https://it-notes.dragas.net/2023/10/25/migrating-from-an-old-linux-server-to-a-new-freebsd-machine/</guid><category>freebsd</category><category>bhyve</category><category>borg</category><category>btrfs</category><category>container</category><category>data</category><category>docker</category><category>filesystems</category><category>jail</category><category>server</category><category>snapshots</category><category>virtualization</category><category>vpn</category><category>proxmox</category><category>backup</category><category>linux</category></item><item><title>Make your own VPN - FreeBSD, Wireguard, ipv6 and ad-blocking included</title><link>https://it-notes.dragas.net/2023/09/23/make-your-own-vpn-freebsd-wireguard-ipv6-and-ad-blocking-included/</link><description>&lt;p&gt;&lt;img src="https://it-notes.dragas.net/featured/lock_iphone.webp" alt="Make your own VPN - FreeBSD, Wireguard, ipv6 and ad-blocking included"&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: This article assumes a setup based on FreeBSD. If you prefer a version based on OpenBSD, &lt;a href="https://it-notes.dragas.net/2023/04/03/make-your-own-vpn-wireguard-ipv6-and-ad-blocking-included/"&gt;it is available here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;VPNs are a fundamental tool for securely connecting to your own servers and devices. Many people use commercial VPNs for various reasons, ranging from not trusting their provider (especially when connecting from a public hotspot) to wanting to "go out" on the Internet with a different IP address, perhaps from another country.&lt;/p&gt;
&lt;p&gt;Whatever the reason, solutions are not lacking. I have always set up management VPNs to allow servers and/or clients to communicate with each other using secure channels. Lately, &lt;a href="https://my-notes.dragas.net/posts/2023/the-urgency-of-transitioning-to-ipv6/"&gt;I have been activating IPv6 connectivity on all my devices&lt;/a&gt; (both desktop/servers and mobile devices) and I needed to quickly create a node that concentrated some networks and allowed them to go out on the network in IPv6. The tools I used and will describe are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VPS - in this case, I used a basic &lt;a href="https://hetzner.cloud/?ref=Wh0bprLCIE7w"&gt;Hetzner Cloud VPS&lt;/a&gt; (using this link, you will receive 20 euros of cloud credits), but any provider that provides IPv6 connectivity will do - if you want IPv6, of course.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.freebsd.org/"&gt;FreeBSD&lt;/a&gt; - a versatile, stable, and secure operating system.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.wireguard.com"&gt;Wireguard&lt;/a&gt; - lightweight, secure, and at the same time, not very "chatty", so it is also gentle on mobile device batteries. When there is no traffic, it simply does not transmit/receive anything. Well supported by all major desktop and server operating systems as well as Android and iOS devices.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nlnetlabs.nl/projects/unbound/about/"&gt;Unbound&lt;/a&gt; - can make DNS queries directly to root servers, not through forwarders. It also allows you to insert block-lists and have a result similar to that of Pi-Hole (i.e., ad-blocking).&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.spamhaus.org/"&gt;SpamHaus&lt;/a&gt; lists - to immediately stop connections to and from users on blacklists.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first step is to activate a VPS and install FreeBSD. On the Hetzner Cloud console, there might not be a pre-built FreeBSD image, but only a selection of Linux distributions. Don't worry, just choose any of them and create the VPS. Once done, the FreeBSD ISO image will be available among the "ISO Images". Just insert the virtual CD, restart the VPS, and the FreeBSD installation will appear in the console.&lt;/p&gt;
&lt;p&gt;I won't go into detail, the operation is simple and straightforward. The only precaution (in the case of a Hetzner Cloud VPS) is to use "DHCP" for IPv4 but, for now, do not configure IPv6. It will be configured later.&lt;/p&gt;
&lt;p&gt;Install all FreeBSD updates (using the &lt;code&gt;freebsd-update fetch install&lt;/code&gt; command) and reboot.&lt;/p&gt;
&lt;p&gt;It seems, moreover, that there is still &lt;a href="https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=165059"&gt;an old bug still present&lt;/a&gt; and manifesting when, on a FreeBSD server installed in a VM based on KVM (thus also tho
se of Hetzner), routing is performed (as in our case) between VNET jails and host.&lt;/p&gt;
&lt;p&gt;Adding this configuration to &lt;code&gt;/boot/loader.conf&lt;/code&gt; will solve the problem:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;hw.vtnet.X.tso_disable=&amp;quot;1&amp;quot;
hw.vtnet.tso_disable=&amp;quot;1&amp;quot;
hw.vtnet.lro_disable=&amp;quot;1&amp;quot;
hw.vtnet.X.lro_disable=&amp;quot;1&amp;quot;
hw.vtnet.csum_disable=&amp;quot;1&amp;quot;
hw.vtnet.X.csum_disable=&amp;quot;1&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Wireguard, on FreeBSD, is now available as a kernel module and the userland can be installed using the &lt;code&gt;pkg install wireguard-tools&lt;/code&gt; package manager. This means you can easily keep it updated alongside other software on the system.&lt;/p&gt;
&lt;p&gt;The first step is to configure IPv6 on the VPS. In the case of Hetzner, unfortunately, they only provide a /64, so it will be necessary to segment the assigned network. In this example, it will be divided into /72 subnetworks - to find valid subclasses, it will be possible &lt;a href="https://subnettingpractice.com/ipv6-subnet-calculator.html"&gt;to use a calculator&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;/etc/rc.conf&lt;/code&gt; file should have entries similar to:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;ifconfig_vtnet0=&amp;quot;DHCP&amp;quot;
ifconfig_vtnet0_ipv6=&amp;quot;inet6 2001:db8:cafe:cafe::1 prefixlen 72&amp;quot;
ipv6_defaultrouter=&amp;quot;fe80::1%vtnet0&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In short, keep the base address assigned by Hetzner, but change the prefix length to 72 - thus giving the possibility of having other networks available.&lt;/p&gt;
&lt;p&gt;It is now necessary to enable forwarding for IPv4 and IPv6. Add these lines to the &lt;code&gt;/etc/sysctl.conf&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;net.inet.ip.forwarding=1
net.inet6.ip6.forwarding=1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After reboot, test it:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ping6 google.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If everything has been configured correctly, the ping will be executed and google.com will reply.&lt;/p&gt;
&lt;p&gt;To configure Wireguard, a few steps will be necessary. First of all, the private key will need to be created:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wg genkey | tee /dev/stderr | wg pubkey  | grep --label PUBLIC -H .&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You will get a private key and a public key. Take note of the public key - it will be needed to configure the clients.&lt;/p&gt;
&lt;p&gt;Now create a new file called &lt;code&gt;/usr/local/etc/wireguard/wg0.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;[Interface]
Address = 172.16.0.1/24,2001:db8:cafe:cafe:100::1/72
ListenPort = 51820
PrivateKey = YUkS6cNTyPbXmtVf/23ppVW3gX2hZIBzlHtXNFRp80w=
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A new Wireguard interface called &lt;code&gt;wg0&lt;/code&gt; is being created. Start the Wireguard interface:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;service wireguard enable
sysrc wireguard_interfaces=&amp;quot;wg0&amp;quot; 
service wireguard start
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If everything has been entered correctly, the interface should come up. Check its status:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wg&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;As for the firewall, FreeBSD comes with no &lt;code&gt;pf&lt;/code&gt; configuration. In my setups, I tend to block what is not needed and be permissive with what may be useful. However, I like to keep out the "bad guys," so I use blacklists. &lt;code&gt;pf&lt;/code&gt; allows elements to be inserted and removed from tables at runtime, so the firewall can be configured accordingly.&lt;/p&gt;
&lt;p&gt;To download and apply the Spamhaus lists, I use a simple but effective &lt;a href="https://daemonforums.org/showthread.php?t=11420"&gt;script found on the Internet&lt;/a&gt;, but for OpenBSD.&lt;/p&gt;
&lt;p&gt;For the Spamhaus lists, continue with the FreeBSD script creation.&lt;/p&gt;
&lt;p&gt;Create the script in &lt;code&gt;/usr/local/sbin/spamhaus.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;#!/bin/sh
#
#this is normally run once per day via cron.
#
echo updating Spamhaus DROP lists:
(
  { fetch -o - https://www.spamhaus.org/drop/drop.txt &amp;amp;&amp;amp; \
    fetch -o - https://www.spamhaus.org/drop/dropv6.txt ; \
  } 2&amp;gt;/dev/null | sed &amp;quot;s/;/#/&amp;quot; &amp;gt; /var/db/drop.txt
)
pfctl -t spamhaus -T replace -f /var/db/drop.txt 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Make it executable and run it. Pf isn't enabled, so you'll get an error - but this will create the &lt;em&gt;/var/db/drop.txt&lt;/em&gt; file:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;chmod a+rx /usr/local/sbin/spamhaus.sh
/usr/local/sbin/spamhaus.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There are many possibilities to configure &lt;code&gt;pf&lt;/code&gt; on FreeBSD. A fairly simple example could be this:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;ext_if=&amp;quot;vtnet0&amp;quot;
wg0_if=&amp;quot;wg0&amp;quot;
wg0_networks=&amp;quot;172.16.0.0/24&amp;quot;

set skip on lo

nat on $ext_if from { $wg0_networks } to any -&amp;gt; ($ext_if)

# Spamhaus DROP list:
table &amp;lt;spamhaus&amp;gt; persist file &amp;quot;/var/db/drop.txt&amp;quot;

block drop log quick from &amp;lt;spamhaus&amp;gt;

# Pass ICMP on ipv6
pass quick proto ipv6-icmp
# Block from ipv6 to wg0 network
block in quick on $ext_if inet6 to { 2001:db8:cafe:cafe:100::/72 }
# Pass Wireguard traffic - in and out
pass quick on $wg0_if

# default deny
block in
block out

pass in on $ext_if proto tcp to port ssh
pass in on $ext_if proto udp to port 51820

pass out on $ext_if
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is a very simple configuration: it blocks everything that is present in the list downloaded from Spamhaus, allows NAT from the Wireguard network to the public interface, allows ICMP traffic in IPv6 (necessary for the network to function properly) while blocking incoming traffic to the Wireguard IPv6 LAN (remember that the IPs will be public and directly reachable, so we don't want to expose our devices by default). All traffic on the Wireguard interface will be allowed to pass. Then everything will be blocked and exceptions will be specified, i.e., allowing SSH and Wireguard connections (of course). Authorization will also be granted to allow traffic to exit from the public network interface.&lt;/p&gt;
&lt;p&gt;Save this configuration to &lt;code&gt;/etc/pf.conf&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Enable and start &lt;code&gt;pf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;service pf enable 
service pf start
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You will probably be kicked out of the system. Don't worry, just reconnect. pf is doing its job.&lt;/p&gt;
&lt;p&gt;If everything went correctly, the firewall should have loaded the new rules.&lt;/p&gt;
&lt;p&gt;To obtain caching of DNS queries and the related ad-block, it is now time to configure Unbound. Let's install it with:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pkg install unbound&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;A while ago, I found a script which I slightly adapted. I don't remember where I got it, so I'll paste it here without citing the original creator.&lt;/p&gt;
&lt;p&gt;Create a script to update the unbound ad-block, in &lt;em&gt;/usr/local/sbin/unbound-adhosts.sh&lt;/em&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;#!/bin/sh
#
# Using blacklist from pi-hole project https://github.com/pi-hole/
# to enable AD blocking in unbound(8)
#
PATH=&amp;quot;/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin&amp;quot;

# Available blocklists - comment line to disable blocklist
_disconad=&amp;quot;https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt&amp;quot;
_discontrack=&amp;quot;https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt&amp;quot;
_stevenblack=&amp;quot;https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts&amp;quot;

# Global variables
_tmpfile=&amp;quot;$(mktemp)&amp;quot; &amp;amp;&amp;amp; echo '' &amp;gt; $_tmpfile
_unboundconf=&amp;quot;/usr/local/etc/unbound/unbound-adhosts.conf&amp;quot;

# Remove comments from blocklist
simpleParse() {
  fetch -o - $1 | \
  sed -e 's/#.*$//' -e '/^[[:space:]]*$/d' &amp;gt;&amp;gt; $2
}

# Parse DisconTrack
[ -n &amp;quot;${_discontrack}&amp;quot; ] &amp;amp;&amp;amp; simpleParse $_discontrack $_tmpfile

# Parse DisconAD
[ -n &amp;quot;${_disconad}&amp;quot; ] &amp;amp;&amp;amp; simpleParse $_disconad $_tmpfile

# Parse StevenBlack
[ -n &amp;quot;${_stevenblack}&amp;quot; ] &amp;amp;&amp;amp; \
  fetch -o - $_stevenblack | \
  sed -n '/Start/,$p' | \
  sed -e 's/#.*$//' -e '/^[[:space:]]*$/d' | \
  awk '/^0.0.0.0/ { print $2 }' &amp;gt;&amp;gt; $_tmpfile

# Create unbound(8) local zone file
sort -fu $_tmpfile | grep -v &amp;quot;^[[:space:]]*$&amp;quot; | \
awk '{
  print &amp;quot;local-zone: \&amp;quot;&amp;quot; $1 &amp;quot;\&amp;quot; redirect&amp;quot;
  print &amp;quot;local-data: \&amp;quot;&amp;quot; $1 &amp;quot; A 0.0.0.0\&amp;quot;&amp;quot;
}' &amp;gt; $_unboundconf &amp;amp;&amp;amp; rm -f $_tmpfile

service unbound reload 1&amp;gt;/dev/null

exit 0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After saving the script, make it executable and run it:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;chmod a+rx /usr/local/sbin/unbound-adhosts.sh
/usr/local/sbin/unbound-adhosts.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, the Unbound configuration file in &lt;code&gt;/usr/local/etc/unbound/unbound.conf&lt;/code&gt; can be modified as follows:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;server:
        verbosity: 1
        log-queries: no
        num-threads: 4
        num-queries-per-thread: 1024
        interface: 127.0.0.1
        interface: 172.16.0.1
        interface: 2001:db8:cafe:cafe:100::1
        interface: ::1
        outgoing-range: 64
        chroot: &amp;quot;&amp;quot;

        access-control: 0.0.0.0/0 refuse
        access-control: 127.0.0.0/8 allow
        access-control: ::0/0 refuse
        access-control: ::1 allow
        access-control: 172.16.0.0/24 allow
        access-control: 2001:db8:cafe:cafe:100::/72 allow

        hide-identity: yes
        hide-version: yes
        auto-trust-anchor-file: &amp;quot;/usr/local/etc/unbound/root.key&amp;quot;
        val-log-level: 2
        aggressive-nsec: yes
        prefetch: yes
        username: &amp;quot;unbound&amp;quot;
        directory: &amp;quot;/usr/local/etc/unbound&amp;quot;
        logfile: &amp;quot;/var/log/unbound.log&amp;quot;
        use-syslog: no
        pidfile: &amp;quot;/var/run/unbound.pid&amp;quot;
        include: /usr/local/etc/unbound/unbound-adhosts.conf

remote-control:
        control-enable: yes
        control-interface: /var/run/unbound.sock
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, enable and start unbound:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;service unbound enable
service unbound start
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If everything has been set up correctly, unbound will be able to respond to DNS requests made on &lt;em&gt;172.16.0.1&lt;/em&gt; and &lt;em&gt;2001:db8:cafe:cafe:100::1&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Now it is possible to configure the Wireguard client. Create a new configuration by inserting "172.16.0.2/32, 2001:db8:cafe:cafe:100::2/128" (the ones that will later be entered in the peer configuration of the server) in the local IP addresses. Set the DNS server address to "172.16.0.1" and/or its corresponding IPv6 address (in the example, 2001:db8:cafe:cafe:100::1 - yours will be different). In the peer section, insert the server's data, including its public key, IP address:port (in the example, the port is 51820), and allowed addresses (setting "0.0.0.0/0, ::0/0" means "all connections will be sent via Wireguard" - all the traffic will pass through the VPN for both IPv4 and IPv6).Each implementation has its own procedure (Android, iOS, MikroTik, Linux, etc.) but essentially it is sufficient to create the right configuration both on the server and on the client.&lt;/p&gt;
&lt;p&gt;Reopen the Wireguard configuration file &lt;code&gt;/usr/local/etc/wireguard/wg0.conf&lt;/code&gt; and add:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;[Interface]
Address = 172.16.0.1/24,2001:db8:cafe:cafe:100::1/72
ListenPort = 51820
PrivateKey = YUkS6cNTyPbXmtVf/23ppVW3gX2hZIBzlHtXNFRp80w=

[Peer]
PublicKey = *client's public key*
AllowedIPs = 172.16.0.2/32, 2001:db8:cafe:cafe:100::2/128
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The client's public key will be shown by the client itself.&lt;/p&gt;
&lt;p&gt;Reload the Wireguard configuration:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;service wireguard restart&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;It is also possible to use the VPN only as an ad-blocker, by only routing DNS traffic through it. To achieve this result, configure the client so that the only allowed address is the one of the just-configured unbound (in this example, 172.16.0.1 and/or 2001:db8:cafe:cafe:100::1) - DNS resolution will occur via VPN, but browsing will continue to work through the main provider.&lt;/p&gt;
&lt;p&gt;To automatically update the spamhaus and ad-block lists, we will use cron.First, create a script, for example, &lt;code&gt;/usr/local/sbin/update-blocklists.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;#!/bin/sh

/usr/local/sbin/unbound-adhosts.sh
/usr/local/sbin/spamhaus.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Make it executable:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;chmod +x /usr/local/sbin/update-blocklists.sh&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Then, add it to the crontab to run daily:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;echo "@daily /usr/local/sbin/update-blocklists.sh" &amp;gt;&amp;gt; /etc/crontab&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This approach benefits from both update management and security perspectives.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Stefano Marinelli</dc:creator><pubDate>Sat, 23 Sep 2023 09:05:51 +0000</pubDate><guid isPermaLink="true">https://it-notes.dragas.net/2023/09/23/make-your-own-vpn-freebsd-wireguard-ipv6-and-ad-blocking-included/</guid><category>freebsd</category><category>ipv6</category><category>server</category><category>networking</category><category>vpn</category><category>wireguard</category><category>hosting</category><category>tutorial</category><category>security</category><category>ownyourdata</category><category>series</category></item><item><title>Boosting Network Performance in FreeBSD's VNET Jails</title><link>https://it-notes.dragas.net/2023/08/14/boosting-network-performance-in-freebsds-vnet-jails/</link><description>&lt;p&gt;&lt;img src="https://images.unsplash.com/photo-1604869515882-4d10fa4b0492?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDIwfHxuZXR3b3JrfGVufDB8fHx8MTY5MjAzNTI4N3ww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Boosting Network Performance in FreeBSD&amp;#x27;s VNET Jails"&gt;&lt;/p&gt;&lt;p&gt;In the world of FreeBSD, jails are a renowned feature that allows for system-level virtualization. As I was setting up the jails for BSDCafe, I stumbled upon an interesting discovery: the network performance of VNET jails was noticeably lower compared to that of VPS or standard jails. Rather than diving into this immediately, I decided to take a mental note and proceed.&lt;/p&gt;
&lt;p&gt;As I delved deeper with various tests, a pattern began to emerge. Anytime there was a NAT (Network Address Translation) acting between the internal bridge of the VNET jails - irrespective of whether it was local or bridged via a VPN - the outgoing performance took a nosedive.&lt;/p&gt;
&lt;p&gt;From using &lt;code&gt;tcpdump&lt;/code&gt; to carrying out MTU (Maximum Transmission Unit) tests, my endeavors seemed fruitless. However, a memory from the past struck me. I recalled setting up a FreeBSD VM on Proxmox (effectively pointing towards an issue with KVM) where I had to make specific tweaks.&lt;/p&gt;
&lt;p&gt;To remedy the situation, I made the following modifications:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Added the following to &lt;code&gt;/boot/loader.conf&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;hw.vtnet.X.csum_disable=1
hw.vtnet.lro_disable=1
&lt;/code&gt;&lt;/pre&gt;

&lt;ol&gt;
&lt;li&gt;Integrated these lines into &lt;code&gt;/etc/sysctl.conf&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;net.link.bridge.pfil_member=0
net.link.bridge.pfil_bridge=0
net.link.bridge.pfil_onlyip=0
&lt;/code&gt;&lt;/pre&gt;

&lt;ol&gt;
&lt;li&gt;And appended to &lt;code&gt;/etc/rc.local&lt;/code&gt; (which I already use for initialization):&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;ifconfig vtnet0 -rxcsum
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The end result was exhilarating: not only did the VNET jails now perform at full bandwidth, but even those interconnected via VPN showcased commendable performance.&lt;/p&gt;
&lt;p&gt;Interestingly, this seems to be linked to a long-standing bug from 2012, &lt;a href="https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=165059"&gt;FreeBSD Bug 165059&lt;/a&gt;. This issue is even highlighted in the official PFSense documentation.&lt;/p&gt;
&lt;p&gt;In the vast landscape of tech, sometimes revisiting the past provides solutions for the present. All's well that ends well, and I'm pleased to share this resolution with my readers. For those dabbling in FreeBSD, I hope this piece offers some guidance in optimizing your VNET jail setups.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Stefano Marinelli</dc:creator><pubDate>Mon, 14 Aug 2023 15:50:14 +0000</pubDate><guid isPermaLink="true">https://it-notes.dragas.net/2023/08/14/boosting-network-performance-in-freebsds-vnet-jails/</guid><category>freebsd</category><category>jail</category><category>kvm</category><category>linux</category><category>networking</category><category>proxmox</category><category>hosting</category><category>vpn</category><category>virtualization</category><category>server</category><category>container</category></item><item><title>Make your own VPN - OpenBSD, Wireguard, ipv6 and ad-blocking included</title><link>https://it-notes.dragas.net/2023/04/03/make-your-own-vpn-wireguard-ipv6-and-ad-blocking-included/</link><description>&lt;p&gt;&lt;img src="https://it-notes.dragas.net/featured/lock_iphone.webp" alt="Make your own VPN - OpenBSD, Wireguard, ipv6 and ad-blocking included"&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: This article assumes a setup based on OpenBSD. If you prefer a version based on FreeBSD, &lt;a href="https://it-notes.dragas.net/2023/09/23/make-your-own-vpn-freebsd-wireguard-ipv6-and-ad-blocking-included/"&gt;it is available here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;VPNs are a fundamental tool for securely connecting to your own servers and devices. Many people use commercial VPNs for various reasons, ranging from not trusting their provider (especially when connecting from a public hotspot) to wanting to "go out" on the Internet with a different IP address, perhaps from another country.&lt;/p&gt;
&lt;p&gt;Whatever the reason, solutions are not lacking. I have always set up management VPNs to allow servers and/or clients to communicate with each other using secure channels. Lately, &lt;a href="https://my-notes.dragas.net/posts/2023/the-urgency-of-transitioning-to-ipv6/"&gt;I have been activating IPv6 connectivity on all my devices&lt;/a&gt; (both desktop/servers and mobile devices) and I needed to quickly create a node that concentrated some networks and allowed them to go out on the network in IPv6. The tools I used and will describe are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VPS - in this case, I used a basic &lt;a href="https://hetzner.cloud/?ref=Wh0bprLCIE7w"&gt;Hetzner Cloud&lt;/a&gt; VPS (using this link, you will receive 20 euros of cloud credits), but any provider that provides IPv6 connectivity will do - if you want IPv6, of course.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.openbsd.org"&gt;OpenBSD&lt;/a&gt; - a clean, stable, and secure operating system.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.wireguard.com"&gt;Wireguard&lt;/a&gt; - lightweight, secure, and at the same time, not very "chatty", so it is also gentle on mobile device batteries. When there is no traffic, it simply does not transmit/receive anything. Well supported by all major desktop and server operating systems as well as Android and iOS devices.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nlnetlabs.nl/projects/unbound/about/"&gt;Unbound&lt;/a&gt; - can make DNS queries directly to root servers, not through forwarders. It also allows you to insert block-lists and have a result similar to that of Pi-Hole (i.e., ad-blocking).&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.spamhaus.org"&gt;SpamHaus&lt;/a&gt; lists - to immediately stop connections to and from users on blacklists.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first step is to activate a VPS and install OpenBSD. On the Hetzner cloud console, there won't be a pre-built OpenBSD image, but only a selection of Linux distributions. Don't worry, just choose any of them and create the VPS. Once done, the OpenBSD ISO image will be available among the "ISO Images". Just insert the virtual CD, restart the VPS, and the OpenBSD installation will appear in the console.&lt;/p&gt;
&lt;p&gt;I won't go into detail, the operation is simple and straightforward. The only precaution (in the case of a Hetzner Cloud VPS) is &lt;em&gt;to use "autoconf" for IPv4 but, for now, do not configure IPv6&lt;/em&gt;. It will be configured later.&lt;/p&gt;
&lt;p&gt;Install all OpenBSD updates (using the &lt;em&gt;syspatch&lt;/em&gt; command) and restart, the kernel will be relinked.&lt;/p&gt;
&lt;p&gt;Wireguard, on OpenBSD, is fully integrated into the base system and does not require the installation of external packages. This is a big advantage because over time, support for everything related to Wireguard will be managed directly by the main OpenBSD development team.&lt;/p&gt;
&lt;p&gt;The first step is to configure IPv6 on the VPS. In the case of Hetzner, unfortunately, they only provide a /64, so it will be necessary to segment the assigned network. In this example, it will be divided into /72 subnetworks - to find valid subclasses, it will be possible to use &lt;a href="https://subnettingpractice.com/ipv6-subnet-calculator.html"&gt;a calculator&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The /etc/hostname.vio0 file should look something like this:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;inet autoconf
inet6 2001:db8:cafe:cafe::1 72 
!route add -net ::/0 fe80::1%vio0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In short, keep the base address assigned by Hetzner, but change the netmask to /72 - thus giving the possibility of having other networks available.&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-sh"&gt;sh /etc/netstart vio0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It will reconfigure the network interface and allow IPv6 to start working. To test it:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-sh"&gt;ping6 google.com
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If everything has been configured correctly, the ping will be executed and google.com will reply.&lt;/p&gt;
&lt;p&gt;It is now necessary to enable forwarding for IPv4 and IPv6. Enter these lines in the /etc/sysctl.conf file:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;net.inet.ip.forwarding=1
net.inet6.ip6.forwarding=1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To apply those changes you can reboot or just type:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;sysctl net.inet.ip.forwarding=1
sysctl net.inet6.ip6.forwarding=1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To configure Wireguard, a few steps will be necessary. First of all, the private key will need to be created:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-sh"&gt;openssl rand -base64 32
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Something like this will come out:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;YUkS6cNTyPbXmtVf/23ppVW3gX2hZIBzlHtXNFRp80w=&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now create a new file called /etc/hostname.wg0:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;172.16.0.1/24 wgport 51820 wgkey YUkS6cNTyPbXmtVf/23ppVW3gX2hZIBzlHtXNFRp80w=
inet6 2001:db8:cafe:cafe:100::1 72
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A new Wireguard interface called wg0 is being created. It will have the IPv4 address "172.16.0.1", Wireguard will listen on port 51820, and with the private key created shortly before. It will also have an IPv6 address on one of the subclasses that the provider will have provided.&lt;/p&gt;
&lt;p&gt;Save and activate the interface:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-sh"&gt;sh /etc/netstart wg0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If everything has been entered correctly, it should enable the interface. Now:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-sh"&gt;ifconfig wg0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And something like this will be returned:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;wg0: flags=80c3&amp;lt;UP,BROADCAST,RUNNING,NOARP,MULTICAST&amp;gt; mtu 1420
    index 5 priority 0 llprio 3
    wgport 51820
    wgpubkey xxxxxxxxxxxxxxx=
    groups: wg
    inet 172.16.0.1 netmask 0xffffff00 broadcast 172.16.0.255
    inet6 2001:db8:cafe:cafe:100::1 prefixlen 72
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Take note of the &lt;em&gt;wgpubkey&lt;/em&gt; - it will be needed to configure the clients.&lt;/p&gt;
&lt;p&gt;As for the firewall, OpenBSD comes with a basic &lt;em&gt;pf&lt;/em&gt; configuration. In my setups, I tend to block what is not needed and be permissive with what may be useful. However, I like to keep out the "bad guys," so I use blacklists. pf allows elements to be inserted and removed from tables in runtime, so the firewall can be configured accordingly.&lt;/p&gt;
&lt;p&gt;To download and apply Spamhaus lists, I use a simple but effective script &lt;a href="https://daemonforums.org/showthread.php?t=11420"&gt;found on the Internet&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So create the script in /usr/local/sbin/spamhaus.sh:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;#!/bin/sh
#
# this is normally run once per day via /etc/daily.local.
#
echo updating Spamhaus DROP lists:
(
  { ftp -o - https://www.spamhaus.org/drop/drop.txt &amp;amp;&amp;amp; \
    ftp -o - https://www.spamhaus.org/drop/dropv6.txt ; \
  } 2&amp;gt;/dev/null | sed &amp;quot;s/;/#/&amp;quot; &amp;gt; /var/db/drop.txt
)
pfctl -t spamhaus -T replace -f /var/db/drop.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Make it executable and run it:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;chmod a+rx /usr/local/sbin/spamhaus.sh
/usr/local/sbin/spamhaus.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There are many possibilities to configure pf. A fairly simple example could be this:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;ext_if=&amp;quot;vio0&amp;quot;
wg0_if = &amp;quot;wg0&amp;quot;
wg0_networks = &amp;quot;172.16.0.0/24&amp;quot;

set skip on lo

# Spamhaus DROP list:
table &amp;lt;spamhaus&amp;gt; persist file &amp;quot;/var/db/drop.txt&amp;quot;

block drop log quick from &amp;lt;spamhaus&amp;gt;

match in all scrub (no-df random-id max-mss 1440)

match out on $ext_if from { $wg0_networks } nat-to ($ext_if)

#Pass ICMP on ipv6
pass quick proto ipv6-icmp
#Block from ipv6 to wg0 network
block in quick on $ext_if inet6 to { 2001:db8:cafe:cafe:100::/72 }
#Pass Wireguard traffic - in and out
pass quick on $wg0_if

# default deny
block in
block out

# By default, do not permit remote connections to X11
block return in on ! lo0 proto tcp to port 6000:6010

# Port build user does not need network
block return out log proto {tcp udp} user _pbuild

pass in on $ext_if proto tcp to port ssh
pass in on $ext_if proto udp to port 51820

pass out on $ext_if
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is a very simple configuration: it blocks everything that is present in the list downloaded from Spamhaus, allows NAT from the Wireguard network to the public interface, allows icmp traffic in IPv6 (necessary for the network to function properly) while blocking incoming traffic to the Wireguard IPv6 LAN (remember that the IPs will be public and directly reachable, so we don't want to expose our devices by default). All traffic on the Wireguard interface will be allowed to pass. Then everything will be blocked and exceptions will be specified, i.e. allowing ssh and Wireguard connections (of course). Authorization will also be granted to allow traffic to exit from the public network interface.&lt;/p&gt;
&lt;p&gt;Reload pf configuration:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-sh"&gt;pfctl -f /etc/pf.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If everything went correctly, the firewall should have loaded the new options.&lt;/p&gt;
&lt;p&gt;To obtain caching of DNS queries and the related ad-block, it is now time to configure Unbound. A while ago, I found a script which I slightly adapted. I don't remember where I got it, so I'll paste it here without citing the original creator.&lt;/p&gt;
&lt;p&gt;Create a script to update the unbound ad-block, in /usr/local/sbin/unbound-adhosts.sh:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;#!/bin/ksh
#
# Using blacklist from pi-hole project https://github.com/pi-hole/
# to enable AD blocking in unbound(8)
#
PATH=&amp;quot;/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin&amp;quot;

# Available blocklists - comment line to disable blocklist
_disconad=&amp;quot;https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt&amp;quot;
_discontrack=&amp;quot;https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt&amp;quot;
_stevenblack=&amp;quot;https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts&amp;quot;

# Global variables
_tmpfile=&amp;quot;$(mktemp)&amp;quot; &amp;amp;&amp;amp; echo '' &amp;gt; $_tmpfile
_unboundconf=&amp;quot;/var/unbound/etc/unbound-adhosts.conf&amp;quot;

# Remove comments from blocklist
function simpleParse {
  ftp -VMo - $1 | \
  sed -e 's/#.*$//' -e '/^[[:space:]]*$/d' &amp;gt;&amp;gt; $2
}

# Parse MalwareDom
#[[ -n ${_malwaredom+x} ]] &amp;amp;&amp;amp; simpleParse $_malwaredom $_tmpfile

# Parse ZeusTracker
#[[ -n ${_zeustracker+x} ]] &amp;amp;&amp;amp; simpleParse $_zeustracker $_tmpfile

# Parse DisconTrack
[[ -n ${_discontrack+x} ]] &amp;amp;&amp;amp; simpleParse $_discontrack $_tmpfile

# Parse DisconAD
[[ -n ${_disconad+x} ]] &amp;amp;&amp;amp;  simpleParse $_disconad $_tmpfile

# Parse StevenBlack
[[ -n ${_stevenblack+x} ]] &amp;amp;&amp;amp; \
  ftp -VMo - $_stevenblack | \
  sed -n '/Start/,$p' | \
  sed -e 's/#.*$//' -e '/^[[:space:]]*$/d' | \
  awk '/^0.0.0.0/ { print $2 }' &amp;gt;&amp;gt; $_tmpfile

# Parse hpHosts
[[ -n ${_hostfiles+x} ]] &amp;amp;&amp;amp; \
  ftp -VMo - $_hostfiles | \
  sed -n '/START/,$p' | tr -d '^M$' | \
  sed -e 's/#.*$//' -e '/^[[:space:]]*$/d' -e 's/^M$//' | \
  awk '/^127.0.0.1/ { print $2 }' &amp;gt;&amp;gt; $_tmpfile

# Create unbound(8) local zone file
sort -fu $_tmpfile | grep -v &amp;quot;^[[:space:]]*$&amp;quot; | \
awk '{
  print &amp;quot;local-zone: \&amp;quot;&amp;quot; $1 &amp;quot;\&amp;quot; redirect&amp;quot;
  print &amp;quot;local-data: \&amp;quot;&amp;quot; $1 &amp;quot; A 0.0.0.0\&amp;quot;&amp;quot;
}' &amp;gt; $_unboundconf &amp;amp;&amp;amp; rm -f $_tmpfile

/usr/sbin/rcctl reload unbound 1&amp;gt;/dev/null

exit 0
#EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Similarly, make the script executable and run it:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;chmod a+rx /usr/local/sbin/unbound-adhosts.sh
/usr/local/sbin/unbound-adhosts.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, the Unbound configuration file in /var/unbound/etc/unbound.conf can be modified as follows:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;# $OpenBSD: unbound.conf,v 1.21 2020/10/28 11:35:58 sthen Exp $

server:
        verbosity: 1
        log-queries: no
        num-threads: 4
        num-queries-per-thread: 1024
        interface: 127.0.0.1
        #interface: 127.0.0.1@5353      # listen on alternative port
        interface: 172.16.0.1
    interface: 2001:db8:cafe:cafe:100::1
        interface: ::1
        outgoing-range: 64
        chroot: &amp;quot;&amp;quot;
        #do-ip6: yes

        # override the default &amp;quot;any&amp;quot; address to send queries; if multiple
        # addresses are available, they are used randomly to counter spoofing
        #outgoing-interface: 192.0.2.1
        #outgoing-interface: 2001:db8::53

        access-control: 0.0.0.0/0 refuse
        access-control: 127.0.0.0/8 allow
        access-control: ::0/0 refuse
        access-control: ::1 allow
        access-control: 172.16.0.0/24 allow
        access-control: 2001:db8:cafe:cafe:100::/72 allow

        hide-identity: yes
        hide-version: yes

        # Perform DNSSEC validation.
        #
        auto-trust-anchor-file: &amp;quot;/var/unbound/db/root.key&amp;quot;
        val-log-level: 2

        # Synthesize NXDOMAINs from DNSSEC NSEC chains.
        # https://tools.ietf.org/html/rfc8198
        #
        aggressive-nsec: yes
        prefetch: yes
        username: &amp;quot;nobody&amp;quot;
        directory: &amp;quot;/var/unbound/etc&amp;quot;
        logfile: &amp;quot;/var/unbound/unbound.log&amp;quot;
        use-syslog: no
        pidfile: &amp;quot;/var/unbound/unbound.pid&amp;quot;
        include: /var/unbound/etc/unbound-adhosts.conf

remote-control:
        control-enable: yes
        control-interface: /var/run/unbound.sock
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Before launching unbound, it is necessary to give the appropriate permissions:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;chown -R nobody:nobody /var/unbound
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now it is possible to enable and start unbound. Since it needs to load the (long) blocklist, it will take a few seconds:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-bash"&gt;rcctl enable unbound
rcctl start unbound
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If everything has been done correctly, unbound will be able to respond to requests made on 172.16.0.1 and 2001:db8:cafe:cafe:100::1, from their respective LANs.&lt;/p&gt;
&lt;p&gt;Now it is possible to configure the Wireguard client. Each implementation has its own procedure (Android, iOS, MikroTik, Linux, etc.) but essentially it is sufficient to create the right configuration both on the server and on the client. For example, the server's public key (visible by typing "&lt;em&gt;ifconfig wg0&lt;/em&gt;" on the OpenBSD server) should be inserted into the "peer" that will be created on the client, while the client's public key will be used on the server in this way:&lt;/p&gt;
&lt;p&gt;Reopen the file /etc/hostname.wg0 and add:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;172.16.0.1/24 wgport 51820 wgkey YUkS6cNTyPbXmtVf/23ppVW3gX2hZIBzlHtXNFRp80w=
inet6 2001:db8:cafe:cafe:100::1 72
wgpeer *client's public key* wgaip 172.16.0.2/32 wgaip 2001:db8:cafe:cafe:100::2/128
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Reload the configuration:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code class="language-sh"&gt;sh /etc/netstart wg0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;On the client, create a new configuration by inserting "172.16.0.2/32, 2001:db8:cafe:cafe:100::2/128" (the ones that were entered in the peer configuration in the hostname.wg0 file) in the local IP addresses. Set the DNS server address to "172.16.0.1" and/or its corresponding IPv6 address (in the example, 2001:db8:cafe:cafe:100::1 - yours will be different). In the peer, insert the server's data, including its public key, IP address:port (in the example, the port is 51820), and allowed addresses (setting "0.0.0.0/0, ::0/0" means "all connections will be sent via Wireguard" - all the traffic will pass through the VPN for both IPv4 and IPv6).&lt;/p&gt;
&lt;p&gt;It is also possible to use the VPN only as an ad-blocker, by only routing DNS traffic through it. To achieve this result, it is sufficient to configure the client so that the only allowed address is the one of the just-configured unbound (in this example, 172.16.0.1 or 2001:db8:cafe:cafe:100::1) - DNS resolution will occur via VPN, but browsing will continue to work through the main provider.&lt;/p&gt;
&lt;p&gt;If you want the spamhaus and ad-block lists to be updated automatically, create the /etc/daily.local file and add the following lines:&lt;/p&gt;
&lt;pre class="highlight"&gt;&lt;code&gt;#!/bin/ksh

/usr/local/sbin/unbound-adhosts.sh
/usr/local/sbin/spamhaus.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;All of this can be achieved simply with a basic installation of OpenBSD, without the need to install any additional packages. This is an advantage both in terms of update management and security.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Stefano Marinelli</dc:creator><pubDate>Mon, 03 Apr 2023 04:16:11 +0000</pubDate><guid isPermaLink="true">https://it-notes.dragas.net/2023/04/03/make-your-own-vpn-wireguard-ipv6-and-ad-blocking-included/</guid><category>openbsd</category><category>server</category><category>tutorial</category><category>vpn</category><category>wireguard</category><category>ipv6</category><category>networking</category><category>security</category><category>hosting</category><category>ownyourdata</category><category>series</category></item></channel></rss>