]> Raphaƫl G. Git Repositories - binary/blob - blacklist
Import composer
[binary] / 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 #Load Data::Dumper
22 use Data::Dumper;
23
24 #use Net::CIDR qw(range2cidr);
25 #use Socket;
26 #use Data::Dump qw(dump);
27
28 #IP v4 hash
29 my %ip4s = ();
30 #IP v6 hash
31 my %ip6s = ();
32 #Blacklist v4 array
33 my @blrule4s = ();
34 #Blacklist v6 array
35 my @blrule6s = ();
36
37 #Init block list
38 my $blocklist = 0;
39
40 #Init spam list
41 my $spamlist = 1;
42
43 #Filter options
44 @ARGV = map {
45 if ($_ eq '-b' || $_ eq '--blocklist') {
46 $blocklist = 1; ();
47 } elsif ($_ eq '-nb' || $_ eq '--noblocklist') {
48 $blocklist = 0; ();
49 } elsif ($_ eq '-s' || $_ eq '--spamlist') {
50 $spamlist = 1; ();
51 } elsif ($_ eq '-ns' || $_ eq '--nospamlist') {
52 $spamlist = 0; ();
53 } else {
54 $_;
55 }
56 } @ARGV;
57
58 #Show usage with invalid argument
59 if (scalar(@ARGV)) {
60 print "Usage: $0 [-b|--blocklist|-nb|--noblocklist|-s|--spamlist|-ns|--nospamlist]\n";
61 exit EXIT_FAILURE;
62 }
63
64 #IP whitelist
65 #my $iplist = qr/^(?:127\.|::1|2a01:4f8:190:22a6:|5\.9\.143\.173|85\.68\.|81\.67\.|89\.157\.|82\.241\.255\.46)/;
66 my %iplist = (
67 ipv4 => [
68 #Localhost
69 '127.0.0.0/8',
70 #Aurae
71 '144.76.27.210/32',
72 #Toulouse
73 '82.241.255.46/32',
74 #Akasha
75 #'89.157.132.244/32'
76 #'89.3.145.115/32'
77 '89.3.147.209/32',
78 #Hotel recamier (because of port scan)
79 '92.154.96.153/32',
80 #Ygg tracker (tracker.yggtracker.cc)
81 '31.220.0.116/32',
82 #Coppersurfer tracker (tracker.coppersurfer.tk)
83 '31.14.40.30/32',
84 #Pussytorrent tracker (tracker.pussytorrents.org)
85 '217.23.12.105/32'
86 ],
87 ipv6 => [
88 #Localhost
89 '::1/32',
90 #Aurae
91 '2a01:4f8:191:1405::/64'
92 ]
93 );
94
95 #IP blacklist
96 my %ipblist = (
97 ipv4 => [
98 #Trident media guard
99 '82.138.74.0/25',
100 '82.138.70.128/26',
101 '91.189.104.0/21',
102 '154.45.216.128/25',
103 '193.107.240.0/22'
104 ],
105 ipv6 => [
106 ]
107 );
108
109 #Create a new NetAddr::IP object without calling slow gethostbyname (load /etc/resolv.conf)
110 sub new_ipv4($) {
111 #Extract ip and mask
112 my ($ip, $mask) = split('/', shift);
113 #Build base struct
114 my $self = {
115 #Set mask
116 mask => !defined($mask)||$mask==32?Ones:shiftleft(Ones, 32 - $mask),
117 #Mark as ipv4
118 isv6 => 0,
119 #Generate fake address
120 #XXX: NetAddr::IP expect a faked Socket6 gethostbyname struct
121 #XXX: see /usr/lib64/perl5/vendor_perl/NetAddr/IP/Util.pm +235
122 addr => inet_4map6(ipv4to6(pack('C4', split('\.', $ip))))
123 };
124 #Return fake NetAddr::IP object
125 return bless $self, 'NetAddr::IP';
126 }
127
128 #User whitelist
129 my @userlist = ('rapsys', 'airlibre');
130
131 #Port whitelist
132 my %portlist = (
133 tcp => [ 80, 443, 8000, 8080, 8443 ],
134 udp => [ 443, 8443 ]
135 );
136
137 #Extract sshd.service scan
138 #map {
139 # #Extract user and ip
140 # if (/Failed password for (?:invalid user )?(.+) from (.+) port [0-9]+ ssh2/ && grep($_ ne $1, @userlist)) {
141 # #Save ip
142 # my $ip = $2;
143 # #Check if v4 ip and not in whitelist
144 # if (is_ipv4($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) {
145 # #Add ip in v4 blacklist
146 # $ip4s{$ip}=1;
147 # #Check if v6 ip
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 # $ip6s{$ip}=1;
150 # }
151 # }
152 #} capturex('journalctl', '-u', 'sshd.service');
153
154 #Extract kernel port scan
155 map {
156 #XXX to ignore: net-fw DROP IN=enp3s0 OUT= MAC=50:46:5d:a1:a1:85:0c:86:10:f5:c6:4b:08:00 SRC=1.39.26.11 DST=144.76.27.210 LEN=348 TOS=0x00 PREC=0x00 TTL=51 ID=52475 PROTO=ICMP TYPE=3 CODE=3 [SRC=144.76.27.210 DST=1.39.26.11 LEN=320 TOS=0x00 PREC=0x00 TTL=47 ID=52473 PROTO=UDP SPT=6700 DPT=40992 LEN=300 ]
157 #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
158 #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
159 if (/net-fw DROP .* SRC=([^\s]+) [^\[]* PROTO=(TCP|UDP) .* DPT=([^\s]+)/ && (! defined ${portlist}{lc($2)} || ! scalar map { unless ($_ eq $3) { (); } } @{$portlist{lc($2)}})) {
160 #Save ip
161 my $ip = $1;
162 #Save proto
163 my $proto = lc($2);
164 #Save dpt
165 my $dpt = $3;
166 #Check if v4 ip and not in whitelist
167 #if (is_ipv4($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) {
168 if (is_ipv4($ip) && not scalar map { my $network = new_ipv4($_); my $netip = new_ipv4($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) {
169 if (!defined $ip4s{$ip}) {
170 %{$ip4s{$ip}} = ('tcp' => {}, 'udp' => {});
171 }
172 #Add ip in v4 blacklist
173 $ip4s{$ip}{$proto}{$dpt}=1;
174 } elsif (is_ipv6($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv6}}) {
175 if (!defined $ip6s{$ip}) {
176 %{$ip6s{$ip}} = ('tcp' => {}, 'udp' => {});
177 }
178 #Add ip in v6 blacklist
179 $ip6s{$ip}{$proto}{$dpt}=1;
180 }
181 #XXX to ignore: imap-login: Disconnected: Inactivity (no auth attempts in 180 secs): user=<>, rip=92.154.96.153, lip=144.76.27.210, TLS handshaking, session=<86h+BZAE1VdcmmCZ>
182 #From journalctl -f -m -t dovecot -o cat --no-hostname | grep -E '^imap-login: Disconnected: .+ user=<(.+)>, .*rip=([^,]+)'
183 #imap-login: Disconnected: Aborted login by logging out (auth failed, 1 attempts in 6 secs): user=<laurence.gertz>, method=PLAIN, rip=103.199.18.128, lip=144.76.27.210, TLS, session=<D7/6qNABW7FnxxKA>
184 } elsif (/^imap-login: Disconnected: (?:Too many invalid commands|Aborted login by logging out|Connection closed \((?:auth failed, [0-9]+ attempts in [0-9]+ secs|tried to use unsupported auth mechanism)\)|Connection closed: (?:read\(size=[0-9]+\)|SSL_accept\(\)|SSL_read) failed).*? user=<(.*?)>, .*?rip=(.+?), / && grep($_ ne $1, @userlist)) {
185 #Save ip
186 my $ip = $2;
187 #Set blacklist for 143, 993, 4190 dest ports on tcp
188 my %blacklist = ('tcp' => [ 143, 993, 4190 ]);
189 #Check if v4 ip and not in whitelist
190 if (is_ipv4($ip) && not scalar map { my $network = new_ipv4($_); my $netip = new_ipv4($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) {
191 if (!defined $ip4s{$ip}) {
192 %{$ip4s{$ip}} = ('tcp' => {}, 'udp' => {});
193 }
194 #Add ip tuples in v4 blacklist
195 map { my $proto = $_; map { $ip4s{$ip}{$proto}{$_}=1; } @{$blacklist{$proto}}; } keys %blacklist;
196 #Check if v6 ip
197 } elsif (is_ipv6($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv6}}) {
198 if (!defined $ip6s{$ip}) {
199 %{$ip6s{$ip}} = ('tcp' => {}, 'udp' => {});
200 }
201 #Add ip tuples in v6 blacklist
202 map { my $proto = $_; map { $ip6s{$ip}{$proto}{$_}=1; } @{$blacklist{$proto}}; } keys %blacklist;
203 }
204 #XXX to ignore: warning: non-SMTP command from lputeaux-656-1-79-153.w92-154.abo.wanadoo.fr[92.154.96.153]: GET / HTTP/1.1
205 #sudo journalctl -f -m -t postfix/smtpd -t postfix/submission/smtpd -t postfix/submissions/smtpd -t postfix/smtps/smtpd -o cat --no-hostname | grep -E '^(warning: Illegal address syntax from [^ ]+\[|warning: numeric hostname: |warning: [^ ]+\[|SSL_accept error from [^ ]+\[)([0-9a-f:\.]+)(|\]:? .*)$'
206 #sudo journalctl -f -m -t postfix/smtpd -t postfix/submission/smtpd -t postfix/submissions/smtpd -t postfix/smtps/smtpd -o cat --no-hostname | perl -pne 'if (/^(?:warning: Illegal address syntax from [^ ]+\[|warning: numeric hostname: |warning: [^ ]+\[|SSL_accept error from [^ ]+\[)([0-9a-f:\.]+)(?:|\]:? .*)$/) { print $1.":".$_; } else { }; undef $_;'
207 #warning: Illegal address syntax from unknown[113.80.102.166] in MAIL command: <quentin@blower,net>
208 #warning: numeric hostname: 187.145.20.160
209 #warning: non-SMTP command from unknown[2001:41d0:8:ed7b::1]: *
210 #warning: unknown[80.94.95.184]: SASL LOGIN authentication failed: UGFzc3dvcmQ6
211 #SSL_accept error from 162.194.196.104.bc.googleusercontent.com[104.196.194.162]: lost connection
212 #} elsif (/^warning: (?:.*\[(.+)\]: .+ authentication failed:.*|numeric hostname: (.+)|non-SMTP command from .*\[(.+)\]: .*|Illegal address syntax from .*\[(.+)\] in .+ command: .*)$/) {
213 } elsif (/^(?:warning: Illegal address syntax from [^ ]+\[|warning: numeric hostname: |warning: [^ ]+\[|SSL_accept error from [^ ]+\[)([0-9a-f:\.]+)(?:|\]:? .*)$/) {
214 #Save ip
215 my $ip = $1;
216 #Set blacklist for 25, 465, 587 dest ports on tcp
217 my %blacklist = ('tcp' => [ 25, 465, 587 ]);
218 #Check if v4 ip and not in whitelist
219 if (is_ipv4($ip) && not scalar map { my $network = new_ipv4($_); my $netip = new_ipv4($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) {
220 if (!defined $ip4s{$ip}) {
221 %{$ip4s{$ip}} = ('tcp' => {}, 'udp' => {});
222 }
223 #Add ip tuples in v4 blacklist
224 map { my $proto = $_; map { $ip4s{$ip}{$proto}{$_}=1; } @{$blacklist{$proto}}; } keys %blacklist;
225 #Check if v6 ip
226 } elsif (is_ipv6($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv6}}) {
227 if (!defined $ip6s{$ip}) {
228 %{$ip6s{$ip}} = ('tcp' => {}, 'udp' => {});
229 }
230 #Add ip tuples in v6 blacklist
231 map { my $proto = $_; map { $ip6s{$ip}{$proto}{$_}=1; } @{$blacklist{$proto}}; } keys %blacklist;
232 }
233 #From journalctl -m -t sshd -o cat --no-hostname | grep -E '^Failed .+ for( invalid user)? (.+) from (.+) port [0-9]+ (ssh)2$'
234 #Failed keyboard-interactive/pam for invalid user supervisor from 168.0.232.246 port 26468 ssh2
235 #Failed password for invalid user mc from 103.38.4.238 port 37738 ssh2
236 } elsif (/^Failed .+ for(?: invalid user)? (.+) from (.+) port [0-9]+ ssh2$/ && grep($_ ne $1, @userlist)) {
237 #Save ip
238 my $ip = $2;
239 #Set blacklist for 22 dest port on tcp
240 my %blacklist = ('tcp' => [ 22 ]);
241 #Check if v4 ip and not in whitelist
242 if (is_ipv4($ip) && not scalar map { my $network = new_ipv4($_); my $netip = new_ipv4($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) {
243 if (!defined $ip4s{$ip}) {
244 %{$ip4s{$ip}} = ('tcp' => {}, 'udp' => {});
245 }
246 #Add ip tuples in v4 blacklist
247 map { my $proto = $_; map { $ip4s{$ip}{$proto}{$_}=1; } @{$blacklist{$proto}}; } keys %blacklist;
248 #Check if v6 ip
249 } elsif (is_ipv6($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv6}}) {
250 if (!defined $ip6s{$ip}) {
251 %{$ip6s{$ip}} = ('tcp' => {}, 'udp' => {});
252 }
253 #Add ip tuples in v6 blacklist
254 map { my $proto = $_; map { $ip6s{$ip}{$proto}{$_}=1; } @{$blacklist{$proto}}; } keys %blacklist;
255 }
256 }
257 } capturex('journalctl', '-m', '-t', 'kernel', '-t', 'dovecot', '-t', 'postfix/smtpd', '-t', 'postfix/submission/smtpd', '-t', 'postfix/submissions/smtpd', '-t', 'postfix/smtps/smtpd', '-t', 'sshd', '-o', 'cat', '--no-hostname');
258
259 #With spamlist
260 if ($spamlist) {
261 #Extract originating ip in spam
262 map {
263 #Open spam file for reading
264 open (my $fh, '<', $_) or die "Can't open < $_: $!";
265
266 #Lookup for X-Originating-IP header
267 while (<$fh>) {
268 if (/X-Originating-IP: (.+)$/) {
269 #Set ip
270 my $ip = ${1};
271
272 #Set blacklist for 80 and 443 dest ports on tcp
273 #my %blacklist = ('tcp' => [ 80, 443, 8000, 8080, 8443 ]);
274 my %blacklist = %portlist;
275
276 #Check if v4 ip and not in whitelist
277 #if (is_ipv4($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) {
278 if (is_ipv4($ip) && not scalar map { my $network = new_ipv4($_); my $netip = new_ipv4($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) {
279 if (!defined $ip4s{$ip}) {
280 %{$ip4s{$ip}} = ('tcp' => {}, 'udp' => {});
281 }
282 #Add ip tuples in v4 blacklist
283 map { my $proto = $_; map { $ip4s{$ip}{$proto}{$_}=1; } @{$blacklist{$proto}}; } keys %blacklist;
284 } elsif (is_ipv6($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv6}}) {
285 if (!defined $ip6s{$ip}) {
286 %{$ip6s{$ip}} = ('tcp' => {}, 'udp' => {});
287 }
288 #Add ip tuples in v6 blacklist
289 map { my $proto = $_; map { $ip6s{$ip}{$proto}{$_}=1; } @{$blacklist{$proto}}; } keys %blacklist;
290 }
291 }
292 }
293
294 #Close spam file
295 close $fh or die "Can't close fh: $!";
296 } glob('/var/spool/mail/*/.Junk/{new,cur,tmp}/*');
297 }
298
299 #Process each ipv4s keys
300 map {
301 #Set proto as either tcp or udp
302 for my $proto (('tcp', 'udp')) {
303 #Check if branch is empty
304 if (!scalar keys %{$ip4s{$_}{$proto}}) {
305 #Prune it
306 delete $ip4s{$_}{$proto};
307 }
308 }
309 } keys %ip4s;
310
311 #Process each ipv6s keys
312 map {
313 #Set proto as either tcp or udp
314 for my $proto (('tcp', 'udp')) {
315 #Check if branch is empty
316 if (!scalar keys %{$ip6s{$_}{$proto}}) {
317 #Prune it
318 delete $ip6s{$_}{$proto};
319 }
320 }
321 } keys %ip6s;
322
323 #Open blrule4s file for reading
324 open (my $fh, '<', '/etc/shorewall/blrules') or die "Can't open < /etc/shorewall/blrules: $!";
325
326 #Populate with comments
327 @blrule4s = map { chomp($_); if (/^#/) { $_; } else { (); } } <$fh>;
328
329 #Prepend each specific ip from whitelist
330 map { push @blrule4s, "WHITELIST\tnet:$1\tall" if (/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/32$/); } @{$iplist{ipv4}};
331
332 #Prepend each specific ip from blacklist
333 #TODO: check that blacklist range is not in a whitelisted one !!!
334 map { push @blrule4s, "DROP\tnet:$1\tall" if (/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2})$/); } @{$ipblist{ipv4}};
335
336 #Close blrule4s file
337 close $fh or die "Can't close fh: $!";
338
339 #With blocklist
340 if ($blocklist) {
341 #Open bt_blocklists.ipv4 file for reading
342 open ($fh, '<', '/usr/local/share/blacklist/shorewall.ipv4') or die "Can't open < /usr/local/share/blacklist/shorewall.ipv4: $!";
343
344 #Prepend bt_blocklists.ipv4 drop
345 map { chomp $_; push @blrule4s, "DROP\t\tnet:$_\tfw"; } <$fh>;
346
347 #Close bt_blocklists.ipv4 file
348 close $fh or die "Can't close fh: $!";
349 }
350
351 #Build blacklist
352 map {
353 #Set proto from hash
354 for my $proto (sort keys %{$ip4s{$_}}) {
355 #Push rule
356 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}});
357 }
358 } sort keys %ip4s;
359
360 #Open blrule4s file for writing
361 open ($fh, '>', '/etc/shorewall/blrules') or die "Can't open > /etc/shorewall/blrules: $!";
362
363 #Inject content of blacklist
364 map { print $fh $_."\n"; } @blrule4s;
365
366 #Close blrule4s file
367 close $fh or die "Can't close fh: $!";
368
369 #Print ipv6 to update hash
370 #XXX; right now it don't seems scanned at all...
371 for (sort keys %ip6s) {
372 #Set proto from hash
373 for my $proto (keys %{$ip6s{$_}}) {
374 #Print the ipv6 scanner
375 print $_."\t$proto\t".join(",", keys %{$ip6s{$_}{$proto}})."\n";
376 }
377 }
378
379 # TODO: add ipv6 ?
380
381 #Restart shorewall service
382 capturex('systemctl', 'restart', 'shorewall.service');