#!/bin/bash

# Uwe Driessen und Christian Roessner 
# Ein kleines script zur Auswertung wer denn da in den letzten logfiles von der
# dynip erwischt worden waere bitte passt es evtl. auf eure beduerfnisse an.
# die dynip noch von gleicher stelle laden , sorgt dafuer das die Pfade
# vorhanden sind oder passt die entsprechend an. Verbesserungsvorschlaege nehme
# ich gerne entgegen unter driessen@fblan.de, 
# am liebsten für die Dinge die noch nicht rennen
# viel spass damit

# WARNING READ the text carefully / lest euch den text bitte durch bevor Ihr die Map dynip einfach einsetzt

# Mit der Verwendung von dynip werden unerwünschte Mails noch bevor Sie auf eurem Server landen 
# verhindert. Ziel ist es den Server von Mails die man sowieso nicht haben möchte zu entlasten.
# Bedenkt bitte das dieses ein sehr harter check ist, wer moderrater prüfen muss sollte sich 
# policyd-weight.org (Robert Felbert) und Postgrey anschauen. 
# Ihr könnt alle diese  Tools auch gleichzeitig auf euren Servern betreiben, der Schutz gegen
# UBE/UCE ist das zusammenspiel vieler kleiner Bausteine. Wer Fragen hat wendet sich an die Postfix-Liste
#
# http://listi.jpberlin.de/pipermail/postfixbuch-users/
# 
# Setzt diese Map aber bitte mit Bedacht ein, bei falscher Anwendung macht Sie einsam
#
# da ich immer wieder nach der versionsnummer gefragt werde 
# Version 21-01-2008 03:02

# schoen formatiert und in leesbares Format gebracht von
# Christian Roessner [christian@roessner-net.com]
# besten dank das du den Spagetticode verbessert hast. 
# Einbau einer eigenen conf die an euere Bedürfnisse angepasst werden können
# definieren von Funktionsvaraiblen um den Code weiter lesbar zu machen 

# aufruf ./dynip-linenr.sh [ /pfad/zur/auswertung ]

# die generierte Datei /etc/postfix/maps/dynip.pcre kann dann mit
# check_client_access pcre:/etc/postfix/maps/dynip.pcre
# in den  smtpd_recipient_restrictions nach permit_sasl.. eingebunden werden 


# Erzeugen einer Default-Konfiguration
# Achtung wird im aktuellen Verzeichniss erzeugt *uwe 
# evtl. muß hier ein Verzeichniss angelegt und hardcodiert eingebaut werden *uwe

if [ ! -f "/etc/postfix/skripte/test-dynip.conf" ]; then
echo "# /etc/postfix/skripte/ttest-dynip.conf

# Pfad zu Postfix mappings
PREFIX_MAPS=/etc/postfix/maps

# Pfad zu den Log-Dateien
PREFIX_LOGS=/var/log

ORGDYNIP=\$PREFIX_MAPS/dynip
EINSATZDYNIP=\$PREFIX_MAPS/DYNIP.PCRE

CURRLOG=\$PREFIX_LOGS/mail/mail.log
LASTLOG=\$PREFIX_LOGS/mail/mail.0.log
ARCHLOG=\$PREFIX_LOGS/mail.log.*.*
ARCHLOGVZ=\$PREFIX_LOGS/
ARCHLOGD=mail.log
" > test-dynip.conf
fi

source test-dynip.conf

# prueft ob die Eintraege stimmen *uwe 
#echo "$PREFIX_LOGS ; $ORGDYNIP ; $EINSATZDYNIP ; $CURRLOG ; $LASTLOG ; $ARCHLOG"


################## Vorbereitungen ##
ARCHANZ=$2
test "x$1" = "x" && DEST=/tmp/auswert || DEST=$1
rm $DEST/dynip.* > /dev/null
mkdir -p $DEST
echo " Auswertungsverzeichnis ist: $DEST"

NRdynip=$DEST/dynip.znr
AUSNAHME=$DEST/dynip.ausnahme
unsortiert=$DEST/dynip.unsortiert
AUSWERTUNG=$DEST/dynip.txt
sortIP=$DEST/dynip.sortiert
gezaehlt=$DEST/dynip.gezaehlt
nichtgetrof=$DEST/dynip.nichtgetroffen
getroffen=$DEST/dynip.getroffen
statistik=$DEST/dynip.stat
nichtsortdns=$DEST/dynip.nichtsortdns
PTR=$DEST/ptr.txt 
IP=$DEST/ip.txt
GESAMT=$DEST/gesamt.txt


