=> 🏡 Home | Back to notes

Tailscale Sidecars

Last updated on 01 September 2024

If you've followed my blog or other notes, you'll be well aware of my extensive use of Tailscale [1] for nearly everything -- securing access to remote servers and for connecting to services across my local network.

=> 1

Tailscale DNS [2] is great for assigning DNS names to individual hosts, but my approach (usually) involves using Docker containers, with which I run several services on a single host. It is then fiddly (or impossible? I'm not sure) to use a single Tailscale "machine" to expose multiple services on the same host nicely via the DNS system (system).

=> 2

Early this year, Alex from Tailscale [3] wrote a blog post [4] describing how to nicely use Tailscale alongside Docker, such that each service can be treated as a separate machine for which DNS can be managed. Further, the inbuilt LetsEncrypt support allows each service to also be served with a valid TLS certificate.

=> 3 | 4

I now use these "sidecars" for pretty much every service I run.

Using Tailscale Sidecars

I use the OAuth approach (rather than Auth Keys), and below is my rough setup.

Tailscale Configuration

Login to your Tailscale dashboard and visit your Access Controls [5] page. Here, add in some config to allow you to use tags in your sidecars:

=> 5

{
    ...
    "tagOwners": {
        "tag:containers": ["autogroup:member"],
    },
    ...
}

On the OAuth clients [6] page, generate a new OAuth client (e.g. with a description of "sidecars"). Select "Read" and "Write" for the "Devices" scope. Add the tag we created earlier ("containers" above) using the dropdown. Click "Generate client".

=> 6

Make a note of the client ID and secret (be sure to keep the secret very safe).

Docker Configuration

We're now ready to connect a service using a sidecar. In my example I'll use Vaultwarden [7], but the same should apply mostly to any service running in Docker.

=> 7

In the directory for the service (i.e. the directory containing the docker-compose.yml file) create a new directory called tailscale-config. In this directory, create a new file called ts.json with the following content:

{
  "TCP": {
    "443": {
      "HTTPS": true
    }
  },
  "Web": {
    "${TS_CERT_DOMAIN}:443": {
      "Handlers": {
        "/": {
          "Proxy": "http://127.0.0.1:80"
        }
      }
    }
  },
  "AllowFunnel": {
    "${TS_CERT_DOMAIN}:443": false
  }
}

The file above tells Tailscale, when running, to expose port 80 on the service as HTTPS on port 443, and to proxy requests to the service on port 80. If your service uses a different port, modify the ts.json file accordingly (e.g. some services also comprise a database as well as a web service, and you want to make sure the web service is targeted).

Next, modify the docker-compose.yml file such that it looks like the following:

services:
  vaultwarden:
    image: vaultwarden/server:latest
    restart: unless-stopped
    volumes:
      - ./vaultwarden-data:/data
    network_mode: service:tailscale-sidecar

  tailscale-sidecar:
    image: tailscale/tailscale:latest
    hostname: vaultwarden
    environment:
      - TS_AUTHKEY=tskey-client-ABC123
      - TS_EXTRA_ARGS=--advertise-tags=tag:containers
      - TS_SERVE_CONFIG=/config/ts.json
      - TS_STATE_DIR=/var/lib/tailscale
    volumes:
      - ./tailscale-data:/var/lib/tailscale
      - ./tailscale-config:/config
      - /dev/net/tun:/dev/net/tun
    cap_add:
      - net_admin
      - sys_module
    restart: unless-stopped

Replace the TS_AUTHKEY value with the client secret you generated earlier. Replace the hostname value with the DNS name you want to use for the service (which will be the subdomain of your tailnet the service will be available at).

Bring up the service with docker compose up -d and give it a minute or so to set up the DNS and provision certificates. Then (in this case) you could visit https://vaultwarden.tailnet-domain.ts.net to view the service.

It's now much easier (and more secure) to expose and access the services across your tailnet, and from any of your devices.

=> Back to notes

Proxy Information
Original URL
gemini://wilw.capsule.town/notes/tailscale-sidecars.gmi
Status Code
Success (20)
Meta
text/gemini;lang=en-GB
Capsule Response Time
251.081524 milliseconds
Gemini-to-HTML Time
1.247513 milliseconds

This content has been proxied by September (ba2dc).