İçindekiler
- Scheduled playbook yaklaşımının sınırları
- Tersine mühendislik: QRadar connector’ı neyi farklı yapıyor?
- 1. info.json’daki iki satır
- 2. Zaman filtreli bir fetch operasyonu
- 3. Etiketli ingestion playbook’ları
- Veri modeli: Alerts mi, ayrı modül mü?
- Indicators modülüne neden aktarmadık?
- Yol boyunca çarptığımız tuzaklar
- Görünüm ve dashboard: Export/Import Wizard ile
- Sonuç
FortiSOAR’da QRadar, FortiSIEM ya da bir EDR connector’ı kurduğunuzda configuration ekranında “Configure Data Ingestion” adında bir buton görürsünüz. Butona basarsınız; wizard örnek veriyi çeker, alanları modüle eşlemenizi ister, zamanlamayı sorar ve birkaç dakika içinde alarmlar FortiSOAR’a akmaya başlar.
Kendi yazdığınız bir connector’da ise bu buton yoktur. Çoğu ekip bu durumda en kısa yolu seçer: Bir scheduled playbook yazılır, connector’ın “Get Incidents” aksiyonu çağrılır, dönen kayıtlar playbook içinde işlenir. Bu yaklaşım çalışır ama ölçeklenmez.
Bu yazıda, bir threat intelligence platformu için yazılmış custom bir connector’ı FortiSOAR’ın standart Data Ingestion yapısına nasıl taşıdığımızı, bunu yaparken Fortinet’in kendi QRadar connector’ını nasıl referans aldığımızı ve yol boyunca karşılaştığımız tuzakları anlatacağım.
Scheduled playbook yaklaşımının sınırları
Başlangıç noktamız şuydu: Bir scheduled playbook, connector’ın Get Incidents aksiyonunu belirli aralıklarla çağırıyor, dönen incident’ı işliyor ve kaynak sistemdeki status’ünü güncelliyordu.
Bu yapının pratikte üç temel sorunu vardı:
- State yönetimi yoktu. Playbook “en son nereye kadar okudum” bilgisini tutmuyordu. Sorgu her çalışmada aynı filtreyle tek bir kayıt alacak şekilde yapılandırılmıştı; aynı anda birden fazla incident oluştuğunda kalanlar sonraki çalışmalara kalıyordu.
- Kayıt tutulmuyordu. Incident’lar FortiSOAR’da bir modülde saklanmadığı için geçmişe dönük arama, filtreleme, raporlama ve dashboard mümkün değildi.
- Her değişiklik playbook düzenlemesi demekti. Hangi incident tiplerinin çekileceği gibi basit bir karar bile playbook’u açıp adım değiştirmeyi gerektiriyordu.
Data Ingestion bu üç sorunu platformun kendi mekanizmasıyla çözüyor: Son çekme zamanını saklıyor, kayıtları upsert ile bir modüle yazıyor ve filtreleri wizard üzerinden yönetilebilir hale getiriyor.
Tersine mühendislik: QRadar connector’ı neyi farklı yapıyor?
Fortinet’in Data Ingestion destekli connector’larından biri olan IBM QRadar connector paketini açıp bizim connector ile karşılaştırdık. Fark üç parçada toplanıyor.
1. info.json’daki iki satır
Butonu görünür yapan şey connector’ın info.json dosyasındaki şu iki alan:
"ingestion_supported": true,
"ingestion_modes": ["scheduled"]
Bu iki satırı eklediğiniz anda buton görünür. Ancak tek başına yetmez; butona bastığınızda wizard, çalıştıracağı playbook’ları aramaya başlar.
2. Zaman filtreli bir fetch operasyonu
QRadar connector’ında Get Offenses’ın yanında ayrıca bir Fetch Offenses aksiyonu var. Farkı, bir “Created After” parametresi alması ve sadece o zamandan sonra oluşan ya da güncellenen kayıtları döndürmesi.
Kendi connector’ımıza bir Fetch Incidents operasyonu ekledik. Tasarımda dikkat ettiğimiz noktalar:
- API’nin desteklediği en sağlam sıralamayı kullanmak. Kaynak API’nin tarih aralığı filtresinin formatı dokümanda net değildi. Bu yüzden ona bağımlı kalmak yerine kayıtları oluşturulma zamanına göre yeniden eskiye sıralayıp okuduk; son çekme zamanına ulaşınca sayfalamayı durdurduk.
- Filtreleri iki katmanda uygulamak. Tek değerli filtreleri API’ye gönderdik. Çoklu seçimleri ise connector içinde süzdük; böylece API’nin çoklu değer desteğinden bağımsız kaldık.
- Kayıt kaybını önlemek. Tek çalışmada üst sınırı aşan kayıt varsa en eskileri önce aldık ve bir sonraki çekme zamanını son alınan kaydın zamanına ayarladık. Kalanlar bir sonraki çalışmada geliyor.
- Bir sonraki çekme zamanını connector’ın kendisinin döndürmesi. Çıktıda
next_pull_timealanı var; playbook bunu hesaplamakla uğraşmıyor, sadece saklıyor.
3. Etiketli ingestion playbook’ları
Asıl iş connector paketindeki playbooks/playbooks.json dosyasında. QRadar paketinde ingestion için özel olarak etiketlenmiş playbook’lar var. Biz yapıyı üç playbook’a indirdik:
| Playbook | Etiketler | Görevi |
|---|---|---|
> Fetch |
#connector_adı #dataingestion #fetch |
Son çekme zamanını okur, fetch aksiyonunu çağırır, sonucu döndürür |
Ingest |
#connector_adı #dataingestion #ingest |
Zamanlanan ana akış: Fetch → her kayıt için Create → son çekme zamanını günceller |
> Create |
#connector_adı #dataingestion #create |
Tek bir kaydı hedef modüle upsert eder |
Wizard’ın ayar ekranı (hangi tipler çekilsin, ilk çalışmada kaç gün geriye gidilsin, tek seferde en fazla kaç kayıt alınsın gibi) Fetch playbook’unun Start adımındaki _configuration_schema değişkeninden oluşuyor. Burada tanımladığınız her alan wizard’da bir form elemanına dönüşüyor; seçilen değerler de aynı isimle Fetch playbook’undaki Configuration adımına yazılıyor.
Son çekme zamanı ise bir global variable’da (macro) tutuluyor. Macro adı playbook’un kendi IRI’sinden türetiliyor; böylece aynı connector’ın birden fazla configuration’ı varsa her biri kendi zaman damgasını tutuyor.
Veri modeli: Alerts mi, ayrı modül mü?
Wizard verileri varsayılan olarak Alerts modülüne yazmayı önerir. Bizim senaryoda bu doğru değildi. Gelen kayıtlar sızıntı istihbaratı niteliğindeydi ve SOC’un alarm kuyruğuyla karışmaması gerekiyordu.
Bu yüzden Module Editor’da ayrı bir modül oluşturduk. Dikkat edilmesi gerekenler:
- Modül adı sonradan değiştirilemiyor. Singular ve plural adları, API’de kullanılacak type değerini de belirliyor. İlk seferde doğru seçmek gerekiyor.
- Record Uniqueness mutlaka tanımlanmalı. Kaynak sistemdeki incident kodunu unique alan yaptık. Create playbook’u upsert ile çalıştığı için aynı kayıt tekrar geldiğinde yeni kayıt açılmıyor, mevcut kayıt güncelleniyor.
- Picklist alanlarını kendiniz eşleyin. Status ve Severity için sistemdeki hazır picklist’leri kullandık. Kaynak sistemdeki değerleri bunlara wizard’ın eşleme ekranından bağladık.
Indicators modülüne neden aktarmadık?
Alerts modülündeki “Indicators” sekmesi cazip görünüyor: Kayıttaki URL ve kullanıcı değerlerini Indicators modülüne aktarırsanız korelasyon otomatik çalışıyor. Ancak birçok kurulumda Indicators modülüne yeni kayıt geldiğinde otomatik olarak dış servislerle zenginleştirme yapan playbook’lar tanımlı.
Sızıntı kayıtlarında kurum içi giriş sayfası adresleri ve kullanıcı bilgileri yer alıyor. Bu değerlerin dış servislere gönderilmesini istemediğimiz için ilişki kurmak yerine bu bilgileri modülün kendi alanlarında tuttuk. Aynı kullanıcının başka kayıtlarda geçip geçmediği, ilgili alanda yapılan basit bir aramayla görülebiliyor.
Aynı nedenle connector, parola, token, cookie ve secret içeren alanları FortiSOAR’a yazmadan önce maskeliyor.
Yol boyunca çarptığımız tuzaklar
Bu bölüm belki de yazının en faydalı kısmı. Dokümantasyonda karşılığını bulamadığımız ve deneyerek öğrendiğimiz noktalar:
1. Playbook etiketi connector adıyla birebir aynı olmalı. Wizard ilk açılışta “No compatible data ingestion playbooks found in this collection. Ensure the playbooks have the #connector_name tag added” hatası verdi. Playbook’ları ürün adıyla etiketlemiştik, oysa wizard info.json’daki name alanıyla birebir aynı etiketi arıyor.
2. Picklist alanlarına ifade değil, alan yolu yazın. Status alanına {% if ... %} ile yazdığımız eşleme ifadesi ingestion sırasında “Invalid Jinja syntax” hatası verdi. Sebebi, wizard’ın picklist alanlarına girilen değeri kendisi {{ <değer> | resolveRange(map) }} kalıbına sarması. Doğru yöntem, alana sadece kaynak alanın yolunu yazıp eşlemeyi yanındaki ok ile açılan tablodan yapmak. Bir FortiSOAR değerine birden fazla kaynak değeri bağlamak için virgülle ayırmak yeterli; örneğin Closed satırına CLOSED,RISK_ACCEPTED,REJECTED.
3. Virgüllü sayısal metinler diziye dönüşebiliyor. Birden fazla kullanıcı adını virgülle birleştirip bir text alana yazdığımızda bazı kayıtlarda alan değeri “Array” olarak göründü. FortiSOAR, playbook’ta oluşan değeri Python ifadesi gibi değerlendiriyor; 11111, 22222 gibi sadece rakamlardan oluşan bir metin tuple’a dönüşüyor. Ayırıcıyı ; yapınca sorun çözüldü, çünkü bu metin bir Python ifadesi olarak değerlendirilemiyor ve metin olarak kalıyor.
4. Aynı sürüm numarasıyla yeniden yükleme güvenilir değil. Connector kodunu değiştirip aynı sürüm numarasıyla yüklediğimizde yeni kod devreye girmedi. Sürümü yükselttiğimizde sorun çözüldü. Her değişiklikte sürümü artırmak en temiz yol.
5. Wizard, kendi playbook kopyalarını oluşturuyor. Wizard, connector paketindeki sample playbook’ları doğrudan kullanmıyor; adında “defaultIngestion” geçen ayrı bir collection oluşturuyor. Paketteki playbook’u güncellemek mevcut kurulumu değiştirmiyor. Eşleme değişiklikleri wizard üzerinden yapılmalı. Sürüm değiştirirken eski collection’ın ve zamanlamasının temizlendiğinden emin olunmalı, aksi halde iki ingestion aynı anda çalışabilir.
6. Sadece tarih içeren alanlar saat dilimine göre kayabilir. Kaynak sistem sızıntı tarihini saatsiz gönderiyordu. FortiSOAR bu değeri UTC gece yarısı olarak kaydedip yerel saate (+3) çevirince arayüzde 03:00 olarak göründü. Veri doğru, ama alan tipini buna göre seçmekte fayda var.
Görünüm ve dashboard: Export/Import Wizard ile
Modül oluşturulduğunda varsayılan detay sayfası, alanları eklenme sırasına göre alt alta dizen tek bir form oluyor. Bunu düzenlemek için şablon editörünü kullanmak yerine farklı bir yol izledik:
- Export Wizard ile yeni modülün view template’lerini ve örnek olarak mevcut bir dashboard’u dışa aktardık. Alerts modülünün detay görünümünü de stil referansı olarak aldık.
- Dışa aktarılan JSON dosyalarındaki widget yapısını (editableFormGroup, tabs, chart, cardList, performanceMetrics) inceleyip yeni modül için detay görünümünü, liste görünümünü ve ayrı bir dashboard’u aynı formatta oluşturduk.
- Paketi Import Wizard ile geri yükledik. Modül şemasını pakete dahil etmedik, böylece import sadece görünümleri ve dashboard’u etkiledi.
Küçük ama zaman kaybettiren bir not: Export Wizard ekranındaki “Import Template” butonu içerik yüklemiyor; sadece export şablonlarını içeri alıyor. İçerik yüklemek için ayrı bir menü olan Import Wizard kullanılmalı.
Sonuç
Data Ingestion, FortiSOAR’da dış kaynaklardan veri almanın platformun öngördüğü yolu. Fortinet’in kendi connector’larında hazır geldiği için çoğu zaman arka planı merak edilmiyor. Ancak custom bir connector’da bu yapıyı kurmak çok da karmaşık değil: info.json’da iki satır, iyi tasarlanmış bir fetch operasyonu ve doğru etiketlenmiş üç playbook yeterli.
Bu yapıya geçtikten sonra entegrasyon artık bakım gerektiren bir playbook olmaktan çıkıyor; kayıtları, filtreleri ve dashboard’u olan, platformun geri kalanıyla aynı mantıkla çalışan bir veri kaynağına dönüşüyor.
Enes ÇALBAN