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.