Caddy ile IP Whitelist ve Coğrafi Erişim Kontrolü

Üretim ortamında bir web sunucusu yönetiyorsanız, erişim kontrolü meselesini er ya da geç ciddiye almak zorunda kalıyorsunuz. Belki admin panelinize sadece ofis IP’sinden girilmesini istiyorsunuz, belki API endpoint’lerinizi belirli ülkelerle kısıtlamak istiyorsunuz ya da botlardan ve zararlı IP bloklarından gelen trafiği durdurmak istiyorsunuz. Nginx ve Apache’de bu işler çeşitli modüllerle yapılıyor ama Caddy’de işler biraz farklı işliyor. Caddy, bu konuda hem daha sade hem de bazı açılardan daha güçlü bir yaklaşım sunuyor.

Bu yazıda Caddy ile IP beyaz listesi oluşturmayı, coğrafi erişim kontrolü yapmayı ve bunları gerçek dünya senaryolarında nasıl birleştireceğinizi adım adım anlatacağım.

Caddy’de Erişim Kontrolüne Genel Bakış

Caddy’nin erişim kontrol mekanizması temelde iki katmanda çalışıyor. Birincisi yerleşik remote_ip direktifi ile IP bazlı filtreleme, ikincisi ise caddy-geo-ip gibi eklentilerle coğrafi konum bazlı filtreleme. Nginx’te ngx_http_geo_module veya ngx_http_geoip2_module ile yapılan işlemlerin karşılığı Caddy’de eklenti mimarisi üzerinden geliyor.

Caddy’nin standart kurulumunda IP filtreleme için remote_ip matcher’ı kullanılıyor. Geo-IP işlevselliği için ise xcaddy ile özel bir build oluşturmanız ya da hazır binary’leri kullanmanız gerekiyor.

Temel IP Whitelist Yapılandırması

En basit senaryodan başlayalım. Diyelim ki bir admin panelini sadece belirli IP adreslerinden erişilebilir yapmak istiyorsunuz.

# /etc/caddy/Caddyfile

admin.orneksite.com {
    @allowed_ips remote_ip 192.168.1.0/24 10.0.0.5 203.0.113.42
    
    handle @allowed_ips {
        reverse_proxy localhost:8080
    }
    
    handle {
        respond "Erisim reddedildi." 403
    }
}

Burada @allowed_ips adında bir matcher tanımladık. Bu matcher, isteğin kaynak IP’sinin belirtilen listede olup olmadığını kontrol ediyor. Eşleşme varsa isteği backend’e iletiyoruz, yoksa 403 dönüyoruz.

remote_ip matcher şu formatları destekliyor:

  • Tekil IP: 192.168.1.100
  • CIDR bloğu: 10.0.0.0/8
  • Birden fazla değer: Boşlukla ayrılmış liste

Birden Fazla Site İçin Yeniden Kullanılabilir Yapı

Aynı IP listesini birden fazla yerde kullanıyorsanız, Caddyfile’ın snippet özelliğinden faydalanabilirsiniz:

# /etc/caddy/Caddyfile

(ofis_ipleri) {
    @izinli_ip remote_ip 192.168.1.0/24 10.10.0.0/16 203.0.113.0/24
    handle @izinli_ip {
        {args[0]}
    }
    handle {
        respond "Bu alana erisim yetkiniz yok." 403
    }
}

admin.orneksite.com {
    import ofis_ipleri "reverse_proxy localhost:8080"
}

monitoring.orneksite.com {
    import ofis_ipleri "reverse_proxy localhost:3000"
}

Snippet kullanımı büyük altyapılarda hayat kurtarıyor. IP listesini tek bir yerden yönetip her yerde kullanabiliyorsunuz.

Reverse Proxy Arkasında Gerçek IP Tespiti

Cloudflare, load balancer veya herhangi bir proxy arkasında çalışıyorsanız, remote_ip matcher proxy’nin IP’sini görür, istemcinin değil. Bu durum beyaz listelerinizi işe yaramaz hale getirir.

# /etc/caddy/Caddyfile

{
    # Guvenmediginiz proxy'leri buraya EKLEMEYINIZ
    trusted_proxies static 10.0.0.1 10.0.0.2
}

api.orneksite.com {
    # trusted_proxies ayarlandiktan sonra remote_ip
    # X-Forwarded-For headerindaki gercek IP'yi kullanir
    @guvenli_kaynaklar remote_ip 203.0.113.0/24 198.51.100.0/24
    
    handle @guvenli_kaynaklar {
        reverse_proxy localhost:9000
    }
    
    handle {
        respond "Yetkisiz erisim." 403
    }
}

