Karmaşık Log Formatları İçin Plugin Geliştirme
Her sistemin kendine has log tutma formatı bulunmaktadır. Böyle bir çeşitlilik sebebiyle logları sınıflandırmak ve analiz etmek oldukça zorlaşmaktadır. Bu karmaşıklık, log işleme yazılımını zorlaştırmakta ve zaman kaybına sebep olmaktadır. Bu sebeple cihazların ürettiği logların standartlaştırılmış bir formatta olması hem maliyet, hem de zaman açısından önemlidir. Fakat her açıdan bu soruları giderecek kapsamlı standart bulunmamaktadır.
Bu standartlardan en çok bilineni CEF’den bahsedelim:
CEF (Common Event Format)
CEF log yönetimini basitleştirmek için oluşturulan bir log formatı standartıdır. Teknoloji üreticileri ürünlerine standartlaştırılmış bir format sunar. Böylece veri kurumsal yönetim sistemi tarafından analiz için kolayca toplanır ve birleştirilir. CEF genişletilebilir, metin tabanlı, yüksek performanslı bir formattır. Log kayıtları için standart bir başlık ve değişken uzantısı olan anahtar-değer çiftleri olarak biçimlendirilmiş bir söz dizimi tanımlar. Aşağıdaki CEF standartı ile hazırlanmış örnek logları görebilirsiniz:
Jun 18 15:56:51 192.168.15.5 CEF: 0|Websense|Security|1.2.3|109|Transaction blocked|7| act=blocked app=http dvc=192.168.15.5 dst=192.168.15.5 dhost=icdncube.milliyetvideo.com dpt=80 src=192.168.15.5 spt=56074 suser=LDAP://192.168.15.5 OU\=abc,OU\=abc,OU\=abc,OU\=abc,OU\=abc,OU\=abc,OU\=abc,DC\=abc,DC\=local/abc destinationTranslatedPort=0 rt=1234567891011 in=0 out=0 requestMethod=GET requestClientApplication=Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0) reason=- cs1Label=Policy cs1=Super Administrator**Default cs2Label=DynCat cs2=0 cs3Label=ContentType cs3=- cn1Label=DispositionCode cn1=1025 cn2Label=ScanDuration cn2=5 request=http://abc...
Jun 18 15:56:51 192.168.15.5 CEF: 0|Websense|Security|1.2.3|5|Transaction permitted|1| act=permitted app=http dvc=192.168.15.5 dst=192.168.15.5 dhost=icube.milliyet.com.tr dpt=80 src=192.168.15.5 spt=56085 suser=LDAP://192.168.15.5 OU\=abc,OU\=abc,OU\=abc,OU\=abc,OU\=abc,OU\=abc,OU\=abc,DC\=abc,DC\=local/abc destinationTranslatedPort=38798 rt=1234567891011 in=803 out=12345 requestMethod=GET requestClientApplication=Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0) reason=- cs1Label=Policy cs1=Super Administrator**Default cs2Label=DynCat cs2=0 cs3Label=ContentType cs3=image/jpeg cn1Label=DispositionCode cn1=1048 cn2Label=ScanDuration cn2=5 request=http://abc...
Aug 4 11:04:47 192.168.15.5 CEF: 0|Websense|Security|7.8.4|5|Transaction permitted|1| act=permitted app=http dvc=192.168.15.5 dst=192.168.15.5 dhost=img-3.onedio.com dpt=80 src=192.168.15.5 spt=50515 suser=LDAP://192.168.15.5 OU\=BankaSigortaciligi,OU\=ouMaksimumSigortaUzmanlari,OU\=ouUsers,DC\=anadolusigorta,DC\=pvt/Aylin Duru destinationTranslatedPort=52193 rt=1234567891011 in=712 out=167107 requestMethod=GET requestClientApplication=Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.125 Safari/537.36 reason=- cs1Label=Policy cs1=Super Administrator**Genel Politika cs2Label=DynCat cs2=0 cs3Label=ContentType cs3=- cn1Label=DispositionCode cn1=1026 cn2Label=ScanDuration cn2=0 request=http://abc...
Aug 4 11:04:50 192.168.15.5 CEF: 0|Websense|Security|7.8.4|113|Transaction blocked|7| act=blocked app=https dvc=192.168.15.5 dst=192.168.15.5 dhost=api.dropbox.com dpt=443 src=192.168.15.5 spt=60438 suser=- destinationTranslatedPort=0 rt=1234567891011 in=0 out=0 requestMethod=CONNECT requestClientApplication=- reason=- cs1Label=Policy cs1=Super Administrator**Wifi_Mobil_Personel cs2Label=DynCat cs2=0 cs3Label=ContentType cs3=- cn1Label=DispositionCode cn1=1025 cn2Label=ScanDuration cn2=0 request=http://abc...
Farklı sistemler tarafından üretilen log dosyalarını parse etmek ve anlamlı alanları ayrıştırmak amacıyla Cryptolog tarafında hazırladığımız parserları plugin olarak adlandırıyoruz.
Cryptolog’da plugin geliştirmek için Plugin Tanım sayfasındaki editörü kullanıyoruz.
Plugin hazırlamak için Code, Regex (Regular Expression), Delimitter, Csv, SQL, API gibi farklı deyim tipleri kullanıyoruz.
Aşağıda bir log örneği bulunmaktadır:
Bu logu incelediğimizde sırasıyla tarihten başlamak üzere; tüm verilerin her satırda aynı sıra ile ve boşluklarla ayrılmış olarak geldiğini görmekteyiz. Bu log örneği için karmaşık olmayan veya düzenli log örneği diyebiliriz. Bu sebeple işimiz daha kolay. Bu tarz bir log örneğini ayraç (delimiter) kullanarak ayrıştırabiliriz. Ayraç (delimiter) plugini oluştururken ayraç olarak boşluk değeri giriyoruz. Plugini oluşturduktan sonra logların bulunduğu yolu ve dosyasının ismini girdikten sonra kaydedip arama yaptığımızda; bilgiler aşağıdaki gibi tablo halinde gelecektir:
Bunun gibi belli bir karakterle ayrılmış ve düzenli olarak gelen pluginler delimitter yardımıyla kolayca parse edilebilir. Bunun dışında alanları virgüllerle ayrılmış düzenli gelen log ifadeleri için özel olarak csv ile plugin geliştiriyoruz.
Örnek log:
2015-03-10 08:50:50 192.168.15.5 User.Informational Mar 10 08:50:51 PA-12345 1,2015/03/10 08:50:50,0009C100938,TRAFFIC,drop,0,2015/03/10 08:50:50,192.168.15.5,192.168.15.5,192.168.15.5,192.168.15.5,ImplicitDeny,cmd\hi-m.halit,,not-applicable,vsys1,Bolgeler,Client,ethernet1/8,,Syslog,2015/03/10 08:50:50,0,1,12345,80,0,0,0x0,tcp,deny,66,66,0,1,2015/03/10 08:50:29,0,any,0,478112345,0x0192.168.15.5-192.168.15.5,192.168.15.5-192.168.15.5,0,1,0,policy-deny
2015-03-10 08:50:50 192.168.15.5 User.Informational Mar 10 08:50:51 PA-12345 1,2015/03/10 08:50:50,0009C100938,THREAT,url,0,2015/03/10 08:50:50,192.168.15.5,192.168.15.5,192.168.15.5,192.168.15.5,Internet1,abc\l.kenez,,web-browsing,vsys1,Client,Untrust,vlan.70,ethernet1/1,Syslog,2015/03/10 08:50:50,33771234,1,12345,80,12345,80,0x40c000,tcp,alert,"content.aform.com/thumbs/ABC123.jpg",(9999),computer-and-internet-info,informational,client-to-server,1074123456,0x0,192.168.15.5-192.168.15.5,US,0,text/html,0,,,2,,,,,,,,0
2015-03-10 08:50:53 192.168.15.5 User.Informational Mar 10 08:50:54 PA-12345 1,2015/03/10 08:50:54,0009C100938,SYSTEM,vpn,0,2015/03/10 08:50:54,,abc-negco-b2-start,Ankara-ABC-ipsec(ctw-ankara-h,0,0,general,informational,"IKE phase-2 negotiation is started as initiator, quick mode. Initiated SA: 192.168.15.5[400]-192.168.15.5[400] message id:0x1CCBDC01.",24791234,0x0
2015-03-10 08:50:53 192.168.15.5 User.Informational Mar 10 08:50:54 PA-12345 1,2015/06/18 16:36:36,001801017030,CONFIG,0,0,2015/06/18 16:36:36,10.10.16.94,,edit,sami.nar,Web,Succeeded, vsys vsys1 log-settings profiles test,2715,0x0
Yukarıdaki log örneğinde gelen alanlar virgüllerle ayrılmış ve sıralı olarak gelmektedir. Bu sebeple csv ile bu plugini hazırlayabiliriz.
Bunun dışında bir de regex pluginiyle ilgili örnek verelim. Aşağıdaki gibi bir log örneğimiz olsun:
Burada dikkat edersek Date, Srcip gibi alan isimleri gelmiş. Biz bu alan isimlerini değil de sadece yanındaki değerleri almak istiyoruz. Bunun dışında Srcip alanının değer kısmında kaynak ip alanından sonra iki nokta ve kaynak port değeri verilmiş. Bu logu ayrıştırırken delimiter veya csv formatını kullanmamız uygun olmaz. Çünkü belli bir karaktere göre ayrım yapmıyoruz ve logdaki alan isimlerini almak istemiyoruz. Bu sebeple bu satıra uygun regex yazacağız:
Cryptolog içindeki Regex Yardımcısı ekranında kolayca regeximizi yazdıktan sonra, pluginin altına deyim olarak tanımlıyoruz ve ilgili dosyayı okutup arama yaptığımızda aşağıdaki ekranla karşılaşıyoruz:
Yukarıdaki örnekteki gibi kendi regex ifademizi yazabileceğimiz gibi, hazır bazı paternleri de kullanabiliriz.
Logstash – grok pattern : Grok patternleri kalıplaşmış regex ifadeleridir. Buna göre belirli alanlar için bazı hazır patternler kullanılır ve bu paternlere göre satırlar kolaylıkla eşlenir. Kelimeleri, sayıları ve tarihleri filtrelemek için hazır grok paternleri bulunmaktadır. Bu paternler daha az efor harcayarak stringleri eşlemenizi ve kullanmanızı sağlar. Desteklenen paternlerin listesini aşağıdaki linkte bulabilirsiniz.
https://github.com/logstash-plugins/logstash-patterns-core/blob/master/patterns/grok-patterns
Eğer ihtiyacınız olan patterni bulamazsanız kendi özel regex ifadenizi de yazabilirsiniz.
Yukardaki log örneklerindeki alanlar belli bir dizilime göre gelmekte ve düzenli olarak devam etmektedir. Ayrıca gelen alanların dizilimi tüm satırlarda aynı şekildedir. Bu tarzda düzenli gelen log örneklerinde plugin hazırlamak kolaydır. Fakat alan dizilimlerinin düzensizlik göstermesi, birinde olan bir alanın diğerinde olmaması gibi durumlarla karşılaşılabilir. Bu tarz durumlarda genelde code plugin kullanılır. Code pluginin hazırlanması diğer pluginlere göre daha zordur. Cryptolog’da code plugin geliştirirken C++, Visual Basic, C# , Python gibi programlama dilleri kullanabiliriz. Örneklerimizde C# programlama dilini kullanarak ilerleyeceğiz. Cryptolog’daki editörde kodumuzu yazıyoruz. Burada sistemde tanımlı olan Parse adında bir fonksiyonumuz var:
Burada her satırda farklı sayıda ve farklı alanlar gelmektedir. Bu sebeple regex, delimitter veya csv kullanmak işimize yaramaz. Burada code plugin kullanacağız.
Öncelikle gelen adları belirleyip deyim adlarını verelim:
Log Örneğini incelediğimizde loglar genel olarak key=value şeklinde gelmektedir.
1.log satırını incelediğimizde burada date, type, subtype, level, policyid alanları gelmektedir.
2.log satırını incelediğimizde burada date , type, subtype, srcip, dstip alanları
3.log satırını incelediğimizde ise date, type, subtype, status, level, sessionid alanları gelmektedir.
Gelen tüm deyim alanlarını belirledikten sonra bunları deyim alanları kısmına aşağıdaki gibi yazıyoruz ve id değerlerini sırasıyla veriyoruz. Bunlar values listesinden sırayla alacağı değerler.
Gelen log satırları parse fonksiyonun string line değişkenine atanacağından biz buradaki değeri alıp anlamlı parçalara ayıracağız. Ayırdığımız parçaları values dizisine ekleyeceğiz. Burada dikkat etmemiz gereken nokta deyim adlarını belirlediğimiz sırayla values listesine değerleri göndermemiz gerekiyor.
Loga baktığımızda her satırın başında tarih ve server ip değeri düzenli bir şekilde gelmiş. O zaman ilk 15 karakter bize tarih bilgisini verir diyebiliriz. O zaman aşağıdaki gibi Substring fonksiyonu ile satırın ilk 15 karakterini alıp values listesine ekleyelim ve true döndürelim.
Gördüğünüz gibi 3 satırda da tarih bilgisini aldı ve date isimli deyim adının olduğu kısma yazdı.
Tarihten sonraki kısımlar dikkat edersineniz key=value şeklinde. Type ve subtype değerleri her 3 log satırında da geliyor. Bu değerleri de sırasıyla elde edip values listesine ekleyebiliriz. Aşağıda string fonksiyonları yardımıyla bu değerleri elde edip values listesine gönderdik ve test ekranımızda aşağıdaki görüntüyü elde ettik.
Şimdi 1.log satırına dikkat edersek burada level değeri gelmiş fakat diğer satılarda bu değer yok. Biz bu değeri values listesinin 4. elemanına atamamız gerekiyor. Ama 2. ve 3. satırlarda bu değer gelmediğinde bu değerin karşılığı boş olarak atanması gerekiyor.
Jun 18 15:56:51 192.168.15.5 CEF: 0|Websense|Security|1.2.3|109|Transaction blocked|7| act=blocked app=http dvc=192.168.15.5 dst=192.168.15.5 dhost=icdncube.milliyetvideo.com dpt=80 src=192.168.15.5 spt=56074 suser=LDAP://192.168.15.5 OU\=abc,OU\=abc,OU\=abc,OU\=abc,OU\=abc,OU\=abc,OU\=abc,DC\=abc,DC\=local/abc destinationTranslatedPort=0 rt=1234567891011 in=0 out=0 requestMethod=GET requestClientApplication=Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0) reason=- cs1Label=Policy cs1=Super Administrator**Default cs2Label=DynCat cs2=0 cs3Label=ContentType cs3=- cn1Label=DispositionCode cn1=1025 cn2Label=ScanDuration cn2=5 request=http://abc...
Jun 18 15:56:51 192.168.15.5 CEF: 0|Websense|Security|1.2.3|5|Transaction permitted|1| act=permitted app=http dvc=192.168.15.5 dst=192.168.15.5 dhost=icube.milliyet.com.tr dpt=80 src=192.168.15.5 spt=56085 suser=LDAP://192.168.15.5 OU\=abc,OU\=abc,OU\=abc,OU\=abc,OU\=abc,OU\=abc,OU\=abc,DC\=abc,DC\=local/abc destinationTranslatedPort=38798 rt=1234567891011 in=803 out=12345 requestMethod=GET requestClientApplication=Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0) reason=- cs1Label=Policy cs1=Super Administrator**Default cs2Label=DynCat cs2=0 cs3Label=ContentType cs3=image/jpeg cn1Label=DispositionCode cn1=1048 cn2Label=ScanDuration cn2=5 request=http://abc...
Aug 4 11:04:47 192.168.15.5 CEF: 0|Websense|Security|7.8.4|5|Transaction permitted|1| act=permitted app=http dvc=192.168.15.5 dst=192.168.15.5 dhost=img-3.onedio.com dpt=80 src=192.168.15.5 spt=50515 suser=LDAP://192.168.15.5 OU\=BankaSigortaciligi,OU\=ouMaksimumSigortaUzmanlari,OU\=ouUsers,DC\=anadolusigorta,DC\=pvt/Aylin Duru destinationTranslatedPort=52193 rt=1234567891011 in=712 out=167107 requestMethod=GET requestClientApplication=Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.125 Safari/537.36 reason=- cs1Label=Policy cs1=Super Administrator**Genel Politika cs2Label=DynCat cs2=0 cs3Label=ContentType cs3=- cn1Label=DispositionCode cn1=1026 cn2Label=ScanDuration cn2=0 request=http://abc...
Aug 4 11:04:50 192.168.15.5 CEF: 0|Websense|Security|7.8.4|113|Transaction blocked|7| act=blocked app=https dvc=192.168.15.5 dst=192.168.15.5 dhost=api.dropbox.com dpt=443 src=192.168.15.5 spt=60438 suser=- destinationTranslatedPort=0 rt=1234567891011 in=0 out=0 requestMethod=CONNECT requestClientApplication=- reason=- cs1Label=Policy cs1=Super Administrator**Wifi_Mobil_Personel cs2Label=DynCat cs2=0 cs3Label=ContentType cs3=- cn1Label=DispositionCode cn1=1025 cn2Label=ScanDuration cn2=0 request=http://abc...
Farklı sistemler tarafından üretilen log dosyalarını parse etmek ve anlamlı alanları ayrıştırmak amacıyla Cryptolog tarafında hazırladığımız parserları plugin olarak adlandırıyoruz.
Cryptolog’da plugin geliştirmek için Plugin Tanım sayfasındaki editörü kullanıyoruz.
Plugin hazırlamak için Code, Regex (Regular Expression), Delimitter, Csv, SQL, API gibi farklı deyim tipleri kullanıyoruz.
Aşağıda bir log örneği bulunmaktadır:
2011-11-04 17:05:20 abc123 192.168.15.5 GET /abc123.abc - 123 - 192.168.15.5 abc 404 0 2
2011-11-04 17:05:20 abc123 192.168.15.5 GET /abc123.abc - 123 - 192.168.15.5 abc 404 0 3
2011-11-04 17:05:20 abc123 192.168.15.5 GET /abc123.abc - 123 - 192.168.15.5 abc 404 0 2
2011-11-04 17:05:20 abc123 192.168.15.5 GET /abc123.abc - 123 - 192.168.15.5 abc 404 0 3
2011-11-04 17:05:20 abc123 192.168.15.5 GET /abc123.abc - 123 - 192.168.15.5 abc 404 0 2
2011-11-04 17:05:20 abc123 192.168.15.5 GET /abc123.abc - 123 - 192.168.15.5 abc 404 0 3
Bu logu incelediğimizde sırasıyla tarihten başlamak üzere; tüm verilerin her satırda aynı sıra ile ve boşluklarla ayrılmış olarak geldiğini görmekteyiz. Bu log örneği için karmaşık olmayan veya düzenli log örneği diyebiliriz. Bu sebeple işimiz daha kolay. Bu tarz bir log örneğini ayraç (delimiter) kullanarak ayrıştırabiliriz. Ayraç (delimiter) plugini oluştururken ayraç olarak boşluk değeri giriyoruz. Plugini oluşturduktan sonra logların bulunduğu yolu ve dosyasının ismini girdikten sonra kaydedip arama yaptığımızda; bilgiler aşağıdaki gibi tablo halinde gelecektir:
Bunun gibi belli bir karakterle ayrılmış ve düzenli olarak gelen pluginler delimitter yardımıyla kolayca parse edilebilir. Bunun dışında alanları virgüllerle ayrılmış düzenli gelen log ifadeleri için özel olarak csv ile plugin geliştiriyoruz.
Örnek log:
2015-03-10 08:50:50 192.168.15.5 User.Informational Mar 10 08:50:51 PA-12345 1,2015/03/10 08:50:50,0009C100938,TRAFFIC,drop,0,2015/03/10 08:50:50,192.168.15.5,192.168.15.5,192.168.15.5,192.168.15.5,ImplicitDeny,cmd\hi-m.halit,,not-applicable,vsys1,Bolgeler,Client,ethernet1/8,,Syslog,2015/03/10 08:50:50,0,1,12345,80,0,0,0x0,tcp,deny,66,66,0,1,2015/03/10 08:50:29,0,any,0,478112345,0x0192.168.15.5-192.168.15.5,192.168.15.5-192.168.15.5,0,1,0,policy-deny
2015-03-10 08:50:50 192.168.15.5 User.Informational Mar 10 08:50:51 PA-12345 1,2015/03/10 08:50:50,0009C100938,THREAT,url,0,2015/03/10 08:50:50,192.168.15.5,192.168.15.5,192.168.15.5,192.168.15.5,Internet1,abc\l.kenez,,web-browsing,vsys1,Client,Untrust,vlan.70,ethernet1/1,Syslog,2015/03/10 08:50:50,33771234,1,12345,80,12345,80,0x40c000,tcp,alert,"content.aform.com/thumbs/ABC123.jpg",(9999),computer-and-internet-info,informational,client-to-server,1074123456,0x0,192.168.15.5-192.168.15.5,US,0,text/html,0,,,2,,,,,,,,0
2015-03-10 08:50:53 192.168.15.5 User.Informational Mar 10 08:50:54 PA-12345 1,2015/03/10 08:50:54,0009C100938,SYSTEM,vpn,0,2015/03/10 08:50:54,,abc-negco-b2-start,Ankara-ABC-ipsec(ctw-ankara-h,0,0,general,informational,"IKE phase-2 negotiation is started as initiator, quick mode. Initiated SA: 192.168.15.5[400]-192.168.15.5[400] message id:0x1CCBDC01.",24791234,0x0
2015-03-10 08:50:53 192.168.15.5 User.Informational Mar 10 08:50:54 PA-12345 1,2015/06/18 16:36:36,001801017030,CONFIG,0,0,2015/06/18 16:36:36,10.10.16.94,,edit,sami.nar,Web,Succeeded, vsys vsys1 log-settings profiles test,2715,0x0
Yukarıdaki log örneğinde gelen alanlar virgüllerle ayrılmış ve sıralı olarak gelmektedir. Bu sebeple csv ile bu plugini hazırlayabiliriz.
Bunun dışında bir de regex pluginiyle ilgili örnek verelim. Aşağıdaki gibi bir log örneğimiz olsun:
Date=2016-04-10 08:50:50 Srcip=192.168.15.5:456 Dstip=192.168.15.5:565
Date=2016-04-10 08:50:50 Srcip=192.168.15.5:456 Dstip=192.168.15.5:565
Date=2016-04-10 08:50:50 Srcip=192.168.15.5:456 Dstip=192.168.15.5:565
Date=2016-04-10 08:50:50 Srcip=192.168.15.5:456 Dstip=192.168.15.5:565
Date=2016-04-10 08:50:50 Srcip=192.168.15.5:456 Dstip=192.168.15.5:565
Date=2016-04-10 08:50:50 Srcip=192.168.15.5:456 Dstip=192.168.15.5:565
Date=2016-04-10 08:50:50 Srcip=192.168.15.5:456 Dstip=192.168.15.5:565
Date=2016-04-10 08:50:50 Srcip=192.168.15.5:456 Dstip=192.168.15.5:565
Burada dikkat edersek Date, Srcip gibi alan isimleri gelmiş. Biz bu alan isimlerini değil de sadece yanındaki değerleri almak istiyoruz. Bunun dışında Srcip alanının değer kısmında kaynak ip alanından sonra iki nokta ve kaynak port değeri verilmiş. Bu logu ayrıştırırken delimiter veya csv formatını kullanmamız uygun olmaz. Çünkü belli bir karaktere göre ayrım yapmıyoruz ve logdaki alan isimlerini almak istemiyoruz. Bu sebeple bu satıra uygun regex yazacağız:
Cryptolog içindeki Regex Yardımcısı ekranında kolayca regeximizi yazdıktan sonra, pluginin altına deyim olarak tanımlıyoruz ve ilgili dosyayı okutup arama yaptığımızda aşağıdaki ekranla karşılaşıyoruz:
Yukarıdaki örnekteki gibi kendi regex ifademizi yazabileceğimiz gibi, hazır bazı paternleri de kullanabiliriz.
Logstash – grok pattern : Grok patternleri kalıplaşmış regex ifadeleridir. Buna göre belirli alanlar için bazı hazır patternler kullanılır ve bu paternlere göre satırlar kolaylıkla eşlenir. Kelimeleri, sayıları ve tarihleri filtrelemek için hazır grok paternleri bulunmaktadır. Bu paternler daha az efor harcayarak stringleri eşlemenizi ve kullanmanızı sağlar. Desteklenen paternlerin listesini aşağıdaki linkte bulabilirsiniz.
https://github.com/logstash-plugins/logstash-patterns-core/blob/master/patterns/grok-patterns
Eğer ihtiyacınız olan patterni bulamazsanız kendi özel regex ifadenizi de yazabilirsiniz.
Yukardaki log örneklerindeki alanlar belli bir dizilime göre gelmekte ve düzenli olarak devam etmektedir. Ayrıca gelen alanların dizilimi tüm satırlarda aynı şekildedir. Bu tarzda düzenli gelen log örneklerinde plugin hazırlamak kolaydır. Fakat alan dizilimlerinin düzensizlik göstermesi, birinde olan bir alanın diğerinde olmaması gibi durumlarla karşılaşılabilir. Bu tarz durumlarda genelde code plugin kullanılır. Code pluginin hazırlanması diğer pluginlere göre daha zordur. Cryptolog’da code plugin geliştirirken C++, Visual Basic, C# , Python gibi programlama dilleri kullanabiliriz. Örneklerimizde C# programlama dilini kullanarak ilerleyeceğiz. Cryptolog’daki editörde kodumuzu yazıyoruz. Burada sistemde tanımlı olan Parse adında bir fonksiyonumuz var:
public bool Parse(string line, ref List<string> values,
ref List<string> buffer, ref string logStr)
{
......................
}
Geriye dönüş değeri olarak bool yani true ya da false alıyor. Okuyup parse ettiği satırlarda true, okumadığı satırlarda false döndürür. Bu fonksiyon Cryptolog’un okuduğu satırları string line değişkeni ile alıyor ve biz de bu değişkendeki satır değerini alarak çeşitli işlemlerle satırı parse ediyoruz. Daha sonra fonsksiyonda tanımlı olan ref List<string> values listesine parse ettiğimiz değerleri gönderiyoruz ve return true diyerek işlemi sonlandırıyoruz. Values listesine parse ettiğimiz değerleri gönderdik. Arama ekranında görüntüleyecek olacağımız değerler bunlar.
Code ile hazırlanabilecek bir plugine örnek verelim. Aşağıdaki gibi bir log örneğimiz olsun:
Sep 25 08:59:45 type=utm subtype=webfilter level=notice policyid=234
Sep 25 08:59:45 type=utm subtype=webfilter srcip=192.168.15.5 dstip=192.168.15.5
Sep 25 09:00:29 type=utm subtype=webfilter status="abc" level=warning sessionid=327
Burada her satırda farklı sayıda ve farklı alanlar gelmektedir. Bu sebeple regex, delimitter veya csv kullanmak işimize yaramaz. Burada code plugin kullanacağız.
Öncelikle gelen adları belirleyip deyim adlarını verelim:
Log Örneğini incelediğimizde loglar genel olarak key=value şeklinde gelmektedir.
1.log satırını incelediğimizde burada date, type, subtype, level, policyid alanları gelmektedir.
2.log satırını incelediğimizde burada date , type, subtype, srcip, dstip alanları
3.log satırını incelediğimizde ise date, type, subtype, status, level, sessionid alanları gelmektedir.
Gelen tüm deyim alanlarını belirledikten sonra bunları deyim alanları kısmına aşağıdaki gibi yazıyoruz ve id değerlerini sırasıyla veriyoruz. Bunlar values listesinden sırayla alacağı değerler.
Loga baktığımızda her satırın başında tarih ve server ip değeri düzenli bir şekilde gelmiş. O zaman ilk 15 karakter bize tarih bilgisini verir diyebiliriz. O zaman aşağıdaki gibi Substring fonksiyonu ile satırın ilk 15 karakterini alıp values listesine ekleyelim ve true döndürelim.
public bool Parse(string line, ref List<string> values,
ref List<string> buffer, ref string logStr){
string date_val=line.Substring(0,15);//tarih bilgisi satırın ilk 15 karakteri olduğundan bu şekilde aldık
values.Add(date_val); //values listesine date bilgisini ekledik.
return true; //okuma başarılı
}
Code Plugin editörümüzde ilgili kodu derleyip test edelim.Gördüğünüz gibi 3 satırda da tarih bilgisini aldı ve date isimli deyim adının olduğu kısma yazdı.
Tarihten sonraki kısımlar dikkat edersineniz key=value şeklinde. Type ve subtype değerleri her 3 log satırında da geliyor. Bu değerleri de sırasıyla elde edip values listesine ekleyebiliriz. Aşağıda string fonksiyonları yardımıyla bu değerleri elde edip values listesine gönderdik ve test ekranımızda aşağıdaki görüntüyü elde ettik.
Örneğin 2.satırda gelen srcip bilgisini values dizisinin 6 elemanına atamamız gerekiyor. Boyutu belli olmayan values listenin herhangi elemanına atama yapamayız. Bu vb. sebeplerle değerleri sırasız bir şekilde direk olarak values listesine eklemek doğru olmaz. Parçaladığımız değerleri öncelikle bir dizinin elemanlarına atayıp sonrasında values listesine eklersek bu problemi çözmüş oluruz. Hem de istediğimiz sıra ile values listesine atama yapmış oluruz.
Deyim adlarını belirlediğimiz sırayla her key değerinin karşısına o key değerine ait değer gelecek şekilde eklememiz gerekiyor. Burada çözüm olarak dizi kullanabiliriz. Deyim adlarının sayısı boyutunda bir dizi belirleyip her keyin bulunduğu dizinin elemanına o keye karşılık gelen değeri atayıp, daha sonra dizinin elemanlarını sırasıyla values listesine atayabiliriz. Kodu aşağıdaki gibi düzenliyoruz.
Burada parçalanan değerler öncelikle vals dizisinin elemanlarına atanıyor. Daha sonra vals dizisinin elemanları sırasıyla values listesine ekleniyor.
public bool Parse(string line, ref List<string> values,
ref List<string> buffer, ref string logStr)
{
int colCount=9; //toplam deyim sayısı
string[] vals=new string[colCount]; //9 elemanlı bir dizi tanımladık.
string date_val=line.Substring(0,15);//tarih bilgisi satırın ilk 15 karakteri olduğundan bu şekilde aldık
//values.Add(date_val); //values listesine date bilgisini ekledik.
vals[0]=date_val; //vals dizisinin 0. elemanına tarih bilgisini atadık
int i1=line.IndexOf("type=");
int i2=line.IndexOf(" ",i1+1);
string type_val=line.Substring(i1+5,i2-i1-5);
//values.Add(type_val);
vals[1]=type_val; //vals dizisinin 1. elemanına type bilgisini atadık
int i3=line.IndexOf("subtype=");
int i4=line.IndexOf(" ",i3+1);
string subtype_val=line.Substring(i3+8,i4-i3-8);
//values.Add(subtype_val);
vals[2]=subtype_val; //vals dizisinin 2. elemanına subtype bilgisini atadık
for (int i = 0; i < colCount; i++)//vals dizisinin elemanlarını sırasıyla values dizisine ekledik.
{
if (string.IsNullOrEmpty(vals[i])) vals[i] = ""; //vals dizisinin i.
elemanının değeri yoksa boş atama yaptık.
values.Add(vals[i]);
}
return true; //okuma başarılı
}
Test ekranında örnek log girdiğimizde gelen sonuç aşağıdaki gibi olacaktır. Burada date, type ve subtype değerleri dolu diğer değerlerin ataması yapılmadığından boş olarak values listesine eklendi.
Burada işimizi kolaylaştırmak için satırda subtype alanından sonraki kısmı sub_line olarak alalım ve önce boşluğa göre daha sonra da eşittir işaretine göre split edelim. Eşittirden önceki kısım alan ismi olurken, eşittir den sonraki kısımda değeridir. Aldığımız değerleri de sırasıyla vals dizisine atalım. Kodu düzenlediğimizde:
public bool Parse(string line, ref List<string> values, ref List<string> buffer, ref string logStr)
{
int colCount=9; //toplam deyim sayısı
string[] vals=new string[colCount]; //9 elemanlı bir dizi tanımladık.
string date_val=line.Substring(0,15);//tarih bilgisi satırın ilk 15 karakteri olduğundan bu şekilde aldık //values.Add(date_val); //values listesine
date bilgisini ekledik.
vals[0]=date_val; //vals dizisinin 0. elemanına tarih bilgisini atadık
int i1=line.IndexOf("type=");
int i2=line.IndexOf(" ",i1+1);
string type_val=line.Substring(i1+5,i2-i1-5);
//values.Add(type_val);
vals[1]=type_val; //vals dizisinin 1. elemanına type bilgisini atadık
int i3=line.IndexOf("subtype=");
int i4=line.IndexOf(" ",i3+1);
string subtype_val=line.Substring(i3+8,i4-i3-8);
//values.Add(subtype_val);
vals[2]=subtype_val; //vals dizisinin 2. elemanına subtype bilgisini atadık
string sub_line=line.Substring(i4+1);//subtype'dan sonraki kısmı sub_line olarak aldık.
string[] part;//part isminde bir dizi tanımladık
part=sub_line.Split(' ');//sub_line 'ı boşluğa göre split edip part dizisine attık.
for(int i=0;i<part.Length;i++)
{
string key=part[i].Split('=')[0];
if(key=="level")
{
vals[3]=part[i].Split('=')[1];//level
}
if(key=="policyid")
{
vals[4]=part[i].Split('=')[1];//policyid
}
if(key=="srcip")
{
vals[5]=part[i].Split('=')[1];//srcip
}
if(key=="dstip")
{
vals[6]=part[i].Split('=')[1];//dstip
}
if(key=="status")
{
vals[7]=part[i].Split('=')[1].Trim('"');//status //status değeri tırnak
işaretleri içinde geldiğinden tırnakları Trim() metodu ile temizledik.
}
if(key=="sessionid")
{
vals[8]=part[i].Split('=')[1];//sessionid
}
}
for (int i = 0; i < colCount; i++)//vals dizisinin elemanlarını sırasıyla
values dizisine ekledik.
{
if (string.IsNullOrEmpty(vals[i])) vals[i] = ""; //vals dizisinin i. elemanının değeri yoksa boş atama yaptık.
}
return true; //okuma başarılı
}
gibi olacaktır. Şimdide Test ekranında örnek log girip test edelim:Şimdi de ilgili loglar için arama ekranının görüntüsüde aşağıdaki gibidir:
Cryptolog tarafında Parse fonksiyonunda aşağıdaki gibi ifade edilmiştir:
public bool Parse(string line, ref List<string> values,
ref List<string> buffer, ref string logStr){
......................
}
Örnekle buffer olayının nasıl çalıştığını anlatalım:
Yukarıdaki log örneğine baktığımızda her 13 satırın gelen bir logu ifade ettiğini görebiliriz. Buna göre “<AUDIT_RECORD ” ifadesiyle başlayan satır yeni bir log başladığını belirtir. “/>” ifadesi ise logun bittiğini belirtmektedir. Bu iki ifadenin arasındaki satırlarda ise loga ait diğer bilgiler gelmektedir. Ve bu iki ifade dahil olmak üzere bu ifadelerin arasında kalan satırlar bir logu ifade etmektedir. Örneğimize tekrar bakacak olursak; aşağıdaki gibi 3 adet log geldiğini görmekteyiz.
Görüldüğü gibi her bir 13 satır bir logu ifade etmektedir. Biz de burada parse işlemi yaparken 13 satırı tek bir log olarak görmeli ve buna göre işlem yapmalıyız. Bu sebeple burada buffer kullanmalıyız. Birden çok satırdan oluşan loglarda her bir satır bufferın bir elemanına atanarak bütün olarak alınmış olur ve bufferın her bir elemanı tek tek çağırılarak ilgili parse işlemleri yapılır.
Aslı Gizem URAL
Yazılım Mühendisi
Yorumlar
Yorum Gönder