WireGuard Protokolünün İçi: Kriptografi, Crypto-Routing ve Linux Çekirdeği Desteği

WireGuard Protokolünün İçi: Kriptografi, Crypto-Routing ve Linux Çekirdeği Desteği

WireGuard protokolü kriptografi ChaCha20 Curve25519 görseli

WireGuard’ın ortaya çıkışı VPN dünyasında bir dönüm noktası oldu. OpenVPN’in TLS karmaşıklığı, IPsec’in IKE müzakere ağırlığı ve özellikle yıllar içinde birikmiş yamalı protokol tasarımları yerini; 4000 satırın altında kernel modülü, modern kriptografi ve son derece sade bir konfigürasyon felsefesiyle yeni bir paradigmaya bıraktı. Bu yazıda WireGuard’ı bir uygulama tutorial’ı olarak değil, protokolün iç işleyişi perspektifinden inceleyeceğiz. Noise framework, ChaCha20-Poly1305, crypto-routing, Linux kernel modülünün tasarımı ve MikroTik RouterOS 7.x üzerindeki implementasyonu bu yazının ana eksenidir.

Bir Linux sistem mühendisi olarak yıllarca OpenVPN ve strongSwan ile boğuştuktan sonra WireGuard’a geçtiğimde duyduğum his, Python yazan birinin Go’ya geçtiğinde hissettiğine benziyor: daha az satır, daha az olasılık, daha öngörülebilir davranış. Bu yazıyı bitirdiğinizde WireGuard’ı sadece “kurmuş” değil içeriden tanımış olacaksınız.

WireGuard ağ paketi protokol akış analizi

Tasarım Felsefesi: “Opinionated” Olmak

WireGuard’ın yaratıcısı Jason A. Donenfeld’in açıkça benimsediği felsefe şudur: “Vendor seçimleri yapmak, kullanıcıya seçim yükü bindirmemek”. OpenVPN ve IPsec’in birden fazla cipher suite, çoklu key exchange, çoklu auth backend desteklemesinin aksine WireGuard tek bir cipher kombinasyonuyla gelir, tek bir handshake mantığıyla çalışır. Bu yaklaşımın faydaları:

  • Atak yüzeyi sıfıra yakın: Cipher downgrade attack mümkün değil; çünkü alternatif yok.
  • Test edilebilirlik: Formal verification mümkün; protokol Tamarin Prover ile matematiksel olarak kanıtlanmıştır.
  • Bakım kolaylığı: 4000 satır kod = 4000 satır audit. OpenVPN ~70.000 satır, strongSwan ~400.000 satır.

Bu felsefeyi anladığınızda WireGuard’ın neden “tek bir [Interface] ve [Peer] bloğu” ile yetindiğini, neden TLS olmadığını, neden DH parametresi sunmadığını da anlarsınız. Sade görünmesi gerçekten basit olduğu için değil; doğru basitleştirilmiş olduğu içindir.

Noise Protocol Framework: WireGuard’ın Kalbi

WireGuard handshake’i Trevor Perrin tarafından tasarlanan Noise Protocol Framework‘ün özel bir varyantı olan Noise_IKpsk2 patterninin üzerine inşa edilmiştir. Bu paterni açmak istersek:

  • IK: Initiator’ın anahtarı statik bilinir (Identity Known), Responder’ın anahtarı da bilinir. Yani her iki tarafın public key’leri önceden out-of-band paylaşılmıştır.
  • psk2: İkinci mesajda opsiyonel olarak Pre-Shared Key karıştırılır. Bu, kuantum sonrası güvenlik açısından önemli bir belt and suspenders katmanıdır.

