Teknoloji

Tailscale/ZeroTier ile Özel Ağ: Çok Sağlayıcılı VPS Mesh Rehberi

Pager o gece 02:17’de çaldı. Frankfurt’taki VPS üzerinde çalışan edge proxy bir anda TCP yeniden denemeleriyle boğulmuştu, Singapur’daki iş katmanı düğümü ise replikaya konuşamıyordu. Latency p95 280 ms’e zıplamış, hatalı retry’lar RDS bağlantı havuzunu şişirmişti. O ana kadar “internet üzerinden güvenli tüneller yeter” diyorduk; sonra DNS cache’lerinin TTL’leri birbirini ısırdı, wildcard SSL yenilemesi de aynı gece geldi. Eğer siz de downtime riski, DNS kaosu ya da SSL yenileme paniği arasında sıkıştıysanız ve Tailscale/ZeroTier ile özel ağ kurarak bunu kalıcı çözmek istiyorsanız, doğru yerdesiniz. Bu yazıda çok sağlayıcılı VPS’ler arasında site‑to‑site mesh’i, gerçek operasyon notları, ölçülebilir metrikler ve çalışır runbook’larla adım adım anlatacağım: mimari tasarım, kurulum, güvenlik, gözlemlenebilirlik ve değişiklik yönetimi. Sonunda, gece alarmı çalsa dahi “bu ağ ayağa kalkar” diyebileceğiniz bir yapı kurmuş olacağız.

Incident Retrosu: Neden Overlay Mesh?

O incident’te kök neden, iki bölgede farklı sağlayıcıların outboud NAT davranışları ve ara sıra tetiklenen asymmetric routing oldu. Frankfurt edge, Singapur app’e TCP açıyordu; dönüş trafiği ise farklı çıkış IP’sinden geldiği için stateful firewall bayılıyordu. VPC peering yoktu, IPsec siteler arası tüneller ise bakım sırasında yeniden bağlanmayı başaramadı. Üstüne bir de DNS TTL kısa tutulduğu için (10s) rota dalgalanmalarında client tarafında “servis dansı” yaşandı ve p95 latency 280 ms, paket kaybı %3,5’e vurdu. Audit loglarında 01:59–02:23 arasında TLS handshake timeout sayısı 112’ydi.

Overlay mesh ile hedeflediğimiz şuydu: heterojen sağlayıcılarda (hetzner, DO, OVH, AWS lightsail fark etmez) WireGuard temelli, otomatik NAT traversal yapan, yönetimi basit bir site‑to‑site ağ. Tailscale ve ZeroTier bu noktada iki güçlü seçenek. İkisi de Layer‑3 overlay kuruyor; Tailscale, WireGuard üstünde key management ve DERP relay’lerle pratikleşiyor. ZeroTier, esnek sanal switch + policy motoru gibi; L2/L3 melezi bir yaklaşım sunabiliyor. Metrikler açısından hedefimiz netti: p95 latency < 120 ms (kıta‑lararası), paket kaybı < %0,5, handshake recovery < 2s ve mevcut deployment pipeline’larına dokunmadan erişim.

Trade‑off notu: Klasik IPsec tünellerde full‑mesh için her noktayı her noktaya tanımlamak gerekir; yönetim yükü katlanır. Burada mesh kontrol düzlemi dışarıda (Tailscale control plane/DERP, ZeroTier controller) ve rotalar dinamik. Dezavantaj: 3. parti kontrol katmanı bağımlılığı ve veri akışının (normalde direct, NAT delme başarısızsa relay) nadiren relay üzerinden geçebilmesi; bu da latency sapması yaratabilir. Ancak SLA hedefleriyle kıyasladığımızda risk kabul edilebilirdi.

Mimari İnceleme: IP Planı, ACL ve Site‑to‑Site Seçenekleri

IP Planlaması ve Alanlar

Overlay mesh’te en kritik tasarım alanı IP planı. Tailscale default 100.64.0.0/10 CGNAT aralığını kullanırken, ZeroTier kendi tanımlı RFC4193 (fd00::/8) + 10.0.0.0/8 gibi atamalarla çalışabilir. Çakışmaları engellemek için her lokasyona /24 alt ağlar verdik:

– eu‑fra: 10.77.10.0/24
– ap‑sin: 10.77.20.0/24
– us‑nyc: 10.77.30.0/24

Tailscale’de her lokasyon için bir subnet router seçtik; ZeroTier’de ise managed routes ile aynı alt ağları ilan ettik. Amaç, host‑to‑host ve site‑to‑site erişimi aynı düzlemde yönetebilmekti.

DNS, MagicDNS ve Split‑Horizon

