SiamCafe.net Blog
Technology

Tailscale Mesh Multi-tenant Design

tailscale mesh multi tenant design
Tailscale Mesh Multi-tenant Design | SiamCafe Blog
2026-03-31· อ. บอม — SiamCafe.net· 9,718 คำ

Tailscale Multi-tenant Mesh

Tailscale WireGuard Mesh VPN Multi-tenant ACL Tag Group Subnet Router Exit Node MagicDNS Headscale Zero Trust Production

Isolation MethodComplexityIsolation LevelBest For
Tag-based ACLต่ำปานกลาง (Shared Tailnet)ทีมภายใน 2-10 กลุ่ม
Group-based ACLปานกลางปานกลาง-สูงองค์กร หลายแผนก
Separate Tailnetสูงสูงสุดลูกค้าภายนอก Compliance
Headscale (Self-hosted)สูงสูงสุด + Data ControlEnterprise Self-hosted

ACL Configuration

# === Tailscale ACL Policy ===

# tailscale ACL (HuJSON format)
# {
#   "groups": {
#     "group:tenant-a-admins": ["alice@example.com"],
#     "group:tenant-a-users": ["bob@example.com", "carol@example.com"],
#     "group:tenant-b-admins": ["dave@example.com"],
#     "group:tenant-b-users": ["eve@example.com"],
#     "group:platform-admins": ["admin@example.com"]
#   },
#   "tagOwners": {
#     "tag:tenant-a": ["group:tenant-a-admins"],
#     "tag:tenant-b": ["group:tenant-b-admins"],
#     "tag:shared": ["group:platform-admins"],
#     "tag:monitoring": ["group:platform-admins"]
#   },
#   "acls": [
#     // Tenant A can access only their devices
#     {"action": "accept", "src": ["group:tenant-a-admins", "group:tenant-a-users"],
#      "dst": ["tag:tenant-a:*"]},
#     // Tenant B can access only their devices
#     {"action": "accept", "src": ["group:tenant-b-admins", "group:tenant-b-users"],
#      "dst": ["tag:tenant-b:*"]},
#     // Everyone can access shared services on port 443
#     {"action": "accept", "src": ["*"], "dst": ["tag:shared:443"]},
#     // Platform admins can access monitoring
#     {"action": "accept", "src": ["group:platform-admins"], "dst": ["tag:monitoring:*"]},
#     // Platform admins can access everything
#     {"action": "accept", "src": ["group:platform-admins"], "dst": ["*:*"]}
#   ],
#   "tests": [
#     {"src": "alice@example.com", "accept": ["tag:tenant-a:22"]},
#     {"src": "alice@example.com", "deny": ["tag:tenant-b:22"]},
#     {"src": "dave@example.com", "accept": ["tag:tenant-b:80"]},
#     {"src": "dave@example.com", "deny": ["tag:tenant-a:80"]}
#   ]
# }

from dataclasses import dataclass

@dataclass
class ACLRule:
    tenant: str
    src: str
    dst: str
    ports: str
    purpose: str

rules = [
    ACLRule("Tenant A",
        "group:tenant-a-admins + users",
        "tag:tenant-a:*",
        "All Ports",
        "Tenant A เข้าถึงเฉพาะ Device ของตัวเอง"),
    ACLRule("Tenant B",
        "group:tenant-b-admins + users",
        "tag:tenant-b:*",
        "All Ports",
        "Tenant B เข้าถึงเฉพาะ Device ของตัวเอง"),
    ACLRule("Shared Services",
        "* (Everyone)",
        "tag:shared:443",
        "443 (HTTPS)",
        "ทุก Tenant เข้าถึง Shared API Gateway"),
    ACLRule("Monitoring",
        "group:platform-admins",
        "tag:monitoring:*",
        "All Ports",
        "Platform Admin ดู Monitoring ทุก Tenant"),
    ACLRule("Platform Admin",
        "group:platform-admins",
        "*:*",
        "All Ports",
        "Super Admin เข้าถึงทุก Device (Emergency)"),
]

print("=== ACL Rules ===")
for r in rules:
    print(f"  [{r.tenant}] {r.src} → {r.dst}:{r.ports}")
    print(f"    Purpose: {r.purpose}")

Network Architecture

# === Multi-tenant Network Design ===

@dataclass
class NetworkComponent:
    component: str
    config: str
    per_tenant: bool
    shared: bool
    note: str

