Jae's Blog

Configuring DNSSEC on systemd-resolved

Enabling DNSSEC on systemd-resolved is quite easy.

First, let’s go in /etc/systemd/resolved.conf.d/main.conf and add/modify the file like so:

[Resolve]
DNSSEC=true
Code language: JavaScript (javascript)

For good measure, you can also enable DoT (DNS Over TLS) in there, which you can use with something like DNS0.

Restart systemd-resolved using systemctl restart systemd-resolved and voilà.

Now, if you type something like resolvectl query j4.lc, you should get an answer like so:

j4.lc: 95.217.179.88                           -- link: enp6s0
       2a12:4946:9900:f00::f00                 -- link: enp6s0

-- Information acquired via protocol DNS in 141.7ms.
-- Data is authenticated: yes; Data was acquired via local or encrypted transport: yes
-- Data from: network
Code language: CSS (css)

On the contrary, if you try to query a domain which has an invalid signature, for instance with resolvectl query badsig.go.dnscheck.tools, you will get:

badsig.go.dnscheck.tools: resolve call failed: All attempts to contact name servers or networks failed
Code language: CSS (css)

Do note some domains might stop resolving because of this, in which case, contact their admin so they can correct the issue.

Also, on my side, resolution hangs rather than displaying a proper error, which seems to be something like this bug (or maybe another, haven’t looked too much into this yet) on the systemd issue tracker.

Making your own web corner

So, you’ve finally bought yourself a domain (or thinking about it), got a server at home, and now you want to host your own corner of the web?

Great! Allow me to be your guide through this journey.

Pre-requisites

You’ll need a bunch of stuff for this tutorial, including:

A domain

Your domain will be the public face and how people (and yourself) will access your corner, choose it wisely.

To get a domain, you need to choose a registrar first, to which you will register it. Registering a domain can cost a fee anywhere from 5€ to 5000€.

Some good registrars include:

  • Spaceship – Really cheap, you can get a .eu for just under 5€ there
  • Hetzner – Well-known hosting service & DNS registrar
  • PorkBun – Well-known, huge selection, cheap sometimes
  • Inwx – German registrar, good service

If your friends also have their own corners, ask them about their registrar, maybe they had good experiences with some others than listed here!

From now on, assume we just bought example.com as a domain. Of course, replace this example.com by the domain you just got in the next steps.

A server

Now here comes the part where you have to choose where your stuff will be hosted. There are multiple ways of doing this:

  • Run a spare computer at home (this tutorial will focus on this)
  • Use a hosting provider like Hetzner or Infomaniak (similar to the first option, so this tutorial also applies)
  • Use GitLab, GitHub or Codeberg pages to host a static website (not covered in this tutorial, coming soon!)

In this example, we assume you have a spare computer at home running Debian Linux.

The boring networking part

DNS stands for Domain Name System. You can read more about it on howdns.works, but the basic gist is:

  • IP addresses are hard for people to remember as-is
  • DNS puts in relation a domain name to an IP address
  • For instance: j4.lc will point to 2a12:4946:9900:f00::f00 when being looked up
  • There are a lot of DNS record types, but the most importants are A and AAAA here
  • A A record maps a domain name to an IPv4 address, for instance: j4.lc -> 95.217.179.88
  • A AAAA record maps a domain name to an IPv6 address, for instance: j4.lc -> 2a12:4946:9900:f00::f00

Pointing your domain to your server

First, let’s figure out what’s the public IP of your server. For this you can execute:

curl -4 ifconfig.me
curl -6 ifconfig.meCode language: Bash (bash)

If the second command fails, this means your ISP doesn’t supports IPv6. In any case, write those IPs down in a notepad and let’s move on.

You will then need to add a DNS record on your domain to point to your server. To do this, log onto your registar and direct yourself to the DNS control panel.