DNS tarafında iki önemli karar vardı: servis keşfi için MagicDNS (Tailscale) ve ZeroTier’ın adlandırma seçenekleri, ayrıca split‑horizon DNS. Public DNS kayıtlarında TTL 60s; overlay içi servislerde (örn. api.mesh.local) TTL 300s. Düşük TTL hızlı failover sağlarken kontrol uçları etrafında fırtına yaratabilir; SLA’nızda “özelleşmiş internal domain” varsa TTL’i biraz daha yüksek tutup health‑check’leri agresif yapmak daha sağlıklı.

Güvenlik ve ACL Modeli

Erişim kontrolünü “default deny” prensibiyle kurduk. Tailscale’de policy dosyası (ACLs) ile servis rollerine izin verdik; ZeroTier’da flow rules ile port/protokol bazlı yetki. Prod ve staging’i overlay seviyesinde segment ettik; ortak araçlar (CI runner, log forwarder) için sadece belirli portları açtık.

Tailscale ile Kurulum: Runbook ve Komutlar

Önkoşullar ve Organizasyon

Şirket alanı ile SSO (SAML/OIDC) bağladık. Pre‑auth key kullanarak headless sunucuların otomatik katılımını sağladık. DERP’leri varsayılan bıraktık ancak EU ve APAC için yakın noktaları seçtik. SSH yerine tailscale ssh’ı devreye aldık ki bastion ihtiyacı ve public portlar azalsın.

Kurulum Adımları (Host Başına)

  • Kernel: WireGuard modülü (5.x) kontrollü.
  • Paket: tailscale kurulumu (repo üzerinden).
  • Auth: pre‑auth key ile otomatik katılım.
  • Subnet router: ilgili /24’ü advertise et.
  • ACL ve DNS: policy güncelle, MagicDNS doğrula.
  • Gözlem: metrics exporter ve logs streaming.
# Debian/Ubuntu
curl -fsSL https://tailscale.com/install.sh | sh

# Headless join (örnek preauth key)
sudo tailscale up 
  --authkey=tskey-auth-kQ3... 
  --hostname=fra-edge-01 
  --advertise-tags=tag:edge,tag:prod 
  --ssh 
  --accept-dns=true 
  --accept-routes=true

# Subnet router olarak 10.77.10.0/24 ilan et
sudo tailscale up 
  --advertise-routes=10.77.10.0/24 
  --snat-subnet-routes=false

# Durum
sudo tailscale status
sudo tailscale status --json | jq '.'

Örnek JSON çıktısı:

{
  "Self": {
    "DNSName": "fra-edge-01.tailnet-abc.ts.net",
    "TailscaleIPs": ["100.96.12.34", "fd7a:115c:a1e0::abcd"],
    "UserID": 101,
    "Hostinfo": {"OS": "linux", "Hostname": "fra-edge-01"}
  },
  "Peers": [
    {
      "DNSName": "sin-app-02.tailnet-abc.ts.net",
      "TailscaleIPs": ["100.98.22.11"],
      "RxBytes": 12900322,
      "TxBytes": 9388821,
      "Latency": {"p50": 172000000, "p90": 198000000},
      "Relay": false
    }
  ],
  "MagicDNSEnabled": true,
  "CurrentTailnet": "acme-corp"
}

Policy (ACL) Örneği

{
  "ACLs": [
    {"Action": "accept", "Users": ["group:platform"], "Ports": ["tag:edge:443", "tag:app:5432"]},
    {"Action": "accept", "Users": ["group:sre"], "Ports": ["tag:edge:22", "tag:infra:9100"]}
  ],
  "Groups": {
    "group:platform": ["[email protected]", "[email protected]"],
    "group:sre": ["[email protected]"]
  },
  "TagOwners": {
    "tag:edge": ["group:sre"],
    "tag:app": ["group:platform"],
    "tag:infra": ["group:sre"]
  },
  "AutoApprovers": {
    "routes": {"100.64.0.0/10": ["group:sre"]}
  }
}

Bu projede şöyle çözdük: Subnet router’ları iki node’da çalıştırıp ECMP yerine aktif/pasif yaptık. Neden? Çünkü bazı sağlayıcılarda MTU farkı (1500 vs 1450) ECMP ile MSS uyumsuzluğu doğurdu. Aktif/pasif rota ile p95 latency’de %12 iyileşme, TCP retransmit’lerde %0,7 azalma gördük.

ZeroTier ile Kurulum: Esnek Sanal Anahtar

Controller ve Ağ Oluşturma