################## Funktionsdefinitionen ##

#
# bringen wir ein bischen Farbe ins Spiel
#

# Black         0;30     0
# Blue          0;34     1
# Green         0;32     2
# Cyan          0;36     3
# Red           0;31     4
# Purple        0;35     5
# Brown         0;33     6
# Light Gray    0;37     7
# Dark Gray     1;30     8
# Light Blue    1;34     9
# Light Green   1;32    10
# Light Cyan    1;36    11
# Light Red     1;31    12
# Light Purple  1;35    13
# Yellow        1;33    14
# White         1;37    15

f=("\033[0m" "\033[0;34m" "\033[0;32m" "\033[0;36m" "\033[0;31m" "\033[0;35m" "\033[0;33m" "\033[0;37m" "\033[1;30m" "\033[1;34m" "\033[1;32m" "\033[1;36m" "\033[1;31m" "\033[1;35m" "\033[1;33m" "\033[1;37m")

#
# f[0] reset [1]magenta [2]rot   
# aufruf = echo -e "${f[1]} das ist magenta ${f[0]}"

#
# boolean check_config_var(Konfigurationsvariable(n))
#
function check_config_var() {
	echo -n " Teste Existenz von $* ... "
	for file in $*; do
		test ! -x $file
		if [ $? -ne 0 ]; then
			test ! -f $file
			if [ $? -ne 0 ]; then
				echo -e "${f[4]}Fehler!${f[0]}"
				exit 0
			fi
		fi
	done
	echo -e "${f[2]}OK${f[0]}"
}

# Wie wurde gepackt? von Christian
# nach oben verschoben da parameter hier benötigt werden 
# -Kommentar: Bei uns werden wegen der Datenvorratshaltung alte Logs im
#             bz2-Format archiviert, weil die Kompression hoeher ist.
PACKEXT=$(echo $ARCHLOG | rev | cut -d "." -f1 | rev)
case "$PACKEXT" in
	gz)
		CATCMD=zcat ;;
	bz2)
		CATCMD=bzcat ;;
	*)
		CATCMD=cat ;;
esac

# noch außer Funktion 
# Anzahl der einzulesenden Archive $2 des Aufrufes
# wenn ARCHANZ = " " dann Fehlermeldung 
# clear | echo -e "\n\n\n ${f[4]} Aufruf ./test-dynip.sh /pfad/zur/Auswertung   Anzahl : a=Alle 0=nur ungepackte >=1 Anzahl an Archiven einlesen ${f[0]}\n\n\n"
# wenn ARCHANZ = a dann alle 
# wenn ARCHANZ = 0 dann nur die ungepackten
# WENN ARCHANZ >= 1 dann entsprechende Anzahl einlesen

# zusammensetzen der einzulesenden Dateinamen 
#for i in $ARCHANZ 
# 	do filelist=$ARCHLOGVZ/$ARCHLOGD.$i.$PACKEXT
#	done





#
# void gen_unsortiert(Kommando, Zieldatei, Eingabedateien)
#
function gen_unsortiert() {
	cmd=$1
	target=$2
	shift 2
	filelist=$*
	declare -i index
	index=0

	for archfile in $filelist;
	do
		echo -n " Bearbeite Datei $archfile ... "
		$cmd $archfile | \
			egrep "NOQUEUE: reject: RCPT from" | \
			egrep -v 'from unknown|localhost' | \
			cut -d "]" -f 2 | \
			cut -d " " -f 6 > $target.$index
		echo -e " ${f[2]}erledigt${f[0]}"
		let index=$index+1
	done

	cat $target.* >> $target
}

#
# void gen_sortiert()
#
function gen_sortiert() {
	awk '{split($0, feld,"[");print (feld[2], feld[1])}' $unsortiert | \
		sort -b -t. -k1,1n -k2,2n -k3,3n -k4,4n| uniq -c | \
		awk '{ print("anzahl:"$1"\t",$2"\t",$3) }' > $sortIP
}

#
# void gen_getroffen()
#
# Neuer Teil zur beschleunigen der postmap abfragen ist jetzt wesentlich
# schneller, rennt wie Sau ich hoffe nur ich hab keinen Fehler drin
#
function gen_getroffen() {
	cat $sortIP | \
		cut -f 3 | \
		cut -d " " -f 2 | \
		postmap -q - pcre:$NRdynip | \
		uniq -c | \
		awk '{ print("anzahl:"$1"\t",$(NF)"\t",$2"\t") }' > $getroffen
}

