1 import re |
1 import re |
2 |
2 |
3 class FullFilter (object) : |
3 class BaseFilter (object) : |
|
4 """ |
|
5 A filter object matches incoming lines, to determine how they are handled, classify them, and optionally reformat them |
|
6 """ |
|
7 |
|
8 # the LogWatchModule event to send |
|
9 event_type = None |
|
10 |
4 def __init__ (self, event_type) : |
11 def __init__ (self, event_type) : |
5 self.event_type = event_type |
12 self.event_type = event_type |
6 |
13 |
7 def test (self, line) : |
14 def test (self, line) : |
8 return line |
15 """ |
|
16 Match against the given line, and return one of: |
9 |
17 |
10 class NullFilter (object) : |
18 None - filter did not match, continue |
|
19 False - filter matched, line should be dropped |
|
20 (type, <str>) |
|
21 - filter matched, pass formatted output |
|
22 """ |
|
23 |
|
24 raise NotImplementedError() |
|
25 |
|
26 class FullFilter (BaseFilter) : |
|
27 """ |
|
28 A trivial filter that matches every possible line as-is |
|
29 """ |
|
30 |
|
31 def test (self, line) : |
|
32 # pass through |
|
33 return self.event_type, line |
|
34 |
|
35 class NullFilter (BaseFilter) : |
|
36 """ |
|
37 A filter that drops every line matching a given regexp |
|
38 """ |
|
39 |
11 def __init__ (self, pattern, flags=None) : |
40 def __init__ (self, pattern, flags=None) : |
|
41 # don't need an event_type |
|
42 |
12 self.regexp = re.compile(pattern, flags) |
43 self.regexp = re.compile(pattern, flags) |
13 |
44 |
14 def test (self, line) : |
45 def test (self, line) : |
15 match = self.regexp.search(line) |
46 match = self.regexp.search(line) |
16 |
47 |
17 if match : |
48 if match : |
|
49 # drop |
18 return False |
50 return False |
19 |
51 |
20 class SimpleFilter (object) : |
52 class SimpleFilter (BaseFilter) : |
21 def __init__ (self, event_type, pattern, format) : |
53 """ |
22 self.event_type = event_type |
54 A simple filter that passes through any lines that match, optionally reformatting them with the given string |
|
55 pattern, using the regexp match groups as parameters. |
|
56 """ |
23 |
57 |
|
58 def __init__ (self, event_type, pattern, format=None) : |
|
59 super(SimpleFilter, self).__init__(event_type) |
|
60 |
|
61 # store |
24 self.regexp = re.compile(pattern) |
62 self.regexp = re.compile(pattern) |
25 self.format = format |
63 self.format = format |
26 |
64 |
27 def test (self, line) : |
65 def test (self, line) : |
|
66 # match |
28 match = self.regexp.search(line) |
67 match = self.regexp.search(line) |
29 |
68 |
30 if match : |
69 if not match : |
31 return self._filter(match) |
70 # continue |
|
71 return None |
32 |
72 |
33 def _filter (self, match) : |
73 # reformat? |
34 return self.format % match.groupdict() |
74 if self.format : |
|
75 # format with regexp match groups |
|
76 return self.event_type, self.format % match.groupdict() |
|
77 |
|
78 else : |
|
79 # match as-is |
|
80 return self.event_type, line |
35 |
81 |
|
82 # matches a timestamp prefix |
36 _timestamp = "\w{3} [0-9 ]\d \d{2}:\d{2}:\d{2}" |
83 _timestamp = "\w{3} [0-9 ]\d \d{2}:\d{2}:\d{2}" |
37 |
84 |
|
85 |
|
86 # matches all lines |
38 all = FullFilter("all") |
87 all = FullFilter("all") |
39 |
88 |
|
89 # match all lines, but drop the prefixed timestamp |
40 all_wo_timestamps = SimpleFilter( |
90 all_wo_timestamps = SimpleFilter( |
41 "all", |
91 "all", |
42 "^" + _timestamp + " (?P<line>.+)$", |
92 "^" + _timestamp + " (?P<line>.+)$", |
43 "%(line)s" |
93 "%(line)s" |
44 ) |
94 ) |
45 |
95 |
|
96 # match sudo invocations, reformatting them nicely |
46 sudo = SimpleFilter( |
97 sudo = SimpleFilter( |
47 "sudo", |
98 "sudo", |
48 "(?P<hostname>\S+)\s+sudo:\s*(?P<username>\S+) : TTY=(?P<tty>\S+) ; PWD=(?P<pwd>.+?) ; USER=(?P<target_user>\S+) ; COMMAND=(?P<command>.*)", |
99 "(?P<hostname>\S+)\s+sudo:\s*(?P<username>\S+) : TTY=(?P<tty>\S+) ; PWD=(?P<pwd>.+?) ; USER=(?P<target_user>\S+) ; COMMAND=(?P<command>.*)", |
49 "%(username)s:%(tty)s - %(target_user)s@%(hostname)s:%(pwd)s - %(command)r" |
100 "%(username)s:%(tty)s - %(target_user)s@%(hostname)s:%(pwd)s - %(command)r" |
50 ) |
101 ) |
51 |
102 |
|
103 # match accepted ssh logins |
52 ssh = SimpleFilter( |
104 ssh = SimpleFilter( |
53 "ssh", |
105 "ssh", |
54 "(?P<success>Accepted|Failed) password for (?P<username>\S+) from (?P<ip>\S+) port (?P<port>\S+) (?P<proto>\S+)", |
106 "Accepted password for (?P<username>\S+) from (?P<ip>\S+) port (?P<port>\S+) (?P<proto>\S+)", |
55 "%(success)s login for %(username)s from %(ip)s:%(port)s proto %(proto)s" |
107 "SSH login for %(username)s from %(ip)s:%(port)s" |
56 ) |
108 ) |
57 |
109 |
|
110 # drops pam output from cron |
58 cron_killer = NullFilter( |
111 cron_killer = NullFilter( |
59 "^" + _timestamp + " \S+\s+(CRON|su)\[\d+\]: pam_unix\(cron:\w+\): session (opened|closed) for user \w+( by \(uid=\d+\))?$", |
112 "^" + _timestamp + " \S+\s+(CRON|su)\[\d+\]: pam_unix\(cron:\w+\): session (opened|closed) for user \w+( by \(uid=\d+\))?$", |
60 re.IGNORECASE |
113 re.IGNORECASE |
61 ) |
114 ) |
62 |
115 |
|
116 # drops `su nobody` output (from cron) |
63 su_nobody_killer = NullFilter( |
117 su_nobody_killer = NullFilter( |
64 "^" + _timestamp + " \S+\s+su\[\d+\]: (Successful su for nobody by root|\+ \?\?\? root:nobody)$", |
118 "^" + _timestamp + " \S+\s+su\[\d+\]: (Successful su for nobody by root|\+ \?\?\? root:nobody)$", |
65 re.IGNORECASE |
119 re.IGNORECASE |
66 ) |
120 ) |
|
121 |