Blog|November 04, 2022

Virtual private services with tsnet

Author

Xe IasoXe Iaso
Branded artwork in greyscale

Tailscale lets you connect to your computers from anywhere in the world. We call this setup a virtual private network. Any device on the tailnet (our term for a Tailscale network) can connect directly to any other device on the tailnet. When you do this you can access your NAS from anywhere, RDP (Remote Desktop Protocol) into your gaming PC in Canada to check messages from the Canadian tax authority, or even SSH into production with Tailscale SSH. Everything will just work.

This isn’t limited to your computers, phones, and servers, though. You can use Tailscale as a library in Go programs to allow them to connect to your tailnet as though it were a separate computer. You can also use Tailscale to run multiple services with different confidentiality levels on the same machine. This will allow you to separate support tooling from data analytics without having to run them on multiple servers or virtual machines. The only way that the tools could be exposed is over Tailscale — meaning that there’s no way to get into them from outside your tailnet.

Today I’m going to explain more about how you can use tsnet to make your internal services easier to run, access, and secure by transforming them into virtual private services on your tailnet. By the end of this post you should have an understanding of what virtual private services are, how they benefit you, and how to write one using Tailscale as a library. Finally, I will give you some ideas for how you could take this one step further.

Virtual private services

When you add a laptop or phone to your tailnet, Tailscale assigns it its own IP address and DNS name. This allows you to connect over Tailscale’s encrypted tunnel so you can access your NAS from the coffee shop to grab whatever files you need. This also allows you to request an HTTPS certificate from Let’s Encrypt so you can run whatever services you want over HTTPS.

However, this only lets you get one DNS name and IP address per system. Currently, running multiple services with separate domain names on the same system is impossible with Tailscale, but there is a workaround. Using tsnet, you can embed Tailscale as a library in an existing Go program. tsnet takes all of the goodness of Tailscale and lets you access it all from userspace instead of having to wade through the nightmare of configuring multiple VPN connections on the same machines.

When you start a virtual private service with tsnet, your Go program will get its own IP address, DNS name, and the ability to grab its own HTTPS certificate. You can ping the service instead of the server it’s on. You can listen on privileged ports like the HTTP and HTTPS ports without having to run your service as root. You can use ACL tags and groups to separate out access to that service individually. Finally, you can run multiple of these services on the same machine without having to have root permissions or do anything beyond running the programs on the machines. You don’t even need to expose them anywhere else besides over Tailscale. All of this happens in the same OS process: All the magic of Tailscale becomes a library like any other, allowing you to create virtual private services for your team.

How to make your own hello server

I’m going to show you how to create a minimal “hello” service that will let any connecting user know who Tailscale thinks they are. To start, install the latest version of the Go programming language and restart your terminal program. Next, create a folder for the code with a command such as this:

mkdir -p ~/code/whoami
cd ~/code/whoami

Then create a new Go project with this command:

go mod init github.com/your-username/whoami

Install tsnet with this command:

go get tailscale.com/tsnet

Then make a main.go file with the following in it:


package main

import (
	"flag"
	"fmt"
	"html"
	"log"
	"net/http"
	"strings"

	"tailscale.com/tsnet"