#
# void gen_statistik()
#
function gen_statistik() {
	grep "#[0-9]" $getroffen | \
		cut -d "#" -f 2 | \
		cut -f 1 | \
		sort -b -t. -k1,1n | \
		uniq -c | \
		awk '{ print("anzahl:"$1"\t","regel:" $2) }' > $statistik
}

#
# void gen_einsatzdynip()
#
function gen_einsatzdynip() {
	echo "#Ausnahmen kommen immer an den Anfang"  > $EINSATZDYNIP
	cat $AUSNAHME >> $EINSATZDYNIP
	echo "#aktuelle Regeln" >> $EINSATZDYNIP
	cat $statistik | \
		awk '{ print($2) }' | \
		cut -d ":" -f 2 | \
		while read line
			do
		   such=`echo "#$line$"`

		   # Zeilennummer rauswerfen aber das ist Geschmacksache 
		   regel=`grep "$such" $NRdynip | sed -e 's/#[[:digit:]]*//g'`

		echo "$regel" >> $EINSATZDYNIP
	done
}

test "$CURRLOG" = "$LASTLOG" && LASTLOG=""

check_config_var $PREFIX_MAPS $PREFIX_LOGS
if [ ! -z "$LASTLOG" ]; then
	check_config_var $EINSATZDYNIP $CURRLOG $LASTLOG
else
	check_config_var $EINSATZDYNIP $CURRLOG
fi
check_config_var $ARCHLOG

echo


echo " versehe $ORGDYNIP mit nummern und werfe kommentare raus "
cat $ORGDYNIP | \
	grep "^\/" | \
	egrep -v "DUNNO" | \
	awk '{ print($0, "#"NR ) }' > $NRdynip

echo " extrahiere Ausnahmen aus $ORGDYNIP "
cat $ORGDYNIP | \
	grep "^\/" | \
        grep  "DUNNO" | \
        awk '{ print($0, "#"NR ) }' > $AUSNAHME


# speicher aktuelle eintraege in Variablen
zeilenalteinsatz=$(wc -l $EINSATZDYNIP | cut -d " " -f 1)

echo " hole aktuelle Logs"
gen_unsortiert cat $unsortiert $LASTLOG $CURRLOG

echo " hole alte Logs"
gen_unsortiert $CATCMD $unsortiert $ARCHLOG

echo "	anzahl	ipaddress	dynipregel	reverse_dns" >> $getroffen
echo
echo " stelle die auswertung $getroffen zusammen bitte etwas geduld je nachdem was Ihr fuer einen Rechner habt."
echo

gen_sortiert
gen_getroffen

echo "wie haeufig wurde welche Regel getroffen $statistik"
echo

gen_statistik

echo " so nun holen wir uns noch die regeln heraus welche in den letzten Wochen gebraucht wurden "
echo " und scheiben eine neue evtl. kleinere ../dynip.pcre."
echo " "
echo " Neue regeln immer in der $ORGDYNIP erfassen ansonsten gehen diese verloren beim ausfuehren des scriptes "

gen_einsatzdynip

zeilendynip=$(wc -l $NRdynip | cut -d " " -f 1)
zeilenneueinsatz=$(wc -l $EINSATZDYNIP | cut -d " " -f 1)

echo
echo " Zeilen in der Vorlage 		$zeilendynip "
echo " davon wurden alt verwendet 	$zeilenalteinsatz Zeilen," 
echo " die neue hat jetzt 		$zeilenneueinsatz Regeln"
echo

cat $getroffen | cut -f 3 | cut -d " " -f 2 | uniq > $PTR

echo
echo " zusammenfuegen von anzahl regel ptr IP um Dialinnetze und deren Groeße besser zu erkennen"
echo
fgrep -f $PTR $sortIP | cut -f 2 | cut -d " " -f 2 | uniq >$IP
# fgrep macht irgendwie mehr Zeilen in der einen Datei wie in der anderen bzw. 
# selektiert aus der sortip mehr Einträge wie in der PTR enthalten sind
# dadurch kommt es zu leichten Verschiebungen, muß ich noch überprüft werden wie viele, warum und wo  


paste $getroffen $IP >$GESAMT

