Skip to content

Instantly share code, notes, and snippets.

@ryuheechul
Last active November 4, 2024 04:56
Show Gist options
  • Save ryuheechul/1762a5b5e7c2a84a143c0c24168c36a5 to your computer and use it in GitHub Desktop.
Save ryuheechul/1762a5b5e7c2a84a143c0c24168c36a5 to your computer and use it in GitHub Desktop.
Zerotier DNS Client Setup on Linux (with systemd-resolved)

This is about the script originally written for https://github.com/ryuheechul/dotfiles/commit/1c5795d4e69ad7ad8c191e3e96fe43155522739a#diff-4f848b5499b37dbeb494a252ab49d56831abb5b12dbd362ab5182f509f7ad97b

This Script Will

the script is pretty short and relatively simple, so I recommend you to read it but the gist is below

  1. get necessary details regarding (your already configured) zerotier network interface and running DNS server
  2. configure DNS routing via resolvectl for systemd-resolved with the parse information from the previous step
  3. (now resolving to FQDN (e.g. host device.home.arpa) should work as well as search domain (e.g. host device)
  4. (unfortunately the change doesn't last after a reboot - so I'm looking into a way to resolve this)

Because

Make Sure

  • that you already have setup zerotier network on your linux machine
    • test via sudo zerotier-cli info and sudo zerotier-cli listnetworks
    • if you are using NixOS see this as an example
  • that you are running zeronsd on one of your zerotier networked node
    • you can get some idea on how to do that at https://github.com/zerotier/zeronsd/blob/main/docs/quickstart.md
      • although this include some other information (like client configuration) too
    • (and this will also not only run the DNS exposed on 53 port but also advertizes or tells that to zerotier central as well)
      • you would need to not block 53 from outside (via firewall) though in order for other machines to reach that port
    • if you are using NixOS see this as an example
  • that you are using systemd-resolved because this script depends on it
    • (and maybe you would want it)
    • test via resolvectl status
    • if you are not using systemd-resolved there seem to be another option
      • (although I have no experience with it)
  • that you have jq installed
  • now you can run it ans enjoy the "Magic DNS" experience (assuming it worked)!
#!/usr/bin/env bash
#
# originally written for https://github.com/ryuheechul/dotfiles/commit/1c5795d4e69ad7ad8c191e3e96fe43155522739a#diff-4f848b5499b37dbeb494a252ab49d56831abb5b12dbd362ab5182f509f7ad97b
#
# # Dependencies of this script:
# - bash
# - zerotier-cli
# - resolvectl (that is configured with systemd-resolved)
# - jq
#
# # How to run:
# simply run `./zerotier-dns-client.sh` and it should do the job
#
# # How to test
# lookup domain name via single label domain that would match your search domain
# e.g. `host mydevice # or "nslookup" or "dig +search" instead of "host"` and resolved in mydevice.home.arpa with an IP address
# (assuming you have `mydevice` connected to the zerotier network and using `home.arpa` as domain)
#
# # How to undo
# `sudo resolvectl revert [interface]`
echo '[info] going to run `sudo zerotier-cli listnetworks -j` to parse necessary network information'
networks=$(sudo zerotier-cli listnetworks -j)
if ! echo "${networks}" | jq > /dev/null 2>&1; then
echo "[error] the result below doesn't seem to be in the right format"
echo "${networks}"
exit 1
fi
# thanks to https://stackoverflow.com/a/33952539/1570165
echo $networks | jq -c '.[]' | while read network; do
domain="$(echo $network | jq -r '.dns.domain')"
dns_list="$(echo $network | jq -r '.dns.servers | join(" ")')"
interface="$(echo $network | jq -r '.portDeviceName')"
echo "[info] detected values: \$domain ($domain), \$dns_list ($dns_list), \$interface ($interface)"
if test -z "$domain" || test -z "$dns_list" || test -z "$interface"; then
echo "[warn] Any of the values above might be wrong so skpping this interface."
else
echo "[info] before the change:"
resolvectl status $interface
echo "[info] configuring with ..."
set -x
sudo resolvectl revert $interface
sudo resolvectl dns $interface $dns_list
sudo resolvectl default-route $interface "false"
sudo resolvectl domain $interface $domain
{ set +x; } 2>/dev/null # thanks to https://stackoverflow.com/a/19226038/1570165
echo "[info] after change:"
set -x
resolvectl status $interface
{ set +x; } 2>/dev/null
fi
done
@ryuheechul
Copy link
Author

ryuheechul commented Oct 30, 2024

^ that didn't work as I hoped as the global setting is different from individual interface.

And now I automated via systemd service and here is the code. Which basically generates and installs a systemd service unit like below.

# systemctl cat zerotier-dns-resolved.service
[Unit]
After=zerotierone.service

[Service]
Environment="LOCALE_ARCHIVE=/nix/store/i67wkd0y4yg008hsqscj4pxwvahv6yzq-glibc-locales-2.40-36/lib/locale/locale-archive"
Environment="PATH=/nix/store/anifmkvq4a5m2glk5vd92p2vs5a9msh8-zerotierone-1.14.0/bin:/nix/store/v0azfsqv58llx12jal5ngx3mj0rj7km6-jq-1.7.1-bin/bin:/nix/store/ph44jcx3ddmlwh394mh1wb7f1qigxqb1-coreutils-9.5/bin:/nix/store/yb8icljkwhk5lla4nci3myndq2m4ywly-findutils-4.10.0/bin:/nix/store/lvnwdmnjm7nvaq0a3vhvvn46iy4ql7gr-gnugrep-3.11/bin:/nix/store/yd9vbyhbxx62j0cyhd6v0iacz11nxpvc-gnused-4.9/bin:/nix/store/xg6f0c5pchmc2jq84s4np19j1rnn90mn-systemd-256.6/bin:/nix/store/anifmkvq4a5m2glk5vd92p2vs5a9msh8-zerotierone-1.14.0/sbin:/nix/store/v0azfsqv58llx12jal5ngx3mj0rj7km6-jq-1.7.1-bin/sbin:/nix/store/ph44jcx3ddmlwh394mh1wb7f1qigxqb1-coreutils-9.5/sbin:/nix/store/yb8icljkwhk5lla4nci3myndq2m4ywly-findutils-4.10.0/sbin:/nix/store/lvnwdmnjm7nvaq0a3vhvvn46iy4ql7gr-gnugrep-3.11/sbin:/nix/store/yd9vbyhbxx62j0cyhd6v0iacz11nxpvc-gnused-4.9/sbin:/nix/store/xg6f0c5pchmc2jq84s4np19j1rnn90mn-systemd-256.6/sbin"
Environment="TZDIR=/nix/store/4isk5ynprzbw7ddbj5rq5ag9a6xbl66f-tzdata-2024b/share/zoneinfo"
ExecStart=/nix/store/9cqgh168bhpzbdwqspvgwj83p02m2wa6-unit-script-zerotier-dns-resolved-start/bin/zerotier-dns-resolved-start
Type=oneshot
Restart=on-failure # since zerotier (local) server might not be ready that quickly

[Install]
WantedBy=multi-user.target

# $LOCALE_ARCHIVE and $TZDIR was provided by default via nix (not important and you can probably just ignore it in this case).
# Important bit is the $PATH which allows my script to access `resolvectl` and `zerotier-cli`, `jq` binaries which are dependencies. 

Although the service file above generated for NixOS, it's a simple service that you can easily mimic on your non-nix system.

/nix/store/9cqg....dns-resolved-start/bin/zerotier-dns-resolved-start's content is same as this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment