From: Raphaƫl Gertz Date: Mon, 30 Jan 2023 15:13:03 +0000 (+0100) Subject: Add spamlist and bloclist options X-Git-Tag: 0.5~1 X-Git-Url: https://git.rapsys.eu/tools/commitdiff_plain/a58e7d2e782f7780d08b0a38992aec6d94c3618a Add spamlist and bloclist options Use ports list for each service Cleanup --- diff --git a/blacklist b/blacklist index e950ba0..a8d27ba 100755 --- a/blacklist +++ b/blacklist @@ -3,11 +3,21 @@ use strict; use warnings; +#Load IPC::System::Simple use IPC::System::Simple qw(capturex); + +#Load Data::Validate::IP use Data::Validate::IP qw(is_ipv4 is_ipv6); + +#Load NetAddr::IP::Util use NetAddr::IP::Util qw(shiftleft inet_4map6 ipv4to6); + +#Load NetAddr::IP use NetAddr::IP qw(:nofqdn Ones); +#Load POSIX +use POSIX qw(EXIT_SUCCESS EXIT_FAILURE); + #IP v4 hash my %ip4s = (); #IP v6 hash @@ -17,23 +27,62 @@ my @blrule4s = (); #Blacklist v6 array my @blrule6s = (); +#Init block list +my $blocklist = 0; + +#Init spam list +my $spamlist = 1; + +#Filter options +@ARGV = map { + if ($_ eq '-b' || $_ eq '--blocklist') { + $blocklist = 1; (); + } elsif ($_ eq '-nb' || $_ eq '--noblocklist') { + $blocklist = 0; (); + } elsif ($_ eq '-s' || $_ eq '--spamlist') { + $spamlist = 1; (); + } elsif ($_ eq '-ns' || $_ eq '--nospamlist') { + $spamlist = 0; (); + } else { + $_; + } +} @ARGV; + +#Show usage with invalid argument +if (scalar(@ARGV)) { + print "Usage: $0 [-b|--blocklist|-nb|--noblocklist|-s|--spamlist|-ns|--nospamlist]\n"; + exit EXIT_FAILURE; +} + +#TODO: add google/microsoft/etc mail range ? + +#TODO: add all configuration in a json config ? + #IP whitelist #my $iplist = qr/^(?:127\.|::1|2a01:4f8:190:22a6:|5\.9\.143\.173|85\.68\.|81\.67\.|89\.157\.|82\.241\.255\.46)/; my %iplist = ( ipv4 => [ - # Localhost + #Localhost '127.0.0.0/8', - # Aurae + #Aurae '144.76.27.210/32', - # Toulouse + #Toulouse '82.241.255.46/32', - # Akasha - '89.3.145.115/32' + #Akasha + #'89.157.132.244/32' + #'89.3.145.115/32' + '89.3.147.209/32', + #Ygg tracker (tracker.yggtracker.cc) + '31.220.0.116/32', + #Coppersurfer tracker (tracker.coppersurfer.tk) + '31.14.40.30/32', + #Pussytorrent tracker (tracker.pussytorrents.org) + '217.23.12.105/32' ], ipv6 => [ - # Localhost + #Localhost '::1/32', - # Aurae + #Aurae '2a01:4f8:191:1405::/64' ] ); @@ -45,14 +94,13 @@ sub new_ipv4($) { #Build base struct my $self = { #Set mask - mask => !defined($mask)||$mask==32?Ones:shiftleft(Ones, 32 - $mask), + mask => !defined($mask)||$mask==32?Ones:shiftleft(Ones, 32 - $mask), #Mark as ipv4 - isv6 => 0, + isv6 => 0, #Generate fake address #XXX: NetAddr::IP expect a faked Socket6 gethostbyname struct #XXX: see /usr/lib64/perl5/vendor_perl/NetAddr/IP/Util.pm +235 - #addr => scalar ($ip, '', AF_INET, 16, NetAddr::IP::Util::inet_4map6(NetAddr::IP::Util::ipv4to6(pack('C4', split('\.', $ip))))) - addr => inet_4map6(ipv4to6(pack('C4', split('\.', $ip)))) + addr => inet_4map6(ipv4to6(pack('C4', split('\.', $ip)))) }; #Return fake NetAddr::IP object return bless $self, 'NetAddr::IP'; @@ -63,15 +111,15 @@ my @userlist = ('rapsys'); #Extract sshd.service scan #map { -# # Extract user and ip +# #Extract user and ip # if (/Failed password for (?:invalid user )?(.+) from (.+) port [0-9]+ ssh2/ && grep($_ ne $1, @userlist)) { -# # Save ip +# #Save ip # my $ip = $2; -# # Check if v4 ip and not in whitelist +# #Check if v4 ip and not in whitelist # if (is_ipv4($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) { -# # Add ip in v4 blacklist +# #Add ip in v4 blacklist # $ip4s{$ip}=1; -# # Check if v6 ip +# #Check if v6 ip # } elsif (is_ipv6($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv6}}) { # $ip6s{$ip}=1; # } @@ -81,47 +129,63 @@ my @userlist = ('rapsys'); #Extract kernel port scan map { #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 - if (/kernel: net-fw DROP .* SRC=([^\s]+) .* PROTO=([^\s]+) .* DPT=([^\s]+)/) { - # Save ip + #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 + if (/net-fw DROP .* SRC=([^\s]+) .* PROTO=([^\s]+) .* DPT=([^\s]+)/) { + #Save ip my $ip = $1; - # Save proto + #Save proto my $proto = lc($2); - # Save dpt + #Save dpt my $dpt = $3; - # Check if v4 ip and not in whitelist + #Check if v4 ip and not in whitelist + #if (is_ipv4($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) { if (is_ipv4($ip) && not scalar map { my $network = new_ipv4($_); my $netip = new_ipv4($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) { if (!defined $ip4s{$ip}) { %{$ip4s{$ip}} = ('tcp' => {}, 'udp' => {}); } + #Add ip in v4 blacklist $ip4s{$ip}{$proto}{$dpt}=1; } elsif (is_ipv6($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv6}}) { if (!defined $ip6s{$ip}) { %{$ip6s{$ip}} = ('tcp' => {}, 'udp' => {}); } + #Add ip in v6 blacklist $ip6s{$ip}{$proto}{$dpt}=1; } - print $ip."\n"; #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' + #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' } elsif (/op=PAM:authentication grantors=\? acct="(.+)" exe="\/usr\/(?:libexec\/dovecot\/auth|sbin\/sshd)" hostname=.+ addr=(.+) terminal=(dovecot|ssh) res=failed/ && grep($_ ne $1, @userlist)) { - # Save ip + #Save ip my $ip = $2; - # Save proto - my $proto = 'tcp'; - # Save dpt - my $dpt = $3 eq 'ssh' ? 22 : 445; - # Check if v4 ip and not in whitelist + #Init blacklist + my %blacklist = ('tcp' => [], 'udp' => []); + #Set ssh proto and port tuple + if ($3 eq 'ssh') { + #Set blacklist for 22 dest port on tcp + #$blacklist{'tcp'}[$#{$blacklist{'tcp'}}+1] = 22; + %blacklist = ('tcp' => [ 22 ]); + #Set dovecot proto and port tuples + } elsif ($3 eq 'dovecot') { + #Set blacklist for 25, 143, 587, 993 dest ports on tcp and udp + %blacklist = ('tcp' => [ 25, 143, 587, 993 ], 'udp' => [ 25, 143, 587, 993 ]); + #Set other proto and port tuples + } else { + } + + #Check if v4 ip and not in whitelist if (is_ipv4($ip) && not scalar map { my $network = new_ipv4($_); my $netip = new_ipv4($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) { if (!defined $ip4s{$ip}) { %{$ip4s{$ip}} = ('tcp' => {}, 'udp' => {}); } - # Add ip in v4 blacklist - $ip4s{$ip}{$proto}{$dpt}=1; - # Check if v6 ip + #Add ip tuples in v4 blacklist + map { my $proto = $_; map { $ip4s{$ip}{$proto}{$_}=1; } @{$blacklist{$proto}}; } keys %blacklist; + #Check if v6 ip } elsif (is_ipv6($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv6}}) { if (!defined $ip6s{$ip}) { %{$ip6s{$ip}} = ('tcp' => {}, 'udp' => {}); } - $ip6s{$ip}{$proto}{$dpt}=1; + #Add ip tuples in v6 blacklist + map { my $proto = $_; map { $ip6s{$ip}{$proto}{$_}=1; } @{$blacklist{$proto}}; } keys %blacklist; } #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' #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 @@ -131,6 +195,45 @@ map { } } capturex('journalctl', '-m', '-t', 'kernel', '-o', 'cat', '--no-hostname'); +#With spamlist +if ($spamlist) { + #Extract originating ip in spam + map { + #Open spam file for reading + open (my $fh, '<', $_) or die "Can't open < $_: $!"; + + #Lookup for X-Originating-IP header + while (<$fh>) { + if (/X-Originating-IP: (.+)$/) { + #Set ip + my $ip = ${1}; + + #Set blacklist for 80 and 443 dest ports on tcp + my %blacklist = ('tcp' => [ 80, 443, 8000, 8080, 8443 ]); + + #Check if v4 ip and not in whitelist + #if (is_ipv4($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) { + if (is_ipv4($ip) && not scalar map { my $network = new_ipv4($_); my $netip = new_ipv4($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv4}}) { + if (!defined $ip4s{$ip}) { + %{$ip4s{$ip}} = ('tcp' => {}, 'udp' => {}); + } + #Add ip tuples in v4 blacklist + map { my $proto = $_; map { $ip4s{$ip}{$proto}{$_}=1; } @{$blacklist{$proto}}; } keys %blacklist; + } elsif (is_ipv6($ip) && not scalar map { my $network = NetAddr::IP->new($_); my $netip = NetAddr::IP->new($ip); unless ($network->contains($netip)) { (); } } @{$iplist{ipv6}}) { + if (!defined $ip6s{$ip}) { + %{$ip6s{$ip}} = ('tcp' => {}, 'udp' => {}); + } + #Add ip tuples in v6 blacklist + map { my $proto = $_; map { $ip6s{$ip}{$proto}{$_}=1; } @{$blacklist{$proto}}; } keys %blacklist; + } + } + } + + #Close spam file + close $fh or die "Can't close fh: $!"; + } glob('/var/spool/mail/*/.Junk/{new,cur,tmp}/*'); +} + #Process each ipv4s keys map { #Set proto as either tcp or udp @@ -164,19 +267,30 @@ open (my $fh, '<', '/etc/shorewall/blrules') or die "Can't open < /etc/shorewall #Prepend each specific ip from whitelist map { push @blrule4s, "WHITELIST\tnet:$1\tall" if (/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/32$/); } @{$iplist{ipv4}}; +#Close blrule4s file +close $fh or die "Can't close fh: $!"; + +#With blocklist +if ($blocklist) { + #Open bt_blocklists.ipv4 file for reading + open ($fh, '<', '/usr/local/share/blacklist/shorewall.ipv4') or die "Can't open < /usr/local/share/blacklist/shorewall.ipv4: $!"; + + #Prepend bt_blocklists.ipv4 drop + map { chomp $_; push @blrule4s, "DROP\t\tnet:$_\tfw"; } <$fh>; + + #Close bt_blocklists.ipv4 file + close $fh or die "Can't close fh: $!"; +} #Build blacklist map { #Set proto from hash - for my $proto (keys %{$ip4s{$_}}) { + for my $proto (sort keys %{$ip4s{$_}}) { #Push rule - push @blrule4s, "DROP\t\tnet:".$_.(length($_)<12?"\t":'')."\tfw\t$proto\t".(scalar keys %{$ip4s{$_}{$proto}}>5||defined $ip4s{$_}{$proto}{0}?'#':'').join(",", keys %{$ip4s{$_}{$proto}}); + 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}}); } } sort keys %ip4s; -#Close blrule4s file -close $fh or die "Can't close fh: $!"; - #Open blrule4s file for writing open ($fh, '>', '/etc/shorewall/blrules') or die "Can't open > /etc/shorewall/blrules: $!"; @@ -196,5 +310,7 @@ for (sort keys %ip6s) { } } -# Restart shorewall service +#TODO: add ipv6 ? + +#Restart shorewall service capturex('systemctl', 'restart', 'shorewall.service');