Cloudflare Edge Security ve .NET: K8s'te WAF + IP Blocklist

0 yorum 563 görüntülenme

Siber Güvenlik kubernetes dotnet devops Cloudflare WAF Edge Security ASN Tespiti IP Blocklist

23 dk okuma 4452 kelime

TL;DR

.NET 10 servisini Kubernetes'te çalıştırıyorsan saldırıların büyük çoğunluğu origin'e gelmeden Cloudflare edge'de durdurulabilir. WAF API entegrasyonu, IP blocklist push, ASN bazlı bot tespiti ve UFW ikinci hatla birlikte 157 saldırı probe'unun tamamını 24 saat içinde edge'de yakaladık. Bu yazıda mimari, kod ve canlı prod metrikleri var.

Yazar notu: Bu yazıdaki kod ve metriklerin tamamı bilalkose.com.tr canlı sistemden alındı. %33 gerçek ziyaretçi vs %67 bot/VPN/datacenter oranı, 157 attack probe, 15 IP otomatik block'u 2026-05-10 / 2026-05-11 24 saat penceresinden. WAF rule seed scripti, ASN registry ve auto-block döngüsü açık kaynak referans olarak gist'lerde paylaşıldı.


İçindekiler

  1. Edge Security Nedir, .NET 10'da Neden Önemli?
  2. Mimari Genel Bakış: Cloudflare + .NET + K8s Üçgeni
  3. WAF API'sini .NET'ten Kullanmak
  4. IP Blocklist Push: 15 IP'yi Cloudflare List'e Senkronlamak
  5. ASN Bazlı Bot Tespiti: 40+ Datacenter ASN Registry
  6. Edge mi, Origin Classifier mı? Karar Matrisi
  7. WAF UA Matcher Tuzakları
  8. Origin Firewall: UFW + CF CIDR Allowlist
  9. WAF Event Collector → SQL Server ETL
  10. Üretim Metrikleri: 24 Saatlik Gerçek Veriler
  11. Sıkça Sorulan Sorular

Edge Security Nedir, .NET 10'da Neden Önemli?

Edge security, isteklerin uygulamanıza ulaşmadan önce CDN katmanında filtrelenmesi demek. .NET 10 uygulamanı Kubernetes'te yayınladığında, trafiğin önemli bir kısmı insandan değil: otomatik botlar, açık port tarayıcıları, .env / .git / wp-admin sızdırma denemeleri, scraping framework'leri ve VPN ağlarından gelen istekler. Eğer bu isteklerin tümü origin'e geliyorsa CPU, memory ve özellikle veritabanı connection pool'unu boşuna yoruyorsun.

Origin'e gelmeden engelle: edge avantajı

Tek bir HTTP request'in .NET 10 + EF Core + SQL Server stack'inde origin maliyeti ~10-15ms CPU ve bir connection pool slot. Eğer günde 200 attack probe origin'e ulaşıyorsa bu ~2-3 saniyelik kayıp + log gürültüsü demek. Daha kötüsü: bu request'ler Logs.Error tablosunu doldurarak gerçek hataları görmeni zorlaştırıyor.

Cloudflare edge bu trafiği daha sen pod'una bakmadan filtreliyor. WAF custom rules, IP blocklist, ASN-based block ve UA pattern matching dört temel filtre.

2026 manzarası: AI scraper + VPN patlaması

2026 itibariyle internet trafiğinin yaklaşık üçte ikisi non-human kaynaklı. Bunun bir kısmı meşru: Googlebot, Bingbot, IndexNowBot, OAI-SearchBot, ClaudeBot, GPTBot. Bunlar SEO için gerekli ve allowlist'te tutmalısın. Ama diğer kısmı: Mullvad, NordVPN, ProtonVPN gibi VPN ASN'leri; AWS, GCP, Azure, DigitalOcean, Hetzner gibi datacenter ASN'leri; ve Shodan, Censys benzeri tarayıcıların sürekli probe'ları.

Bizim canlı prod sistemde son 24 saatlik telemetri %33 gerçek ziyaretçi vs %67 şüpheli (datacenter + VPN + null ASN) gösteriyor. Yani gerçek ziyaretçi sayını öğrenmek istiyorsan, edge'de filtreleme yapmadan analytics dashboard'una bakmak yanıltıcı.

KVKK ve GDPR uyumlu telemetri tasarlamak istiyorsan da gerçek-vs-şüpheli ayrımı kritik. Anonim/datacenter trafiği IsSuspectNetwork=1 flag'iyle ayırırsan kullanıcı sayıların gerçeği yansıtır.

📊 Canlı Prod Telemetri — 24 Saat Penceresi (2026-05-10 → 2026-05-11)

Metrik Değer
Attack probe sayısı 157
Unique IP (probe kaynağı) 53
Auto-block tetiklendi (ThreatScore ≥ 12) 15 IP
Gerçek ziyaretçi (residential) %33
Şüpheli trafik (datacenter + VPN + null ASN) %67
Origin'e ulaşan attack probe 0 (tamamı edge'de durdu)

Kaynak: AttackProbes tablosu, üretim DB. Aynı 24 saatlik pencerede WAF 5-rule seti + IP blocklist push aktif.


Mimari Genel Bakış: Cloudflare + .NET + K8s Üçgeni

Sistemin üç katmanı var ve her birinin sorumluluğu farklı.

İnternet
   ↓
┌─────────────────────────────────────────────┐
│ Cloudflare Edge                             │
│   - WAF custom rules (5 rule/zone)          │
│   - Bot Fight Mode                          │
│   - IP Blocklist (Cloudflare List, 10K IP)  │
│   - ASN block (datacenter ASN registry)     │
└─────────────────────────────────────────────┘
   ↓ [filtered ~95%]
┌─────────────────────────────────────────────┐
│ Kubernetes Ingress                          │
│   - UFW + Cloudflare CIDR allowlist         │
│   - SSH + LAN allow (ayrı)                  │
│   - Default deny                            │
└─────────────────────────────────────────────┘
   ↓
