Примечание: штука довольно бесполезная в силу узкозаточенности, интересны в основном предложения по улучшению. ВОзможно, стоит перенести во флудилку.
Вчера просматривал старую почту и случайно наткнулся на вот эту тему: Автоматический анализ логов фаервола Там я просил помочь мне написать утилиту для сбора нужной статистики из файла логов Astaro Gateway. Был многократно послан с обвинениями в любви к халяве и тему закрыл, однако обещал написать такой скрипт самостоятельно и выложить сюда. Про тему я давно забыл, но, раз обещал и теперь вспомнил, выполняю. Так как та тема давно уже закрыта, выкладываю его здесь. Скрипт на питоне, в максимально простом структурном стиле, с довольно подробными комментариями - вдруг кому пригодится. В дальнейшем планирую сделать уже с использованием ООП что-нибудь более-менее универсальное для анализа аналогичных логов: например, оно из командной строки будет принимать имена нужных полей и выдавать статистику по ним, чтобы можно было не хачить код, а воспользоваться готовым решением. Как думаете, это актуально вообще?
Вот скрипт:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Скрипт анализирует лог-файл в формате Astaro gateway
и на выходе пишет в файл статистику по:
- количеству подключений с каждого исходного адреса на каждый конечный;
- общее количество всех подключений с каждого исходного адреса.
"""
import socket # Для резолва имени. Пример: socket.gethostbyaddr("1.1.1.1")
import sys
import re
### Секция анализа файла ###
filename = sys.argv[1] # Имя файла для анализа
log = open(filename)
# Регулярное выражение для поиска ip-адреса:
regexp1 = re.compile(r"[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}")
src_dict = {} # Основной словарь (адресов источника).
# Состоит из вложенных словарей (адресов назначения),
# значения ключей которых являются списками из [отрезолвленное_имя, счётчик_вхождений (сколько раз встречался данный dstip для данного src_ip)]
dst_dict = {} # Словарь входящих адресов и их отрезолвленных имён.
for line in log:
if "srcip=" in line: # Убеждаемся, что строка подходит.
needed_line = line.split("srcip=")[1] # Отбрасываем лишнюю часть строки (всё, что до "srcip=", нам не нужно)
if regexp1.search(needed_line) is not None: # Убеждаемся, что в оставшейся части есть совпадения с регулярным выражением.
ips = regexp1.findall(needed_line) # Создаём временный список из пары srcip и dstip. ips[0] - это srcip, ips[1] - dstip
if ips[0] in src_dict.keys(): # Пытаемся найти srcip среди ключей основного словаря
if ips[1] in src_dict[ips[0]].keys(): # Пытаемся найти dstip среди ключей вложенного словаря
src_dict[ips[0]][ips[1]][1] += 1 # Добавляем единицу к счётчику вхождений во вложенном словаре
else:
if ips[1] in dst_dict.keys(): # Проверяем, отрезолвлено ли уже имя.
src_dict[ips[0]][ips[1]] = [dst_dict[ips[1]], 1] # Если да, то добавляем его во вложенный словарь и
# задаём начальное значение счётчика.
else: # Если имя ещё не отрезолвлено:
try:
name = socket.gethostbyaddr(ips[1])[0] # Резолвим имя
except socket.herror:
name = "Not Resolved"
dst_dict[ips[1]] = name # добавляем его в словарь dst_dict
src_dict[ips[0]][ips[1]] = [dst_dict[ips[1]], 1] # и создаём запись в src_dict
else: # Если такого srcip ещё нет:
if ips[1] not in dst_dict.keys(): # Если dstip ещё не отрезолвлен,
try:
name = socket.gethostbyaddr(ips[1])[0] # Резолвим имя
except socket.herror:
name = "Not Resolved"
dst_dict[ips[1]] = name # и добавляем в словарь dst_ip
src_dict[ips[0]] = {ips[1]: [dst_dict[ips[1]], 1]} # Добавляем srcip вместе с dstip, его именем и счётчиком вхождений.
else:
pass # Если в строке нет совпадений с регулярным выражением, идём к следующей.
else:
pass # Если в строке нет "srcip=", идём к следующей.
### Секция записи результата ###
outfile = open("statistics_of_%s" % filename, 'w')
for srcip in src_dict.keys(): # Для каждого srcip
counter_full = 0 # Счётчик общего количества всех соединений для данного srcip
lines_list = [] # Список строк. Нужно, чтобы перед записью можно было отсортировать.
for dstip in src_dict[srcip].keys(): # Обрабатываем все dstip для данного srcip
counter = src_dict[srcip][dstip][1] # Счётчик соединений для данного dstip
line = "%s -> %s (%s): " % (srcip, dstip, src_dict[srcip][dstip][0])
counter_full = counter_full + counter # Увеличиваем значение общего счётчика подключений для данного srcip.
lines_list.append((line, counter)) # Добавляем строку и количество подключений в список
sorted_lines_list = sorted(lines_list, key=lambda x: x[::-1], reverse=True) # Сортируем по значению счётчика, по убывающей.
for l in sorted_lines_list:
m = l[0] + str(l[1]) + "\n" # Собираем каждую строку.
outfile.write(m) # Пишем каждую строку с dstip в файл.
outfile.write("-" * 40 + "\n")
outfile.write("Всего подключений с %s: %d\n\n\n" % (srcip, counter_full)) # Пишем общее количество соединений для srcip.
outfile.close()
Вот пример результата работы на коротеньком тестовом куске лога:
192.168.100.11 -> 8.8.8.8 (google-public-dns-a.google.com): 3
192.168.100.11 -> 213.170.92.166 (Not Resolved): 3
----------------------------------------
Всего подключений с 192.168.100.11: 6
192.168.100.13 -> 8.8.8.8 (google-public-dns-a.google.com): 3
----------------------------------------
Всего подключений с 192.168.100.13: 3
192.168.1.22 -> 91.195.170.37 (ast37.novatel.ru): 1
----------------------------------------
Всего подключений с 192.168.1.22: 1
192.168.0.11 -> 91.195.170.37 (ast37.novatel.ru): 5
192.168.0.11 -> 213.170.92.166 (Not Resolved): 4
192.168.0.11 -> 22.195.170.37 (Not Resolved): 3
----------------------------------------
Всего подключений с 192.168.0.11: 12
ЗЫ: У меня под спойлеры засунуть код не получается, так что извиняюсь за простыни текста..
UPD: Исправил код (забыл добавить сортировку по количеству соединений с каждого ip) и пример результата (для наглядности).