- 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
217 lines
7.1 KiB
Markdown
217 lines
7.1 KiB
Markdown
# 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)
|
|
```json
|
|
{
|
|
"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
|
|
```bash
|
|
# 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
|
|
```bash
|
|
# 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
|
|
```bash
|
|
# 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. |