ZeroTier

Introduction

Connecting remotely to computers remains a challenge. Industry’s response to that is VPN. ZeroTier is a popular VPN platform for creating secure private networks. It helps safely connect to your machines from anywhere in the world. It helps safely access servers in the cloud. It enables intranet applications for external users. Last-but-not-least, it can be used to provide remote assistance to family and friends, while keeping them safe from bad wolves out there.

Best of all, ZeroTier is free for up to 50 connected machines. Ability to manage it with REST API and scripts makes it particularly interesting for advanced users.

What does it do?

Brief introduction into what VPN is about and how it can be useful.

TL; DR; If you already know what VPN is, you can go straight to the tutorial.

What is VPN, is becoming increasingly unclear these days. For many, VPN is something used for anonymous file sharing, or remedy for overly restrictive DRM protections of streaming platforms. But is that all? Far from it!

Experienced gamers surely know Hamachi. Many years ago it was used to play multi-user LAN games over the internet. Hamachi, also a VPN platform, allowed creation of local networks over internet.

VPN’s primary use is creation of safe private networks over inherently unsafe medium such as public internet. All other things, such as providing level of privacy or fooling DRM restrictions - those are just side-effects of this primary function.

vpn|https://en.wikipedia.org/wiki/Virtual_private_network#/media/File:Virtual_Private_Network_overview.svg|CC BY-SA 4.0

Whenever I need access my home PC while in travel, I face big dilemma. My computer has to run software which enables such connection. It has to run remote-access daemon such as Windows RDP or VNC server. Router ports have to be opened. Firewall rules have to be relaxed. It introduces risks and attack vectors, which rogue parties can use to gain access.

VPN server steps in as intermediate trusted party. Network members connect to VPN server using secure and encrypted connection. Because connection is initiated by the client, there is no need to open ports or run anything listening permanently. VPN server assigns connected clients with local-range IP addresses. The server routes all traffic between machines, as they talk to each other, using secure encrypted channel. Simple, yet secure and fast.

From user’s perspective this feels like good old LAN. Safe, secure and fully isolated from outside access. Network members can be scattered all over the world yet they act as if connected to the same local network.

Why ZeroTier?

If you have Linux server at your disposal, it’s actually not so hard to run your own VPN. Then, there are many commercial VPN products. Many of us use corporate VPN for remote work. Even Hamachi is still there, only with five-computers restriction on free plan. ZeroTier is yet another such platform.

I like ZeroTier for its spartan simplicity. For me, it’s been the easiest VPN platform to set up. Equally important is their generous free plan, which allows connecting up to 50 computers. Additional benefit for computer geeks is ZeroTier API. The platform can be managed and controlled using REST API. This allows scripting and automation of connections, deployments, tests etc.

Installation and Configuration

To create private network using ZeroTier, you need to:

Your first virtual private network has been created.

zerotier-networks

The UI is sturdy and simple. I hope it stays that way.

You will need the assigned network identifier to connect clients to the network. As you can see, you can create multiple networks. This can be very useful. For example, one network for work, another for family or friends, so that your uncle won’t attempt logging in to your VPS.

Now it’s time to connect your devices - computers, tablets etc. Install ZeroTier client on each one of them. All popular platforms are supported - Linux, MacOS, Windows, Android, iOS, even FreeBSD. Find the software on https://www.zerotier.com/download/

Installation on Linux is easy:

curl -s https://install.zerotier.com | sudo bash

Once installed, use zerotier-cli to connect. On other platforms there’s usually some GUI. Check documentation for details. You will need the identifier of network which you’ve just created:

sudo zerotier-cli join <networkid>

You should see 200 join OK. Your computer has joined the network. But it still needs to be authorized to use the network. Otherwise it wouldn’t be quite secure ;-)

Go back to https://my.zerotier.com and click on the listed network. You will see the details of the network. Scroll down, to find list of connected clients. Your just connected client should be there, for example:

zerotier-client

Check the checkbox on the left, and the client is now authorized to use the network. Give the client unique friendly name, such as home-pc or mom. Wait a few seconds, and local IP address should be assigned to the connected client. This is the address to use, when connecting to this computer from another machine on the same private network.

Repeat the same on every other computer or device, which you want to be part of the network. It can be your VPS, your family computers, RaspberryPI in the basement, iPad, anything you want to access securely. All connected machines can run different operating systems, yet they will be able to connect to each other using ZeroTier.

You have built your first virtual private network.

What can I do with it?

Take note of IP address of computer you want to connect to. Now ping it, and it responds. The thrill of it! From here, anything is possible, such as:

