# 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.