ZeroTier’da ya yönetilen cloud controller’ı ya da kendi controller’ınızı kullanabilirsiniz. Ağ oluşturduktan sonra node’ları join edip managed routes ve flow rules ile yetkiyi yönetirsiniz.

# Kurulum
curl -s https://install.zerotier.com | sudo bash
sudo zerotier-cli info
# > 200 info 9c3d1a2b3c 1.12.2 ONLINE

# Ağa katıl
sudo zerotier-cli join 8056c2e21c000001
sudo zerotier-cli listnetworks
# Routes ve assigned addresses'i kontrol et

Network ayar örneği (controller tarafı):

{
  "config": {
    "name": "acme-mesh",
    "v4AssignMode": {"zt": true},
    "v6AssignMode": {"rfc4193": true},
    "routes": [
      {"target": "10.77.10.0/24", "via": null},
      {"target": "10.77.20.0/24", "via": null},
      {"target": "10.77.30.0/24", "via": null}
    ]
  },
  "rules": [
    {"rule": "drop not ethertype ipv4"},
    {"rule": "accept ipprotocol tcp dport 22 from 10.77.0.0/16"},
    {"rule": "accept ipprotocol tcp dport 443"},
    {"rule": "accept ipprotocol tcp dport 5432 from 10.77.20.0/24 to 10.77.10.10/32"},
    {"rule": "drop"}
  ]
}

ZeroTier’ın avantajı, L2’ye yakın davranabilmesi ve aynı ağda discovery gerektiren bazı protokollerde (örn. konsensus cluster management araçları) işleri kolaylaştırması. Dezavantaj, yanlış kurgulanırsa geniş yayın (broadcast) ve ARP gibi L2 davranışları overlay üzerinde gürültü yaratabilir. Bunu rules ile sıkılaştırmak şart.

Site‑to‑Site Köprüleme: Rota İlanı, MTU ve NAT

Tailscale Subnet Router ve Route Approval

Prod’da autoApprovers.routes ile ilan edilen ağları otomatik onaylattık. Geriye sadece sağlıklılık kontrolü kaldı. Bir rota düştüğünde alert üretiyoruz, failover node “advertise” etmeye hazır bekliyor.

# Pasif node hazır beklerken (advertise yok)
sudo tailscale up --authkey=tskey-auth-... --hostname=fra-edge-02 --accept-routes=true

# Failover anında
sudo tailscale up --advertise-routes=10.77.10.0/24 --snat-subnet-routes=false

ZeroTier Managed Routes ve Policy

ZeroTier tarafında aynı alt ağlar için managed routes kullanıyoruz. Health check script’i RTT ve packet loss eşiklerini aştığında controller API’si ile route “via” güncelleniyor. Bu, saniye mertebesinde failover sağlıyor.

MTU ve MSS Clamping

Farklı sağlayıcılar, farklı MTU. WireGuard üzerinde genelde 1420 civarı güvenli. Aksi halde “ICMP Fragmentation Needed” düşmüyor ve sessizce performans çöküyor. TCP için MSS clamp, UDP için Path MTU Discovery’ye güvenmek bazen hayal kırıklığı yaratır. Aşağıdaki kural, overlay arayüzünde MSS’i sınırlar:

# nftables örneği
sudo nft add table inet mangle
sudo nft add chain inet mangle prerouting { type filter hook prerouting priority -150; }
sudo nft add rule inet mangle prerouting tcp flags syn tcp option maxseg size set 1360

Bu projede bu ayarla p95 latency’de 18–25 ms arası iyileşme ve yük altında TCP retransmit oranında %0,4 azalma gördük.

Gözlemlenebilirlik: Metrikler, Loglar ve Paneller

Hangi Metrikleri İzledik?

İlk hafta “güzel çalışıyor” hissine güvenmedik. Overlay mesh için şu metrikleri topladık:

– Handshake süresi (p50/p95)
– NAT traversal başarı oranı (% direct vs % DERP/relay)
– RTT ve jitter (p50/p95)
– Packet loss
– Bytes in/out, connections/s
– Route health (ilan edilen prefix’ler ve reachable state)

Toplama ve Paneller

Tailscale’de tailscale status --json ve tailscale netcheck çıktıları export edildi. ZeroTier’da zerotier-cli listpeers ve controller API’leri kullanıldı. Exporter’lar Prometheus’a scrape edildi; Grafana’da “Mesh Health” panelleri kurduk.

# Örnek netcheck
sudo tailscale netcheck
# Report:
# * UDP: true
# * IPv4: yes, 203.0.113.10:41641
# * Latency: 24ms (direct), 41ms (via derp-eu)
# * MappingVariesByDestIP: false

Alert kuralları:

# PrometheusRule (basitleştirilmiş)
- alert: MeshRttDegraded
  expr: histogram_quantile(0.95, sum(rate(mesh_rtt_bucket[5m])) by (le, link)) > 200
  for: 10m
  labels: {severity: warning}
  annotations:
    summary: "p95 RTT yavaş"

- alert: MeshRelayUsageSpike
  expr: increase(mesh_relay_bytes_total[15m]) / increase(mesh_bytes_total[15m]) > 0.3
  for: 15m
  labels: {severity: critical}
  annotations:
    summary: "Relay kullanım oranı arttı"

Güvenlik: Kimlik, Anahtar Döndürme, Segmentasyon

Kimlik ve SSO

Makine kimlikleri SSO ile bağlandı. Tailscale’de tag’leri sahiplik modeline bağladık; ZeroTier’da member authorization’ı GitOps’a taşıdık. Her node’un katılımı code review’dan geçti; “join‑request” PR kapanmadan prod ağa erişim yok.

Anahtar Yaşam Döngüsü

Pre‑auth key’ler kısa ömürlü (24h) ve tek kullanımlık. Dönüşüm pipeline’ı her deploy’da anahtarları yeniliyor. Tailscale’de ephemeral node’lar test için kullanışlı; prod’da kalıcı kimlik tercih ettik. ZeroTier node secret’ları SOPS ile şifreli olarak repo’da tutuldu.

Segmentasyon

Prod/staging/ops ağlarını ayrı overlay ID’lerine böldük. Cross‑env erişimler sadece belirli port/protokollere, belirli etiketlere izinli. Bu sayede lateral movement yüzeyini daralttık.

CI/CD ve IaC: Tekrarlanabilirlik

Terraform ile Policy Yönetimi

Policy dosyaları ve ZeroTier network tanımları Terraform ile yönetildi. Plan/apply öncesi dry‑run ve canary stage’leri var.

# Terraform (özet örnek)
provider "tailscale" {}
provider "zerotier" {}

resource "tailscale_acl" "main" {
  acl = file("acl.json")
}

resource "zerotier_network" "mesh" {
  name = "acme-mesh"
  assign_ipv4 = true
  route {
    target = "10.77.10.0/24"
  }
}

GitOps Akışı

# GitHub Actions
name: mesh-policy
on:
  pull_request:
    paths: ["infra/mesh/**"]
  push:
    branches: ["main"]
    paths: ["infra/mesh/**"]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: terraform fmt -check
      - run: terraform init
      - run: terraform validate
      - run: terraform plan -out tfplan
  apply:
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    needs: validate
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: terraform init
      - run: terraform apply -auto-approve tfplan

Ansible ile Host Hazırlığı

---
- hosts: vps
  become: yes
  tasks:
    - name: Install Tailscale
      shell: curl -fsSL https://tailscale.com/install.sh | sh
      args:
        warn: false
    - name: Up tailscale
      command: tailscale up --authkey={{ ts_authkey }} --ssh --accept-dns=true

- hosts: zerotier
  become: yes
  tasks:
    - name: Install ZeroTier
      shell: curl -s https://install.zerotier.com | bash
    - name: Join network
      command: zerotier-cli join {{ zt_network_id }}

Gerçek Hikayeler: İki Post‑Mortem Notu

PM‑01: DERP Üzerinden Sessiz Yavaşlama

Belirli saatlerde Avrupa düğümleri arası RTT 24 ms’den 55 ms’ye çıkıyordu. Paket kaybı yoktu. tailscale netcheck raporlarında direct “true” görünürken akış verisinde relay bytes oranı %28’e vurmuş. Kök neden: Bir sağlayıcı inbound UDP rate limit’ini gece yedeklemesi sırasında sıkılaştırıyordu; NAT mapping süreleri kısalıyor, bağlantılar relay’e düşüyordu. Çözüm: Health check’e “relay ratio” metriğini dahil ettik; oran %10’u geçerse alternatif çıkış IP’siyle yeni mapping oluşturduk. Maliyet: DERP trafiği düştü, p95 RTT 27 ms’ye indi.

PM‑02: MTU ve Parçalanma Kabusu

APAC kullanıcılarının bazı istekleri 2–3 saniye bekledikten sonra zaman aşımına düşüyordu. ICMP bloklu olduğu için PMTUD başarısızdı. Overlay üstünde büyük TLS paketleri parçalanamıyordu. Kök neden: Bir sağlayıcı 1500 MTU, diğeri 1472; WireGuard overhead’i ile efektif MTU 1420’nin altına inmişti. Çözüm: MSS clamping ve tailscale up --mtu=1280 sınırı. Sonuç: p95 2.3s → 410ms; retransmit %1.9 → %0.6.

