- 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
7.1 KiB
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:
- Docker Bridge Network - Infrastructure layer for container communication
- 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/10range - 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:
- Application layer: Uses Tailscale IP
100.64.0.2 - Encryption layer: Tailscale encrypts the packet using WireGuard
- Transport layer: Encrypted packet travels via Docker network
10.99.0.22:port - Decryption layer:
client2decrypts 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
headscale→10.99.0.10(Control plane access)client1→10.99.0.21(Container hostname)client2→10.99.0.22(Container hostname)webserver→10.99.0.30(Test web server)
Tailscale MagicDNS
client1→100.64.0.1(Tailscale hostname)client2→100.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.