Önemli uyarı: trusted_proxies ayarını dikkatli yapılandırın. Yanlış yapılandırma, saldırganların X-Forwarded-For header’ını taklit ederek IP kontrolünüzü atlatmasına neden olabilir.

Cloudflare kullanıyorsanız, Cloudflare’in IP aralıklarını trusted proxy olarak tanımlamanız gerekiyor:

{
    trusted_proxies static 
        173.245.48.0/20 
        103.21.244.0/22 
        103.22.200.0/22 
        103.31.4.0/22 
        141.101.64.0/18 
        108.162.192.0/18 
        190.93.240.0/20 
        188.114.96.0/20 
        197.234.240.0/22 
        198.41.128.0/17 
        162.158.0.0/15 
        104.16.0.0/13 
        104.24.0.0/14 
        172.64.0.0/13 
        131.0.72.0/22
}

Geo-IP ile Ülke Bazlı Erişim Kontrolü

Coğrafi erişim kontrolü için önce xcaddy ile özel bir Caddy binary’si derlemeniz gerekiyor. Ülke bazlı filtreleme için github.com/porech/caddy-maxmind-geolocation eklentisini kullanacağız.

xcaddy Kurulumu ve Özel Build

# xcaddy kurulumu
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest

# Geo-IP eklentisi ile Caddy build etme
xcaddy build 
    --with github.com/porech/caddy-maxmind-geolocation

# Binary'yi system path'e tasima
sudo mv caddy /usr/local/bin/caddy
sudo setcap cap_net_bind_service=+ep /usr/local/bin/caddy

Alternatif olarak, Docker tabanlı build kullanabilirsiniz:

docker run --rm -v $PWD:/output caddy:builder 
    xcaddy build 
    --output /output/caddy 
    --with github.com/porech/caddy-maxmind-geolocation

MaxMind GeoLite2 Veritabanı Kurulumu

Ülke tespiti için MaxMind’ın GeoLite2 veritabanına ihtiyacınız var. Ücretsiz hesap açarak indirebilirsiniz:

# MaxMind hesabinizdan license key alindiktan sonra
# mmdbinspect aracini kurun
sudo apt-get install mmdb-bin  # Ubuntu/Debian
# veya
sudo dnf install libmaxminddb-utils  # RHEL/Fedora

# Veritabanini indirin (license key gerekli)
wget "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=SIZIN_KEY&suffix=tar.gz" 
    -O GeoLite2-Country.tar.gz

tar -xzf GeoLite2-Country.tar.gz
sudo mkdir -p /etc/caddy/geoip
sudo mv GeoLite2-Country_*/GeoLite2-Country.mmdb /etc/caddy/geoip/

# Otomatik guncelleme icin cron job
echo "0 3 * * 2 root wget -q 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=SIZIN_KEY&suffix=tar.gz' -O /tmp/GeoLite2.tar.gz && tar -xzf /tmp/GeoLite2.tar.gz -C /tmp && mv /tmp/GeoLite2-Country_*/GeoLite2-Country.mmdb /etc/caddy/geoip/ && systemctl reload caddy" | sudo tee /etc/cron.d/geoip-update

Ülke Bazlı Erişim Yapılandırması

# /etc/caddy/Caddyfile

{
    order maxmind_geolocation before file_server
}

orneksite.com {
    maxmind_geolocation {
        db_path /etc/caddy/geoip/GeoLite2-Country.mmdb
        allow_countries TR GB DE FR NL
    }
    
    reverse_proxy localhost:8080
}

Bu yapılandırma ile Türkiye (TR), İngiltere (GB), Almanya (DE), Fransa (FR) ve Hollanda (NL) dışındaki ülkelerden gelen istekler otomatik olarak reddedilecek.

Belirli Ülkeleri Bloklama (Blacklist Yaklaşımı)

Whitelist yerine blacklist yaklaşımı tercih ediyorsanız:

orneksite.com {
    maxmind_geolocation {
        db_path /etc/caddy/geoip/GeoLite2-Country.mmdb
        deny_countries CN RU KP IR
    }
    
    reverse_proxy localhost:8080
}

Gelişmiş Senaryo: IP Whitelist ve Geo Filtreyi Birleştirme

Gerçek dünyada genellikle birden fazla kontrol katmanı istiyorsunuz. Örneğin: “API endpoint’lerime sadece Türkiye’den erişilebilsin, ama ofis IP’lerimiz her yerden erişebilsin.”

# /etc/caddy/Caddyfile