Handshake iki mesajdan oluşur: Initiation ve Response. Bu kadar. Kullanılan ilkel kriptografi yapı taşları:

  • Curve25519: Elliptic Curve Diffie-Hellman (ECDH) için. 32 baytlık anahtarlar, sabit zaman implementasyonu, kanal-yan saldırılara karşı dirençli.
  • BLAKE2s: Hash fonksiyonu. SHA-2’den hızlı, SHA-3 finalisti BLAKE’in soyundan gelir, MAC modu yerleşik.
  • ChaCha20-Poly1305: AEAD (Authenticated Encryption with Associated Data) şifreleyici. Donanım hızlandırması olmayan ARM/RISC sistemlerde AES-GCM’den önemli ölçüde daha hızlıdır.
  • HKDF: HMAC-based Key Derivation Function. Tek bir paylaşılan sırdan birden fazla anahtar üretmek için.
  • SipHash24: Hash table flooding’e karşı koruma için, paket alış-veriş tarafında.

Handshake Adımları

Tipik bir handshake şu sırayı izler. Burada I = Initiator, R = Responder, S = Statik, E = Ephemeral anahtardır:

# Mesaj 1: I -> R
ephemeral_I = curve25519_generate()
ck = HASH("Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s")
ck = HKDF(ck, ephemeral_I.public)
shared = DH(ephemeral_I, R.static_public)
ck, k = HKDF(ck, shared)
encrypted_static_I = AEAD(k, 0, I.static_public, hash)
shared2 = DH(I.static, R.static_public)
ck, k2 = HKDF(ck, shared2)
encrypted_timestamp = AEAD(k2, 0, tai64n_now(), hash)

# Mesaj 2: R -> I
ephemeral_R = curve25519_generate()
ck = HKDF(ck, ephemeral_R.public)
ck = HKDF(ck, DH(ephemeral_R, ephemeral_I.public))
ck = HKDF(ck, DH(ephemeral_R, I.static_public))
ck, t, k3 = HKDF(ck, PSK)
encrypted_empty = AEAD(k3, 0, "", hash)

Bu iki mesajın sonunda her iki taraf bir transport key pair türetmiş olur. Asıl veri akışı bu anahtarlarla AEAD altında şifrelenir. Önemli bir nokta: handshake’in tamamı 148 bayt + 92 bayt’tan ibarettir. Toplam 240 bayt. OpenVPN TLS handshake’i 5-10 paket ve birkaç kilobayt sürer.

Rekeying ve Anti-Replay

Her transport oturum maksimum 120 saniye veya 2^60 mesaja kadar geçerlidir. Bu süre dolmadan WireGuard otomatik olarak handshake’i tekrarlar (rekey). Replay attack koruması ise sabit boyutta sliding window (varsayılan 2048) ile sağlanır. Bu, IPsec’in Anti-Replay Window mantığına çok benzer ancak daha sade implemente edilmiştir.

Cookie Mechanism ve DoS Direnci

Handshake mesajlarının doğrulanması ucuz olmadığından, WireGuard’a yapılacak yoğun handshake floodu kaynak tüketebilir. Bu durumda responder, initiator’a cookie mesajı gönderir. Initiator, sonraki handshake’lerinde bu cookie’yi MAC2 alanına ekler ve bu sayede responder pahalı DH işlemine başlamadan önce gerçek bir client olduğunu doğrular. Bu mekanizma DTLS-SRTP’deki cookie değişimine benzer ancak daha kompakttır.

Crypto-Routing: WireGuard’ın En Devrimci Fikri

WireGuard’ı diğer VPN’lerden ayıran teknik fikirlerin en güzeli crypto-routing‘dir. Klasik VPN’lerde “policy” ve “key” iki ayrı kavramdır: hangi trafik tünelden gidecek (selector/SAD) ve hangi anahtarla şifrelenecek (SAD’deki anahtar). WireGuard bu iki kavramı birleştirir.

Mantık şudur: Her peer’in bir AllowedIPs listesi vardır. Bu liste, hem outbound hem inbound trafik için çift yönlü çalışır.

  • Outbound (giden): Çekirdek, hedef IP’sini kontrol eder. Hangi peer’in AllowedIPs‘ine düşüyorsa o peer’in anahtarıyla şifrelenir ve o peer’in endpoint’ine gönderilir. Bu bir routing table lookup‘tan farksızdır.
  • Inbound (gelen): Şifre çözüldükten sonra, paketin kaynak IP’si kontrol edilir. Eğer bu IP, paketi gönderen peer’in AllowedIPs‘inde yoksa paket düşürülür. Bu otomatik bir reverse-path filter‘dır.