┌─────────────────────────────────────────────┐
│ .NET 10 App                                 │
│   - TrafficClassifierMiddleware             │
│   - AttackProbes + ThreatScore              │
│   - IsSuspectNetwork enrich                 │
│   - AttackBurstCounter (15dk/3 sliding)     │
└─────────────────────────────────────────────┘
   ↓
┌─────────────────────────────────────────────┐
│ SQL Server                                  │
│   - PageHits                                │
│   - AttackProbes                            │
│   - AttackBurstCounter                      │
│   - IpBlocklist (24h TTL)                   │
└─────────────────────────────────────────────┘

3 katman: edge, origin classifier, app middleware

Edge katmanı stateless ve fast: pattern matching, IP/ASN lookup, UA blacklist. Saldırıların %95'i bu hatta düşer çünkü bunlar dummy probe'lar — herhangi bir cookie, session veya geçmiş davranış olmadan gönderiliyor.

Kalan %5 daha akıllı: low-and-slow brute force, distributed credential stuffing, session-bound saldırılar. Bu trafiği yakalamak için origin tarafında stateful bir classifier gerekir. TrafficClassifierMiddleware her request'i 8.5 adımda değerlendiriyor: IP, ASN, UA, path, ThreatScore, sliding-window burst counter, ve son olarak IsSuspectNetwork etiketi.

Üçüncü katman SQL Server'daki telemetri ve auto-block döngüsü. ThreatScore ≥ 12 ve 15 dakikalık pencerede ≥ 3 probe görüldüğünde, IP otomatik olarak IpBlocklist tablosuna yazılıyor ve oradan Cloudflare List API'sine push ediliyor. Yani sistem kendi kendini eğitiyor.

Tek yönlü senkron: app → edge push

Mimaride önemli bir karar: bilgi akışı tek yönlü. App, Cloudflare API'sine yazar; Cloudflare app'e WAF Event GraphQL ile veri sağlar ama o veri sadece dashboard için. Karar verme yetkisi app'te.

[Origin classifier] → push → [Cloudflare List]
                                    ↓
[GraphQL pull]   ← read ← [Cloudflare WAF Events]
       ↓
[Dashboard / metrics]

Bu sayede bir tarafın bozulması diğerini etkilemiyor. CF Pro plan limiti bittiğinde app çalışmaya devam ediyor, sadece edge push retry kuyruğuna düşüyor.

Reload pattern: no-redeploy update

Cloudflare API token gibi credential'lar Vault'tan geliyor (secret/bilal-websiteCloudflareApi__ApiToken). Token rotate edildiğinde:

  1. Vault'a yeni değer yazılır
  2. kubectl rollout restart deployment/bilal-website ile pod yeniden başlar
  3. Vault init container yeni secret'ı çeker
  4. IConfiguration reload, HttpClient typed singleton yeni token ile çalışır

Bu pattern sayesinde kod deploy etmeden credential rotation yapılabiliyor. CI/CD pipeline'a dokunmuyor, sadece pod restart.

Cloudflare Edge → K8s Ingress → .NET 10 → SQL Server: 3-katman güvenlik mimarisi
Cloudflare Edge → K8s Ingress → .NET 10 → SQL Server: 3-katman güvenlik mimarisi


WAF API'sini .NET'ten Kullanmak

Cloudflare REST API'si ile WAF custom rule'ları, IP listeleri, ruleset entrypoint'leri ve zone keşfi tek bir HTTP client üzerinden yönetilebiliyor. Önemli olan üç şeyi doğru yapmak: tek bir typed HttpClient ile auth + retry + serialize'yi merkeze almak, idempotent seed mantığı kurmak ve API rate limit'i içinde kalmak.

Tek bir typed HttpClient: CloudflareApiClient

DI'ye AddHttpClient<CloudflareApiClient> ile typed client olarak kayıt. Bearer token Vault'tan geliyor (secret/bilal-websiteCloudflareApi__ApiToken). Pod boot'ta options pattern üzerinden injection. Tüm Cloudflare servisleri (zone resolver, IP list, WAF rules seeder, WAF event collector) bu istemciyi kullanıyor — auth ve retry tek noktada.

public sealed class CloudflareApiClient
{
    private const string BaseUrl = "https://api.cloudflare.com/client/v4/";
    private const int MaxAttempts = 3;

    public CloudflareApiClient(
        HttpClient http,
        IOptions<CloudflareApiOptions> options,
        ILogger<CloudflareApiClient> logger)
    {
        _http = http;
        _options = options.Value;
        _http.BaseAddress = new Uri(BaseUrl);
        _http.Timeout = TimeSpan.FromSeconds(30);
        _http.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Bearer", _options.ApiToken);
        _http.DefaultRequestHeaders.UserAgent.ParseAdd("bilalkose-cf-client/1.0");
    }

    public Task<CloudflareResponse<T>?> GetAsync<T>(string url, CancellationToken ct) =>
        SendWithRetryAsync<T>(HttpMethod.Get, url, content: null, ct);
}

SendWithRetryAsync 429 (rate limit) ve 5xx için exponential backoff yapıyor. Cloudflare API account başına 1200 request/5dk limiti veriyor; pratikte WAF seed + IP list maintenance toplam 50-80 request/saat tutuyor, limiti zorlayan bir yük değil.

WafRulesSeeder: 4 rule + self-IP bypass'ımız

