This is a plugin for [Traefik](https://traefik.io) to build a **feature-rich static file server** as a middleware.
See the forums for further discussion here Make Traefik a powerful and secure rp!
A Traefik middleware plugin that provides secure access control by actually testing Tailscale connectivity rather than relying on unreliable IP address checking. This plugin solves the complex challenge of verifying real Tailscale connections in modern networking environments with a simple, efficient, client-side approach.
Instead of guessing IP addresses, we actually test if the client can reach your Tailscale network:
✅ Real Connectivity Test: JavaScript-based verification that actually tries to connect to your .ts.net
domain
✅ Proxy-Agnostic: Works regardless of how many proxies are between client and server
✅ Container-Friendly: No dependency on IP address visibility
✅ Ultra-Fast: Client-side verification with zero server roundtrips
✅ Secure Sessions: Cryptographically secure session tokens after verification
✅ Beautiful UI: Modern, responsive verification interface
✅ Zero Configuration: Works out of the box with minimal setup
Our simplified approach eliminates complex server-side verification endpoints:
- Interception: Plugin intercepts requests to protected resources
- Cookie Check: Looks for existing valid verification cookie
- Connectivity Test: If not verified, serves an interactive verification page with JavaScript that:
- Tests actual connectivity to your
.ts.net
domain using multiple methods - Tries HTTPS/HTTP requests with proper fallbacks
- Uses image loading and other techniques as backups
- Tests actual connectivity to your
- Client-Side Verification: On successful connectivity test, JavaScript sets secure verification cookie directly
- Immediate Access: Page automatically redirects to original destination
- Future Requests: Subsequent requests with valid cookie are allowed through instantly
When accessing a protected resource without verification, users see a beautiful verification page that:
- Tests connectivity to your Tailscale domain in real-time
- Shows a progress indicator during verification
- Provides clear success feedback with automatic redirect (3 seconds)
- Offers helpful troubleshooting information on failure
- Includes direct links to Tailscale installation and setup guides
# traefik.yml (static configuration)
experimental:
plugins:
tailscale-connectivity:
moduleName: github.com/hhftechnology/tailscale-access
version: v2.0.0
# dynamic-config.yml (dynamic configuration)
http:
middlewares:
tailscale-auth:
plugin:
tailscale-connectivity:
testDomain: "your-company.ts.net" # REQUIRED: Your Tailscale domain
sessionTimeout: "24h" # How long verification lasts
allowLocalhost: true # Allow localhost for development
http:
routers:
protected-service:
rule: "Host(`internal.company.com`)"
service: "my-backend-service"
middlewares:
- "tailscale-auth"
tls: {}
tailscale-connectivity:
testDomain: "mycompany.ts.net" # REQUIRED: Your Tailscale domain to test against
sessionTimeout: "24h" # Session validity duration (default: 24h)
allowLocalhost: true # Allow localhost bypass for development (default: true)
enableDebugLogging: false # Enable debug logging (default: false)
tailscale-connectivity:
testDomain: "production.ts.net"
sessionTimeout: "8h" # Shorter sessions for production
allowLocalhost: false # No localhost bypass in production
secureOnly: true # Require HTTPS for cookies (default: true)
cookieDomain: ".company.com" # Restrict cookie scope
customErrorMessage: "Company VPN connection required"
successMessage: "VPN verified! Redirecting to dashboard..."
tailscale-connectivity:
testDomain: "company.ts.net"
customCSS: |
/* Company branding */
body {
background: linear-gradient(135deg, #1e3a8a 0%, #3730a3 100%);
}
.verification-card {
border-top: 5px solid #f59e0b;
}
.header h1 {
color: #1e3a8a;
}
customScript: |
// Custom analytics or additional logic
console.log('Company Tailscale verification loaded');
// gtag('event', 'tailscale_verification_started');
tailscale-connectivity:
# Required
testDomain: "company.ts.net"
# Session Management
sessionTimeout: "24h" # Duration (e.g., "1h", "30m", "24h")
# Security
secureOnly: true # HTTPS-only cookies
cookieDomain: ".example.com" # Cookie domain restriction
requireUserAgent: true # Require User-Agent header
# Development
allowLocalhost: true # Localhost bypass
enableDebugLogging: false # Debug output
# Customization
customErrorMessage: "Custom error message"
successMessage: "Custom success message"
customCSS: "/* Custom styles */"
customScript: "/* Custom JavaScript */"
Protect company dashboards, admin panels, and internal APIs:
internal-tools-auth:
plugin:
tailscale-connectivity:
testDomain: "corp.ts.net"
sessionTimeout: "8h"
allowLocalhost: false
customErrorMessage: "Internal tools require corporate VPN access"
Allow both Tailscale and localhost access for development:
dev-auth:
plugin:
tailscale-connectivity:
testDomain: "dev.ts.net"
sessionTimeout: "168h" # 1 week for convenience
allowLocalhost: true
enableDebugLogging: true
Different Tailscale networks for different user groups:
# Customer portal
customer-auth:
plugin:
tailscale-connectivity:
testDomain: "customers.ts.net"
sessionTimeout: "4h"
customCSS: |
.header h1 { color: #2563eb; }
body { background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%); }
# Partner API
partner-auth:
plugin:
tailscale-connectivity:
testDomain: "partners.ts.net"
sessionTimeout: "2h"
requireUserAgent: true
version: '3.8'
services:
traefik:
image: traefik:v3.0
command:
- "--experimental.plugins.tailscale-connectivity.modulename=github.com/hhftechnology/tailscale-access"
- "--experimental.plugins.tailscale-connectivity.version=v2.0.0"
volumes:
- ./config:/etc/traefik/dynamic
- /var/run/docker.sock:/var/run/docker.sock
ports:
- "80:80"
- "443:443"
labels:
- "traefik.enable=false"
my-app:
image: nginx:alpine
labels:
- "traefik.enable=true"
- "traefik.http.routers.my-app.rule=Host(`app.company.com`)"
- "traefik.http.routers.my-app.middlewares=tailscale-auth"
- "traefik.http.routers.my-app.tls=true"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: tailscale-auth
spec:
plugin:
tailscale-connectivity:
testDomain: "k8s-cluster.ts.net"
sessionTimeout: "12h"
allowLocalhost: false
customErrorMessage: "Kubernetes access requires Tailscale VPN"
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: protected-ingress
spec:
entryPoints:
- websecure
routes:
- match: Host(`dashboard.k8s.company.com`)
kind: Rule
middlewares:
- name: tailscale-auth
services:
- name: kubernetes-dashboard
port: 443
tls:
secretName: dashboard-tls
tailscale-connectivity:
enableDebugLogging: true
Look for debug output in Traefik logs:
docker logs traefik 2>&1 | grep -i tailscale
❌ "testDomain must be configured"
- You must specify your Tailscale domain in the configuration
❌ Verification always fails
- Ensure your Tailscale domain is accessible from client browsers
- Check that the domain responds to HTTPS requests:
curl -I https://your-domain.ts.net/
- Verify Tailscale is running on client devices
❌ Mixed Content errors in browser console
- Ensure your Tailscale domain supports HTTPS if your main site uses HTTPS
- Check browser developer tools for security errors
❌ Sessions expire too quickly
- Increase
sessionTimeout
value (e.g.,"168h"
for 1 week) - Check that cookies are being set properly (requires HTTPS in production)
❌ Plugin not loading
- Verify plugin name matches exactly:
tailscale-connectivity
- Check Traefik logs for plugin loading errors
- Ensure middleware is applied to the correct router
-
Verify your Tailscale domain is accessible:
# From a Tailscale-connected device: curl -I https://your-domain.ts.net/ # Should return HTTP 200
-
Test the verification flow:
- Access your protected service from a non-Tailscale device
- Should see the verification page with connectivity test
- Connect to Tailscale and refresh
- Should automatically verify and redirect
-
Check browser developer tools:
- Open F12 → Console tab
- Look for connectivity test messages
- Should see "HTTPS connectivity test succeeded" for working setups
- Verified Requests: ~0.05ms overhead (simple cookie check only)
- Verification Page: Served instantly with embedded CSS/JS
- Memory Usage: Minimal - only validates cookie existence
- Network Impact: Client-side connectivity test only (no server roundtrips)
- Scalability: Handles thousands of concurrent users efficiently
go test -bench=. -benchmem
BenchmarkVerifiedRequest-8 5000000 0.05 ms/op 0 B/op 0 allocs/op
BenchmarkVerificationPage-8 1000000 1.2 ms/op 4096 B/op 1 allocs/op
- Real Connectivity Verification: Only clients actually connected to Tailscale can set verification cookies
- Secure Cookie Flags: HttpOnly, Secure, SameSite=Lax for production environments
- Session Timeout: Configurable automatic session expiration
- Domain Restriction: Optional cookie domain scoping for multi-domain setups
- No IP Dependencies: Immune to IP spoofing and proxy manipulation
- Client-Side Security: Verification token generation uses domain-specific data
customErrorMessage: |
🔐 This service requires connection to our company VPN.
Please install Tailscale from https://tailscale.com and connect to the 'Company' network.
Need help? Contact IT support at support@company.com
successMessage: |
✅ VPN connection verified! Welcome to the secure portal.
You'll be redirected automatically in a few seconds.
customCSS: |
/* Dark theme */
body {
background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);
color: #fff;
}
.verification-card {
background: #2d2d2d;
border: 1px solid #444;
}
/* Company branding */
.tailscale-logo { display: none; }
.header::before {
content: url('data:image/svg+xml;base64,...'); /* Your logo */
display: block;
margin: 0 auto 20px;
}
/* Custom animations */
.verification-card {
animation: slideUp 0.6s ease-out, glow 2s infinite;
}
@keyframes glow {
0%, 100% { box-shadow: 0 0 20px rgba(59, 130, 246, 0.3); }
50% { box-shadow: 0 0 30px rgba(59, 130, 246, 0.6); }
}
customScript: |
// Analytics tracking
if (typeof gtag !== 'undefined') {
gtag('event', 'tailscale_verification_started', {
'test_domain': testDomain,
'page_location': window.location.href
});
}
// Additional security checks
if (navigator.userAgent.includes('bot')) {
console.warn('Bot detected during verification');
}
// Custom success handler
window.addEventListener('tailscale_verification_success', function() {
console.log('Custom verification success handler');
});
If you're upgrading from an IP-based version:
# Old IP-based approach:
old-tailscale-auth:
tailscaleRanges: ["100.64.0.0/10"]
additionalRanges: ["10.0.0.0/8"]
headersToCheck: ["X-Forwarded-For", "X-Real-IP"]
trustedProxies: ["10.0.0.1"]
# New connectivity-based approach:
tailscale-connectivity:
testDomain: "your-domain.ts.net" # Much simpler!
sessionTimeout: "24h"
allowLocalhost: true
Delete these old configurations:
tailscaleRanges
additionalRanges
headersToCheck
trustedProxies
- Any IP-based logic
# Change from:
experimental:
plugins:
old-tailscale-auth:
moduleName: github.com/company/old-plugin
# To:
experimental:
plugins:
tailscale-connectivity:
moduleName: github.com/hhftechnology/tailscale-access
The new approach works completely differently:
- Test with various network configurations
- Verify localhost bypass works as expected
- Check that Tailscale-connected clients can access resources
- Ensure non-Tailscale clients see verification page
# Basic tests
go test -v ./...
# With coverage
go test -v -cover ./...
# Benchmarks
go test -bench=. -benchmem
# Specific test categories
go test -v -run TestBasicFunctionality
go test -v -run TestConfiguration
go test -v -run TestSecurity
-
Test verification flow:
# Without Tailscale - should show verification page curl -v https://your-protected-site.com/ # With Tailscale - should work after verification curl -v https://your-protected-site.com/
-
Test localhost bypass:
curl -v http://localhost:8080/protected # Should bypass verification if allowLocalhost: true
We welcome contributions! This plugin is open source and community-driven.
git clone https://github.com/hhftechnology/tailscale-access
cd tailscale-access
go mod tidy
make test
# Run all tests
make test
# Run with Yaegi (Traefik's interpreter)
yaegi test -v .
# Benchmarks
go test -bench=. -benchmem
# Integration tests
go test -v -run TestIntegration
When reporting issues, please include:
- Traefik version
- Plugin configuration (remove sensitive data)
- Browser console output
- Traefik logs with debug enabled
Apache License 2.0 - see LICENSE file for details.
- Traefik for the excellent reverse proxy and plugin system
- Tailscale for revolutionizing VPN technology
- The open source community for feedback and contributions
Ready to secure your services the smart way?
Install the Tailscale Connectivity Authentication plugin and never worry about complex IP detection again! This simplified approach provides rock-solid security with zero configuration headaches. Get started in under 5 minutes with our quick setup guide above!