Bu basit kuralın getirisi büyüktür:

  • IPsec’in karmaşık SPD/SAD ayrımı yok.
  • “Hangi paket hangi tünele girer” sorusu artık routing decision’ın doğal bir parçası.
  • Bir peer’in çalabileceği maximum trafik yelpazesi onun AllowedIPs‘iyle sınırlı; deceptive source IP spoofing imkânsız.

Pratik bir örnek: hub-and-spoke topolojide hub’ın peer config’i şu şekildedir:

[Interface]
PrivateKey = 
Address = 10.99.0.1/16
ListenPort = 51820

[Peer] # branch-01
PublicKey = 
AllowedIPs = 10.99.1.0/24, 10.10.1.0/24

[Peer] # branch-02
PublicKey = 
AllowedIPs = 10.99.2.0/24, 10.10.2.0/24

Hub’a düşen “10.10.2.5” hedefli paket otomatik olarak branch-02’nin anahtarıyla şifrelenir; başka bir peer’in trafiğine karışmaz. Bu, açıkça yazılmış statik route’lar gibi davranır ama aslında dinamik bir lookup’tır.

Linux Kernel Modülü: WireGuard’ın Anatomisi

WireGuard’ın Linux 5.6’da mainline’a girmesi (Mart 2020) önemli bir milestone’du. Linus Torvalds bile “WireGuard’a aşkım var” tarzı bir mesaj yayınlamıştı. Kernel modülünün yapısı şöyledir:

  • drivers/net/wireguard/: Modülün ana dizini. ~4000 satır C.
  • device.c: Network device (wg0) registration ve queue yönetimi.
  • noise.c: Handshake state machine ve key derivation.
  • cookie.c: DoS direnci için cookie işleme.
  • peer.c, allowedips.c: Peer veri yapıları ve allowed-ips trie tablosu (modified radix tree).
  • send.c, receive.c: Paket I/O path.

İlginç tasarım kararları:

  • Allowed-ips lookup, modified radix trie ile O(log n) zamanda yapılır; nftables veya iptables’a göre lookup pathi çok daha sıkıdır.
  • Crypto işlemleri ksoftirqd context’inde, per-CPU paralel kuyruklarda işlenir. Bu sayede modern multi-core sistemlerde lineer ölçek tutturulur.
  • Skbuff allocation paterni optimize edilmiş, GRO/GSO ile uyumlu çalışır.

Modülü yüklemek artık birkaç satır:

# Linux 5.6+ kernel'inde yerleşik
modprobe wireguard
# Eski kernel'lerde DKMS ile
apt install wireguard-dkms wireguard-tools

# Kullanıcı alanı aracı
wg genkey | tee privatekey | wg pubkey > publickey
ip link add dev wg0 type wireguard
ip address add 10.99.0.1/24 dev wg0
wg setconf wg0 wg0.conf
ip link set up dev wg0

wireguard-tools paketindeki wg-quick betiği bu komutları otomatize eder ve sysctl + PostUp/PostDown hook’ları ekler. Production deployment’larında ben genelde wg-quick yerine doğrudan systemd-networkd .netdev + .network dosyaları kullanmayı tercih ediyorum; daha öngörülebilir.

WireGuard Linux kernel modülü kaynak kodu

Performans: ChaCha20-Poly1305 vs AES-GCM

Modern x86 CPU’lar AES-NI ile AES-GCM’i hardware accelerated yapar. Bu durumda AES-GCM, ChaCha20-Poly1305’ten 2-3x daha hızlı olabilir. Peki WireGuard neden AES yerine ChaCha tercih etti?

  • Universal hız: AES-NI olmayan platformlarda (ARMv7, RISC-V, embedded MIPS) ChaCha20-Poly1305 yazılımda 4-5x daha hızlı. Yani router’lar, mobil cihazlar ve IoT için doğal seçim.
  • Side-channel direnç: ChaCha20 saf XOR/ADD operasyonlarıyla çalışır; cache-timing saldırılarına karşı daha dirençli. AES’in S-box’ları yıllarca cache-timing saldırılarının hedefi olmuştur.
  • Tek bir cipher: WireGuard’ın felsefesi: “bir cipher seçilir, her platformda yeterince hızlı olmalı”. Daha sonra AES eklemek protokol karmaşıklığını artırırdı.