{
    order maxmind_geolocation before reverse_proxy
}

api.orneksite.com {
    # Oncelikli kontrol: Ofis IP'leri her zaman gecebilir
    @ofis_agi remote_ip 203.0.113.0/24 10.0.0.0/8
    handle @ofis_agi {
        reverse_proxy localhost:9000
    }
    
    # Geo kontrolu: Sadece Turkiye'den giris
    maxmind_geolocation {
        db_path /etc/caddy/geoip/GeoLite2-Country.mmdb
        allow_countries TR
    }
    
    # Geo filtreden gecenler icin normal islem
    reverse_proxy localhost:9000
}

API Anahtari ile Geo Kontrolunu Bypass Etme

Bazı durumlarda yabancı ülkelerden gelen ama geçerli API anahtarına sahip istemcilerin geçmesine izin vermek isteyebilirsiniz. Bu senaryoda request matcher’larını birleştiriyoruz:

api.orneksite.com {
    # Gecerli API anahtarina sahip istekler geo kontrolunu atlar
    @api_key_bypass {
        header X-API-Key "gizli-api-anahtariniz-buraya"
    }
    handle @api_key_bypass {
        reverse_proxy localhost:9000
    }
    
    # Diger istekler icin geo kontrolu
    maxmind_geolocation {
        db_path /etc/caddy/geoip/GeoLite2-Country.mmdb
        allow_countries TR DE GB
    }
    
    reverse_proxy localhost:9000
}

Rate Limiting ile Entegrasyon

IP whitelist ve geo filtreye ek olarak rate limiting ekleyerek daha kapsamlı bir koruma katmanı oluşturabilirsiniz. Bunun için caddy-rate-limit eklentisini de build’e dahil etmeniz gerekiyor:

xcaddy build 
    --with github.com/porech/caddy-maxmind-geolocation 
    --with github.com/mholt/caddy-ratelimit
# /etc/caddy/Caddyfile

api.orneksite.com {
    # Ofis aglari icin rate limit yok
    @ofis_agi remote_ip 10.0.0.0/8 192.168.0.0/16
    handle @ofis_agi {
        reverse_proxy localhost:9000
    }
    
    # Geo filtre
    maxmind_geolocation {
        db_path /etc/caddy/geoip/GeoLite2-Country.mmdb
        allow_countries TR
    }
    
    # Dis dunya icin rate limiting
    rate_limit {
        zone api_zone {
            key {remote_host}
            events 100
            window 1m
        }
    }
    
    reverse_proxy localhost:9000
}

Caddyfile’i Harici Dosyalarla Modüler Hale Getirme

Büyük altyapılarda IP listelerini ana Caddyfile’dan ayrı tutmak yönetimi kolaylaştırıyor:

# /etc/caddy/ip_lists/whitelist.conf icerigine yazin:
# 203.0.113.0/24
# 198.51.100.10
# 10.0.0.0/8

# /etc/caddy/Caddyfile

(guvenli_ip_listesi) {
    @yetkili_ip remote_ip {
        # Dosyadan okuma icin caddy-exec veya ayni dosyada
        # listeleme yapmak daha pratiktir
        203.0.113.0/24
        198.51.100.10  
        10.0.0.0/8
    }
}

admin.orneksite.com {
    import guvenli_ip_listesi
    
    handle @yetkili_ip {
        reverse_proxy localhost:8080
    }
    
    handle {
        respond "Yetkisiz erisim. IP adresiniz: {remote_host}" 403
    }
}

Log Yapılandırması ile Erişim Takibi

Hangi IP’lerin bloklandığını takip etmek için detaylı loglama önemli:

# /etc/caddy/Caddyfile

{
    log {
        level INFO
        output file /var/log/caddy/access.log {
            roll_size 100mb
            roll_keep 10
            roll_keep_for 720h
        }
        format json
    }
}

admin.orneksite.com {
    log {
        output file /var/log/caddy/admin-access.log
        format json
        level DEBUG
    }
    
    @yetkili_ip remote_ip 203.0.113.0/24 10.0.0.0/8
    
    handle @yetkili_ip {
        reverse_proxy localhost:8080
    }
    
    handle {
        log_append blocked_ip {remote_host}
        respond "Erisim engellendi." 403
    }
}

JSON formatındaki logları analiz etmek için:

# Son 1 saatte engellenen IP'leri listele
sudo journalctl -u caddy --since "1 hour ago" | 
    grep '"status":403' | 
    jq -r '.request.remote_addr' | 
    sort | uniq -c | sort -rn | head -20

