Ouroboros

Ouroboros is a simple but effective private VPN manager for self hosting.

It provides a means for users of a Tailnet managed by Headscale to manage their own devices, similar to the official Tailscale dashboard.

System admins create Headscale users, and manually match them up to GitHub user IDs for authentication. Then, users can log in and manage their devices, and register devices seamlessly.

Supported features:

Not supported features:

Demos

The easiest way to show how it works is demo videos:

Setup

Here are instructions to configure Headscale 0.23.0 with Ouroboros 0.3.1 with docker-compose and Caddy.

First, check instructions for setting up a GitHub app and more in depth explanation of the config keys here.

First, set up your docker containers as so:

caddy:
  image: caddy
  ports:
    - 80:80
    - 443:443
    - 443:443/udp
  volumes:
    - ./wherever/caddy/Caddyfile:/etc/caddy/Caddyfile
    - ./wherever/caddy/data:/data
    - ./wherever/caddy/config:/config
  cap_add: [NET_ADMIN]

headscale:
  image: headscale/headscale:0.23.0
  ports: [3478:3478] # DERP STUN
  command: serve
  volumes:
    - ./wherever/headscale:/etc/headscale

ouroboros:
  image: yellosink/ouroboros:0.3.1
  environment:
   - HS_IS_REMOTE=true
   - HS_ADDRESS=my.server.com:443
   - HS_API_KEY=xxxxxxx
   - HS_LOGIN_URL=my.server.com
   - GH_CLIENT_ID=xxxxxxx
   - GH_CLIENT_SECRET=xxxxxx
   - 'USER_MAP={ "12345678": "john" }'

Copy the config file from the headscale repo, and make the following change:

# we need this as caddy is supplying our TLS, else we can't access it
# we aren't exposing this port outside of docker, and caddy will secure it, so this is safe
grpc_allow_insecure: true

Setup your caddyfile as so:

my.server.com {
  @grpc protocol grpc
  handle @grpc {
    reverse_proxy h2c://headscale:50443
  }

  redir /ouroboros /ouroboros/ 301
  redir /          /ouroboros/ 301

  reverse_proxy /ouroboros/* ouroboros:8080
  reverse_proxy /register/* ouroboros:8080

  reverse_proxy headscale:8080
}

Then generate an API key with docker compose exec headscale headscale apikeys --create and put it in the HS_API_KEY env var.

Now ensure you’ve created all the users you need to exist, then you’re done!