Pratik rakamlar (10 Gbit fiziksel, Xeon E5-2680 v4):

  • WireGuard tek peer, single-stream iperf3: 4-6 Gbit/s
  • WireGuard, çoklu peer + multi-stream: 9.5 Gbit/s (saturated)
  • OpenVPN UDP/AES-256-GCM, single-stream: 1-1.5 Gbit/s
  • IPsec/AES-GCM, hardware crypto enabled: 5-7 Gbit/s

MikroTik 7.x Entegrasyonu

RouterOS 7.x ile birlikte WireGuard MikroTik’in standart enstrümanlarından biri haline geldi. CLI’de:

/interface/wireguard
add name=wg-corp listen-port=51820 
    private-key="cMtP...=" mtu=1420

/interface/wireguard/peers
add interface=wg-corp 
    public-key="aZQv...=" 
    allowed-address=10.99.0.10/32,10.10.0.0/24 
    endpoint-address=peer.example.com endpoint-port=51820 
    persistent-keepalive=25s

/ip/address
add address=10.99.0.1/24 interface=wg-corp

/ip/route
# AllowedIPs zaten route oluşturur, ekstra rotaya genelde gerek yok

Önemli noktalar:

  • MikroTik implementasyonu kernel modülü kullanmaz; RouterOS’un kendi networking stack’ına entegredir (Linux kernel’i değil, FreeRTOS benzeri bir kernel üzerinde çalışır CCR’larda). Performans Linux’a göre biraz geride kalabilir.
  • RouterOS 7.10+ ile multi-core crypto desteği geldi. Daha eski sürümlerde tek-core sınırı vardı.
  • WireGuard interface’i de aynen ether gibi davranır; firewall, mangle, queue tree, NAT kuralları aynen uygulanabilir.

Linux Peer Tarafında MikroTik ile Konuşmak

# /etc/wireguard/wg0.conf (Linux client tarafında)
[Interface]
PrivateKey = 
Address = 10.99.0.10/24
DNS = 10.99.0.1

[Peer]
PublicKey = 
Endpoint = router.example.com:51820
AllowedIPs = 10.99.0.0/24, 10.10.0.0/24
PersistentKeepalive = 25

wg-quick up wg0 ve bitti. Test:

# Tünel durumu
wg show wg0

# Latency
ping -c 5 10.99.0.1

# Bandwidth
iperf3 -c 10.99.0.1 -t 30 -P 4

Roaming, NAT Traversal ve Persistent Keepalive

WireGuard endpoint adresleri dinamiktir. Bir peer’in endpoint’i değiştiğinde (örneğin mobil cihaz WiFi’den 4G’ye geçtiğinde), WireGuard otomatik olarak yeni adresi öğrenir. Bunun mekanizması şudur: handshake mesajı geldiğinde, paketin geldiği UDP src adresi peer için yeni endpoint olarak kaydedilir. Bu işlem “endpoint discovery” değil “endpoint update” olarak çalışır; sessiz bir roaming.

NAT arkasındaki peer’ler için PersistentKeepalive = 25 seçeneği kritiktir. Her 25 saniyede bir küçük bir keepalive paketi gönderilerek NAT eşleştirmesinin (binding) düşmesi engellenir. 25 saniye değeri ampiriktir: çoğu carrier-grade NAT’ın binding timeout’u 30-60 saniye civarındadır.

