Skip to content

Conversation

@tvarohohlavy
Copy link

Tailscale VPN Integration with MagicDNS Support

Overview

This PR adds native Tailscale VPN integration to containerlab, enabling secure remote access to lab management networks from anywhere. The integration includes optional DNS server support with intelligent DNS doctoring for seamless name resolution via Tailscale's MagicDNS feature.

Features

🔐 Tailscale VPN Integration

  • Automatic deployment of Tailscale container as lab infrastructure
  • Route advertisement of management network (IPv4/IPv6) to Tailscale network
  • Flexible configuration with sensible defaults
  • Lifecycle management - Tailscale container created/destroyed with lab
  • Infrastructure labeling - Generic clab-is-infrastructure label for extensibility

🌐 DNS Services

  • CoreDNS for authoritative lab DNS resolution (A and AAAA records)
  • Python DNS Proxy with location-aware DNS doctoring:
    • Tailscale clients automatically receive NAT-translated IPs
    • Local management network clients receive original IPs
    • Automatic detection based on client source address
  • Dynamic DNS records - Nodes automatically registered as <node-name>.<lab-name>.clab
  • Split DNS ready - Integrates with Tailscale MagicDNS for seamless resolution
  • Configurable - Custom domain suffix and CoreDNS version
  • Auto-updates - DNS records populated after node deployment

🛠️ Advanced Features

  • 1:1 NAT support - Advertise different subnet with bidirectional mapping using iptables NETMAP
  • DNS Doctoring - Automatic IP translation in DNS responses when NAT is enabled
  • Configurable image - Pin specific Tailscale version
  • ACL tags - Support for Tailscale tag-based access control
  • Health checks - Container health monitoring
  • Custom IP addressing - Override automatic IP assignment

Configuration

Basic Setup

name: my-lab
mgmt:
  tailscale:
    authkey: "tskey-auth-xxxxx"

With MagicDNS

name: my-lab
mgmt:
  tailscale:
    authkey: "tskey-auth-xxxxx"
    dns:
      enabled: true
      domain: "my-lab.clab"     # optional, defaults to "<lab-name>.clab"
      coredns-version: "1.13.1" # optional

Advanced Configuration with NAT and DNS Doctoring

mgmt:
  ipv4-subnet: 172.20.20.0/24
  tailscale:
    enabled: true
    authkey: "tskey-auth-xxxxx"
    image: "tailscale/tailscale:v1.56.0"
    ipv4-address: "172.20.20.254"
    tags:
      - "lab-access"
    snat: true
    ephemeral-state: true
    accept-routes: false
    accept-dns: false
    one-to-one-nat: "10.0.0.0/24"
    dns:
      enabled: true
      domain: "lab.example.com"
      coredns-version: "1.13.1"

Architecture

DNS Integration

  • Post-deployment DNS update - UpdateInfrastructureDNS() called after all nodes deployed
  • CoreDNS installation - Downloaded and configured automatically in Tailscale container
  • Dynamic record generation - Extracts node info (shortname, IPs) and creates DNS records
  • Split DNS workflow - Tailscale MagicDNS → CoreDNS → containerlab nodes

DNS Architecture with Doctoring

Tailscale Clients (port 53)
    ↓
Python DNS Proxy (port 53)
    ↓ [rewrites IPs: mgmt subnet → NAT subnet]
    ↓ [based on client source address]
    ↓
CoreDNS (port 5353)
    ↓ [serves from /etc/coredns/hosts]
    ↓
Docker DNS (127.0.0.11) [fallback]

How it works:

  1. CoreDNS serves authoritative DNS for lab domain with real management IPs
  2. When NAT is enabled, Python DNS proxy intercepts queries
  3. Proxy detects client source: Tailscale network vs. management network
  4. For Tailscale clients: translates management IPs → NAT IPs in responses
  5. For local clients: passes through unchanged

Files Changed

Core Implementation

  • types/types.go - Added TailscaleConfig and TailscaleDNSConfig structs
  • constants/labels.go - Added IsInfrastructure label constant
  • tailscale.go - Complete Tailscale lifecycle management
  • clab/clab.go - Added DeployInfrastructure(), DestroyInfrastructure(), UpdateInfrastructureDNS()
  • clab/deploy.go - Integrated infrastructure deployment and DNS updates
  • core/destroy.go - Integrated infrastructure cleanup
  • clab/config.go - Generic infrastructure container detection
  • utils/ip.go - Added LastHostIPInSubnet() helper

Script Files (NEW)

  • runtime/docker/scripts/nat-setup.sh - NAT configuration with iptables NETMAP
  • runtime/docker/scripts/dns-proxy.py - Python DNS proxy with intelligent doctoring
  • runtime/docker/scripts/coredns-install.sh - Optimized CoreDNS installation
  • Corefile.tmpl - CoreDNS configuration template
  • runtime/docker/scripts/README.md - Comprehensive script documentation

