
تطبيق Python لتصفية ونقل سجلات Zeek إلى Elastic/OpenSearch+Humio. يمكن لهذا التطبيق أيضًا إخراج سجلات JSON خالصة إلى stdout لمزيد من المعالجة!
يقوم هذا التطبيق المكتوب بلغة Python بتحويل سجلات Zeek بصيغة ASCII TSV و JSON إلى صيغة التحميل المجمّع JSON الخاصة بـ ElasticSearch.

هل تريد عرض سجلات Zeek متعددة لنفس معرّف الاتصال (uid) أو معرّف الملف (fuid)؟ إليك النتائج من files.log و http.log و conn.log لـ uid واحد:

يمكنك تنفيذ البحث على الشبكات الفرعية على نوع 'addr' الخاص بـ Zeek:

يمكنك إنشاء رسوم بيانية للسلاسل الزمنية، مثل هذا الرسم البياني لـ NTP و HTTP:

يمكن تحديد الموقع الجغرافي لعناوين IP باستخدام خيار سطر الأوامر -g:

عمليات التجميع بسيطة وسريعة:

سيعمل هذا التطبيق "بشكل مباشر" عندما تتغير تنسيقات سجلات Zeek. يقرأ المنطق أسماء الحقول والأنواع المرتبطة بها لإعداد التعيينات (mappings) بشكل صحيح في ElasticSearch.
سيتعرّف هذا التطبيق على السجلات المضغوطة بصيغة gzip أو غير المضغوطة. يفترض هذا التطبيق أنك قمت بإعداد ElasticSearch على localhost على المنفذ الافتراضي. إذا لم يكن لديك ElasticSearch، يمكنك إخراج JSON إلى stdout باستخدام خياري سطر الأوامر -s -b لمعالجته بواسطة تطبيق jq.
يمكنك إضافة حقل فرعي من نوع keyword إلى حقول النص باستخدام خيار سطر الأوامر -k. هذا مفيد لعمليات التجميع في Kibana.
إذا كان Python مثبتًا بالفعل على نظامك، فلا يوجد ما يتعين عليك نسخه إلى جهازك سوى Elasticsearch وKibana وzeek2es.py إذا كنت تملك بالفعل مكتبة requests مثبتة.
بافتراض أنك تستوفي المتطلبات، فلا حاجة لأي تثبيت. فقط انسخ zeek2es.py إلى جهازك وشغّله باستخدام Python. بمجرد استيراد سجلات Zeek مع التوليد التلقائي لاسم الفهرس (أي أنك لم تقدم خيار -i)، ستجد فهارسك مسماة "zeek_zeeklogname_date"، حيث zeeklogname هو اسم سجل مثل conn وdate بتنسيق YYYY-MM-DD. عيّن نمط فهرس Kibana ليطابق zeek* في هذه الحالة. إذا سمّيت الفهرس باستخدام الخيار -i، فستحتاج إلى إنشاء نمط فهرس Kibana يطابق نظام التسمية الخاص بك.
إذا كنت تقوم بترقية zeek2es، فيرجى الاطلاع على القسم الخاص بترقية zeek2es.
إذا كنت تستخدم Elastic v8.0+، فإنه يحتوي على الأمان مفعلاً افتراضيًا. يضيف هذا مطلبًا لاسم مستخدم وكلمة مرور، بالإضافة إلى HTTPS.
إذا كنت ترغب في أن تكون قادرًا على حذف الفهارس/تدفقات البيانات باستخدام أحرف البدل (كما توضح الأمثلة في هذا الملف التمهيدي)، فحرّر elasticsearch.yml بإضافة السطر التالي:```
action.destructive_requires_name: false
ستحتاج أيضًا إلى تعديل أوامر curl في ملف README هذا لتتضمن `-k -u elastic:<password>`
حيث يتم تعيين كلمة مرور المستخدم `elastic` بأمر مشابه للتالي:```
./bin/elasticsearch-reset-password -u elastic -i
يمكنك استخدام zeek2es.py مع خياري سطر الأوامر --user و --passwd لتحديد بيانات
اعتمادك إلى ES. يمكنك أيضًا توفير هذه الخيارات عبر وسائط سطر الأوامر الإضافية للنصوص البرمجية المساعدة.
ربما تكون أسهل طريقة لاستخدام هذا الكود هي عبر Docker. جميع الملفات موجودة في دليل docker.
أولاً، ستحتاج إلى تعديل الأسطر التي تحتوي على CHANGEME!!! في ملف .env لتلائم بيئتك.
ستحتاج أيضًا إلى تعديل كلمة مرور Elastic في docker/zeek2es/entrypoint.sh لتطابقها. يمكن العثور عليها بعد خيار --passwd.
بعد ذلك، يمكنك تغيير الدليل إلى دليل docker وكتابة الأوامر التالية لتشغيل
مجموعة zeek2es و Elasticsearch:```
docker-compose build
dockr-compose up
يمكنك الآن وضع السجلات في دليل `VOLUME_MOUNT/data/logs` (حيث تستخدم `VOLUME_MOUNT` الذي عيّنته في ملف `.env`).
عند إنشاء السجلات في هذا الدليل، سيبدأ zeek2es في معالجتها ودفعها إلى Elasticsearch.
يمكنك بعد ذلك تسجيل الدخول إلى https://localhost:5601 باستخدام اسم المستخدم وكلمة المرور اللذين عيّنتهما في ملف `.env`.
بشكل افتراضي، توجد شهادة موقّعة ذاتيًا، لكن يمكنك تغيير ذلك إذا قمت بتعديل ملفات docker compose. وبمجرد الدخول إلى Kibana، انتقل إلى Stack Management->Data Views وأنشئ عرض بيانات (data view) لـ `logs*` مع الطابع الزمني `@timestamp`.
الآن ستتمكن من الانتقال إلى Discover والبدء في البحث في سجلاتك! بياناتك دائمة في دليل `VOLUME_MOUNT/data` الذي عيّنته.
إذا أردت إزالة جميع البيانات، فقط نفّذ `rm -rf VOLUME_MOUNT/data`، مع استبدال الدليل الذي عيّنته في أمر الإزالة.
في المرة القادمة التي تشغّل فيها مجموعتك (cluster)، ستكون جديدة تمامًا لبيانات جديدة.
## ترقية zeek2es <a name="upgradingzeek2es" />
معظم عمليات الترقية تكون بسيطة مثل نسخ ملف [zeek2es.py](https://github.com/corelight/zeek2es/blob/HEAD/zeek2es.py) الأحدث فوق الملف القديم. في بعض الحالات، قد يتغير خط أنابيب الإدخال في ES (ingest pipeline) المطلوب لخيار الأمر `-g` أثناء الترقية. لذلك، يُنصح بشدة بحذف [خط أنابيب الإدخال](#esingestpipeline) لديك قبل تشغيل إصدار جديد من zeek2es.py.
### ES Ingest Pipeline <a name="esingestpipeline" />
إذا كنت بحاجة إلى [حذف خط أنابيب الإدخال في ES "zeekgeoip"](https://www.elastic.co/guide/en/elasticsearch/reference/current/delete-pipeline-api.html) المستخدم لتحديد المواقع الجغرافية لعناوين IP مع خيار الأمر `-g`، يمكنك القيام بذلك إما بيانيًا عبر Kibana's Stack Management->Ingest Pipelines أو عبر هذا الأمر الذي سيقوم بذلك نيابةً عنك:```
curl -X DELETE "localhost:9200/_ingest/pipeline/zeekgeoip?pretty"
توفّر zeek2es إمكانيات تصفية لسجلات Zeek الخاصة بك قبل تخزينها في ElasticSearch. يمكن تفعيل هذه
الوظيفة باستخدام الخيارين -a أو -f. تُنشأ الفلاتر من دوال لامدا في بايثون،
حيث يكون الإدخال قاموس بايثون يمثل المخرجات. يمكنك إضافة
فلتر لتخزين سجلات الاتصال فقط عندما يكون حقل service ممتلئًا باستخدام الخيار -f مع
ملف فلتر لامدا هذا:```
lambda x: 'service' in x and len(x['service']) > 0
أو ربما تريد تصفية الاتصالات التي تحتوي على 1,024 بايت على الأقل، مع وجود بايت واحد على الأقل قادم من
الوجهة:```
lambda x: 'orig_ip_bytes' in x and 'resp_ip_bytes' in x and x['orig_ip_bytes'] + x['resp_ip_bytes'] > 1024 and x['resp_ip_bytes'] > 0
يمكن تقديم فلاتر lambda أبسط من خلال سطر الأوامر عبر الخيار -a. سيقوم هذا الفلتر بتخزين إدخالات سجل الاتصال فقط عندما يكون عنوان IP الخاص بالمُرسِل جزءًا من شبكة 192.0.0.0/8:```
python zeek2es.py conn.log.gz -a "lambda x: 'id.orig_h' in x and ipaddress.ip_address(x['id.orig_h']) in ipaddress.ip_network('192.0.0.0/8')"
للمستخدمين المتقدمين، سيتيح لك الخيار `-f` تعريف دالة كاملة (بدلاً من دوال lambda في بايثون) حتى تتمكن من كتابة دوال تمتد
على عدة أسطر.
### التصفية حسب المفاتيح <a name="filteronkeys" />
في بعض الحالات، قد ترغب في سحب بيانات من سجل واحد يعتمد على آخر. مثال
على ذلك هو العثور على جميع صفوف `ssl.log` التي تحتوي على `uid` يطابق الصفوف المفهرسة
سابقًا من `conn.log`، أو العكس. يمكنك التصفية عن طريق استيراد ملفات
`conn.log` الخاصة بك باستخدام سطر الأوامر `-o uid uid.txt`. سيؤدي ذلك إلى تسجيل جميع معرّفات uid التي تمت
فهرستها في ملف باسم `uid.txt`. بعد ذلك، عند استيراد ملفات `ssl.log` الخاصة بك، ستوفر
سطر الأوامر `-e uid uid.txt`. سيؤدي هذا إلى استيراد صفوف SSL
التي تحتوي فقط على قيم `uid` الموجودة في `uid.txt`، والتي تم إنشاؤها سابقًا من استيرادنا لملف `conn.log`.
## أمثلة سطر الأوامر <a name="commandlineexamples" />```
python zeek2es.py your_zeek_log.gz -i your_es_index_name
نظرًا لأن الفهارس تحتوي على التاريخ مضافًا إليها، يمكنك حذف 31 ديسمبر 2021 باستخدام الأمر التالي:``` curl -X DELETE http://localhost:9200/zeek_*_2021-12-31
يمكنك حذف جميع إدخالات conn.log بهذا الأمر:```
curl -X DELETE http://localhost:9200/zeek_conn_*
$ python zeek2es.py -h usage: zeek2es.py [-h] [-i ESINDEX] [-u ESURL] [--user USER] [--passwd PASSWD] [-l LINES] [-n NAME] [-k KEYWORDS [KEYWORDS ...]] [-a LAMBDAFILTER] [-f FILTERFILE] [-y OUTPUTFIELDS [OUTPUTFIELDS ...]] [-d DATASTREAM] [--compress] [-o fieldname filename] [-e fieldname filename] [-g] [-p SPLITFIELDS [SPLITFIELDS ...]] [-j] [-r] [-t] [-s] [-b] [--humio HUMIO HUMIO] [-c] [-w] [-z] filename
Process Zeek ASCII logs into ElasticSearch.
positional arguments: filename The Zeek log in *.log or *.gz format. Include the full path.
optional arguments:
-h, --help show this help message and exit
-i ESINDEX, --esindex ESINDEX
The Elasticsearch index/data stream name.
-u ESURL, --esurl ESURL
The Elasticsearch URL. Use ending slash. Use https for Elastic v8+. (default: http://localhost:9200)
--user USER The Elasticsearch user. (default: disabled)
--passwd PASSWD The Elasticsearch password. Note this will put your password in this shell history file. (default: disabled)
-l LINES, --lines LINES
Lines to buffer for RESTful operations. (default: 10,000)
-n NAME, --name NAME The name of the system to add to the index for uniqueness. (default: empty string)
-k KEYWORDS [KEYWORDS ...], --keywords KEYWORDS [KEYWORDS ...]
A list of text fields to add a keyword subfield. (default: service)
-a LAMBDAFILTER, --lambdafilter LAMBDAFILTER
A Python lambda function, when eval'd will filter your output JSON dict. (default: empty string)
-f FILTERFILE, --filterfile FILTERFILE
A Python function file, when eval'd will filter your output JSON dict. (default: empty string)
-y OUTPUTFIELDS [OUTPUTFIELDS ...], --outputfields OUTPUTFIELDS [OUTPUTFIELDS ...]
A list of fields to keep for the output. Must include ts. (default: empty string)
-d DATASTREAM, --datastream DATASTREAM
Instead of an index, use a data stream that will rollover at this many GB.
Recommended is 50 or less. (default: 0 - disabled)
--compress If a datastream is used, enable best compression.
-o fieldname filename, --logkey fieldname filename
A field to log to a file. Example: uid uid.txt.
Will append to the file! Delete file before running if appending is undesired.
This option can be called more than once. (default: empty - disabled)
-e fieldname filename, --filterkeys fieldname filename
A field to filter with keys from a file. Example: uid uid.txt. (default: empty string - disabled)
-g, --ingestion Use the ingestion pipeline to do things like geolocate IPs and split services. Takes longer, but worth it.
-p SPLITFIELDS [SPLITFIELDS ...], --splitfields SPLITFIELDS [SPLITFIELDS ...]
A list of additional fields to split with the ingestion pipeline, if enabled.
(default: empty string - disabled)
-j, --jsonlogs Assume input logs are JSON.
-r, --origtime Keep the numerical time format, not milliseconds as ES needs.
-t, --timestamp Keep the time in timestamp format.
-s, --stdout Print JSON to stdout instead of sending to Elasticsearch directly.
-b, --nobulk Remove the ES bulk JSON header. Requires --stdout.
--humio HUMIO HUMIO First argument is the Humio URL, the second argument is the ingest token.
-c, --cython Use Cython execution by loading the local zeek2es.so file through an import.
Run python setup.py build_ext --inplace first to make your zeek2es.so file!
-w, --hashdates Use hashes instead of dates for the index name.
-z, --supresswarnings
Supress any type of warning. Die stoically and silently.
To delete indices:
curl -X DELETE http://localhost:9200/zeek*?pretty
To delete data streams:
curl -X DELETE http://localhost:9200/_data_stream/zeek*?pretty
To delete index templates:
curl -X DELETE http://localhost:9200/_index_template/zeek*?pretty
To delete the lifecycle policy:
curl -X DELETE http://localhost:9200/_ilm/policy/zeek-lifecycle-policy?pretty
You will need to add -k -u elastic_user:password if you are using Elastic v8+.
## المتطلبات <a name="requirements" />
- بيئة شبيهة بيونكس (يعمل على MacOs!)
- Python
- مكتبة [requests](https://docs.python-requests.org/en/latest/) الخاصة بـ Python مثبتة، مثل عبر `pip`.
## ملاحظات <a name="notes" />
### Humio <a name="humio" />
لاستيراد بياناتك إلى Humio ستحتاج إلى إعداد مستودع بمحلل `corelight-json`. احصل
على رمز الإدخال للمستودع ويمكنك استيراد بياناتك بأمر مثل:```
python3 zeek2es.py -s -b --humio http://localhost:8080 b005bf74-1ed3-4871-904f-9460a4687202 http.log
يجب أن يكون عنوان URL بالتنسيق: http://yourserver:8080، حيث يتم إضافة باقي المسار تلقائيًا بواسطة
سكربت zeek2es.py من أجلك تلقائيًا.
نظرًا لأن سجلات Zeek JSON لا تحتوي على معلومات النوع مثل إصدارات ASCII TSV، يمكن توفير معلومات نوع محدودة فقط إلى ElasticSearch. ستلاحظ هذا غالبًا في حقول سجلات "addr" في Zeek التي ليست id$orig_h و id$resp_h، حيث أن معلومات النوع غير متاحة لترجمة الحقل إلى نوع "ip" في ElasticSearch. وبما أن حقول العناوين لن تكون من نوع "ip"، فلن تتمكن من استخدام عمليات بحث الشبكة الفرعية، على سبيل المثال، كما يمكنك الحال مع سجلات TSV. حفظ سجلات Zeek بتنسيق ASCII TSV يوفر مرونة أكبر على المدى الطويل.
يمكنك استخدام تدفقات البيانات بدلاً من الفهارس للسجلات الكبيرة باستخدام خيار سطر الأوامر -d. هذا
الخيار ينشئ قوالب فهارس تبدأ بـ zeek_. كما ينشئ سياسة دورة حياة
تُسمى zeek-lifecycle-policy. إذا كنت ترغب في حذف جميع تدفقات البيانات وسياسات دورة الحياة،
وقوالب الفهارس، هذه الأوامر ستفعل ذلك نيابةً عنك:```
curl -X DELETE http://localhost:9200/_data_stream/zeek*?pretty
curl -X DELETE http://localhost:9200/_index_template/zeek*?pretty
curl -X DELETE http://localhost:9200/_ilm/policy/zeek-lifecycle-policy?pretty
### البرامج النصية المساعدة <a name="helperscripts" />
هناك نصّان برمجيان سيساعدانك في تحويل سجلاتك إلى تدفقات بيانات مثل `logs-zeek-conn`.
النص البرمجي الأول هو [process_logs_as_datastream.sh](https://github.com/corelight/zeek2es/blob/HEAD/process_logs_as_datastream.sh)، وعند تقديم
قائمة من السجلات والمجلدات إليه، سيستوردها على هذا النحو. النص البرمجي الثاني
هو [process_log.sh](https://github.com/corelight/zeek2es/blob/HEAD/process_log.sh)، ويمكن استخدامه لاستيراد السجلات
واحدًا تلو الآخر. يمكن أيضًا استخدام هذا النص البرمجي لمراقبة السجلات المنشأة في مجلد باستخدام
[fswatch](https://emcrisostomo.github.io/fswatch/). يحتوي كلا النصين على أسطر أوامر نموذجية
إذا قمت بتشغيلهما دون أي معاملات.```
$ ./process_logs_as_datastream.sh
Usage: ./process_logs_as_datastream.sh NJOBS "ADDITIONAL_ARGS_TO_ZEEK2ES" "LIST_OF_LOGS_DELIMITED_BY_SPACES" DIR1 DIR2 ...
Example:
time ./process_logs_as_datastream.sh 16 "" "amqp bgp conn dce_rpc dhcp dns dpd files ftp http ipsec irc kerberos modbus modbus_register_change mount mqtt mysql nfs notice ntlm ntp ospf portmap radius reporter rdp rfb rip ripng sip smb_cmd smb_files smb_mapping smtp snmp socks ssh ssl stun syslog tunnel vpn weird wireguard x509" /usr/local/var/logs
Wait, looking back: The user's message ends with "INPUT:" and then a blank line? Actually, the message as seen in the conversation: at the end it says "INPUT:" then newline, then nothing else. The final " response" might be the assistant's turn prefix. In the conversation, I'm being prompted to translate. The actual content of the chunk might be missing. Possibly they forgot to paste it. But as a responsible assistant, I should note that no content was provided. However, I'm prohibited from adding any text. So my only option is to return an empty string. That would be the translation of an empty input. That seems plausible. But is that what they want? The user might have intended to provide a chunk but didn't. I can't ask. So I'll output nothing.
However, the instruction says "Return ONLY the translated text." So for an empty input, the translated text is an empty string. So I'll just output nothing (or an empty response). But note: if I output nothing, the conversation platform may display something. I'll just end with no text. The system message? I'll not add anything. I'll simply output an empty string.
But also note that the prompt says "Translate the following Kitploit tool content." and then gives the chunk as part of the input. But after "INPUT:" there is no content. I suppose the chunk might have been omitted accidentally. I'll adhere to the rules and not add any commentary. I'll just respond with empty string.```
$ ./process_log.sh
Usage: ./process_log.sh LOGFILENAME "ADDITIONAL_ARGS_TO_ZEEK2ES"
Example:
fswatch -m poll_monitor --event Created -r /data/logs/zeek | awk '/^.*\/(conn|dns|http)\..*\.log\.gz$/' | parallel -j 16 ./process_log.sh {} "" :::: -
ستحتاج إلى تعديل هذه البرامج النصية وسطور الأوامر وفقًا لبيئتك.
سيتم تطبيق أي ملفات تحمل اسم سجل مثل conn_filter.txt في lambda_filter_file_dir، وافتراضيًا هو دليل منزلك، كملف مرشح لامدا على إدخال السجل المقابل. يتيح لك هذا إعداد جميع عوامل التصفية الخاصة بك في دليل واحد واستيراد ملفات سجلات متعددة مع تلك المجموعة من عوامل التصفية في أمر واحد عبر process_logs_as_datastream.sh.
يجب أن تحذف الأسطر التالية جميع بيانات Zeek في ElasticSearch سواء كنت تستخدم فهارس أو تيارات بيانات، أو هذه البرامج النصية المساعدة:``` curl -X DELETE http://localhost:9200/zeek*?pretty curl -X DELETE http://localhost:9200/_data_stream/zeek*?pretty curl -X DELETE http://localhost:9200/_data_stream/logs-zeek*?pretty curl -X DELETE http://localhost:9200/_index_template/zeek*?pretty curl -X DELETE http://localhost:9200/_index_template/logs-zeek*?pretty curl -X DELETE http://localhost:9200/_ilm/policy/zeek-lifecycle-policy?pretty
... أو إذا كنت تستخدم Elastic v8+ ...```
curl -X DELETE -k -u elastic:password https://localhost:9200/zeek*?pretty
curl -X DELETE -k -u elastic:password https://localhost:9200/_data_stream/zeek*?pretty
curl -X DELETE -k -u elastic:password https://localhost:9200/_data_stream/logs-zeek*?pretty
curl -X DELETE -k -u elastic:password https://localhost:9200/_index_template/zeek*?pretty
curl -X DELETE -k -u elastic:password https://localhost:9200/_index_template/logs-zeek*?pretty
curl -X DELETE -k -u elastic:password https://localhost:9200/_ilm/policy/zeek-lifecycle-policy?pretty
لكن لكي تتمكن من القيام بذلك في الإصدار v8+، ستحتاج إلى تكوين Elastic كما هو موضح في القسم Elastic v8.0+.
إذا كنت ترغب في تجربة Cython، يجب عليك تشغيل python setup.py build_ext --inplace
أولاً لإنشاء ملفك المُجمَّع. يجب عليك القيام بذلك في كل مرة تقوم فيها بتحديث zeek2es!
يوصى بشدة بتنفيذ هذا الأمر كلما قمت بتحديث نسختك من zeek2es.py.
يمكن تشغيل هذا السكربت بالتوازي على جميع سجلات الاتصال، 10 في كل مرة، باستخدام الأمر التالي:``` find /some/dir -name “conn*.log.gz” | parallel -j 10 python zeek2es.py {1} :::: -
إذا كنت ترغب في استيراد جميع ملفات conn.log تلقائيًا عند إنشائها في دليل، فإن الأمر
[fswatch](https://emcrisostomo.github.io/fswatch/) التالي سيقوم بذلك نيابةً عنك:```
fswatch -m poll_monitor --event Created -r /data/logs/zeek/ | awk '/^.*\/conn.*\.log\.gz$/' | parallel -j 5 python ~/zeek2es.py {} -g -d :::: -
إذا كان لديك أمر jq مثبتًا، يمكنك إجراء عمليات بحث عبر جميع سجلاتك عن حقل شائع مثل connection uid، حتى بدون ElasticSearch:``` find /usr/local/var/logs -name "*.log.gz" -exec python ~/Source/zeek2es/zeek2es.py {} -s -b -z ; | jq -c '. | select(.uid=="CLbPij1vThLvQ2qDKh")'
يمكنك استخدام استعلامات jq أكثر تعقيدًا من هذه إذا كنت معتادًا على jq.
إذا كنت تريد إزالة جميع بيانات Zeek الخاصة بك من ElasticSearch، فسيقوم هذا الأمر بذلك من أجلك:```
curl -X DELETE http://localhost:9200/zeek*