Production Notları ve Best Practices

  • MTU: WireGuard 60 byte overhead ekler (IP + UDP + WG header). 1500 MTU’lu link üzerinde wg interface MTU’sunu 1420 yapın. PPPoE üzerinde 1380, mobile bağlantılarda 1280 daha güvenli.
  • Pre-shared keys: Quantum resistance için PSK ekleyin. wg genpsk ile üretip her peer için ayrı ayrı paylaşın.
  • Key rotation: Yılda en az bir kez peer key’lerini değiştirin. Otomatize edilebilir (Ansible vault + scripted rollout).
  • Monitoring: wg show all dump çıktısı parse edilerek Prometheus’a node_exporter textfile collector ile beslenebilir. Handshake yaşı, RX/TX byte sayaçları izlenebilir.
  • IPv6: AllowedIPs = ::/0 ile IPv6 trafiği de tünelden geçirilebilir. Çift stack ortamlarda DNS sızıntısını engellemek için fwmark + policy routing kullanın.
  • Logging: WireGuard çok sessizdir. dynamic_debug ile geçici tanılama yapılabilir: echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control

Observability: Prometheus + Grafana ile WireGuard İzleme

Üretim ortamında WireGuard’ı izlemek için kullandığım küçük bir exporter snippet’i:

#!/usr/bin/env bash
# /usr/local/bin/wg-exporter.sh
OUT=/var/lib/node_exporter/textfile/wireguard.prom
echo "# HELP wg_peer_handshake_seconds Last handshake epoch" > $OUT.tmp
echo "# TYPE wg_peer_handshake_seconds gauge" >> $OUT.tmp
wg show all dump | awk 'NR>1 && NF>=8 {
  iface=$1; peer=$2; hs=$6; rx=$7; tx=$8;
  printf "wg_peer_handshake_seconds{iface="%s",peer="%s"} %sn", iface, peer, hs;
  printf "wg_peer_rx_bytes{iface="%s",peer="%s"} %sn", iface, peer, rx;
  printf "wg_peer_tx_bytes{iface="%s",peer="%s"} %sn", iface, peer, tx;
}' >> $OUT.tmp
mv $OUT.tmp $OUT

Bu script cron ile her 30 saniyede bir çalıştırılır; Prometheus node_exporter textfile collector tarafından scrape edilir. Grafana’da “handshake 180 saniyeden eski” alert’i kurarak sessizce kopan peer’leri yakalayabilirsiniz.

ChaCha20-Poly1305 şifreleme ve VPN güvenlik kavramı

Formal Verification ve Akademik Onay

WireGuard’ı diğer VPN protokollerinden ayıran bir diğer özellik, akademik olarak formally verified olmasıdır. Tamarin Prover ile yapılan analiz (Donenfeld + Milner, 2018) WireGuard handshake’inin şu güvenlik özelliklerini matematiksel olarak kanıtlamıştır:

  • Authentication (mutual)
  • Session uniqueness
  • Forward secrecy
  • Identity hiding (responder)
  • KCI (Key Compromise Impersonation) resistance
  • Replay attack resistance

OpenVPN için karşılaştırılabilir kapsamlı bir formal proof bulunmamaktadır. IPsec’in IKEv2’si için zayıf bir formal model vardır ancak protokol kompleksitesi tam kapsamı zorlaştırmaktadır. Bu, akademik dünyada WireGuard’ın saygı kazanmasının temel nedenidir.

Sık Sorulan Sorular

WireGuard neden TCP üzerinden çalışmıyor?

WireGuard sadece UDP destekler. TCP-over-TCP problemi (segment retransmission’ın hem tünel hem inner stream’de yaşanması) WireGuard’ın deliberately kaçındığı bir tasarım kararıdır. UDP-blokeli ortamlar için udp2raw, wstunnel veya AmneziaWG gibi obfuscation çözümleri kullanılabilir.

OpenVPN’den WireGuard’a geçiş ne kadar sürer?

Tipik bir SME ortamı için: konfigürasyon migration 1-2 gün, peer paralel deployment 1 hafta, klimatize edilmiş cut-over 1 ay. Aralarında protocol farkı olmadığından, kullanıcı deneyimi açısından geçiş şeffaftır. Sadece istemci yazılımı değişir.

