Caddy ile Rate Limiting ve Güvenlik Katmanı Oluşturma

Modern web uygulamalarını yönetirken sadece uygulamanın çalışıyor olması yetmez. Her gün binlerce bot, scraper ve potansiyel saldırgan sunucularınıza çarpmaya devam eder. Caddy’nin sunduğu rate limiting ve güvenlik özellikleri, bu tehditlere karşı sağlam bir kalkan oluşturmanın en pratik yollarından biri. Bu yazıda gerçek dünya senaryoları üzerinden Caddy ile kapsamlı bir güvenlik katmanı nasıl kurulur, adım adım inceleyeceğiz.

Caddy’de Rate Limiting Neden Önemli?

Bir e-ticaret sitesi işlettiğinizi düşünün. Sabah 3’te aniden binlerce istek gelmeye başlıyor, veritabanı bağlantıları tükeniyor, uygulama yanıt veremez hale geliyor. Sabah uyandığınızda site çökmüş, müşteriler erişemiyor. Bu klasik bir brute force ya da DDoS senaryosu.

Rate limiting, bir IP adresinin ya da kullanıcının belirli bir zaman diliminde yapabileceği istek sayısını sınırlayarak bu tür saldırıları önler. Caddy, yerleşik modülleri ve ek eklentileriyle bu konuda oldukça güçlü bir araç.

Caddy’nin güvenlik ekosisteminde şunlar öne çıkar:

  • Rate limiting: İstek hızı kontrolü
  • IP filtreleme: Belirli IP’leri engelleme veya izin verme
  • Bot koruması: User-agent tabanlı filtreleme
  • Header manipülasyonu: Güvenlik başlıkları ekleme
  • Geo-blocking: Ülke bazlı erişim kontrolü (eklenti ile)

Caddy Kurulumu ve Temel Yapılandırma

Rate limiting özelliklerini kullanmadan önce sisteminizde güncel bir Caddy kurulumunun olması gerekiyor. Caddy’nin rate limiting için caddy-ratelimit modülünü kullanacağız.

# Caddy'yi xcaddy ile özel modüllerle derle
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest

xcaddy build 
  --with github.com/mholt/caddy-ratelimit 
  --with github.com/caddy-dns/cloudflare

# Derlenen binary'yi sistem dizinine taşı
sudo mv caddy /usr/local/bin/caddy
sudo chmod +x /usr/local/bin/caddy

# Versiyonu doğrula
caddy version

Eğer Docker kullanıyorsanız, özel bir Dockerfile ile de ilerliyebilirsiniz:

# Dockerfile
FROM caddy:builder AS builder

RUN xcaddy build 
    --with github.com/mholt/caddy-ratelimit

FROM caddy:latest
COPY --from=builder /usr/local/bin/caddy /usr/local/bin/caddy

Rate Limiting Temel Konfigürasyonu

Caddy’de rate limiting iki farklı şekilde yapılandırılabilir: Caddyfile formatı ve JSON API üzerinden. Günlük operasyonlarda Caddyfile çok daha okunabilir ve yönetilebilir olduğu için bu formatı tercih edeceğiz.

En basit rate limiting yapılandırması şu şekilde görünür:

example.com {
    rate_limit {
        zone dynamic {
            key    {remote_host}
            events 100
            window 1m
        }
    }
    
    reverse_proxy localhost:3000
}

Bu yapılandırma, her IP adresinin dakikada 100 istekten fazla yapmasını engeller. Sınır aşıldığında Caddy otomatik olarak 429 Too Many Requests döner.

Farklı Endpoint’ler İçin Farklı Limitler

Gerçek dünyada her endpoint aynı limiti hak etmez. Login sayfası çok daha agresif korunmalıyken, statik dosya sunumu daha gevşek olabilir:

api.sirketim.com {
    
    # Login endpoint - çok sıkı limit
    handle /auth/login {
        rate_limit {
            zone login_protection {
                key    {remote_host}
                events 5
                window 1m
            }
        }
        reverse_proxy localhost:3000
    }
    
    # API endpoint - orta sıkı limit
    handle /api/* {
        rate_limit {
            zone api_general {
                key    {remote_host}
                events 200
                window 1m
            }
        }
        reverse_proxy localhost:3000
    }
    
    # Genel rotalar - daha gevşek limit
    handle {
        rate_limit {
            zone general {
                key    {remote_host}
                events 500
                window 1m
            }
        }
        reverse_proxy localhost:3000
    }
}

Token Bazlı Rate Limiting

API’lerde yaygın bir pattern, farklı kullanıcılara farklı rate limit kotaları vermektir. Bunu Caddy’de header değerine göre yapabilirsiniz:

api.sirketim.com {
    
    # API anahtarı olan kullanıcılar için yüksek limit
    @authenticated {
        header X-API-Key *
    }
    
    handle @authenticated {
        rate_limit {
            zone authenticated_users {
                key    {http.request.header.X-API-Key}
                events 1000
                window 1m
            }
        }
        reverse_proxy localhost:3000
    }
    
    # API anahtarı olmayan kullanıcılar için düşük limit
    handle {
        rate_limit {
            zone anonymous_users {
                key    {remote_host}
                events 30
                window 1m
            }
        }
        reverse_proxy localhost:3000
    }
}

Güvenlik Başlıkları Ekleme

Rate limiting tek başına yeterli değil. HTTP güvenlik başlıkları, XSS, clickjacking ve diğer web saldırılarına karşı önemli bir savunma katmanı sağlar. Caddy’de bu başlıkları merkezi bir snippet olarak tanımlayıp tüm sitelerde yeniden kullanabilirsiniz:

(guvenlik_basliklari) {
    header {
        # XSS koruması
        X-XSS-Protection "1; mode=block"
        
        # Clickjacking önleme
        X-Frame-Options "SAMEORIGIN"
        
        # MIME sniffing önleme
        X-Content-Type-Options "nosniff"
        
        # Referrer politikası
        Referrer-Policy "strict-origin-when-cross-origin"
        
        # Content Security Policy
        Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;"
        
        # HSTS - 1 yıl
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        
        # Permissions Policy
        Permissions-Policy "geolocation=(), microphone=(), camera=()"
        
        # Sunucu bilgisini gizle
        -Server
    }
}

example.com {
    import guvenlik_basliklari
    reverse_proxy localhost:3000
}

api.example.com {
    import guvenlik_basliklari
    reverse_proxy localhost:8080
}

IP Tabanlı Erişim Kontrolü

Bazı senaryolarda belirli IP adreslerini veya aralıklarını tamamen engellemeniz gerekebilir. Bir müşteri projesinde yönetim panelini sadece ofis IP’sinden erişilebilir yapmak çok yaygın bir gereksinimdir:

admin.sirketim.com {
    
    # Sadece güvenilir IP'lere izin ver
    @guvenilir_ip {
        remote_ip 192.168.1.0/24 10.0.0.0/8 203.0.113.50
    }
    
    # Güvenilir IP değilse engelle
    @engellenen_ip {
        not remote_ip 192.168.1.0/24 10.0.0.0/8 203.0.113.50
    }
    
    handle @engellenen_ip {
        respond "Erişim Reddedildi" 403
    }
    
    handle @guvenilir_ip {
        import guvenlik_basliklari
        reverse_proxy localhost:8080
    }
}

Bilinen Kötü Aktörleri Engelleme

Birden fazla IP’yi veya aralığı tek seferde engellemek istediğinizde liste halinde tanımlayabilirsiniz:

(engellenen_ipler) {
    @kotu_ipler {
        remote_ip 
            185.220.0.0/16 
            89.248.160.0/19 
            91.108.0.0/16 
            45.148.10.0/24
    }
    
    handle @kotu_ipler {
        respond 403
    }
}

example.com {
    import engellenen_ipler
    import guvenlik_basliklari
    
    rate_limit {
        zone main {
            key    {remote_host}
            events 100
            window 1m
        }
    }
    
    reverse_proxy localhost:3000
}

Bot ve Scraper Koruması

User-agent filtreleme, kötü niyetli botları ve scraper’ları uzak tutmanın hızlı bir yolu. Tabii ki deneyimli bir saldırgan user-agent’ını değiştirebilir, ama bu basit botların büyük çoğunluğunu durdurur:

(bot_korumasi) {
    @kotu_botlar {
        header User-Agent *sqlmap*
        header User-Agent *nikto*
        header User-Agent *nmap*
        header User-Agent *masscan*
        header User-Agent *zgrab*
        header User-Agent *python-requests/2.1*
        header User-Agent *curl/7.29*
        header User-Agent *Go-http-client/1.1*
    }
    
    handle @kotu_botlar {
        respond 403
    }
    
    # Boş user-agent'ları da engelle
    @bos_user_agent {
        not header User-Agent *
    }
    
    handle @bos_user_agent {
        respond 403
    }
}

Gerçek Dünya Senaryosu: E-Ticaret Sitesi

Şimdi tüm bu yapılandırmaları birleştiren kapsamlı bir e-ticaret senaryosu oluşturalım. Bu senaryo, hem performansı hem de güvenliği dengeleyen bir yapılandırmayı gösteriyor:

{
    # Global ayarlar
    email [email protected]
    
    # Log formatı
    log {
        output file /var/log/caddy/access.log {
            roll_size 100mb
            roll_keep 10
        }
        format json
    }
}

# Global snippet'lar
(guvenlik_basliklari) {
    header {
        X-XSS-Protection "1; mode=block"
        X-Frame-Options "SAMEORIGIN"
        X-Content-Type-Options "nosniff"
        Referrer-Policy "strict-origin-when-cross-origin"
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
        -Server
        -X-Powered-By
    }
}

(bot_korumasi) {
    @kotu_bot {
        header User-Agent *sqlmap*
        header User-Agent *nikto*
        header User-Agent *masscan*
    }
    handle @kotu_bot {
        respond 403
    }
}

(engellenen_ipler) {
    @kotu_ip {
        remote_ip 185.220.0.0/16 89.248.160.0/19
    }
    handle @kotu_ip {
        respond 403
    }
}

# Ana site
magazam.com {
    import guvenlik_basliklari
    import bot_korumasi
    import engellenen_ipler
    
    # Genel rate limit
    rate_limit {
        zone genel {
            key    {remote_host}
            events 300
            window 1m
        }
    }
    
    # Checkout endpoint - sıkı limit
    handle /checkout* {
        rate_limit {
            zone checkout {
                key    {remote_host}
                events 20
                window 5m
            }
        }
        reverse_proxy localhost:3000
    }
    
    # Login - çok sıkı
    handle /giris {
        rate_limit {
            zone login {
                key    {remote_host}
                events 5
                window 10m
            }
        }
        reverse_proxy localhost:3000
    }
    
    # Statik dosyalar - caching ile
    handle /static/* {
        root * /var/www/magazam/static
        file_server {
            precompressed gzip br
        }
        header Cache-Control "public, max-age=31536000, immutable"
    }
    
    # Ana uygulama
    handle {
        reverse_proxy localhost:3000 {
            health_uri /health
            health_interval 30s
            health_timeout 5s
        }
    }
    
    # Gzip sıkıştırma
    encode gzip
}

# Admin paneli - sadece iç ağ
admin.magazam.com {
    @yetkisiz {
        not remote_ip 10.0.0.0/8 192.168.0.0/16
    }
    
    handle @yetkisiz {
        respond "Yetkisiz Erişim" 403
    }
    
    import guvenlik_basliklari
    
    rate_limit {
        zone admin {
            key    {remote_host}
            events 50
            window 1m
        }
    }
    
    reverse_proxy localhost:8080
}

Rate Limit Aşımında Özel Hata Sayfaları

Varsayılan 429 yanıtı yerine özel bir sayfa veya mesaj göstermek daha iyi bir kullanıcı deneyimi sağlar:

example.com {
    rate_limit {
        zone main {
            key    {remote_host}
            events 100
            window 1m
        }
    }
    
    handle_errors 429 {
        respond `
        <!DOCTYPE html>
        <html lang="tr">
        <head>
            <meta charset="UTF-8">
            <title>Çok Fazla İstek</title>
            <style>
                body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }
                h1 { color: #e74c3c; }
            </style>
        </head>
        <body>
            <h1>Çok Fazla İstek</h1>
            <p>Lütfen bir dakika bekleyip tekrar deneyin.</p>
            <p>Sorun devam ederse [email protected] adresine yazın.</p>
        </body>
        </html>
        ` 429
    }
    
    reverse_proxy localhost:3000
}

Caddy ile Fail2Ban Entegrasyonu

Caddy loglları Fail2Ban ile birleştirerek kalıcı IP engellemesi yapabilirsiniz. Bu kombinasyon, rate limiting’in ötesine geçen saldırılara karşı güçlü bir savunma oluşturur:

# /etc/fail2ban/filter.d/caddy-ratelimit.conf
[Definition]
failregex = ^.*"remote_ip":"<HOST>".*"status":429.*$
ignoreregex =

# /etc/fail2ban/jail.d/caddy.conf
[caddy-ratelimit]
enabled  = true
port     = http,https
filter   = caddy-ratelimit
logpath  = /var/log/caddy/access.log
maxretry = 10
findtime = 600
bantime  = 3600
# Fail2ban'ı yeniden başlat
sudo systemctl restart fail2ban

# Durumu kontrol et
sudo fail2ban-client status caddy-ratelimit

# Manuel IP engelleme testi
sudo fail2ban-client set caddy-ratelimit banip 1.2.3.4

# Engellenen IP'leri listele
sudo fail2ban-client status caddy-ratelimit | grep "Banned IP"

Konfigürasyonu Test Etme ve Doğrulama

Yapılandırmaları üretime almadan önce test etmek kritik önem taşır:

# Caddyfile syntax kontrolü
caddy validate --config /etc/caddy/Caddyfile

# Rate limiting'i test et (apache bench ile)
ab -n 200 -c 10 https://example.com/api/test

# Curl ile hızlı test
for i in {1..20}; do
    status=$(curl -s -o /dev/null -w "%{http_code}" https://example.com/auth/login)
    echo "İstek $i: HTTP $status"
    sleep 0.1
done

# Header kontrolü
curl -I https://example.com | grep -E "X-Frame|X-XSS|Strict-Transport"

# Rate limit header'larını kontrol et
curl -v https://example.com/api/ 2>&1 | grep -i "ratelimit|retry-after"

Performans Optimizasyonu

Rate limiting, yoğun trafik altında performansı etkileyebilir. Bunu minimize etmek için birkaç önlem:

{
    # Caddy'nin worker sayısını CPU çekirdek sayısına göre ayarla
    servers {
        trusted_proxies static 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
    }
}

example.com {
    rate_limit {
        zone optimized {
            key    {remote_host}
            events 100
            window 1m
            
            # Distributed rate limiting için Redis
            # (caddy-ratelimit modülü Redis desteği sunar)
        }
    }
    
    # Statik içerikler için rate limit bypass
    @statik {
        path *.css *.js *.png *.jpg *.gif *.ico *.woff *.woff2
    }
    
    handle @statik {
        root * /var/www/html
        file_server
        header Cache-Control "public, max-age=86400"
    }
    
    encode {
        gzip
        minimum_length 1024
    }
    
    reverse_proxy localhost:3000
}

Loglama ve İzleme

Güvenlik olaylarını takip etmek için yapılandırılmış loglama şart:

{
    log {
        output file /var/log/caddy/access.log {
            roll_size 50mb
            roll_keep 20
            roll_keep_for 720h
        }
        format json {
            time_format iso8601
        }
        level INFO
    }
}
# Rate limit olaylarını gerçek zamanlı izle
tail -f /var/log/caddy/access.log | jq 'select(.status == 429)'

# En çok istek atan IP'leri bul
cat /var/log/caddy/access.log | 
    jq -r '.request.remote_ip' | 
    sort | uniq -c | sort -rn | head -20

# 4xx hatalarını özetle
cat /var/log/caddy/access.log | 
    jq 'select(.status >= 400 and .status < 500)' | 
    jq -r '.status' | sort | uniq -c | sort -rn

Yaygın Sorunlar ve Çözümleri

Gerçek IP adresi görünmüyor: Caddy bir load balancer veya proxy arkasındaysa, istemci IP’si yanlış alınabilir.

{
    servers {
        trusted_proxies static 10.0.0.1 10.0.0.2
    }
}

example.com {
    rate_limit {
        zone main {
            # X-Forwarded-For başlığından IP al
            key    {http.request.header.X-Real-IP}
            events 100
            window 1m
        }
    }
    
    reverse_proxy localhost:3000
}

Meşru kullanıcılar engelleniyor: Paylaşımlı IP adreslerinden gelen kurumsal kullanıcılar sorun yaşayabilir. Sınırları biraz daha gevşetmek veya authenticated kullanıcılara muafiyet tanımak iyi bir yaklaşım.

Caddy yeniden başlatıldığında rate limit sıfırlanıyor: Bu normal bir davranış. Kalıcı rate limit için Redis tabanlı dağıtık çözümler düşünülmeli.

Sonuç

Caddy ile rate limiting ve güvenlik katmanı oluşturmak, ilk bakışta karmaşık görünse de doğru yapılandırıldığında son derece güçlü bir koruma sağlıyor. Bu yazıda ele aldığımız konuları kısa bir özet olarak sıralayalım:

  • Rate limiting ile IP başına istek sınırı belirleyerek brute force ve DDoS saldırılarını zayıflatabilirsiniz
  • Güvenlik başlıkları ile XSS, clickjacking gibi yaygın web açıklarına karşı önlem alabilirsiniz
  • IP filtreleme ile bilinen kötü aktörleri ve yetkisiz erişimleri tamamen engelleyebilirsiniz
  • Bot koruması ile otomatik tarama araçlarını filtreleyebilirsiniz
  • Fail2Ban entegrasyonu ile proaktif ve kalıcı IP engelleme yapabilirsiniz

Prodüksiyona geçmeden önce tüm yapılandırmaları staging ortamında test edin, log dosyalarını düzenli olarak inceleyin ve threshold değerlerini gerçek trafik verilerinize göre kalibre edin. Güvenlik statik bir hedef değil, sürekli iyileştirilen bir süreçtir. Caddy’nin konfigürasyonunu düzenli olarak gözden geçirip yeni tehditlere karşı güncel tutmak, uzun vadede en sağlıklı yaklaşım olacaktır.

Yorum yapın