Değişiklik Yönetimi: Canary mi Blue/Green mi?

Network policy değişikliklerinde canary yaklaşımını tercih ettim. Bir/iki node’u yeni ACL ve rota seti ile güncelliyoruz, panellerde 30 dakika gözlüyoruz. Eğer p95 RTT +%10’dan fazla artarsa ya da relay ratio %20’ye yaklaşırsa otomatik geri alıyoruz. DNS tarafında TTL tartışması hep çıkar: düşük TTL hızlı toparlar ama kontrol düzlemi yükünü ve cache sapmalarını artırır. Biz overlay içi domainlerde 300s, public yüzlerde 60s kullandık ve SLA’yı bunu varsayarak yazdık.

Operasyon Runbook’u: Gece Alarmına Kısa Cevap

Bağlantı Sorununda İlk 10 Dakika

  • Durum: tailscale status --json veya zerotier-cli listpeers.
  • Netcheck: tailscale netcheck; relay ratio’yu not al.
  • MTU: test ping: ping -M do -s 1372 100.x.x.x.
  • Rotalar: Tailscale’de --advertise-routes aktif mi; ZeroTier managed routes up mı?
  • Failover: pasif subnet router’ı advertise et.
  • Log: sağlayıcı firewall/NAT değişikliklerini teyit et.

Planlı Değişiklik

  • PR: Terraform plan + policy diff.
  • Canary: 1–2 node, 30 dk gözlem.
  • Rollout: batch ile genişlet, her batch arası 10 dk.
  • Rollback: tek komutla eski policy ve rota setine dön.

Maliyet ve Performans: Gerçekçi Beklentiler

Overlay maliyeti çoğunlukla “zaman kazanma”dır. Relay kullanım oranı düşükse ek latency ihmal edilebilir. Ancak yüksek throughput (örn. veri replikasyonu) senaryolarında doğrudan peering veya site‑to‑site IPsec hâlâ ekonomik olabilir. Bizim vakada replikasyon trafiğini overlay dışına aldık (dedike tüneller), uygulama trafiğini overlay’de tuttuk; toplam bant genişliği maliyeti %18 düştü, operasyon karmaşıklığı ise ciddi azaldı.

Kapanış: Ayağa Kalkan, Yönetilebilir Bir Mesh

O geceki pager olayı bize şunu öğretti: Farklı sağlayıcılar arasında istikrarlı bir yol bulmak, tek tek tünelleri idare etmekten daha önemli. Tailscale/ZeroTier ile özel ağ kurduğunuzda, kimlik temelli erişim, otomatik NAT traversal ve merkezî policy yönetimi birlikte çalışıyor. Metriklere yaslanın: RTT, relay ratio, packet loss ve handshake süresi; panellerde bunlar yeşilse rahat uyursunuz. Operasyonel olarak runbook’ları netleştirin; “ilk 10 dakika” ve “planlı değişiklik” adımlarını ekibin cebine koyun. Güvenlikte default deny ve kısa ömürlü anahtarlar sizi ileri taşır. Değişiklikleri canary ile yaymak, DNS TTL ve SLA tartışmalarını somut veriye bağlar. Bugün küçük başlayın: iki VPS ve bir subnet router. Yarın üç kıtada beş sağlayıcıya yayılmak, aynı disiplinle sadece bir pipeline işi olacak. Ekibinize söyleyin: Bu mesh, sizin kadar iyi; runbook’ları okuyun, panellere bakın, küçük değişiklikleri sık sık yapın. Gece pager çalarsa, ne yapacağınızı biliyorsunuz.

Sıkça Sorulan Sorular

WireGuard tabanlı, hızlı kurulum ve güçlü ACL istiyorsanız Tailscale pratik. L2/L3 esnekliği, gelişmiş sanal switch kuralları ve özelleştirilebilir controller gerekirse ZeroTier. Yüksek throughput replikasyon içinse overlay’i yalnızca kontrol ve app trafiğinde bırakıp veri kanalı için ayrı tünel düşünün.

Farklı sağlayıcı karışık olduğunda çoğu zaman evet. 1420 güvenli bir başlangıç, sorun sürerse 1280’e inip MSS clamping ekleyin. ICMP kapalı ortamlarda PMTUD çalışmayabilir; bu durumda manuel MSS clamping kurtarıcıdır.

Evet. Tailscale’de --accept-routes ve --advertise-routes ile site‑to‑site’i açarken host’lar arası doğrudan erişim korunur. ZeroTier’da managed routes ve flow rules ile hangi alt ağlara kimlerin erişeceğini detaylıca kısıtlayabilirsiniz.