#!/bin/sh

# Copyright 2019 Jordan Geoghegan

# Permission to use, copy, modify, and/or distribute this software for any 
# purpose with or without fee is hereby granted, provided that the above 
# copyright notice and this permission notice appear in all copies.

# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
# PERFORMANCE OF THIS SOFTWARE.

# Version: 0.2

# This script downloads some of the most popular IP Blocklists, but you can add 
# any lists you like. Simply add a line in the format below: 
# Example: "ftp -o $tmpdir/$(random_string) https://www.example.com/example.txt"
# IPv4 or IPv6 lists containing individual addresses or CIDR blocks are supported.
# The address parser also supports mixed lists. 

# To enable blocking by country, authlog analysis or enable IPv6 and optional blocklists, see below.

function parse_v4 {
	# If your platform supports Rust, you should replace the use of the 
	# system grep with ripgrep for a large performance increase
	# replace "grep -E" with "rg" to use ripgrep
	grep -E -o "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(/[1-9][0-9]?[0-9]?)?" | \
	awk -F '[/.]' '$1 > 0 && $1<256 && $2<256 && $3<256 && $4<256 && !/\.\./ && $5<32'
}

function parse_v6 {
	# If your platform supports Rust, you should replace the use of the 
	# system grep with ripgrep for a large performance increase
	# replace "grep -E" with "rg" to use ripgrep
	grep -E -o "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(/[1-9][0-9]?[0-9]?)?" | \
	awk -F '/' '$2<128'
}

function random_string {
	cat /dev/urandom | head -n 100 | sha256 
}

function abort {
	cp /dev/null /etc/pf-badhost.txt ; rm -r $tmpdir $outdir ; exit 1
}

function hail_mary_mitigate {
        doas zcat -f /var/log/authlog /var/log/authlog.0.gz | grep -E -v 'Accepted|disconnect|Disconnect' | whitelist | parse_v4 | awk '{ a[$0]++ }END{ for(i in a) print a[i],i }' | awk '$1>50 {print $2}' | sort -Vu
	# Uncomment to enable IPv6 authlog scanning -
	# (can be slow, enabling ripgrep in "parse_v6" is recommended)
	#doas zcat -f /var/log/authlog /var/log/authlog.0.gz | grep -E -v 'Accepted|disconnect|Disconnect' | whitelist | parse_v6 | awk '{ a[$0]++ }END{ for(i in a) print a[i],i }' | awk '$1>50 {print $2}'
}

# Add addresses to whitelist from SSH Auth log analysis
# Supports IPv4 and IPv6 addresses
function whitelist {
        grep -E -v '198.18.1.5|198.18.2.4'
}

tmpdir=$(mktemp -d)
outdir=$(mktemp -d)
v4list=$(random_string)
v6list=$(random_string)
finout=$(random_string)
autlog=$(random_string)

# Download popular IP blocklists
cd $tmpdir  
ftp -o $tmpdir/$(random_string) https://www.binarydefense.com/banlist.txt
ftp -o $tmpdir/$(random_string) https://rules.emergingthreats.net/blockrules/compromised-ips.txt
ftp -o $tmpdir/$(random_string) https://rules.emergingthreats.net/fwrules/emerging-Block-IPs.txt
ftp -o $tmpdir/$(random_string) https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level3.netset
ftp -o $tmpdir/$(random_string) https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level2.netset
ftp -o $tmpdir/$(random_string) https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level1.netset

# Spamhause DROP lists (Don't Peer or Route)
ftp -o $tmpdir/$(random_string) https://www.spamhaus.org/drop/drop.txt
ftp -o $tmpdir/$(random_string) https://www.spamhaus.org/drop/edrop.txt

# Block Shodan
ftp -o $tmpdir/$(random_string) https://isc.sans.edu/api/threatlist/shodan/shodan.txt 

# Optional blocklists (uncomment to enable)

###################################################################
# Haily Mary Cloud mitigation (authlog analysis)
# Requires adding line to /etc/doas.conf:
# permit nopass _pfbadhost cmd zcat args -f /var/log/authlog /var/log/authlog.0.gz
#
# hail_mary_mitigate > $tmpdir/$autlog # Search /var/log/authlog for brutes
###################################################################

# Spamhause DROP lists (Don't Peer or Route) IPv6
# ftp -o $tmpdir/$(random_string) https://www.spamhaus.org/drop/dropv6.txt

# Combined list of all blocklist.de lists (adds over 30,000 addresses, uncomment to enable)
# ftp -o $tmpdir/$(random_string) https://lists.blocklist.de/lists/all.txt

# SSH attackers list 
# ftp -o $tmpdir/$(random_string) https://lists.blocklist.de/lists/ssh.txt

###################################################################
# Block by country (Uses ipdeny.com aggregate country block lists)
# ipdeny.com country blocklists can be found here:
# IPv4: http://www.ipdeny.com/ipblocks/
# IPv6: http://www.ipdeny.com/ipv6/ipaddresses/aggregated/
# Uncomment the lines below to enable country blacklisting
###################################################################

# China IPv4
# ftp -o $tmpdir/$(random_string) http://www.ipdeny.com/ipblocks/data/aggregated/cn-aggregated.zone

# China IPv6 
# ftp -o $tmpdir/$(random_string) http://www.ipdeny.com/ipv6/ipaddresses/aggregated/cn-aggregated.zone

# Iran IPv4
# ftp -o $tmpdir/$(random_string) http://www.ipdeny.com/ipblocks/data/aggregated/ir-aggregated.zone

# Iran IPv6
# ftp -o $tmpdir/$(random_string) http://www.ipdeny.com/ipv6/ipaddresses/aggregated/ir-aggregated.zone

# User added lists
# ftp -o $tmpdir/$(random_string) https://www.example.com/example.txt

# Parse Address lists
cat $tmpdir/* | parse_v4 | sort -Vu > $outdir/$v4list
# Uncomment to enable IPv6 
#cat $tmpdir/* | parse_v6 | sort -Vu > $outdir/$v6list

# Add date header to list
echo "# Date Created: $(date)" > $outdir/$finout

# User Defined Rules (add or negate addresses from block list)
printf "\n# User Defined Rules:\n\n" >> $outdir/$finout
# echo "!169.254.169.254" >> $outdir/$finout
# echo "!2001:19f0:ffff::1" >> $outdir/$finout
# echo "192.168.17.0/24" >> $outdir/$finout

# Create and move banlist into /etc and reload the pf table
printf "\n# Rules Generated from /var/log/authlog:\n\n" >> $outdir/$finout
cat $tmpdir/$autlog >> $outdir/$finout
printf "\n# List Generated Rules:\n\n" >> $outdir/$finout
cat $outdir/$v4list $outdir/$v6list >> $outdir/$finout
cp $outdir/$finout /etc/pf-badhost.txt
doas pfctl -nf /etc/pf.conf || abort
doas pfctl -t pfbadhost -T replace -f /etc/pf-badhost.txt

# Clean up after ourselves
rm -r $tmpdir $outdir