WireGuard pre-shared key olmadan post-quantum güvenli mi?

Hayır. Curve25519 Shor algoritması altında kuantum bilgisayar tarafından çözülebilir. Bu nedenle PresharedKey alanı sunulmuştur. Symmetric PSK kuantum saldırılarına karşı doğrudan direnç sağlar. NIST PQC standartlarının kernel-level entegrasyonu beklenirken, PSK ekleme önerilen yaklaşımdır.

WireGuard ile site-to-site mesh kurulabilir mi?

Evet, ancak her peer’in tüm diğer peer’lerin public key’ini bilmesi gerekir. Bu manuel olarak çekilmez hale geldiğinde Headscale, Tailscale, Netbird veya Innernet gibi koordinatör araçlar devreye girer. Bunlar bir control plane sunar; peer’ler arasındaki anahtar dağıtımını otomatize eder.

MikroTik üzerinde WireGuard performansı Linux’a göre nasıl?

RB5009 üzerinde ~1 Gbit/s, CCR2004 üzerinde ~3-4 Gbit/s typical. Aynı yapıdaki bir Linux server’da 8-10 Gbit/s mümkündür. MikroTik için software-only implementation darboğazdır; ARM çekirdeği üzerinde ChaCha20-Poly1305 yine de iyi performans verir ancak bir Xeon-D + AES-NI’nin gerisindedir.

WireGuard fail-over ve high availability nasıl sağlanır?

WireGuard kendi başına HA sunmaz; stateless tasarımı sayesinde keepalived/VRRP ile aktif-pasif yapı kurulabilir. Endpoint adresinin bir floating IP’ye işaret etmesi ve config’in iki node arasında sync olması yeterlidir. Active-active için ECMP + birden fazla peer endpoint kullanılır.

WireGuard ile zero-trust uyumlu mu?

Tamamen. Her peer’in kendine ait anahtarı vardır; kimlik kriptografik olarak doğrulanır; her bağlantı segment edilebilir (AllowedIPs ile). Headscale/Tailscale gibi koordinatörler ACL tabanlı micro-segmentation ekler; bu BeyondCorp tarzı bir zero-trust arc oluşturmak için yeterlidir.

Sonuç

WireGuard’ı sadece “modern VPN” diye geçiştirmek haksızlık olur. Protokol; kriptografi tasarımı, kernel implementasyonu ve crypto-routing kavramıyla VPN dünyasının on yıllık sorunlarına temiz cevaplar getirdi. Linux çekirdeğinde mainline olarak yer alması, MikroTik dahil tüm ana router platformlarına yayılması ve formal verification ile akademik onay kazanması onu yeni standart haline getirdi. Bir sonraki yazımızda WireGuard’ı Headscale ile mesh topolojide nasıl orkestre edeceğinizi ve adaptive firewall ile zero-trust ACL’leri nasıl ekleyeceğinizi inceleyeceğiz.

Devamı İçin Okumanızı Öneririz

Konuyu daha geniş ele almak için aşağıdaki yazılarımıza da göz atabilirsiniz:


Kaynaklar ve Daha Fazla Bilgi

Bu yazıdaki konuları derinleştirmek için aşağıdaki otoriter kaynaklara başvurabilirsiniz:

Mikrotikbox

Her projede size özel çözümler

Her projede size özel çözümler

Müşterilerimizin ihtiyaçlarını en üst düzeyde karşılamak için her projeye mükemmeliyetçi bir anlayışla yaklaşıyoruz. Teknolojinin en yeni ve en güçlü araçlarını kullanarak, her adımda kaliteyi ve verimliliği ön planda tutuyoruz. Bu sayede, standart çözümler yerine her müşterimize özel, ihtiyaçlarına tam anlamıyla uygun ve uzun vadeli başarı sağlayacak projeler geliştiriyoruz. Yenilikçi düşünce yapımız ve titiz çalışma prensiplerimizle, beklentileri aşan sonuçlar sunmayı hedefliyoruz.