# Veya log dosyasindan
cat /var/log/caddy/admin-access.log | 
    jq -r 'select(.status == 403) | .request.remote_addr' | 
    sort | uniq -c | sort -rn

Yapılandırma Testleri ve Doğrulama

Değişikliklerinizi production’a almadan önce mutlaka test edin:

# Caddyfile syntax kontrolu
caddy validate --config /etc/caddy/Caddyfile

# Yapılandırmayı yeniden yukleme (downtime yok)
sudo systemctl reload caddy

# Veya Caddy'nin kendi API'si ile
curl -X POST "http://localhost:2019/load" 
    -H "Content-Type: text/caddyfile" 
    --data-binary @/etc/caddy/Caddyfile

# Belirli bir IP'yi test etmek icin curl
curl -H "X-Forwarded-For: 203.0.113.100" https://admin.orneksite.com/

# Geo filtreyi test etmek (VPN ile farkli ulkeden test)
curl -v https://api.orneksite.com/test

Geo filtrenin doğru çalışıp çalışmadığını test etmek için:

# mmdbinspect ile bir IP'nin hangi ulkeye ait oldugunu kontrol et
mmdbinspect -db /etc/caddy/geoip/GeoLite2-Country.mmdb 8.8.8.8
# Cikti: US (Amerika Birlesik Devletleri)

mmdbinspect -db /etc/caddy/geoip/GeoLite2-Country.mmdb 5.26.0.1
# Cikti: TR (Turkiye)

Sık Karşılaşılan Sorunlar ve Çözümleri

Problem: Tüm istekler bloklanıyor, whitelist’teki IP’ler de giremiyorso

Bu genellikle proxy arkasında çalışırken oluyor. Caddy’nin gördüğü IP, gerçek istemci IP’si değil proxy IP’si. Çözüm:

{
    trusted_proxies static 10.0.0.1  # load balancer IP'niz
}

Problem: Geo-IP doğru ülkeyi tanımıyorsa

MaxMind veritabanının güncel olduğundan emin olun. Veritabanı aylık güncelleniyor ve eski sürümler %5-10 hata payına sahip olabiliyor.

Problem: maxmind_geolocation direktifi tanınmıyorsa

Caddy binary’nizin eklentiyle build edildiğini doğrulayın:

caddy list-modules | grep maxmind
# Cikti: http.handlers.maxmind_geolocation gozukmuyorsa
# binary'yi yeniden olusturmaniz gerekiyor

Performans Değerlendirmesi

Geo-IP kontrolü her request için veritabanı sorgusu anlamına geliyor. MaxMind MMDB formatı bellek içi (in-memory) çalışma için optimize edilmiş olsa da yüksek trafikli sitelerde benchmark yapmanızı öneririm:

  • Düşük trafik (saniyede 100 istek altı): Herhangi bir performans etkisi görmezsiniz
  • Orta trafik (saniyede 100-1000 istek): Milisaniye düzeyinde gecikme artışı olabilir, genellikle kabul edilebilir
  • Yüksek trafik (saniyede 1000+ istek): Caddy’nin önüne bir CDN/WAF katmanı koyarak geo filtrelemeyi orada yapmayı düşünün

IP whitelist kontrolü ise neredeyse sıfır maliyetli. Caddy bu kontrolü hash tablosunda O(1) karmaşıklıkla yapıyor.

Sonuç

Caddy ile IP whitelist ve geo erişim kontrolü, nginx veya Apache’ye kıyasla daha temiz ve okunabilir bir yapılandırma sunuyor. Temel IP filtreleme için ekstra eklenti gerekmezken, ülke bazlı kontrol için xcaddy ile özel build oluşturmanız gerekiyor. Bu biraz zahmetli görünse de, eklenti mimarisi uzun vadede büyük esneklik sağlıyor.

Pratik açıdan şunu öneriyorum: Önce remote_ip matcher ile temel IP whitelist’inizi kurun ve doğru çalıştığından emin olun. Sonra geo filtreyi ekleyin. İkisini birden aynı anda devreye almak, sorun çıktığında nereden geldiğini bulmayı zorlaştırıyor. Ayrıca her değişiklikten sonra logları takip edin; meşru kullanıcıları bloklamak, zararlı trafiğe izin vermekten çok daha kötü bir deneyim yaratıyor.

Son olarak, geo-IP veritabanını düzenli güncellemeyı unutmayın. Cron job’u bir kez kurup unutun, her hafta otomatik güncellensin. MaxMind’ın veritabanı canlı bir şey; IP bloklarının ülke atamaları değişiyor ve güncel kalmak önemli.

Yorum yapın