Remote desktop access

Assume that your computers run Windows. Enable Remote Desktop on them. Now you can connect to your Windows desktops using RDP client. Enter the IP address assigned by ZeroTier and connect. Safely, securely, even if you are on another continent. No RDP ports had to be opened on internet router. The computer remains safe from login attempts from the outside. Similarly, on Linux machines you can install VNC server and enjoy safe access to desktop sessions.

Shell access

On Linux and Mac machines I usually enable SSH a.k.a. secure shell. Now I can remotely log on, transfer files and execute commands using ssh and scp straight from terminal. All it takes on Linux is to run

sudo apt install openssh-server
sudo ufw enable ssh

and your machine is ready for incoming SSH connections.

Intranet applications

You can run intranet web applications on your connected computers. For example, team wiki or department discussion forum. All members of your private network can access it safely, without inherent risk of exposing internal website to outside world. Quite useful for businesses big and small.

Networked devices

You can access network printers and other devices. Anything that can be done on LAN, can be done with VPN, no matter where you are.

Enjoy!

ZeroTier API

Time for the real deal. ZeroTier network can be managed using REST API, documented at https://docs.zerotier.com/central/v1/. Also ZeroTier client software, running on your computer, can be controlled using REST API described at https://docs.zerotier.com/service/v1. ZeroTier GUI and CLI themselves use this API to do their job.

This opens amazing possibilities. You can automate your processes, create your own UI for managing the network, pull information about ZeroTier nodes straight into your own applications etc.

Myself, I use ZeroTier API to simplify access to my family network. Being “the computer guy” of the family, I often need to connect and help. I use SSH and VNC connections over ZeroTier network to run periodic checks and updates, resolve problems etc.

To connect to mom’s computer using ssh (yes, she runs Linux) I need her IP address. I can look it up on ZeroTier account page. But it would be nice if ZeroTier helped me figure it out automatically. Ideally, I’d like to run a command like this:

zerotier mom

and this would:

  • Determine whether my ZeroTier network is available
  • Determine whether her computer is online
  • Determine its IP address
  • Connect using ssh
  • Tell me if anything went wrong

This is where ZeroTier API comes handy.

ZeroTier API token

First, you need API token associated with your account. Log on to https://my.zerotier.com. Go to account settings and create new token. Store the token safely, as it won’t be displayed again! If you lose it, you will have to create a new token.

zerotier-api-token

The token has to be passed with API requests in Authorization header. For example, if your token is 88205430e4bd, you need to pass the following HTTP header with each ZeroTier API request.

Authorization: bearer 88205430e4bd

Executing API requests

Let’s start with ZeroTier network status. To retrieve it, use the following endpoint:

https://my.zerotier.com/api/v1/status

Use curl and jq to execute API request and parse the received JSON response:

curl -s -X GET -H "Authorization: bearer 88205430e4bd" "https://my.zerotier.com/api/v1/status" | jq

Example output:

{
  "online": true,
  "apiVersion": "1",
  "user": {
    "id": "80e468ce-c8a7-41da-a068-37c27af38370",
    "type": "User",
    "displayName": "Tomasz Waraksa",
    "email": "[email protected]"
  }
}

Use jq filters to extract specific fields from the response. For example, to extract API version, user name and e-mail, use the following script:

TOKEN="88205430e4bd"
ENDPOINT="https://my.zerotier.com/api/v1/status"
STATUS=$(curl -s -X GET -H "Authorization: bearer $TOKEN" $ENDPOINT)
MESSAGE=echo $STATUS | jq -r '. | "API: v.\(.apiVersion)\nUser: \(.user.displayName)\nE-mail: \(.user.email)"'
echo $MESSAGE

Everything you can see and do within ZeroTier website, you can do with API. This includes:

  • Listing your networks
  • Listing clients connected to networks
  • Determining IP addresses of connected clients

This is exactly what I needed for building my zerotier mom command.

Retrieving network status and client details

I wrote bash script which retrieves information about my ZeroTier network and connected clients. Save it in zerotier-status file, remember to add execute permission.

Run the script without any parameters to display status of the API, your default network and list of clients connected to it.

zerotier-status

Run it with client name, to retrieve current IP address of the specified client.

zerotier-status home-pc

Script code:

#!/usr/bin/env bash
# zerotier-status
# bash script for retrieving status of ZeroTier network and connected clients

ZEROTIER_API="https://my.zerotier.com/api/v1"
ZEROTIER_API_TOKEN="your-api-token"

