headscale/docker-dev/NETWORK-ARCHITECTURE.md
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.1 KiB

Network Architecture Documentation

This document provides a detailed explanation of the networking setup in the Headscale Docker development environment.

Overview

The setup uses two distinct networking layers that work together to provide a complete Tailscale network simulation:

  1. Docker Bridge Network - Infrastructure layer for container communication
  2. Tailscale Overlay Network - Encrypted VPN layer for secure data transmission

Layer 1: Docker Bridge Network

Network Configuration

  • Subnet: 10.99.0.0/24
  • Gateway: 10.99.0.1
  • Network Name: headscale-dev_headscale-net

Container IP Assignments

Container IP Address Role
headscale-server 10.99.0.10 Control plane server
tailscale-client1 10.99.0.21 Tailscale node 1
tailscale-client2 10.99.0.22 Tailscale node 2
test-webserver 10.99.0.30 HTTP test server

Port Mappings

Headscale Server

Host Port Container Port Service
8180 8080 HTTP API
9090 9090 Metrics
50443 50443 gRPC

Note: Port 8180 is used on the host instead of 8080 to avoid conflicts with other services.

Other Services

Container Exposed Ports Purpose
test-webserver 80 (internal only) HTTP test content
tailscale-client1 None exposed VPN endpoint
tailscale-client2 None exposed VPN endpoint

Layer 2: Tailscale Overlay Network

Network Configuration

  • IPv4 Subnet: 100.64.0.0/10 (Tailscale CGNAT range)
  • IPv6 Subnet: fd7a:115c:a1e0::/48 (Tailscale IPv6 range)
  • Allocation Strategy: Sequential

Tailscale IP Assignments

Node IPv4 Address IPv6 Address Hostname
client1 100.64.0.1 fd7a:115c:a1e0::1 client1
client2 100.64.0.2 fd7a:115c:a1e0::2 client2

Network Communication Flow

1. Control Plane Communication

Tailscale Client → Docker Network → Headscale Server
      │                 │               │
   100.64.0.1      10.99.0.21      10.99.0.10
      │                 │               │
      └─────── HTTP/GRPC over ──────────┘
              headscale:8080
  • Clients connect to Headscale using Docker's internal DNS (headscale:8080)
  • Authentication happens via pre-auth keys
  • Headscale assigns Tailscale IP addresses from the 100.64.0.0/10 range
  • Policy enforcement and network map distribution

2. Data Plane Communication

Client1 ←→ Encrypted Tailscale Tunnel ←→ Client2
  │                                        │
100.64.0.1                              100.64.0.2
  │                                        │
10.99.0.21 ←── Docker Bridge Network ──→ 10.99.0.22

When client1 pings client2:

  1. Application layer: Uses Tailscale IP 100.64.0.2
  2. Encryption layer: Tailscale encrypts the packet using WireGuard
  3. Transport layer: Encrypted packet travels via Docker network 10.99.0.22:port
  4. Decryption layer: client2 decrypts and processes the packet

Key insight: The ping result shows:

pong from client2 (100.64.0.2) via 10.99.0.22:49212 in 0s

This demonstrates how Tailscale IPs are used at the application level while Docker IPs handle the actual transport.

Security Architecture

1. Noise Protocol (Tailscale v2)

  • Purpose: Encrypts control plane communication between clients and Headscale
  • Key Storage: /var/lib/headscale/noise_private.key
  • Protocol: Noise_IK_25519_ChaChaPoly_BLAKE2s

2. WireGuard Encryption

  • Purpose: Encrypts data plane communication between Tailscale nodes
  • Key Exchange: Managed by Headscale control plane
  • Cipher: ChaCha20Poly1305

3. Access Control Lists (ACL)

{
  "acls": [
    {
      "action": "accept",
      "src": ["testuser@headscale"],
      "dst": ["testuser@headscale:*"]
    }
  ]
}
  • Controls which nodes can communicate with each other
  • Applied at the Tailscale overlay network level
  • Independent of Docker network security

DNS Resolution

Docker Internal DNS

  • headscale10.99.0.10 (Control plane access)
  • client110.99.0.21 (Container hostname)
  • client210.99.0.22 (Container hostname)
  • webserver10.99.0.30 (Test web server)

Tailscale MagicDNS

  • client1100.64.0.1 (Tailscale hostname)
  • client2100.64.0.2 (Tailscale hostname)
  • Domain: headscale.local (configured in Headscale)

Network Isolation and Security

Container Isolation

  • Each container has its own network namespace
  • Containers can only communicate via the Docker bridge network
  • No direct host network access (except through port mappings)

Tailscale Overlay Isolation

  • Encrypted tunnels between authorized nodes only
  • ACL policies enforce access control
  • Zero-trust architecture: containers on same Docker network still use encrypted communication

Firewall Considerations

  • Docker bridge network: Internal communication only
  • Host ports: Only Headscale API (8180) exposed to host
  • Tailscale network: Controlled by ACL policies

Debugging Network Issues

Check Docker Network

# View network configuration
docker network inspect headscale-dev_headscale-net

# Test Docker-level connectivity
docker exec tailscale-client1 ping headscale
docker exec tailscale-client1 ping 10.99.0.22

Check Tailscale Network

# View Tailscale status
docker exec tailscale-client1 tailscale status

# Test Tailscale connectivity
docker exec tailscale-client1 tailscale ping client2
docker exec tailscale-client1 ping 100.64.0.2

Verify Control Plane

# Test Headscale API
curl http://localhost:8180/health

# View registered nodes
docker exec headscale-server headscale nodes list

Performance Characteristics

Latency

  • Docker bridge: Sub-millisecond latency (same host)
  • Tailscale overlay: Minimal additional latency due to encryption
  • Control plane: Periodic updates, not in data path

Throughput

  • Limited by: Docker bridge network bandwidth and CPU encryption
  • Typical: Near-native performance for local container communication
  • Encryption overhead: Minimal with modern ChaCha20 implementation

Scalability

  • Current setup: 2 clients, easily expandable
  • Docker limitations: Network MTU, container limits
  • Headscale limitations: Database backend (SQLite vs PostgreSQL)

Real-World Mapping

This setup simulates real Tailscale deployments:

Docker Environment Real World
Docker bridge network Internet infrastructure
Container IP addresses Public/private IP addresses
Headscale control server Tailscale SaaS control plane
ACL policies Corporate network policies
Pre-auth keys Device enrollment tokens
Encrypted tunnels WireGuard VPN connections

The key difference is that in production, nodes are typically on different networks (home, office, cloud) rather than the same Docker host, but the Tailscale protocol behavior is identical.