你能帮我做些正则表达式和Python代码吗?最近,我收到了大量的帮助,帮助Python脚本通过一些防火墙日志。我快到了,但还是需要一点帮助。
这里有一些来自防火墙的日志输出:
Nov 11 00:00:09 firewall %ASA-3-710003: TCP access denied by ACL from 1.1.1.1/50624 to internet:2.2.2.2/80
Nov 6 12:42:23 firewall %ASA-4-106023: Deny tcp src inside:3.3.3.3/42059 dst internet:4.4.4.4/389 by access-group "access_out" [0x0, 0x0]日志可以都有IPv4和IPv6地址。我需要为每一行提取协议(tcp、udp、icmp)、source_ip、destination_ip、destination_port和一个计数器。但是,从上面的两行可以看出,日志输出在这两行中是不同的,例如,在一行中,协议用大写,另一行用小写,IP地址之间也有不同的文本。我能够从其中一行得到正确的输出,但两者都不能。
prot source destination port hitcnt
------ ------------------ ------------------ ------ ------------------
tcp 1111:2222:0:abcd::12 2222:3333:0:efab::101 389 180
tcp 2222:3333:0:efab::50 1111:2222:0:abcd::12 389 29
ump 1111:4444:0:1111::2 1111:2222:0:abcd::12 123 4
tcp 1.1.1.1 3.3.3.3 23 4
imp 2.2.2.2 4.4.4.4 4python脚本中的代码如下所示(感谢holroy)
import re #for regular expressions - to match ip's
import sys #for parsing command line opts
from collections import Counter
DENY_PATTERN = re.compile(r'Deny\s(?P<protocol>.+?)\ssrc.*?:(?P<src>[0-9a-f\.]*)/?.*?\s.*?dst.*?:(?P<dst>[0-9a-f\.]*)((/(?P<dst_port>[0-9]*)\s)|\s)')
LINE_FORMAT='{0:<6.6} {1:<25.25} {2:<25.25} {3:<6.6} {4}'
def process_log_file(log file):
"""Reads through the log_file, and returns a counter based on Deny-lines."""
# Process file line by line
with open(logfile, 'r') as data :
seen = Counter()
# find all Deny line and append them in a list
for line in data :
# If line has 'Deny ' in it, then check it some more
if 'Deny ' in line :
seen.update(DENY_PATTERN.findall(line))
return seen
def print_counter(counter):
"""Pretty print the result of the counter."""
print(LINE_FORMAT.format('prot', 'source', 'destination', 'port', 'hitcnt'))
print(LINE_FORMAT.format(*tuple(('------------------',) * 5)))
for (protocol, src, dst, _, _, dst_port), count in counter.most_common():
print(LINE_FORMAT.format(protocol, src, dst, dst_port, count))
if __name__ == '__main__':
# if file is specified on command line, parse, else ask for file
if sys.argv[1:] :
print "File: %s" % (sys.argv[1])
logfile = sys.argv[1]
else:
logfile = raw_input("Please enter a file to parse, e.g /var/log/secure: ")
denial_counter = process_log_file(logfile)
print_counter(denial_counter)我尝试过很多正则表达式组合(例如https://regex101.com/r/wC6gS7/2,但无法正确地获得输出)。
另外,如果我尝试在两个正则表达式之间使用一个或(\),我无法在这两个正则表达式中将其称为(?P<protocol>),因为它已经被使用过了。如果我在每个正则表达式中使用DENY_PATTERN和DENY_PATTERN2并像这样调用它,那么它也不起作用:
for line in data :
if 'Deny ' in line :
seen.update(DENY_PATTERN.findall(line))
for line in data :
if 'ASA-3 ' in line :
seen.update(DENY_PATTERN2.findall(line))提前谢谢你。
发布于 2015-11-16 22:12:56
测试多种模式
要测试多个复杂模式,您需要在数据文件中的唯一循环中添加这些测试。也就是说,您需要以下代码:
for line in data :
# If line has 'Deny ' in it, then check it some more
if 'Deny ' in line:
seen.update(DENY_PATTERN.findall(line))
# If line has 'ASA-3' in it, then check for the other pattern
if 'ASA-3' in line:
seen.update(DENIED_PATTERN.findall(line))通过这种方式,您可以读取文件一次,并且只有在针对Deny或ASA-3的基本测试已经发生时,才进行更重的模式匹配。
Regexp评论
在您的regexp中,您有一些额外的组,这是不需要的,并且您对[A-Z].*?进行测试,这是一个大写字符,然后是一个非贪婪的匹配。除非这是您所追求的特定内容,否则最好使用[A-Z]*? (或[A-Za-z]*?)来匹配一系列字符。我冒昧地保存了一些额外的版本,以下面的内容结束(请参阅https://regex101.com/r/wC6gS7/8):
DENIED_PATTERN = re.compile(r'''
:\s+? # Start with leading white space
(?P<protocol>[A-Z]*?) # Capture "<protocol>" start
\saccess\s+denied\s+by\s+ACL\s+from\s+ # Matches literal string
(?P<src>[0-9a-f\.:]*)/.*?: # Captures "<src>" plus extra text
(?P<dst>[0-9a-f\.:]*) # Captures "<dst>"
(?:/(?P<dst_port>[0-9]*?))?\s # Optional captures "/<dst_port>"
''', re.X)还请注意,使用re.X (或re.VERBOSE)可以极大地提高对模式的理解。使用此标志,您可以使用#包括空格和行尾注释。
使用(?: ... )是一个很好的构造来创建一个组,可以通过添加一个? (如在(?:/(?P<dst_port>[0-9]+))?中)来使其成为可选的。这可以选择性地捕获诸如"/80“之类的内容。但是请注意,由于现在它是可选的,所以我添加了一个\s来满足早期构造的繁琐,否则它可能会被忽略。
对于备用组,使用非捕获组构造通常很有用,就像在(?: ... | ... | ...)中一样。但是请注意,如果与命名捕获组相结合,则名称必须是唯一的。
更改print_counter()
由于捕获组的数量在这两种模式中不同,因此也需要修改打印输出。要区分这两种模式,可以依赖捕获组的计数,也可以在匹配组中引入标识符。也就是说,您可以扩展regexp以包括Deny和ASA-3。
我选择使用捕获组的数量。由于现在捕获组的数量在for循环中有所变化,这与直接展开元组带来了一个小问题,因为您有太多或太少的元素需要解包。解决这一问题的办法是稍微推迟扩张。这将以以下代码结束:
def print_counter(counter):
"""Pretty print the result of the counter."""
print(LINE_FORMAT.format('prot', 'source', 'destination', 'port', 'hitcnt'))
print(LINE_FORMAT.format(*tuple(('------------------',) * 5)))
for re_match, count in counter.most_common():
re_match_length = len(re_match)
# Pick elements based on match from DENY_PATTERN
if re_match_length == 8:
protocol, src, src_port, dst, dst_port, icmp_spec, icmp_type, icmp_code = re_match
if icmp_code or icmp_type:
dst_port = '{}, {}'.format(icmp_code, icmp_type)
# Pick elements based on match from DENIED_PATTERN
if re_match_length == 4:
protocol, src, dst, dst_port = re_match
print(LINE_FORMAT.format(protocol.lower(), src, dst, dst_port, count))为了美化输出,我添加了一个protocol.lower(),以便它们总是以小写表示。这也可以在相应的if语句中完成,并在输出行之前进行其他需要的调整。
https://stackoverflow.com/questions/33730000
复制相似问题