components = [
    NetworkComponent("Subnet Router",
        "tailscale up --advertise-routes=10.1.0.0/24",
        True,
        False,
        "แต่ละ Tenant มี Subnet Router แยก เชื่อม On-prem"),
    NetworkComponent("Exit Node",
        "tailscale up --advertise-exit-node",
        True,
        False,
        "แยก Exit Node ต่อ Tenant สำหรับ IP Isolation"),
    NetworkComponent("MagicDNS",
        "device.tailnet-name.ts.net",
        False,
        True,
        "ทุก Device มี DNS Name อัตโนมัติ"),
    NetworkComponent("Split DNS",
        "Resolve *.tenant-a.internal → Subnet Router A",
        True,
        False,
        "แต่ละ Tenant มี Internal DNS แยก"),
    NetworkComponent("Auth Key",
        "tailscale up --auth-key=tskey-xxx",
        True,
        False,
        "สร้าง Auth Key ต่อ Tenant สำหรับ Auto-join"),
    NetworkComponent("Funnel",
        "tailscale funnel 443",
        False,
        True,
        "เปิด Shared Services สู่ Internet (ถ้าต้องการ)"),
]

print("=== Network Components ===")
for c in components:
    tenant = "Per-tenant" if c.per_tenant else "Shared"
    print(f"  [{c.component}] {tenant}")
    print(f"    Config: {c.config}")
    print(f"    Note: {c.note}")

Automation & Monitoring

# === Tailscale API Automation ===

# Tailscale API
# curl -s https://api.tailscale.com/api/v2/tailnet/example.com/devices \
#   -H "Authorization: Bearer tskey-api-xxx" | jq '.devices[] | {name, online}'

# Terraform Provider
# terraform {
#   required_providers {
#     tailscale = { source = "tailscale/tailscale" }
#   }
# }
# resource "tailscale_acl" "main" {
#   acl = file("acl.json")
# }
# resource "tailscale_tailnet_key" "tenant_a" {
#   reusable      = true
#   ephemeral     = false
#   preauthorized = true
#   tags          = ["tag:tenant-a"]
# }

@dataclass
class AutomationTask:
    task: str
    tool: str
    trigger: str
    action: str

tasks = [
    AutomationTask("Device Provisioning",
        "Terraform + Tailscale Provider",
        "New Tenant Onboarding",
        "สร้าง Auth Key + ACL Rule + Subnet Router"),
    AutomationTask("ACL Update",
        "Terraform + Git PR Review",
        "Tenant Change Request",
        "Update ACL JSON → PR → Review → Apply"),
    AutomationTask("Device Monitoring",
        "Tailscale API + Prometheus",
        "Continuous (every 60s)",
        "ตรวจ Device Online/Offline Last Seen"),
    AutomationTask("Key Rotation",
        "Tailscale API + Cron Job",
        "Every 90 days",
        "Rotate Auth Keys สร้าง Key ใหม่ ลบ Key เก่า"),
    AutomationTask("Audit Log Review",
        "Tailscale Admin Console",
        "Weekly",
        "ตรวจ Login Attempts ACL Changes Device Joins"),
]

print("=== Automation Tasks ===")
for t in tasks:
    print(f"  [{t.task}] Tool: {t.tool}")
    print(f"    Trigger: {t.trigger}")
    print(f"    Action: {t.action}")

เคล็ดลับ

Tailscale คืออะไร

Zero-config VPN WireGuard Mesh Peer-to-peer NAT Traversal MagicDNS ACL Subnet Router Exit Node Taildrop SSH Funnel Free 3 Users

Multi-tenant Design ทำอย่างไร

Tag-based ACL Group-based Separate Tailnet Headscale Self-hosted Tailnet Sharing Namespace Isolation Compliance

ACL กำหนดอย่างไร

HuJSON groups tagOwners acls tests src dst accept deny Tag กำหนด Device Group กำหนด User Tests ตรวจ Rule ก่อน Deploy

Production ตั้งอย่างไร

Subnet Router Exit Node Split DNS Auth Key Terraform API Monitoring Prometheus Key Rotation 90d Audit Log MFA Security

สรุป

Tailscale Multi-tenant WireGuard Mesh ACL Tag Group Subnet Router Exit Node MagicDNS Terraform Headscale Monitoring Production

📖 บทความที่เกี่ยวข้อง

Tailscale Mesh Business Continuityอ่านบทความ → QuestDB Time Series Multi-tenant Designอ่านบทความ → Tailscale Mesh Home Lab Setupอ่านบทความ → Netlify Edge Multi-tenant Designอ่านบทความ → Tailscale Mesh Architecture Design Patternอ่านบทความ →

📚 ดูบทความทั้งหมด →