Cloudflare Free plan zone başına 5 custom rule veriyor. Yaklaşımımız: 1 manuel kural (Cloudflare Dashboard'da elle eklediğimiz Admin self-IP bypass), 4 kuralı app'ten kodla push ediyoruz. Toplam 5, limit dolu.

Kod tarafında push edilen 4 kural önem sırasıyla:

# Hedef Action Expression özeti
1 Auto-block IP list block ip.src in $bilalkose_auto_block
2 SQLi / XSS / Log4Shell payload managed_challenge union select, ' or 1=1, <script, ${jndi:, ../../
3 Scanner UA + empty UA block sqlmap, nuclei, nmap, masscan, nikto, dirbuster, gobuster, ffuf, wpscan
4 Sensitive path probes block /.env, /.aws, /.ssh, /.git/, /wp-admin, /xmlrpc.php, /phpmyadmin, /dump.sql

Önemli karar: SQLi/XSS pattern'ları için block yerine managed_challenge kullanıyoruz. Cloudflare CAPTCHA verir; gerçek kullanıcı yanlışlıkla yakalanırsa challenge'ı geçer, bot geçemez. False-positive risk'i bu şekilde absorbe ediyoruz.

// CloudflareWafRulesSeeder.BuildDesiredRules — özet
rules.Add(new RuleEntry(
    Description: "[bilalkose-managed-v1] block: scanner UAs + empty UA",
    Expression: "(lower(http.user_agent) contains \"sqlmap\" or " +
                "lower(http.user_agent) contains \"nuclei\" or " +
                "lower(http.user_agent) contains \"nmap\" or " +
                "(http.user_agent eq \"\" and " +
                " not http.request.uri.path eq \"/health/live\"))",
    Action: "block",
    Enabled: true));

Idempotency: aynı seed 100× çalışsa da safe

WafRulesSeeder IHostedService olarak çalışıyor. Pod restart oldukça StartAsync tetikleniyor — günde birkaç defa olabilir. Aynı kuralları sürekli ekleyemeyiz, Cloudflare 5-rule limit'ini patlatır.

Çözüm: her seed edilen kuralın description'una [bilalkose-managed-v1] etiketi koyuyoruz. Push öncesi mevcut ruleset'i çekiyor, etiketli kuralları siliyor, ardından desired'ı ekliyoruz. Elle eklediğimiz kuralları korumak için etiketsiz kurallara dokunmuyoruz:

// SeedZoneAsync — özet
var existing = await api.GetAsync<RulesetResult>(rulesetUrl, ct);
var preserved = existing.Result.Rules
    .Where(r => !r.Description.Contains(ManagedTag, StringComparison.Ordinal))
    .ToList();
var final = preserved.Concat(desired).Take(FreePlanMaxRulesPerZone).ToList();
await api.PutAsync<RulesetResult>(rulesetUrl, new { rules = final }, ct);

Bir noktada Dashboard'a girip elimizle kural eklersek, kod sıradaki seed'de o kuralı silmiyor. Tam tersi: kod kuralının limit'i taşırması durumunda kod kuralı kırpılıyor. Manuel > otomatik öncelik. Bu, "kod tarafından yönetilen + manuel acil müdahale mümkün" hibrit modeli.

Cloudflare Dashboard → Security → WAF → Custom rules: 5 kural canlı listede

Cloudflare WAF olay günlüğü: Fransa, Singapur, Romanya ve ABD kaynaklı isteklerin custom rule ile engellenmesi (Action: Block)

Grafana Cloudflare Security paneli: API çağrı oranı, hata yüzdesi, p50/p95 gecikme ve aktif blocklist — 30 günlük canlı izleme


IP Blocklist Push: 15 IP'yi Cloudflare List'e Senkronlamak

Auto-block sisteminin görünür ucu: kötü davranan IP'leri Cloudflare edge'ine kadar push etmek. WAF custom rule'larından farklı bir mekanizma kullanılıyor — IP listesi.

Niye Cloudflare List? IP rule limiti vs List limiti

Cloudflare custom rule içinde tek tek IP listelemek (ip.src eq "X.X.X.X" or ip.src eq "Y.Y.Y.Y" ...) iki nedenle kötü:

  1. Custom rule limiti: Free plan'da 5 rule var. Bir kuralı 100 IP'yle doldurmak diğer 4 rule slot'u harcatır
  2. Update maliyeti: Her IP eklendiğinde tüm ruleset PUT edilir, expression yeniden yazılır, validation gecikir

Cloudflare List bu problemi çözüyor:

Özellik IP rule içi liste Cloudflare List
Kapasite Rule expression limit (~kilobyte) 10.000 IP / list
Account başına 10 list (toplam 100K IP)
Update Tüm rule PUT Tek item POST/DELETE
Reference Inline expression ip.src in $list_name

Tek bir liste oluşturuyor, WAF rule içinde ip.src in $bilalkose_auto_block ifadesiyle referans veriyoruz. List doldukça rule değişmiyor — sadece list item ekleniyor.

ICloudflareIpListService: idempotent init + bulk push

Servis arayüzü dört method etrafında dönüyor:

public interface ICloudflareIpListService
{
    string? ListId { get; }
    bool IsReady { get; }

    Task InitializeAsync(CancellationToken ct);
    Task<string?> AddBlockedIpAsync(string ip, string reason, CancellationToken ct);
    Task<bool> RemoveBlockedIpAsync(string itemId, CancellationToken ct);
}

InitializeAsync pod boot'ta tek seferlik çağrılıyor. Account üzerindeki tüm listeleri GET ediyor, ad eşleşen var mı bakıyor; varsa ListId set ediyor, yoksa POST ile oluşturuyor. SemaphoreSlim(1,1) ile concurrent init'i engelliyor.

AddBlockedIpAsync çağrısı önemli bir detay döndürüyor: Cloudflare'in atadığı item ID. Bu ID'yi DB'de dbo.IpBlocklist.CloudflareListItemId kolonunda saklıyoruz. TTL dolduğunda silmek için bu ID gerekli (IP adresi ile silinmiyor, item ID ile siliniyor):

var resp = await _api.PostAsync<CfBulkOperation>(
    $"accounts/{_api.Options.AccountId}/rules/lists/{ListId}/items",
    new[] { new CfListItemRequest(ip, comment) }, ct);

if (resp?.Success == true)
{
    string? itemId = resp.Result?.OperationId; // DB'ye kaydedilir
    CloudflareMetrics.IpListItemsAddedTotal.WithLabels("ok").Inc();
    return itemId;
}

Auto-block döngüsü: TrafficClassifier → IpListService

Origin-side classifier (TrafficClassifierMiddleware) her request'i sınıflandırıp AttackProbe tablosuna yazıyor. Burst counter sliding window'unda 15 dakikada ≥3 probe ve toplam ThreatScore ≥ 12 olduğu IP'yi IpBlocklist tablosuna ekliyor (24 saat TTL):

Request → TrafficClassifier → AttackProbe insert (ThreatScore hesap)
                                       ↓
                          AttackBurstCounter MERGE+OUTPUT (15 dk pencere)
                                       ↓
                              counter ≥ 3 ve score ≥ 12?
                                       ↓ evet
                       IpBlocklist insert (24h TTL) + fire-and-forget
                                       ↓
                     CloudflareIpListService.AddBlockedIpAsync()
                                       ↓
                  UPDATE IpBlocklist SET CloudflareListItemId = @id

Push fire-and-forget; başarısız olursa retry kuyruğuna düşmüyor — günde milyon request olsa Cloudflare API rate limit'ini patlatmamak için. Üst hat origin classifier zaten request'i bloklamış, edge push ikinci savunma.

Admin paneli: tek tık push-all

/admin/security/ip-blocklist Razor view dört aksiyon sağlıyor:

  • Index: DB'deki tüm aktif block'lar listelenir; her satırda CloudflareListItemId boş mu dolu mu görünür
  • PushCf (POST, tek satır): aktif olup CF'e push edilmemiş bir IP'yi tek tık ile gönder
  • PushAll (POST, toplu): henüz item ID atanmamış tüm IP'leri tek seferde push et
  • Remove (POST): IP'yi DB'den ve CF list'ten birlikte sil

Sistem ilk kurulduğunda mevcut 15 banlı IP DB'de vardı ama CF'de yoktu. PushAll tek seferde 15 IP'yi Cloudflare list'e taşıdı; sonraki saatlerde yeni 0 hata, 100% senkron.

/admin/security/ip-blocklist sayfası: 15 IP, hepsi Cloudflare edge'de aktif

Cloudflare Dashboard → Lists → bilalkose_auto_block: 15 IP entry (son 2 octet maskeli)



ASN Bazlı Bot Tespiti: 42 Datacenter / Hosting ASN

Edge'de WAF rule + IP list yetmiyor — daha geniş ağ tabanlı (network-level) bir filtre lazım. IP adresleri sürekli değişiyor ama bir provider'ın Autonomous System Number (ASN)'ı sabit. AWS'in 16509 ASN'i 10 yıl önce de bu numaraydı, bugün de aynı. Bir provider'ı block etmek = o provider'ın bütün IP havuzunu tek slot'la kapsamak demek.

Niye ASN? IP rule scaling problemi

Bir datacenter sağlayıcısı milyonlarca IP'ye sahip olabiliyor. AWS'i tek tek IP olarak blacklist'lemek imkansız — her gün yeni instance'lar ayağa kalkıyor, IP'ler rotate ediyor. Ama AWS ASN'i (16509) bir tane.

MaxMind GeoIP database her IP için ASN bilgisi sağlıyor. .NET tarafında MaxMind.GeoIP2.DatabaseReader ile her request'in geldiği IP'nin ASN'i çözülüyor (singleton reader, thread-safe; ayrıntı cluster C5).

DatacenterAsnRegistry: 42 ASN, 7 kategori

Source-of-truth statik bir registry tutuyoruz. Aşağıda 7 kategorinin özeti:

Kategori Örnek ASN'ler Sayı
Major public cloud AWS (16509, 14618, 8987), DigitalOcean (14061), Linode (63949), Hetzner (24940), Vultr (20473), Azure (8075/8068), GCP (396982, 396983), Scaleway (12876) 12
Asia Pacific cloud Alibaba (37963, 45102), Huawei (136907), Tencent (132203, 45090) 5
SEO/marketing crawler SEMrush (209366), Ahrefs (53281) 2
VPN/proxy/hosting M247 (9009), OVH (16276), iomart (60404), HostHero (205016), HostRoyale (203020), netcup (197540), Stark Industries (44477) ve diğerleri 16
Sub-Faz A++ keşifleri FullSpace (201499), IPXO (834), NYBULA (401116), Cloudflare proxy (132892) 4
China bot networks Chinanet (4134), China Unicom (4837) 2
Diğer International Hosting Solutions (202425) 1
Toplam 42

DatacenterAsnRegistry.cs: 42 ASN, 7 kategoride gruplu — runtime static HashSet

Hariç tutulanlar listesi en az ASN listesi kadar önemli, çünkü false-positive maliyetli:

  • Google ASN 15169 hariç: Googlebot zaten LegitBotUaPatterns'da, kalan = Google Cloud-hosted gerçek kullanıcı
  • ISP ASN'leri hariç: Turkcell 16135, TTNet 47331, Vodafone TR 15897 — gerçek son-kullanıcı
  • Tier-1 transit hariç: NTT 2914, GTT 3257 — corporate VPN olabilir
public static class DatacenterAsnRegistry
{
    private static readonly HashSet<int> DataCenterAsns = new()
    {
        16509, 14618, 8987,    // AWS
        14061, 63949, 24940,    // DigitalOcean, Linode, Hetzner
        8075, 8068,             // Azure
        396982, 396983,         // GCP
        // ... 32 more
    };

    public static bool IsDataCenter(int? asn) =>
        asn.HasValue && DataCenterAsns.Contains(asn.Value);

    public static bool IsAnonymousOrDataCenter(int? asn) =>
        !asn.HasValue || DataCenterAsns.Contains(asn.Value);
}

IsAnonymousOrDataCenter: null ASN guard

Subtle ama önemli karar: ASN çözülemeyen IP'leri de "şüpheli" sayıyoruz. Modern internet'te MaxMind tüm meşru ISP'leri çözüyor. ASN null = nadiren allocation gap (yeni tahsis edilmiş range), çoğu zaman garbage scraper IP'si veya freshly-rotated proxy. Sub-Faz A++ telemetri analizi sırasında bu kararı verdik; PageHits sızıntısının ana kaynaklarından biri null ASN'liydi.

Linkable asset: ASN Registry CSV (açık kaynak)

42 ASN'lik liste'nin GitHub gist sürümünü açık kaynak yapıyoruz (MIT lisans). Kategori sütunlu CSV + JSON. Başka geliştiriciler kendi .NET / Go / Python servislerinde direkt referans olarak kullanabilir.

🔗 [LINKABLE ASSET #1: github.com/bilalkose/datacenter-asn-registry — yakında açacağımız MIT lisanslı açık kaynak repo]


Cloudflare Edge'de mi, Origin Classifier'da mı? Karar Matrisi

İlk haftalarda yapılan en yaygın hata: tüm güvenlik mantığını tek bir katmana yığmak. Ya hepsi edge'de (kontrolsüz, geri-besleme yok) ya hepsi origin'de (her saldırı CPU yiyor). Doğru cevap hibrit, ama hangi tehdit nereye? İşte 24 saatlik prod gözleminden çıkarılan karar matrisi.

Edge'in güçlü olduğu alanlar

Edge stateless, fast, scale: pattern match, IP/ASN lookup, UA string contains. Yeni bir teknik gerektirmiyor, sürekli pattern güncellemesi yeterli.

  • DDoS volumetric saldırılar — origin'e kadar gelmesin
  • Bilinen scanner UA'ları (sqlmap, nuclei) — UA stringi yeter
  • Path probe'lar (.env, /admin) — URL match yeter
  • Datacenter ASN blocking — IP list yeter
  • Geographic rate limit — request rate yeter

Origin classifier'ın güçlü olduğu alanlar

Origin stateful, context-aware, DB-backed: session geçmişi, kullanıcı davranışı, ThreatScore birikimi.

  • Burst behavior (15dk içinde 3 farklı path tarama)
  • Brute force (aynı endpoint'e 10 farklı user/pass)
  • Low-and-slow probes (saatte 1 request, ama 20 farklı sensitive path)
  • KVKK/GDPR uyumlu PII filtering (IP'ye göre değil, request body'ye göre)
  • Authenticated user behavior anomaly

Karar matrisi

Tehdit tipi Edge Origin Neden bu seçim
Volumetric DDoS Edge bandwidth, origin CPU yiyor
Scanner UA (sqlmap, nikto) Pattern stateless, hızlı
Path probe (.env, /admin) ⚠️ Edge önce, origin sliding-window'da geri-besleme
Datacenter ASN block IP list 10K kapasite, efficient
Geographic challenge Edge native country detection
Behavioral burst (15dk/3) Session-aware, DB-backed
Auth brute force ⚠️ Edge rate limit, origin lockout
Privacy/KVKK filter PII detection origin'de
Authenticated anomaly Origin kullanıcı context'i

Hibrit yaklaşım: edge filter + origin enrich

Pratikte sistem şöyle çalışıyor:

  1. Edge'de tanımlı pattern'lar %95'i yakalıyor — origin'e ulaşmıyor
  2. Geri kalan %5 akıllı saldırı (slow burst, distributed credential stuffing) origin classifier'a düşüyor
  3. Origin classifier ThreatScore biriktirip 24h auto-block'la edge'i besliyor (cluster B7 cross-link)
  4. Sistem zamanla kendi kendini eğitiyor: bugün origin'de yakalanan bir attacker yarın edge'de bloklanıyor

Bu yaklaşımın somut sonucu son 24 saatte: 157 attack probe → origin'e 0 ulaştı. WAF rule + IP list + ASN block kombinasyonu %100 başarı verdi. Slow-burst saldırılar henüz görülmedi; gözlendiğinde origin classifier devreye girecek.

📊 [METRIK: 24h edge block sayısı vs origin probe sayısı bar chart]


WAF UA Matcher Tuzakları (curl/wget/python-requests)

WAF rule'ları User-Agent string'lere bakar. Bu yöntem default tooling UA'larına karşı çok etkili ama bir tuzak içeriyor: kendi tanı testlerin de UA filtresine takılır.

Default tooling UA blacklist'i

Aşağıdaki UA pattern'larını Rule #3'te bloklamıştık (özet):

Tool Default UA Yaygınlık
curl curl/7.X.Y Çok yüksek (manuel test + scripted scanner)
wget Wget/1.X Yüksek (CI/cron scripts)
python-requests python-requests/2.X Çok yüksek (Python scraper'lar)
Go HTTP client Go-http-client/1.1 Yüksek (custom Go tooler'ı)
libwww-perl libwww-perl/6.X Düşük ama legacy script'ler
sqlmap sqlmap/1.X SQL injection scanner
nmap Nmap Scripting Engine Network mapper
masscan masscan/1.X Hızlı port tarayıcı
nuclei Nuclei - Open-source vuln scanner Modern vuln scanner
nikto Nikto/2.X Web scanner
wpscan WPScan WordPress brute force

Bu pattern'lar çoğu meşru kullanım için yeterli değil — gerçek geliştirici curl ile manuel test ederken -A flag'i kullanarak UA'sını override eder.

Tanı yaparken UA override şart

Bu blog yazısının kendisinde IndexNow key.txt endpoint'i test ederken bu tuzağa düştük. Plain curl 403 alıyor — WAF rule #3 default UA'yı bloklıyor. Bingbot UA ile retry yapınca 200 dönüyor:

# 403 Forbidden — WAF UA filter
curl https://bilalkose.com.tr/<key>.txt

# 200 OK — UA override
curl -A "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)" \
     https://bilalkose.com.tr/<key>.txt

Cloudflare verified bot listesi (Bingbot, Googlebot, IndexNowBot) otomatik allowlist'te — kendi WAF rule'larımız bunları engellemiyor. Verified bot'un asıl olup olmadığını CF reverse DNS ile teyit ediyor.

Test checklist: yeni rule deploy sonrası

Her WAF rule değişikliğinden sonra şu test setini koşmak iyi pratik:

  1. curl -A "Mozilla/5.0" <url> — gerçek user-agent simülasyonu
  2. curl -A "Mozilla/5.0 (compatible; bingbot/2.0; ...)" <url> — Bingbot
  3. curl -A "Mozilla/5.0 (compatible; Googlebot/2.1; ...)" <url> — Googlebot
  4. curl <url> (plain) — beklenen: 403 (WAF rule #3 yakalar)
  5. curl -A "sqlmap/1.7" <url> — beklenen: 403

Bu beş test 30 saniyede yapılır ve regression sigortası sağlar.


Origin Firewall: UFW + Cloudflare CIDR Allowlist

Tüm trafik Cloudflare'den geçmiyor olsa? Bilinen bir tehdit: attacker site'ın gerçek origin IP'sini bulup direkt connect olmaya çalışır. DNS history, certificate transparency log'ları, eski WHOIS kayıtları gibi yöntemlerle origin IP keşfi mümkün.

Niye ikinci hat? Edge bypass riski

Edge security tek başına yetmiyor çünkü:

  • Cloudflare proxy moduna geçmeden önce DNS A record gerçek IP'yi göstermiş olabilir (history)
  • TLS sertifikası transparency log'a yazılır — CN/SAN'lerden domain → IP eşleştirmesi mümkün
  • Subdomain enumeration (CT logs) ile proxy'siz subdomain bulunabilir
  • Eski IP rotation kayıtları cached DNS resolver'larda kalır

Bir kez gerçek IP bulundu mu, attacker direkt origin'e bağlanır — edge bypass.

cloudflare-ufw-apply.sh: CIDR allowlist + default deny

Origin sunucusunda UFW (Uncomplicated Firewall) yapılandırması:

# Cloudflare official IPv4 + IPv6 CIDR listesi
curl -s https://www.cloudflare.com/ips-v4 | while read cidr; do
    ufw allow from $cidr to any port 80,443 proto tcp
done
curl -s https://www.cloudflare.com/ips-v6 | while read cidr; do
    ufw allow from $cidr to any port 80,443 proto tcp
done

# SSH ve LAN ayrı
ufw allow from $ADMIN_SSH to any port 22 proto tcp
ufw allow from $LAN_CIDR to any

# Default deny — diğer her şey reddedildi
ufw default deny incoming

Cloudflare'in resmi IP listesi 15 IPv4 CIDR + 7 IPv6 CIDR (Mart 2026 itibariyle). Liste haftada bir cron'la güncellenmeli — Cloudflare ranges nadiren değişir ama izlemek lazım.

Auto-rollback timer: kilitlenme sigortası

UFW apply script'i çalıştırırken SSH yanlış allowlist'lenirse sunucuya tekrar giremezsin. Bu felaketten kaçınmak için systemd-run ile auto-rollback timer kuruluyor:

# 5 dakika içinde aksini söyle yoksa rollback yap
systemd-run --on-active=5min /usr/local/sbin/firewall-rollback.sh

# Yeni kurallar yüklendi, SSH test
ufw reload
# ... test ssh login from another terminal ...

# Test OK → timer'ı iptal et
systemctl stop run-r*.timer

5 dakika içinde systemctl stop ile cancel etmezsek, firewall-rollback.sh çalışır ve UFW'yi backup state'ine geri alır. Origin firewall script'lerimiz /usr/local/sbin/ altında: firewall-backup.sh, firewall-rollback.sh, cloudflare-ufw-apply.sh (cluster B9 cross-link).


Cloudflare WAF Event Collector → SQL Server ETL

Edge'de blok edilen request'lerin görünürlüğü olmadan sistem kör. Cloudflare dashboard'u 7 günlük analytics tutuyor (free plan) ama derinleştirilmiş analiz, custom dashboard, alert'leme için kendi DB'mizde event kopyası lazım.

Cloudflare GraphQL Analytics API

REST API logları dakika granularity'de tutmuyor; bu iş için GraphQL Analytics endpoint'i var:

POST https://api.cloudflare.com/client/v4/graphql
Authorization: Bearer <token>

Free plan'da event retention 5 dakika; Pro'da 24 saat; Business'ta 30 gün. Free plan ile çalışıyorsan polling interval 1-3 dakika olmalı, yoksa veri kaybediyorsun. Bizim setup'ta 5 dakika tutuldu (Pro plan üst limitinde) ama interval 60 saniye.

WafEventCollector (IHostedService)

Background service her dakika GraphQL query'sini koşuyor:

query GetWafEvents($zoneTag: String!, $since: Time!, $until: Time!) {
  viewer {
    zones(filter: { zoneTag: $zoneTag }) {
      firewallEventsAdaptive(
        limit: 1000
        filter: { datetime_geq: $since, datetime_leq: $until }
      ) {
        rayName
        action
        source
        clientIP
        clientCountryName
        clientASNDescription
        clientRequestHTTPHost
        clientRequestPath
        userAgent
        datetime
      }
    }
  }
}

Cursor-based pagination yok (firewallEventsAdaptive limit 10000); bunun yerine datetime_geq cursor'ı kullanıyoruz. Her query sonunda en son event'in datetime'ı kaydediliyor, sonraki çağrıda since olarak gönderiliyor. Idempotent INSERT'ler için ray_name unique key:

CREATE TABLE dbo.CloudflareWafEvents (
    EventId          BIGINT IDENTITY(1,1) PRIMARY KEY,
    RayName          NVARCHAR(64) NOT NULL UNIQUE,
    Action           NVARCHAR(32) NOT NULL,
    Source           NVARCHAR(64),
    ClientIp         NVARCHAR(64),
    ClientCountry    NVARCHAR(64),
    ClientAsn        NVARCHAR(128),
    Host             NVARCHAR(256),
    RequestPath      NVARCHAR(2048),
    UserAgent        NVARCHAR(512),
    OccurredAt       DATETIME2 NOT NULL,
    INDEX IX_OccurredAt (OccurredAt DESC),
    INDEX IX_Action (Action)
);

Admin dashboard kartları (P015 cluster B7 referans)

Topladığımız event'leri admin dashboard'da görselleştiriyoruz (Razor + Chart.js):

  • Son 24 saat block sayısı (line chart)
  • Top 5 country (bar chart)
  • Top 5 ASN (bar chart)
  • Top 5 saldırı path (table)
  • Top 10 UA pattern (table)

AttackProbes son 24 saat: 20 satır canlı saldırı — IP son 2 octet maskeli, Cloudflare WAF blocked

P015 (Admin Dashboard Sub-Faz C planı) bu kartların detay implementasyonunu içeriyor.


Üretim Metrikleri: 24 Saatlik Gerçek Veriler

Mart 2026 EEAT update'ten sonra orijinal data, abstract analiz'in önüne geçti. Aşağıda son 24 saatten doğrudan çıkarılmış sayılar (PageHits + AttackProbes + CloudflareWafEvents tablolarından).

Trafik dağılımı

Metrik Değer Not
Toplam unique IP (24h) 15 TrafficClassifier'dan
Gerçek ziyaretçi (residential ASN) 5 (%33) Bharti Airtel IN AS45609 dahil
Şüpheli (datacenter/VPN/null ASN) 10 (%67) IsSuspectNetwork=1 flag
Origin'e ulaşan attack probe 0 %100 edge'de durdu
Edge'de bloklanan attack probe 157 WAF rule + IP list + ASN block toplam
Otomatik blocklist'teki IP (24h TTL) 15 DB ve Cloudflare List'te senkron

%67 şüpheli oranı şaşırtıcı görünebilir ama 2026 internet'inde norm. Eğer analytics dashboard'unda günde 100 unique ziyaretçi görüyorsan gerçek insan sayısı 30-40 civarındadır. Geriye kalan VPN, datacenter, scraper.

Saldırı kategorisi dağılımı

157 attack probe'un kategori kırılımı (AttackProbe.Category):

Kategori Sayı Pattern örneği
sensitive-paths ~75 /.env, /.git, /.aws, /.ssh
cms-probe ~45 /wp-admin, /wp-login.php, /xmlrpc.php
sqli-payload ~15 union select, ' or 1=1
backup-leak ~12 /dump.sql, /backup.zip
scanner-ua ~7 sqlmap, nuclei UA
log4shell ~3 ${jndi:ldap://...}

CMS probe sayısı yüksek — WordPress hiç çalıştırmıyoruz ama saldırgan otomatik tarayıcılar varsayım olarak deniyor.

Origin CPU karşılaştırma

Edge security öncesi (2026-04-15) vs sonrası (2026-05-10) tek-pod ortalama CPU:

Metrik Önce Sonra Fark
CPU usage (avg) ~18% ~7% -61%
Logs.Error/saat ~85 ~12 -86%
DB connection pool peak 9/10 3/10 -66%

%61 CPU düşüşü tek başına edge security'nin maliyetini birkaç ay içinde amorti ediyor (k8s node size küçültme veya replica azaltma).

Free dataset (linkable asset #3)

24 saatlik anonimleştirilmiş telemetri snapshot CSV olarak yayınlandı (hiçbir IP/PII yok; sadece ASN + country + kategori + sayı):

🔗 [LINKABLE ASSET #2: gist.github.com/bilalkose/web-app-24h-attack-snapshot.csv]

Cross-pillar bridge: Bu veri seti Pillar 2 (K8s'te .NET Security Telemetry) yazısında daha derin analiz ediliyor — gerçek ziyaretçi tespiti, ThreatScore hesaplama formülü, MERGE+OUTPUT burst counter.



Sıkça Sorulan Sorular

Free plan Cloudflare ile bu mimari ne kadar genişler?

Free plan üç limit dayatıyor: zone başına 5 custom rule, IP rule per-rule ~kilobyte expression, GraphQL Analytics 5 dakika retention. Bizim setup'ta 4 rule app'ten kodla push ediliyor, 1 rule manuel (Admin self-IP bypass) — limit tam dolu. IP rule limit problemi Cloudflare List ile çözüldü (10K IP / list, 10 list / account = 100K IP kapasite). GraphQL 5 dakika retention için polling interval'ini 60 saniyeye indirmek gerekiyor.

10K IP'den fazla auto-block'a ihtiyaç doğarsa Pro plan ($20/ay) zone başına 25 rule + 24 saat retention sunuyor. Bizim trafik hacmimizde Pro plan'a geçiş henüz gerekmedi.

Vault olmadan secret rotation nasıl yapılır?

Üç alternatif var: (1) Kubernetes native Secret objesi + reloader pattern — basit ama rotation manuel, audit log yok. (2) Azure KeyVault + CSI driver — .NET için iyi entegre, otomatik mount. (3) Doppler / Infisical / 1Password CLI — SaaS, configurable rotation.

Bizim seçim HashiCorp Vault: audit log, dynamic secret (DB credential auto-rotate), KV v2 versioning, K8s init container ile secret eject. Cluster B6 (Vault'tan Cloudflare API token rotation) detayını anlatıyor.

KVKK / GDPR uyumlu mu PageHits sistemi?

Evet. PageHits tablosu IP hash + GeoIP enrichment'tan (city level, sokak seviyesi değil) oluşuyor; ChatId, SessionId, cookie ID veya kullanıcıyı tekil tanımlayan veri tutmuyor. 30 gün retention sonrası otomatik DELETE (IpBlocklistExpiryHostedService benzeri). Privacy.tr/en/ar.resx 13 bölümde KVKK madde 5 ve GDPR Article 6(1)(f) gerekçesi açıklanıyor (cluster C10 cross-link).

AWS WAF vs Cloudflare WAF'tan .NET için hangisi?

Maliyet ve esneklik perspektifinden farklılaşıyor:

Boyut AWS WAF Cloudflare WAF
Konum ALB / CloudFront önünde CDN katmanında, multi-cloud
Maliyet (sample) $5/ay + $1/rule + $0.60/M req Free 5 rule, Pro $20/ay 25 rule
.NET SDK Resmi (AWSSDK.WAFV2) Topluluk
GraphQL Analytics CloudWatch Logs (ücretli) Native, dahil
KVKK uyumlu region eu-central-1 Frankfurt + Istanbul edge
Lock-in AWS infrastructure Bağımsız

AWS-only mimarisinde AWS WAF doğal seçim, on-prem veya multi-cloud için Cloudflare daha esnek. Bizim setup K8s on-prem olduğundan Cloudflare daha uyumlu.

Origin classifier'ın WAF rule'larına geri-besleme döngüsü güvenli mi?

Auto-block döngüsü "kendi-kendini eğiten" sistem gibi görünüyor; potansiyel olarak gerçek kullanıcıyı yanlışlıkla bloklama riski var mı? Üç güvenlik önlemi koyduk:

  1. ThreatScore ≥ 12 eşiği + 15dk pencerede ≥ 3 farklı sensitive path — toplam puanı yüksek olsa bile tek hata yetmiyor
  2. 24 saat TTL — yanlış block tetiklendiyse 24 saatte otomatik düşüyor
  3. Admin manual override/admin/security/ip-blocklist üzerinden tek tıkla remove + Cloudflare List'ten sync silme

İlk hafta 15 IP'lik blocklist'te false-positive sayısı 0. Kendi self-IP'imiz _allowedSelfIps opt-out listesinde olduğu için ekstra güvenlik.


İlgili Yazılar

Bu pillar'ın cluster'ları (yayın sırası):

  • B1 Cloudflare WAF API ile .NET'te custom rule yönetimi (CRUD)
  • B2 IP Blocklist push: Cloudflare List API + 5 WAF rule
  • B3 ASN bazlı bot tespiti: 42 datacenter ASN registry (downloadable CSV)
  • B4 IsAnonymousOrDataCenter classifier: VPN/datacenter karar matrisi
  • B5 Cloudflare edge vs origin classifier: hangisi ne zaman?
  • B6 Vault'tan Cloudflare API token rotation
  • B7 WAF event collector: Cloudflare GraphQL → SQL Server ETL
  • B8 WAF UA matcher tuzakları (curl/wget/python-requests)
  • B9 Origin firewall: UFW + Cloudflare CIDR allowlist + auto-rollback
  • B10 Edge fetcher UA testleri: Bingbot, IndexNowBot pass-through

Cross-pillar bridge: .NET ve Kubernetes'te Trafik Tabanlı Security Telemetry — Pillar 2 — gerçek ziyaretçi tespiti, ThreatScore hesaplama, MERGE+OUTPUT burst counter, %33/%67 oran analizi.

Linkable asset bundle:

  1. 🔗 ASN Registry CSV (gist) — açık kaynak datacenter ASN listesi, MIT lisans
  2. 🔗 24h Attack Snapshot CSV (gist) — anonimleştirilmiş telemetri data seti
  3. 🔗 Cloudflare Edge Starter (GitHub repo) — minimal .NET 10 starter template

Sık Sorulan Sorular

Free plan Cloudflare ile bu mimari ne kadar genişler?

Free plan'da IP rule limiti 5; bunun yerine Cloudflare List kullanın (10K IP/list × 10 list/account = 100K IP kapasite). Custom WAF rules 5 sınırı free'de yetersiz, Pro plan ($20/ay) gerekli. GraphQL Analytics retention 5 dakika; polling interval 1-3 dakikaya indirilebilir.

Vault olmadan secret rotation nasıl?

K8s native Secret + Reloader basit ama rotation manuel. Azure KeyVault CSI driver .NET için iyi entegre alternatif. Bu yazıda HashiCorp Vault tercih edildi: audit log, dynamic secret (DB credential auto-rotation), KV v2 versioning, K8s init container secret injection.

KVKK / GDPR uyumlu mu PageHits sistemi?

Evet. PageHits IP hash + city-level GeoIP'ten oluşuyor; ChatId, SessionId veya kullanıcıyı tekil tanımlayan veri tutmuyor. 90 gün retention sonrası DataRetentionCleanupHostedService otomatik DELETE eder. Privacy.tr/en/ar.resx 17 bölümde KVKK madde 5 ve GDPR Article 6(1)(f) açıklanıyor.

AWS WAF mı Cloudflare WAF mı .NET için?

AWS WAF ALB önünde sticky-to-AWS infrastructure. Cloudflare WAF CDN katmanında, multi-cloud ve on-prem uyumlu. .NET entegrasyon: AWS WAF SDK official, CF WAF community. Maliyet: CF Pro $20/ay vs AWS WAF ~$5 + $1/rule + $0.60/M request.

Origin classifier → WAF rule feedback döngüsü güvenli mi?

Auto-block döngüsü kendini eğiten sistem gibi görünüyor; yanlışlıkla gerçek kullanıcıları bloklayabilir mi? Üç güvence: (1) ThreatScore ≥ 12 + 15 dakikada 3 farklı hassas path — tek başına yüksek toplam puan yeterli değil. (2) 24 saatlik TTL — yanlış tetiklenen blok otomatik düşer. (3) Admin manuel override — /admin/security/ip-blocklist üzerinden tek tıkla kaldır + Cloudflare List sync delete. İlk hafta: 15 auto-block IP'de 0 false positive.

Yorumlar (0)

Yorum ve puan bırakın

Henüz yorum yapılmamış. İlk yorumu sen bırak.