Documentation

  • docs/manual/network.md - Brief Tailscale introduction with reference
  • docs/manual/tailscale.md - Comprehensive guide (NEW)
  • mkdocs.yml - Added Tailscale VPN navigation entry

Usage Examples

Remote Lab Access

# Deploy lab with Tailscale
sudo clab deploy -t topology.yml

# Accept routes in Tailscale admin console
# Access nodes from anywhere
ssh [email protected]

With MagicDNS

# Deploy lab with DNS enabled
sudo clab deploy -t topology.yml

# Configure split DNS in Tailscale admin console
# Access nodes by name from anywhere
ssh [email protected]
ping switch1.my-lab.clab

Testing

Tested with:

  • ✅ Basic Tailscale deployment
  • ✅ Route advertisement (IPv4/IPv6)
  • ✅ 1:1 NAT functionality
  • ✅ DNS server installation
  • ✅ DNS record generation
  • ✅ DNS Doctoring for NAT + DNS combination
  • ✅ MagicDNS split DNS resolution
  • ✅ Location-aware DNS responses (Tailscale vs local clients)
  • ✅ Container lifecycle management
  • ✅ Multiple labs with different configurations

Known Limitations

  • PTR (Reverse DNS) records are not currently supported - This is documented as TODO in the code. CoreDNS hosts plugin can auto-generate PTR records, but proper zone configuration is needed to make CoreDNS authoritative for in-addr.arpa zones. This requires further investigation.

Documentation

Comprehensive documentation includes:

  • Prerequisites and auth key setup
  • Quick start guide
  • Full configuration reference
  • MagicDNS setup walkthrough
  • NAT and DNS doctoring configuration
  • Script architecture and troubleshooting
  • Use cases and examples
  • Operations and troubleshooting

Migration Path

N/A - New feature with opt-in configuration. Fully backward compatible.

Future Enhancements

Potential follow-ups:

  • PTR (reverse DNS) records support
  • Separate DNS infrastructure container option
  • IPv6 NAT support
  • Custom DNS record injection

Related Issues: #2396 #1394

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces comprehensive Tailscale VPN integration to containerlab, enabling secure remote access to lab management networks. The integration includes automatic deployment of Tailscale containers as infrastructure, optional DNS services with CoreDNS and MagicDNS support, and advanced features like 1:1 NAT with DNS doctoring for IP translation.

Key Changes

  • Tailscale VPN Integration: Automatic deployment and lifecycle management of Tailscale containers with route advertisement, configurable addressing, ACL tags, and NAT support
  • DNS Services: CoreDNS integration with automatic node record generation, split DNS support via Tailscale MagicDNS, and intelligent DNS proxy for NAT scenarios
  • Infrastructure Management: New infrastructure container concept with dedicated lifecycle methods and labeling system to distinguish from user nodes

Reviewed Changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
types/types.go Defines TailscaleConfig and TailscaleDNSConfig structs with comprehensive configuration options
utils/ip.go Adds LastHostIPInSubnet helper function to calculate container IP addresses
runtime/docker/tailscale.go Core implementation (~1000 lines) handling Tailscale deployment, DNS setup, and container management
runtime/docker/scripts/nat-setup.sh Embedded shell script for iptables NETMAP rules to enable 1:1 NAT
runtime/docker/scripts/dns-proxy.py Python DNS proxy with intelligent IP rewriting based on client source address
runtime/docker/scripts/coredns-install.sh CoreDNS installation script with optional Python dependency
runtime/docker/scripts/Corefile.tmpl CoreDNS configuration template for lab domain resolution
runtime/docker/scripts/README.md Comprehensive documentation for embedded scripts architecture
core/clab.go Infrastructure lifecycle methods (Deploy/Destroy/UpdateDNS)
core/deploy.go Integration point for infrastructure deployment after network creation
core/destroy.go Integration point for infrastructure cleanup before network deletion
core/config.go Infrastructure container exclusion from uniqueness checks
constants/labels.go New IsInfrastructure label constant
docs/manual/tailscale.md Extensive documentation (795 lines) covering configuration, use cases, and troubleshooting
docs/manual/network.md Brief introduction to Tailscale feature with reference to full documentation
mkdocs.yml Navigation menu entry for Tailscale documentation

@tvarohohlavy
Copy link
Author

Thanks @hellt
I have fixed the suggestions from Copilot.

@kaelemc kaelemc self-requested a review November 12, 2025 16:53
@kaelemc
Copy link
Member

kaelemc commented Nov 12, 2025

@tvarohohlavy looks cool, I will try this one out :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants