Ryan Malloy 5abc3c87b2 OIDC groups implementation
- Add Groups field to User struct with JSON storage
- Include GetGroups() and SetGroups() helper methods
- Extract groups from OIDC claims in FromClaim()
- Add database migration 202509161200 for groups column
- Update config-example.yaml with groups scope
- Add comprehensive documentation and testing
2026-05-21 17:55:31 -06:00

7.5 KiB

Headscale Docker Development Environment

This directory contains a complete Docker Compose setup for running Headscale with Tailscale clients in a local development environment.

Overview

This setup includes:

  • Headscale server: The control plane server
  • Two Tailscale clients: Simulated nodes that connect through Headscale
  • Test web server: Optional nginx server for connectivity testing
  • Helper scripts: Automated setup and management tools

Architecture

┌─────────────────────────────────────────┐
│         Docker Network (10.99.0.0/24)   │
├─────────────────────────────────────────┤
│                                         │
│  ┌──────────────┐                      │
│  │  Headscale   │ 10.99.0.10           │
│  │   Server     │ :8080 (API)          │
│  │              │ :9090 (Metrics)      │
│  │              │ :50443 (gRPC)        │
│  └──────┬───────┘                      │
│         │                               │
│    ┌────┴────┬─────────┐               │
│    │         │         │               │
│ ┌──▼───┐ ┌──▼───┐ ┌──▼───┐           │
│ │Client│ │Client│ │ Web  │            │
│ │  1   │ │  2   │ │Server│            │
│ │.0.21 │ │.0.22 │ │.0.30 │            │
│ └──────┘ └──────┘ └──────┘            │
│                                         │
│  Tailscale Network (100.64.0.0/16)     │
└─────────────────────────────────────────┘

Quick Start

1. Start the environment

# Start all services and automatically set up users/keys
make up

# Or manually:
docker compose up -d
make setup

2. Check status

# View all nodes
make status

# Watch logs
make logs

3. Test connectivity

# Test ping between clients
make ping-test

# Test web server access
make test-web

Verified Working Setup

This environment has been tested and verified working with:

  • Headscale: headscale/headscale:latest (as of September 2024)
  • Tailscale: tailscale/tailscale:latest
  • Network: Docker bridge network 10.99.0.0/24
  • Tailscale Network: 100.64.0.0/10 with IPv6 fd7a:115c:a1e0::/48
  • Port Mapping: Host port 8180 → Container port 8080 (Headscale API)

Test Results

  • Headscale server starts and serves on port 8180
  • Both Tailscale clients register automatically with pre-auth keys
  • Clients receive IP addresses: 100.64.0.1 and 100.64.0.2
  • Bidirectional ping works between clients
  • tailscale status shows both nodes online
  • Traffic flows through encrypted Tailscale tunnel

Key Insights from Testing

Network Architecture: This setup demonstrates two distinct networking layers:

  1. Docker Bridge Network (10.99.0.0/24) - Physical layer for container communication
  2. Tailscale Overlay Network (100.64.0.0/10) - Encrypted VPN tunnel for secure communication

When clients ping each other, the traffic uses Tailscale IPs (100.64.x.x) but actually travels through the Docker network infrastructure, demonstrating how Tailscale creates an encrypted overlay on top of existing network infrastructure.

Available Commands

Run make help to see all available commands:

  • make up - Start all services with automatic setup
  • make down - Stop all services
  • make clean - Remove everything including volumes
  • make status - Show status of all nodes
  • make logs - Show logs from all services
  • make ping-test - Test connectivity between clients
  • make shell-headscale - Open shell in Headscale container
  • make shell-client1 - Open shell in Client1 container
  • make shell-client2 - Open shell in Client2 container

Manual Operations

Creating users

docker exec headscale-server headscale users create myuser

Generating pre-auth keys

docker exec headscale-server headscale preauthkeys create \
  --user myuser \
  --reusable \
  --expiration 24h

Listing nodes

docker exec headscale-server headscale nodes list

Manual node registration

If automatic registration fails:

  1. Start the client registration:
docker exec tailscale-client1 tailscale up \
  --login-server=http://headscale:8080
  1. Copy the node key from the output

  2. Register the node:

docker exec headscale-server headscale nodes register \
  --user testuser \
  --key <nodekey>

Configuration

Headscale Configuration

Edit headscale-config.yaml to modify:

  • IP ranges for nodes
  • DNS settings
  • DERP server configuration
  • Logging levels

ACL Policy

Edit acl.hujson to modify access control rules. Default policy allows all traffic between all nodes.

Environment Variables

The .env file contains:

  • COMPOSE_PROJECT_NAME: Docker Compose project name
  • TS_AUTHKEY_CLIENT1: Pre-auth key for client 1
  • TS_AUTHKEY_CLIENT2: Pre-auth key for client 2

Testing Connectivity

Between Tailscale clients

# From client1 to client2
docker exec tailscale-client1 tailscale ping client2

# Using regular ping with Tailscale IPs
docker exec tailscale-client1 ping -c 3 100.64.0.2

Through the web server

# Access the test web server
docker exec tailscale-client1 curl http://webserver

Troubleshooting

Clients not connecting

  1. Check Headscale logs:
make logs-headscale
  1. Check client logs:
make logs-clients
  1. Verify pre-auth keys are set:
cat .env
  1. Try manual registration:
make register-manual

Network issues

  1. Verify Docker network:
docker network inspect headscale-dev_headscale-net
  1. Check Tailscale status in clients:
docker exec tailscale-client1 tailscale status
docker exec tailscale-client2 tailscale status
  1. Test basic connectivity:
docker exec tailscale-client1 ping headscale

Reset everything

make clean
make up

Development Workflow

Using local Headscale build

  1. Build Headscale from source:
make build-local
  1. Update docker-compose.yml:
headscale:
  image: headscale:local  # Instead of headscale/headscale:latest
  1. Restart:
make down
make up

Modifying ACL policies

  1. Edit acl.hujson
  2. Restart Headscale to apply changes:
docker compose restart headscale

Adding more clients

  1. Copy the client service definition in docker-compose.yml
  2. Update the container name, hostname, and IP address
  3. Add a new auth key environment variable
  4. Run make setup to generate a new key
  5. Start the new client

Security Notes

  • This setup is for development only
  • Uses HTTP instead of HTTPS for simplicity
  • Pre-auth keys have 24-hour expiration by default
  • All traffic between nodes is allowed by default ACL

Clean Up

To completely remove the environment:

make clean

This removes:

  • All containers
  • All volumes (including Headscale database)
  • Generated auth keys in .env