awk '{print($1"\t",$2"\t",$4"\t",$3)}' $GESAMT >$getroffen
echo -e "${f[14]} \n\n evtl. falsepostive genauer pruefen bitte <ENTER> drücken für die Auswertung"
echo " diese kurze Auswertung sollte euch nicht davon abhalten in der dynip.getroffen"
echo " nach weiteren falsepositiven zu fanden. Bitte sei dir darüber im klaren das diese"
echo " map in die Quick and Dirty Ecke gehört und deshalb einer genauen Prüfung unterzogen "
echo -e " werden muß \n\n\n${f[0]}"
echo -e "${f[4]} Auswertung mit <q> verlassen${f[0]}"
#read
#egrep "mail|smtp" $getroffen |less


echo " nicht getroffene Eintraege werden jetzt performant ermittel und in $nichtgetrof abgespeichert,"
echo " interesant PTR mit nur 1-2 Verbindungsaufbauten"
#echo "nicht getroffene Server" >$nichtgetrof
fgrep -v -f $PTR $sortIP >$nichtgetrof

echo " extrahiere alle Server die nicht in der dynip gefunden wurden "
echo " evtl. falsenegative fuer neue Regeln $nichtsortdns sortiert nach PTR";

#ich nehme mal an das niemand PTR mit mail smtp mta usw. blocken möchte 

egrep -v -i "mail|smtp|mta|mx|google|apple" $nichtgetrof | \
	cut -f 3 | \
	cut -d " " -f 2| \
	sort >$nichtsortdns 
                               #nochmal nur die rDNS sortiert ist uebersichtlicher

#cat /var/amavis/dynip.nichtsortdns | \
#	awk '{print($1)}'| \
#	while read line 
#	do 
#	   grep $line /var/amavis/dynip.sortiert >>/var/amavis/z.txt
#done

# raeume auf			       
rm $GESAMT
rm $IP
rm $PTR
rm $target.*

exit 0

##### TODO #####

# auslagern der Variablen in eine eigene .conf damit nicht nach jeder Aenderung des scripts Anpassungen an andere Systeme verloren gehen
# Config Parameter beim erstmaligen Start abfragen und in einer conf speichern 
# Einbau einer Ramdisk zum beschleunigen der Auswertung und aktivierung bei Bedarf (macht die Verarbeitung um einiges schneller)
# test auf großen Mailsystemen
# Teile der Listen und Checks über Array's abwickeln wo es Geschwindigkeit bringt
# Beschränkung auf einen bestimmten Zeitraum bzw. Anzahl an Archiv logs (dann muß nicht mehr alles gegrept werden)
# Abgleich der eingesetzten .pcre und nur einfügen wenn wirklich neue regeln dazukommen (wobei das generieren so schnell geht...)
# weitere Optimierung beim selectieren der Datensätze (evtl. eigene User ausklammern) wurde geändert mal schauen was es bringt
# weitere Verallgemeinerung der Regeln ohne Gefahr von falsepositiven (fortdauernder Prozess, ebenso das neu erfassen von Regeln) 
# einbau von funktionen um auch unknown in einer Auswertung einzubauen (bei bestehendem DNS kann jetzt schon auf Treffer geprüft werden)
# evtl. mal eine Versionsnummer einbauen wobei ich das für Scripte nicht so wichtig finde, sind nur ein paar byte die man im Zweifel 
# umsonst geladen hat. 
# wen es interesiert kann mit grep "version" test-dynip.sh Datum und Uhrzeit der letzten Änderung sehen.
# yes we have to do a lot befor we are ready, translate in english is importent 

#noch ein paar codeschnipsel     
#sed -e 's/#[[:digit:]]*//g' dynip.spiel >dynip.spiel2 #zeilennummern rauswerfen

# cat /root/dynip.txt |grep "#[0-9]" |cut -d " " -f 2 |cut -d "." -f 1,2,3 | sort -b -t. -k1,1n -k2,2n -k3,3n |uniq -c | awk '{print($1, $2)}'|less
# fassst die erwischten in Class C netze zusammen

# cat /root/dynip.txt |grep "#[0-9]" |cut -d " " -f 2 |cut -d "." -f 1,2 | sort -b -t. -k1,1n -k2,2n |uniq -c | awk '{print($1, $2)}'|less
# und das selbe fuer class B netze 

# -Christian Roessnr-
# Immer noch Clean-Up nötig
#
# jo mach mal gruß uwe *g
# vim: ts=4 sw=4
