In this article I describe the steps I took for running a dockerised version of the zerotier-one client on my Synology NAS DSM 7.0, using docker.

I use ZeroTier One for joining together some/most of my devices, be them a local laptop, my mobile phone, or a remote VPS somewhere.

It’s a useful technology that provides a safe and secure link between those devices, so I can access services hosted on them without exposing those same services to the outside world.

I use it to run a personal wiki, as well as a few other web applications that are for my own usage only.

I wanted my NAS to also be available inside that network, so I could share its docker-based private registry across my fleet of systems, as well as being able to back all those systems to the nas via a simple tar and rsync.

My Synology NAS runs the DSM 7.0 beta, and the ZeroTier One package, built for version 6, no longer works in it. Fortunately, DSM 7.0 has a Docker package which works, and with some sweat and tears it’s possible to make it run properly.

Firstly, we need to build the Docker container for zerotier-one. I dislike using other people’s docker images, so I built my own using this Dockerfile:

FROM debian:buster-slim
LABEL com.darkpan.github-check github.com/zerotier/ZeroTierOne ZT_VERSION
ENV ZT_VERSION 1.6.5
RUN echo "deb http://deb.debian.org/debian buster main non-free contrib" > /etc/apt/sources.list
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN apt-get update && apt-get install -y \
        ca-certificates \
        curl \
        gnupg2 \
    --no-install-recommends \
    && curl -sSL 'https://download.zerotier.com/contact%40zerotier.com.gpg' | apt-key add - \
    && echo "deb http://download.zerotier.com/debian/buster buster main" > /etc/apt/sources.list.d/zerotier-one.list \
    && apt-get update && apt-get install -y \
        zerotier-one \
    --no-install-recommends \
    && apt-get purge --auto-remove -y curl gnupg2 \
    && rm -rf /var/lib/apt/lists/*
ENTRYPOINT ["/bin/bash"]

I created a ~/zt/ directory on my NAS where I put the above Dockerfile.

I then built a local “zerotier-one” image using it, i.e.:

cd ~/zt
sudo docker build -t zerotier-one .

Next up, running it. The service requires a few things: firstly, and most importantly, a TUN/TAP device.

This isn’t available by default during the boot, so I had to ensure the proper kernel module is available and started after every reboot.

I’ve created a /usr/local/etc/rc.d/tun.sh file, which will get run after reboot, containing the following:

#!/bin/sh
insmod /lib/modules/tun.ko

Save the file, then chmod +x /usr/local/etc/rc.d/tun.sh and restart the NAS to ensure it’s ran.

Once it has, the following should display the “tun” module as being loaded:

$ lsmod | grep 'tun\s'
tun                    18941  2

Next up, the “launch” script. I use this once, and then the container is always restarted after a reboot:

#!/bin/bash
set -e
ZT_NAME=zerotier-one
ZT_LOCAL_VOLUME="$HOME/zt/zerotier-one"
ZT_IMAGE=zerotier-one
mkdir -p "$ZT_LOCAL_VOLUME"
docker stop "$ZT_NAME"
docker rm "$ZT_NAME"
set +e
# Create the necessary file structure for /dev/net/tun
# Load the tun module if not already loaded
if ( !(lsmod | grep -q "^tun\s") ); then
  insmod /lib/modules/tun.ko
fi
mkdir -m 755 /dev/net
mknod /dev/net/tun c 10 200
chmod 0666 /dev/net/tun
state=$(docker inspect --format "{{.State.Running}}" "$ZT_NAME" 2>/dev/null)
if [[ "$state" == "true" ]]; then
    docker stop "$ZT_NAME" && docker rm "$ZT_NAME" && $0 "$@"
    exit $?
fi
set -e
if [[ -n "$1" ]]; then
    docker run -it --rm \
        --device=/dev/net/tun --net host --cap-add=NET_ADMIN --cap-add=SYS_RAWIO \
        --name "$ZT_NAME" \
        --memory='128MB' \
        --entrypoint /bin/bash \
        -v "$ZT_LOCAL_VOLUME:/var/lib/zerotier-one" \
        "$ZT_IMAGE"
    exit 0
fi
exec docker run -d --restart=always \
    --device=/dev/net/tun --net host --cap-add=NET_ADMIN --cap-add=SYS_RAWIO \
    --name "$ZT_NAME" \
    --memory='128MB' \
    --entrypoint zerotier-one \
    -v "$ZT_LOCAL_VOLUME:/var/lib/zerotier-one" \
    "$ZT_IMAGE"

Running it (sudo ./launch) just starts the container, but doesn’t yet join you to any network.

To do so, “enter” the container and join the network:

$ sudo docker exec -it zerotier-one /bin/bash
# zerotier-cli join f0f0f0f0f0f0f0

If all goes well, you should be able to see the network as having properly been joined:

# zerotier-cli listnetworks
200 listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>
200 listnetworks f0f0f0f0f0f0f0 blah f0:f0:f0:f0:f0 OK PRIVATE ztrta2fcse 999.88.77.66/16

And that’s it. Enjoy your NAS being part of your ZeroTier one network.