When adding a record, you will have a few properties to fill:

  • name – Which subdomain you want to use. Setting this to @ will mean the root of the domain, in our case example.com, setting this to anything else, for instance awoo will “create” the subdomain awoo.example.com and make it point to your IP instead of the root
  • type – We’ve seen this earlier, we want this to be A or AAAA depending of if we’re adding an IPv4 or IPv6 (both can be present at the same time)
  • ttl – This is the time (in seconds) the record will stay cached. Leave it as-is. This is how long you will have to wait when you do a change to this record for you to see it
  • data – The IP address you want the record to point to
  • proxy status – This is for CloudFlare only, this setting controls if we want our site to be through CloudFlare, let’s disable this for now

Note: you do not need to specify the port of your application in the record. It is up to the app you are using (for instance, a web browser) to query the right ports. Adding a record to 95.217.179.88:8080 will be invalid for instance.

In our example, we can set everything (once again replace with your own data):

  • Name: @
  • Type: AAAA
  • TTL: 60 (default)
  • Data: 2a12:4946:9900:f00::f00

Meaning our root domain example.com will resolve to 2a12:4946:9900:f00::f00.

We can also add a A record to provide IPv4 connectivity:

  • Name: @
  • Type: A
  • TTL: 60 (default)
  • Data: 95.217.179.88

Opening ports

Now that your domain is pointing to your home server, you will need to open a few ports to make it accessible from the outside.

First, here are the list of ports you need:

  • 80 is the default HTTP port that you will need to later to obtain SSL certificates
  • 443 is the default HTTPS port that you will need to serve your corner

You will then need to allow those two ports in two places:

  • Your OS firewall, can be done through ufw usually
  • Your router’s settings (also called “port opening”, “port redirection” and a lot of other names), make sure the two ports are open on both TCP and UDP and pointing to your home server

Warning: in some countries, some ISPs will not allow you to open those two ports. It’s probably because you are behind something called CGNAT which allows ISPs to share the same IP address between multiple customers.
If this is the case call your ISP to get a proper IP that is not behind CGNAT. If this is not possible, you will have to either rent a server at a hosting provider, or get a tunnel.

Once this is done, congratulations, the external world can now reach you.

Web server shenanigans

Now, to serve your corner to the external world, you will need a web server. In this case, we will use Caddy which is really easy to use and takes care of HTTPS renewals for you.

Installing a web server

First, we’re gonna need to install Caddy, it goes a bit like this:

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddyCode language: Bash (bash)

After doing this, make sure Caddy is started and enabled (meaning it will start with the server) by doing:

sudo systemctl start caddy sudo systemctl enable caddy

Now, if you visit your domain, you will see the example Caddy page, meaning you are now online!

Editing the web server configuration

The configuration for Caddy is located at /etc/caddy/Caddyfile. You can see basic examples of it on the Caddy documentation website.

In our example, we’re gonna use the following simple configuration (as always, replace example.com by your domain):

https://example.com {
    root * /var/www/corner
    file_server
}
Code language: JavaScript (javascript)

Now, create the directory /var/www/corner and add your website files in there, for instance an index.html.

Restart Caddy using sudo systemctl restart caddy, wait a minute for the HTTPS certificate to be issued and you’re in business, you now have your own corner on the internet!

Have fun editing it and sharing it to your friends.
A blog post will be published later this month on how to create your own blog (for free) using GitLab pages!

Reading more

Here are some links to help you get started with your newfound internet home:

If you feel like I missed something, please do contact me and I will add it there.

The state of IPv6 in Resonite

Given sessions in Resonite are hosted by the players themselves, IPv6 is very useful in this context as there are no needs to battle with CGNAT or other network shenanigans and restrictions ISPs might put in place to save up on IP space.

As full native IPv6 support is currently being worked on (see GH-143 for a more in-depth status), some parts already do support it.

This is the case for:

The main website:

; <<>> DiG 9.20.4 <<>> AAAA resonite.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42354
;; flags: qr rd ra; QUERY: 1, ANSWER: 7, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;resonite.com.                  IN      AAAA

;; ANSWER SECTION:
resonite.com.           300     IN      AAAA    2606:4700:3030::6815:4001
resonite.com.           300     IN      AAAA    2606:4700:3030::6815:2001
resonite.com.           300     IN      AAAA    2606:4700:3030::6815:5001
resonite.com.           300     IN      AAAA    2606:4700:3030::6815:6001
resonite.com.           300     IN      AAAA    2606:4700:3030::6815:1001
resonite.com.           300     IN      AAAA    2606:4700:3030::6815:3001
resonite.com.           300     IN      AAAA    2606:4700:3030::6815:7001

;; Query time: 16 msec
;; SERVER: 192.168.1.1#53(192.168.1.1) (UDP)
;; WHEN: Tue Jan 14 17:44:41 EET 2025
;; MSG SIZE  rcvd: 237Code language: CSS (css)

The assets server:

; <<>> DiG 9.20.4 <<>> AAAA assets.resonite.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41689
;; flags: qr rd ra; QUERY: 1, ANSWER: 7, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;assets.resonite.com.           IN      AAAA

;; ANSWER SECTION:
assets.resonite.com.    300     IN      AAAA    2606:4700:3030::6815:4001
assets.resonite.com.    300     IN      AAAA    2606:4700:3030::6815:3001
assets.resonite.com.    300     IN      AAAA    2606:4700:3030::6815:7001
assets.resonite.com.    300     IN      AAAA    2606:4700:3030::6815:2001
assets.resonite.com.    300     IN      AAAA    2606:4700:3030::6815:1001
assets.resonite.com.    300     IN      AAAA    2606:4700:3030::6815:5001
assets.resonite.com.    300     IN      AAAA    2606:4700:3030::6815:6001

;; Query time: 16 msec
;; SERVER: 192.168.1.1#53(192.168.1.1) (UDP)
;; WHEN: Tue Jan 14 17:44:12 EET 2025
;; MSG SIZE  rcvd: 244Code language: CSS (css)

The API:

; <<>> DiG 9.20.4 <<>> AAAA api.resonite.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 1091
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;api.resonite.com.              IN      AAAA

;; ANSWER SECTION:
api.resonite.com.       91      IN      CNAME   skyfrost-api.azurewebsites.net.
skyfrost-api.azurewebsites.net. 120 IN  CNAME   waws-prod-am2-757.sip-v4andv6.azurewebsites.windows.net.
waws-prod-am2-757.sip-v4andv6.azurewebsites.windows.net. 91 IN AAAA 2603:1020:206:6::24

;; Query time: 26 msec
;; SERVER: 192.168.1.1#53(192.168.1.1) (UDP)
;; WHEN: Tue Jan 14 17:46:08 EET 2025
;; MSG SIZE  rcvd: 183Code language: CSS (css)

The site status.yellowdogman.com also has out-of-the box IPv6 support.

Some others have to be setup manually (as per GH-3232) via the hosts file or other user-run DNS servers, with:

  • wiki.resonite.com
  • metrics.yellowdogman.com
  • go.resonite.com

To access those domains over IPv6, you will need to amend your /etc/hosts or C:\Windows\System32\drivers\etc\hosts with the following:

[2a01:4f9:2b:2b4a::2] wiki.resonite.com
[2a01:4f9:2b:2b4a::2] metrics.yellowdogman.com
[2a01:4f9:2b:2b4a::2] go.resonite.com
Code language: CSS (css)

Edit: those AAAA records are now added!

Joining any session using a network string like lnl://[<IPv6 address>]:<port>/ is already supported, which only leaves the relays and bridges needing IPv6 support, a mod made by a community member already existing to solve this issue until official support is added.

In the end, I’m very confident that we will see full native IPv6 support land in Resonite this year, if not already in Q1, given this is actively being worked on.

Once those those two issues (relays and bridges + AAAA records missing) are addressed, the only thing missing IPv6 will be… the bug tracker, GitHub, which I already talked about in this article (spoiler, we ain’t seeing IPv6 from them anytime soon).

Also special thanks to ProbablePrime for looking into it!

GitHub and IPv6 in 2025

In this year 2025, the main GitHub domain still doesn’t serve over IPv6.

Some subdomains do have IPv6 though, just… not what you would expect.

For instance, avatars.githubusercontent.com:

; <<>> DiG 9.18.28-1~deb12u2-Debian <<>> AAAA avatars.githubusercontent.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18015
;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;avatars.githubusercontent.com. IN      AAAA

