Tailscale Integration with Management Network
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-infrastructurelabel 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:
- CoreDNS serves authoritative DNS for lab domain with real management IPs
- When NAT is enabled, Python DNS proxy intercepts queries
- Proxy detects client source: Tailscale network vs. management network
- For Tailscale clients: translates management IPs → NAT IPs in responses
- For local clients: passes through unchanged
Files Changed
Core Implementation
-
types/types.go- AddedTailscaleConfigandTailscaleDNSConfigstructs -
constants/labels.go- AddedIsInfrastructurelabel constant - tailscale.go - Complete Tailscale lifecycle management
-
clab/clab.go- AddedDeployInfrastructure(),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- AddedLastHostIPInSubnet()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.arpazones. 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: https://github.com/srl-labs/containerlab/issues/2396 https://github.com/srl-labs/containerlab/issues/1394
Thanks @hellt I have fixed the suggestions from Copilot.
@tvarohohlavy looks cool, I will try this one out :)