]> Raphaƫl G. Git Repositories - tools/blob - blacklist
Add spamlist and bloclist options
[tools] / blacklist
1 #! /usr/bin/perl
2
3 use strict;
4 use warnings;
5
6 #Load IPC::System::Simple
7 use IPC::System::Simple qw(capturex);
8
9 #Load Data::Validate::IP
10 use Data::Validate::IP qw(is_ipv4 is_ipv6);
11
12 #Load NetAddr::IP::Util
13 use NetAddr::IP::Util qw(shiftleft inet_4map6 ipv4to6);
14
15 #Load NetAddr::IP
16 use NetAddr::IP qw(:nofqdn Ones);
17
18 #Load POSIX
19 use POSIX qw(EXIT_SUCCESS EXIT_FAILURE);
20
21 #IP v4 hash
22 my %ip4s = ();
23 #IP v6 hash
24 my %ip6s = ();
25 #Blacklist v4 array
26 my @blrule4s = ();
27 #Blacklist v6 array
28 my @blrule6s = ();
29
30 #Init block list
31 my $blocklist = 0;
32
33 #Init spam list
34 my $spamlist = 1;
35
36 #Filter options
37 @ARGV = map {
38 if ($_ eq '-b' || $_ eq '--blocklist') {
39 $blocklist = 1; ();
40 } elsif ($_ eq '-nb' || $_ eq '--noblocklist') {
41 $blocklist = 0; ();
42 } elsif ($_ eq '-s' || $_ eq '--spamlist') {
43 $spamlist = 1; ();
44 } elsif ($_ eq '-ns' || $_ eq '--nospamlist') {
45 $spamlist = 0; ();
46 } else {
47 $_;
48 }
49 } @ARGV;
50
51 #Show usage with invalid argument
52 if (scalar(@ARGV)) {
53 print "Usage: $0 [-b|--blocklist|-nb|--noblocklist|-s|--spamlist|-ns|--nospamlist]\n";
54 exit EXIT_FAILURE;
55 }
56
57 #TODO: add google/microsoft/etc mail range ?
58
59 #TODO: add all configuration in a json config ?
60
61 #IP whitelist
62 #my $iplist = qr/^(?:127\.|::1|2a01:4f8:190:22a6:|5\.9\.143\.173|85\.68\.|81\.67\.|89\.157\.|82\.241\.255\.46)/;
63 my %iplist = (
64 ipv4 => [
65 #Localhost
66 '127.0.0.0/8',
67 #Aurae
68 '144.76.27.210/32',
69 #Toulouse
70 '82.241.255.46/32',
71 #Akasha
72 #'89.157.132.244/32'
73 #'89.3.145.115/32'
74 '89.3.147.209/32',
75 #Ygg tracker (tracker.yggtracker.cc)
76 '31.220.0.116/32',
77 #Coppersurfer tracker (tracker.coppersurfer.tk)
78 '31.14.40.30/32',
79 #Pussytorrent tracker (tracker.pussytorrents.org)
80 '217.23.12.105/32'
81 ],
82 ipv6 => [
83 #Localhost
84 '::1/32',
85 #Aurae
86 '2a01:4f8:191:1405::/64'
87 ]
88 );
89
90 #Create a new NetAddr::IP object without calling slow gethostbyname (load /etc/resolv.conf)
91 sub new_ipv4($) {
92 #Extract ip and mask
93 my ($ip, $mask) = split('/', shift);
94 #Build base struct
95 my $self = {
96 #Set mask
97 mask => !defined($mask)||$mask==32?Ones:shiftleft(Ones, 32 - $mask),
98 #Mark as ipv4
99 isv6 => 0,
100 #Generate fake address
101 #XXX: NetAddr::IP expect a faked Socket6 gethostbyname struct
102 #XXX: see /usr/lib64/perl5/vendor_perl/NetAddr/IP/Util.pm +235
103 addr => inet_4map6(ipv4to6(pack('C4', split('\.', $ip))))
104 };
105 #Return fake NetAddr::IP object
106 return bless $self, 'NetAddr::IP';
107 }
108
109 #User whitelist
110 my @userlist = ('rapsys');
111
112 #Extract sshd.service scan
113 #map {
114 # #Extract user and ip
115 # if (/Failed password for (?:invalid user )?(.+) from (.+) port [0-9]+ ssh2/ && grep($_ ne $1, @userlist)) {
116 # #Save ip
117 # my $ip = $2;
118 # #Check if v4 ip and not in whitelist
119 # if (is_ipv4($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) {
120 # #Add ip in v4 blacklist
121 # $ip4s{$ip}=1;
122 # #Check if v6 ip
123 # } elsif (is_ipv6($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv6}}) {
124 # $ip6s{$ip}=1;
125 # }
126 # }
127 #} capturex('journalctl', '-u', 'sshd.service');
128
129 #Extract kernel port scan
130 map {
131 #oct. 04 19:10:30 aurae.aoihime.eu kernel: net-fw DROP IN=enp3s0 OUT= MAC=50:46:5d:a1:a1:85:0c:86:10:f5:c6:4b:08:00 SRC=61.227.52.153 DST=144.76.27.210 LEN=52 TOS=0x00 PREC=0x00 TTL=116 ID=29123 DF PROTO=TCP SPT=64349 DPT=445 WINDOW=8192 RES=0x00 SYN URGP=0
132 #net-fw DROP IN=enp3s0 OUT= MAC=50:46:5d:a1:a1:85:0c:86:10:f5:c6:4b:08:00 SRC=110.34.70.110 DST=144.76.27.210 LEN=40 TOS=0x00 PREC=0x00 TTL=50 ID=17488 PROTO=TCP SPT=58225 DPT=34567 WINDOW=53283 RES=0x00 SYN URGP=0
133 if (/net-fw DROP .* SRC=([^\s]+) .* PROTO=([^\s]+) .* DPT=([^\s]+)/) {
134 #Save ip
135 my $ip = $1;
136 #Save proto
137 my $proto = lc($2);
138 #Save dpt
139 my $dpt = $3;
140 #Check if v4 ip and not in whitelist
141 #if (is_ipv4($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) {
142 if (is_ipv4($ip) && not scalar map { my $network = new_ipv4($_); my $netip = new_ipv4($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) {
143 if (!defined $ip4s{$ip}) {
144 %{$ip4s{$ip}} = ('tcp' => {}, 'udp' => {});
145 }
146 #Add ip in v4 blacklist
147 $ip4s{$ip}{$proto}{$dpt}=1;
148 } elsif (is_ipv6($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv6}}) {
149 if (!defined $ip6s{$ip}) {
150 %{$ip6s{$ip}} = ('tcp' => {}, 'udp' => {});
151 }
152 #Add ip in v6 blacklist
153 $ip6s{$ip}{$proto}{$dpt}=1;
154 }
155 #oct. 04 19:17:10 aurae.aoihime.eu kernel: audit: type=1100 audit(1570209430.543:17321294): pid=5890 uid=0 auid=4294967295 ses=4294967295 msg='op=PAM:authentication grantors=? acct="root" exe="/usr/sbin/sshd" hostname=195.154.112.70 addr=195.154.112.70 terminal=ssh res=failed'
156 #audit: type=1100 audit(1570291573.615:8660): pid=3225 uid=0 auid=4294967295 ses=4294967295 msg='op=PAM:authentication grantors=? acct="root" exe="/usr/sbin/sshd" hostname=222.186.180.9 addr=222.186.180.9 terminal=ssh res=failed'
157 } elsif (/op=PAM:authentication grantors=\? acct="(.+)" exe="\/usr\/(?:libexec\/dovecot\/auth|sbin\/sshd)" hostname=.+ addr=(.+) terminal=(dovecot|ssh) res=failed/ && grep($_ ne $1, @userlist)) {
158 #Save ip
159 my $ip = $2;
160 #Init blacklist
161 my %blacklist = ('tcp' => [], 'udp' => []);
162 #Set ssh proto and port tuple
163 if ($3 eq 'ssh') {
164 #Set blacklist for 22 dest port on tcp
165 #$blacklist{'tcp'}[$#{$blacklist{'tcp'}}+1] = 22;
166 %blacklist = ('tcp' => [ 22 ]);
167 #Set dovecot proto and port tuples
168 } elsif ($3 eq 'dovecot') {
169 #Set blacklist for 25, 143, 587, 993 dest ports on tcp and udp
170 %blacklist = ('tcp' => [ 25, 143, 587, 993 ], 'udp' => [ 25, 143, 587, 993 ]);
171 #Set other proto and port tuples
172 } else {
173 }
174
175 #Check if v4 ip and not in whitelist
176 if (is_ipv4($ip) && not scalar map { my $network = new_ipv4($_); my $netip = new_ipv4($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) {
177 if (!defined $ip4s{$ip}) {
178 %{$ip4s{$ip}} = ('tcp' => {}, 'udp' => {});
179 }
180 #Add ip tuples in v4 blacklist
181 map { my $proto = $_; map { $ip4s{$ip}{$proto}{$_}=1; } @{$blacklist{$proto}}; } keys %blacklist;
182 #Check if v6 ip
183 } elsif (is_ipv6($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv6}}) {
184 if (!defined $ip6s{$ip}) {
185 %{$ip6s{$ip}} = ('tcp' => {}, 'udp' => {});
186 }
187 #Add ip tuples in v6 blacklist
188 map { my $proto = $_; map { $ip6s{$ip}{$proto}{$_}=1; } @{$blacklist{$proto}}; } keys %blacklist;
189 }
190 #nov. 30 15:30:07 aurae.aoihime.eu kernel: audit: type=1100 audit(1575124207.371:38129): pid=685 uid=985 auid=4294967295 ses=4294967295 msg='op=PAM:authentication grantors=? acct="toto" exe="/usr/bin/pwauth" hostname=? addr=? terminal=? res=failed'
191 #XXX: Until mod_authnz pass to pwauth the (SERVER_NAME|SERVER_ADDR) + REMOTE_ADDR+REMOTE_PORT in env it's impossible to know who did a failed auth
192 #XXX: see https://github.com/phokz/mod-auth-external/blob/master/mod_authnz_external/TODO
193 #} elsif (/op=PAM:authentication grantors=\? acct="(.+)" exe="\/usr\/bin\/pwauth" hostname=.+ addr=(.+) terminal=\? res=failed/ && grep($_ ne $1, @userlist)) {
194 # ...
195 }
196 } capturex('journalctl', '-m', '-t', 'kernel', '-o', 'cat', '--no-hostname');
197
198 #With spamlist
199 if ($spamlist) {
200 #Extract originating ip in spam
201 map {
202 #Open spam file for reading
203 open (my $fh, '<', $_) or die "Can't open < $_: $!";
204
205 #Lookup for X-Originating-IP header
206 while (<$fh>) {
207 if (/X-Originating-IP: (.+)$/) {
208 #Set ip
209 my $ip = ${1};
210
211 #Set blacklist for 80 and 443 dest ports on tcp
212 my %blacklist = ('tcp' => [ 80, 443, 8000, 8080, 8443 ]);
213
214 #Check if v4 ip and not in whitelist
215 #if (is_ipv4($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) {
216 if (is_ipv4($ip) && not scalar map { my $network = new_ipv4($_); my $netip = new_ipv4($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) {
217 if (!defined $ip4s{$ip}) {
218 %{$ip4s{$ip}} = ('tcp' => {}, 'udp' => {});
219 }
220 #Add ip tuples in v4 blacklist
221 map { my $proto = $_; map { $ip4s{$ip}{$proto}{$_}=1; } @{$blacklist{$proto}}; } keys %blacklist;
222 } elsif (is_ipv6($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv6}}) {
223 if (!defined $ip6s{$ip}) {
224 %{$ip6s{$ip}} = ('tcp' => {}, 'udp' => {});
225 }
226 #Add ip tuples in v6 blacklist
227 map { my $proto = $_; map { $ip6s{$ip}{$proto}{$_}=1; } @{$blacklist{$proto}}; } keys %blacklist;
228 }
229 }
230 }
231
232 #Close spam file
233 close $fh or die "Can't close fh: $!";
234 } glob('/var/spool/mail/*/.Junk/{new,cur,tmp}/*');
235 }
236
237 #Process each ipv4s keys
238 map {
239 #Set proto as either tcp or udp
240 for my $proto (('tcp', 'udp')) {
241 #Check if branch is empty
242 if (!scalar keys %{$ip4s{$_}{$proto}}) {
243 #Prune it
244 delete $ip4s{$_}{$proto};
245 }
246 }
247 } keys %ip4s;
248
249 #Process each ipv6s keys
250 map {
251 #Set proto as either tcp or udp
252 for my $proto (('tcp', 'udp')) {
253 #Check if branch is empty
254 if (!scalar keys %{$ip6s{$_}{$proto}}) {
255 #Prune it
256 delete $ip6s{$_}{$proto};
257 }
258 }
259 } keys %ip6s;
260
261 #Open blrule4s file for reading
262 open (my $fh, '<', '/etc/shorewall/blrules') or die "Can't open < /etc/shorewall/blrules: $!";
263
264 #Populate with comments
265 @blrule4s = map { chomp($_); if (/^#/) { $_; } else { (); } } <$fh>;
266
267 #Prepend each specific ip from whitelist
268 map { push @blrule4s, "WHITELIST\tnet:$1\tall" if (/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/32$/); } @{$iplist{ipv4}};
269
270 #Close blrule4s file
271 close $fh or die "Can't close fh: $!";
272
273 #With blocklist
274 if ($blocklist) {
275 #Open bt_blocklists.ipv4 file for reading
276 open ($fh, '<', '/usr/local/share/blacklist/shorewall.ipv4') or die "Can't open < /usr/local/share/blacklist/shorewall.ipv4: $!";
277
278 #Prepend bt_blocklists.ipv4 drop
279 map { chomp $_; push @blrule4s, "DROP\t\tnet:$_\tfw"; } <$fh>;
280
281 #Close bt_blocklists.ipv4 file
282 close $fh or die "Can't close fh: $!";
283 }
284
285 #Build blacklist
286 map {
287 #Set proto from hash
288 for my $proto (sort keys %{$ip4s{$_}}) {
289 #Push rule
290 push @blrule4s, "DROP\t\tnet:".$_.(length($_)<12?"\t":'')."\tfw\t$proto\t".(scalar keys %{$ip4s{$_}{$proto}}>5||defined $ip4s{$_}{$proto}{0}?'#':'').join(",", sort { $a <=> $b } keys %{$ip4s{$_}{$proto}});
291 }
292 } sort keys %ip4s;
293
294 #Open blrule4s file for writing
295 open ($fh, '>', '/etc/shorewall/blrules') or die "Can't open > /etc/shorewall/blrules: $!";
296
297 #Inject content of blacklist
298 map { print $fh $_."\n"; } @blrule4s;
299
300 #Close blrule4s file
301 close $fh or die "Can't close fh: $!";
302
303 #Print ipv6 to update hash
304 #XXX; right now it don't seems scanned at all...
305 for (sort keys %ip6s) {
306 #Set proto from hash
307 for my $proto (keys %{$ip6s{$_}}) {
308 #Print the ipv6 scanner
309 print $_."\t$proto\t".join(",", keys %{$ip6s{$_}{$proto}})."\n";
310 }
311 }
312
313 #TODO: add ipv6 ?
314
315 #Restart shorewall service
316 capturex('systemctl', 'restart', 'shorewall.service');