;; ANSWER SECTION:
avatars.githubusercontent.com. 159 IN   AAAA    2606:50c0:8002::154
avatars.githubusercontent.com. 159 IN   AAAA    2606:50c0:8001::154
avatars.githubusercontent.com. 159 IN   AAAA    2606:50c0:8003::154
avatars.githubusercontent.com. 159 IN   AAAA    2606:50c0:8000::154

;; Query time: 32 msec
;; SERVER: 2a09::#53(2a09::) (UDP)
;; WHEN: Wed Jan 08 04:55:56 UTC 2025
;; MSG SIZE  rcvd: 170Code language: CSS (css)

Whereas none of the other domains accessed when accessing github.com (named github.com, github.githubassets.com and alive.github.com) has IPv6.

The package registry ghcr.io as well doesn’t support IPv6:

; <<>> DiG 9.18.28-1~deb12u2-Debian <<>> AAAA ghcr.io
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 967
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
; EDE: 29: (Result from negative cache)
;; QUESTION SECTION:
;ghcr.io.                       IN      AAAA

;; AUTHORITY SECTION:
ghcr.io.                547     IN      SOA     ns-773.awsdns-32.net. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400

;; Query time: 32 msec
;; SERVER: 2a09::#53(2a09::) (UDP)
;; WHEN: Wed Jan 08 04:57:35 UTC 2025
;; MSG SIZE  rcvd: 152Code language: CSS (css)

And so does gist:

; <<>> DiG 9.18.28-1~deb12u2-Debian <<>> AAAA gist.github.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 52139
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;gist.github.com.               IN      AAAA

;; ANSWER SECTION:
gist.github.com.        1019    IN      CNAME   github.com.

;; AUTHORITY SECTION:
github.com.             182     IN      SOA     dns1.p08.nsone.net. hostmaster.nsone.net. 1656468023 43200 7200 1209600 3600

;; Query time: 32 msec
;; SERVER: 2a09::#53(2a09::) (UDP)
;; WHEN: Wed Jan 08 04:58:11 UTC 2025
;; MSG SIZE  rcvd: 123Code language: CSS (css)

So yeah, years later, always the same excuse, and no support at all.

Verkkokauppa.com DNS

Verkkokauppa.com is a chain of web and physical stores originating from (and limited to) Finland, akin of Amazon here if you will.

When trying to search for a USB-C computer mouse, IPvfoo told me that the website was accessible over IPv6, which I never noticed before.

I then queried the DNS server to see where it was hosted and:

Server:		127.0.0.53
Address:	127.0.0.53#53

Non-authoritative answer:
Name:	verkkokauppa.com
Address: 34.95.73.242Code language: CSS (css)

Weird, no v6 there, let’s try with the www subdomain now:

Server:		127.0.0.53
Address:	127.0.0.53#53

Non-authoritative answer:
www.verkkokauppa.com	canonical name = www.verkkokauppa.com.cdn.cloudflare.net.
Name:	www.verkkokauppa.com.cdn.cloudflare.net
Address: 104.18.33.183
Name:	www.verkkokauppa.com.cdn.cloudflare.net
Address: 172.64.154.73
Name:	www.verkkokauppa.com.cdn.cloudflare.net
Address: 2606:4700:4400::ac40:9a49
Name:	www.verkkokauppa.com.cdn.cloudflare.net
Address: 2606:4700:4400::6812:21b7Code language: PHP (php)

Turns out the www subdomain (which the apex redirects to) is proxied through CloudFlare, therefore offering IPv6 connectivity, and uses different nameservers.

The apex uses Netnod DNS (AS8674)

; <<>> DiG 9.20.4 <<>> NS verkkokauppa.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 35173
;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;verkkokauppa.com.		IN	NS

;; ANSWER SECTION:
verkkokauppa.com.	2986	IN	NS	nsu.dnsnode.net.
verkkokauppa.com.	2986	IN	NS	ns2.verkkokauppa.com.
verkkokauppa.com.	2986	IN	NS	nordic1.dnsnode.net.
verkkokauppa.com.	2986	IN	NS	ns1.verkkokauppa.com.
verkkokauppa.com.	2986	IN	NS	nsp.dnsnode.net.

;; Query time: 16 msec
;; SERVER: 192.168.1.1#53(192.168.1.1) (UDP)
;; WHEN: Sun Dec 29 17:41:39 EET 2024
;; MSG SIZE  rcvd: 150Code language: CSS (css)

The www subdomain uses CloudFlare (AS13335)

; <<>> DiG 9.20.4 <<>> NS www.verkkokauppa.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41779
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;www.verkkokauppa.com.		IN	NS

;; ANSWER SECTION:
www.verkkokauppa.com.	1020	IN	CNAME	www.verkkokauppa.com.cdn.cloudflare.net.

;; AUTHORITY SECTION:
cloudflare.net.		1800	IN	SOA	ns1.cloudflare.net. dns.cloudflare.com. 2359389931 10000 2400 604800 1800

;; Query time: 16 msec
;; SERVER: 192.168.1.1#53(192.168.1.1) (UDP)
;; WHEN: Sun Dec 29 17:42:04 EET 2024
;; MSG SIZE  rcvd: 157Code language: CSS (css)

Now, back to finding my USB-C mouse.

Setting up WireGuard tunnels from a BGP router

I recently re-started my BGP shenanigans, and with that, re-setup some VPNs using WireGuard for my personal machines.

I basically use those to whitelist connections to certain applications to only the prefix used by my machines.

The host machine runs Debian and BIRD, and the end devices are diverse from standard Linux machines, to Windows desktops, to iOS devices.

First, the BIRD configuration is pretty trivial, just adding a route for the prefix via lo:

route 2a12:4946:9900:dead::/64 via "lo";Code language: PHP (php)

I’m aware my subnet configurations can be sub-optimal, but I’m just running this for fun, not for it to be perfect¨.

Then, generating WireGuard keys on the host (the package wireguard-tools will need to be installed):

$ umask 077
$ wg genkey > privatekey
$ wg pubkey < privatekey > publickeyCode language: Bash (bash)

Now, the WireGuard host configuration is pretty trivial:

[Interface]
Address = 2a12:4946:9900:dead::1/128
ListenPort = 1337
PrivateKey = myVeryPrivateKey=

The key generation on the client follows the same procedure, if not easier via a GUI. The configuration itself looks like this:

[Interface]
PrivateKey = myVerySecretKey=
Address = 2a12:4946:9900:dead::1337/128

[Peer]
PublicKey = serverPubKey=
AllowedIPs = ::/1, 8000::/1
Endpoint = [2a12:4946:9900:dead::1]:1337
PersistentKeepalive = 30
Code language: JavaScript (javascript)

Note that I’m using ::/1 and 8000::/1 in AllowedIPs on Windows as setting it to ::/0 kills IPv4 connectivity (that is sadly still needed) and local connectivity to stuff like my storage array. On Linux, ::/0 works as expected, letting IPv4 through correctly.

Now, we can add a Peer section into the server’s configuration:

[Peer]
# PC Client
PublicKey = clientPubKey=
AllowedIPs = 2a12:4946:9900:dead::1337/128
Code language: PHP (php)

Now you should be all set and ready to bring up the tunnel on both ends.

On the server (assuming your configuration file is named tunnels.conf):

$ systemctl enable wg-quick@tunnels
$ systemctl start wg-quick@tunnelsCode language: Bash (bash)

And on the client using the same procedure, or just clicking the “Connect” button on the GUI client.

I’ve had some cases where this all of this alone isn’t enough, and had to add the prefixes to lo.

For instance:

$ ip -6 add 2a12:4946:9900:dead::/64 dev lo

And in /etc/network/interfaces:

iface lo inet6 static
        address 2a12:4946:9900:dead::/64
Code language: JavaScript (javascript)

Tho I will admit, I had more issues setting this up than I should have, and most configs would benefit from being re-written. Admittedly, I executed and documented this procedure while being extremely tired, which of course causes some issues.

But at least, this works, and can be very useful when I’m connected to networks not offering IPv6 connectivity as well.

Jae 2012-2025, CC BY-SA 4.0 unless stated otherwise.