# Execute ZeroTier API request
# $1 endpoint
request () {
  local endpoint="$1"
  local response=$(curl -s -X GET -H "Authorization: bearer $ZEROTIER_API_TOKEN" "$ZEROTIER_API/$endpoint" | jq)
  echo $response
}

# Retrieves ZeroTier API status
status () {
  request "status" | jq -r '. | "API: v.\(.apiVersion)\nUser: \(.user.displayName)\n      \(.user.email)"'
}

# Retrieves list of networks
networks () {
  local response=$(request "network")
  echo "$response" | jq -r '.[] | "\(.id) \(.config.name)"'
}

# Retrieves identifier of the first network
network () {
  local response=$(request "network")
  local network_id=$(echo "$response" | jq -r '.[0] | "\(.id)"')
  echo $network_id
}

# Retrieves list of clients currently logged into the specified network
# $1 Network identifier
clients () {
  local network_id="$1"
  # request "network/$network_id/member" | jq
  request "network/$network_id/member" | jq -r '.[] | select(.physicalAddress | . != null) | "\(.name) \(.config.ipAssignments[0])"'
}

# Retrieves IP address of a client connected to the specified network
# $1 Network identifier
# $2 Client name
ipOfClient () {
  local network_id="$1"
  local name="$2"
  local all=$(clients "$network_id" "$name")
  local found=$(echo "$all" | grep "$name")
  local arr=($found)
  echo ${arr[1]}
}

# If run with client name, returns client's IP address,
# otherwise shows the whereabouts of your ZeroTier private network.
CLIENT="$1"
if [ "$CLIENT" == "" ]; then
  echo "ZeroTier"
  echo "-------------------------------"
  status
  echo

  echo "Network"
  echo "-------------------------------"
  NETWORK_ID=$(network)
  echo "$NETWORK_ID"
  echo

  echo "Clients"
  echo "-------------------------------"
  clients "$NETWORK_ID"
  echo

else
   NETWORK_ID=$(network)
   ipOfClient "$NETWORK_ID" "$CLIENT"
fi

Connecting to ZeroTier clients

I’ve created another script, named zerotier, which uses the previous script to determine client IP and connect using ssh. Place them both somewhere in path. Connecting to home PC is now as easy as:

zerotier home-pc

This will connect to home PC using the account I’m currently logged in with. You can also connect using different account present on the remote machine, for example:

zerotier home-pc tomasz

The script can also be used to display network status and restart ZeroTier daemon, if there’s any problem:

zerotier status
zerotier restart

Run the script without parameters to see help. Enjoy!

#!/usr/bin/env bash
# zerotier
# bash script for connecting to ZeroTier clients using ssh

# ZeroTier client to connect or command to execute is passed as first parameter
CLIENT="$1"
COMMAND="$1"
# User account to login as is passed as second parameter.
# If not specified, current account is used, unless
# you have defined below another default account for that machine
LOGIN="$2"

# Default accounts used to log into clients
declare -A USERS
USERS=(
    ["jan-pc"]="jan"
    ["mary-pc"]="mary"
    ["bob-pc"]="bob"
)

if [ "$CLIENT" == "" ]; then
  echo "Connects to ZeroTier client in your private network using SSH"
  echo
  echo "Syntax:"
  echo
  echo "  zerotier <client> <user>"
  echo "  zerotier <command>"
  echo
  echo "  client:    Name of ZeroTier network client to log into"
  echo "  user:      Account to login as"
  echo "  command:   Command to execute:"
  echo "    restart  Restarts ZeroTier daemon"
  echo "    status   Displays status of ZeroTier network"
  echo

elif [ "$COMMAND" == "restart" ]; then
  echo "Restarting ZeroTier ..."
  sudo systemctl restart zerotier-one
  echo "Done."

elif [ "$COMMAND" == "status" ]; then
  zerotier-status

else
  # Determine account to log in as.
  # It can be passed explicitly as second parameter.
  # If not present, check if defined above.
  # Finally, if none defined, assume currently logged in user.
  [[ "$LOGIN" == "" ]] && LOGIN="${USERS[$CLIENT]}"
  LOGIN="${LOGIN:-$USER}"

  echo "Connecting to ZeroTier client $CLIENT ..."
  echo "Fetching IP address ..."
  IP=$(zerotier-status "$CLIENT")
  if [ "$IP" == "" ]; then
    echo "Client $CLIENT is invalid or currently not connected"
  else
    echo "Establishing SSH connection $LOGIN@$IP ..."
    ssh [email protected]$IP
  fi
fi