Böse bleiben draußen

Mein Server ist genervt. Ständig probieren irgend welche Bots aus, Passworte zu erraten und scannen Ports ab. Daher habe ich fail2ban installiert, das notorische Passwort-Ausprobierer ausbremst. Es gibt aber auch Betreiber von Honeypot-Netzwerken, die besonders aggresive IPs sammeln und diese in Listen zur Verfügung stellen. Ich habe mir eine solche Liste von einem Czechischen Anbieter herausgesucht und in einen iptables Filter umgewandelt. Und das geht so:

#!/bin/bash

BLKSRC="https://security.etnetera.cz/feeds/etn_aggressive.txt"
CHAIN="aggressiveips"
IPTBIN="/sbin/iptables"
TDAY=$(date +%d)
YDAY=$(date +%d --date="-1 day")
ADMIN="admin@internet.tld"

# create new chain for today
$IPTBIN -N $CHAIN$TDAY
# insert all IPs from the source into the chain (this takes a while)
for BIP in $(curl $BLKSRC 2>/dev/null | egrep -v "^#|^$|\:"); do
$IPTBIN -A $CHAIN$TDAY -s $BIP -j LOG --log-prefix "[netfilter] $CHAIN$TDAY "
$IPTBIN -A $CHAIN$TDAY -s $BIP -j DROP
done
# add a RETURN policy to allow other chains to be examined
$IPTBIN -A $CHAIN$TDAY -j RETURN
# insert chain into the INPUT chain
$IPTBIN -A INPUT  -j $CHAIN$TDAY
# delete yesterdays chain from INPUT chain
$IPTBIN -D INPUT -j $CHAIN$YDAY
$IPTBIN -F $CHAIN$YDAY
$IPTBIN -X $CHAIN$YDAY
# report
BLKCNT=$($IPTBIN -n -L $CHAIN$TDAY | fgrep DROP | wc -l)
echo "Installed $BLKCNT blocked IPs from $CHAIN - $BLKSRC on $(date)" | mail -s "$CHAIN result" $ADMIN

Das Script lädt als cronjob, der ein Mal nächtlich läuft, die aktuelle Liste herunter, sortiert alle Leerzeilen und Kommentarzeilen sowie IPv6-Adressen (egrep den Doppelpunkt) aus und installiert dann eine iptables chain. Ich nutze den heutigen und gestrigen Tag als Teil der chain, damit beim Neuladen nicht während der Installation der chain der Server von nervenden Anfragen gekitzelt wird.

Und damit ich einfacher für jeden Tag eine Auswertung machen kann. Und da kommt eine ganze Menge zusammen. Das aber ander mal.

Böse bleiben draußen

Abgelaufene SSL Zertifikate

Es ist mir leider mehr als nur ein mal passiert, dass der Mailserver mit abgelaufenen SSL-Zertifikaten lief. Bei Let’s Encrypt laufen diese Zertifikate ja relativ schnell (3 Monate) ab. Daher brauche ich eine Warnung, falls das passiert und der automatische Restart nicht geklappt hat.

Man kann natürlich die Cert Dateien im Filesystem prüfen, aber viel besser ist doch den Dienst direkt zu fragen. Und das geht so (und das kann auch remote laufen):

#!/bin/bash

# SMTPS Host to check
HOST=my.mailserver.tld
# Days before end of cert to warn/take action
WARNDAYS=14
# admin email
CONTACT="adminofmymailserver@gmail.com"

ENDDATE=$(echo QUIT | openssl s_client -connect $HOST:465 -verify_return_error 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'| openssl x509 -noout -dates  | fgrep notAfter | sed 's/notAfter=//g')
# echo $ENDDATE cert expires
EPOCHENDDATE=$(date --date="$ENDDATE" +"%s")
# echo epoch is $EPOCHENDDATE
EPOCHTODAY=$(date +"%s")
let TIMELEFT=$EPOCHENDDATE-$EPOCHTODAY
DAYSLEFT=$(echo "$TIMELEFT/86400" | bc)
# echo time left $DAYSLEFT days
if [ $DAYSLEFT -le $WARNDAYS ]; then
  echo "WARNING: Cert for $HOST expires in $DAYSLEFT days!" | mail -s "WARNING: Cert for $HOST expires in $DAYSLEFT days!" $CONTACT
  # /etc/init.d/exim4 restart
fi

Die Herausforderung ist, das Ablaufdatum im Zertifikat in Tage gerechnet von Heute umzuwandeln. Das geht mit dem Befehl „date“, der in epoch umwandelt und einer einfachen Division ohne Rest durch 86400 (Sekunden pro Tag) der Differenz mit „bc“.

Abgelaufene SSL Zertifikate

May the brute-force be with you, Botnet!

Das Internet ist voller böser Jungs und Mädchen. Ich arbeite für eine Firma, die mit ihren Produkten Firmen und Menschen hilft, sich gegen diese Bösewichte zu wehren. Doch auch privat betreibe ich eigene Server und Dienste. Da ich ein wenig zu paranoid bin, nutze ich meinen eigenen Mailserver und keine gmail oder andere „free“-mail adresse.

Nicht jeder kann einen eigenen Server betreiben. Denn wenn man nichts davon versteht, kann man es auch gleich einem Dritten überlassen, der weiß, was er tut. Falls nicht, ist man mit gmail doch besser aufgehoben.

Auf meinem Server befinden sich daher noch andere Benutzer, die gerne freemail-frei emailen wollen. Sie kennen mich alle persönlich und vertrauen mir, dass ich keinen Schindluder mit ihren Daten treibe.

Auf meinem Server läuft das IDS/IPSfail2ban„. Das ist ein System, das brute-force Angriffe auf Passworte erkennt und die Übeltäter automatisch blockiert. Das ist schön und dient als erste Verteidigungslinie. Aber so richtig Sicherheit bringt es nicht.

Ich sehe in den Logs, dass versucht wird, Passworte herauszufinden. Hier ein paar echte Logs aus dem Server, in der ich nur die Domain des potentiellen Opfers geändert habe:

2019-02-03 10:11:26 plain authenticator failed for (127.0.0.1) [191.102.28.34]: 535 Incorrect authentication data (set_id=info@victim.tld)
2019-02-03 10:11:28 login authenticator failed for (127.0.0.1) [191.102.28.34]: 535 Incorrect authentication data (set_id=info@victim.tld)
2019-02-03 10:31:16 plain authenticator failed for isp-31-222-13-236.saowifi.net (127.0.0.1) [31.222.13.236]: 535 Incorrect authentication data (set_id=info@victim.tld)
2019-02-03 10:31:16 login authenticator failed for isp-31-222-13-236.saowifi.net (127.0.0.1) [31.222.13.236]: 535 Incorrect authentication data (set_id=info@victim.tld)
2019-02-03 11:18:28 plain authenticator failed for (127.0.0.1) [88.81.93.221]: 535 Incorrect authentication data (set_id=info@victim.tld)
2019-02-03 11:18:29 login authenticator failed for (127.0.0.1) [88.81.93.221]: 535 Incorrect authentication data (set_id=info@victim.tld)
2019-02-03 11:55:13 plain authenticator failed for (127.0.0.1) [186.148.80.202]: 535 Incorrect authentication data (set_id=info@victim.tld)
2019-02-03 11:55:15 login authenticator failed for (127.0.0.1) [186.148.80.202]: 535 Incorrect authentication data (set_id=info@victim.tld)
2019-02-03 12:15:34 plain authenticator failed for (127.0.0.1) [178.219.123.243]: 535 Incorrect authentication data (set_id=info@victim.tld)
2019-02-03 12:15:35 login authenticator failed for (127.0.0.1) [178.219.123.243]: 535 Incorrect authentication data (set_id=info@victim.tld)

Wie man sieht, wird pro IP genau zwei Mal probiert und dann aufgegeben. Das liegt daran, dass man ab da Gefahr läuft, von einem IDS/IPS entdeckt zu werden. Wer aber genügend IPs zur Verfügung hat, wechselt eben von einer IP zur anderen.

Diese IPs bekommt man über Botnetze oder Anonymisierungsdienste. Gegen die kann man sich kaum zur Wehr setzen, ohne echte Benutzer auszusperren.

Es hilft also nur, gute Passworte zu nutzen und für jeden Dienst ein eigenes. Und wann immer es möglich ist, auf eine Zwei-Faktor-Authentifizierung zu setzen.

May the brute-force be with you, Botnet!

Bandbreite anzeigen

Die aktuell genutzte Bandbreite eines Linux Systems wird ermittelt als übertragene Bytes pro Zeitabstand. Einen „aktuellen“ Wert zum Auslesen gibt es nicht, man muss also die Bandbreite aus zwei Deltawerten übertragener Bytes berechnen. Ich habe ein Script gefunden und etwas erweitert. Es zeigt die Bandbreite für alle Interfaces in Kbits und Mbits an. Das Script ist simpel gehalten und ohne Parameter, die irgend etwas steuern. Aber das kann man ja nachträglich einbauen. Wenn man will.

#!/bin/bash                                                                          

intervalo=2
info="/sys/class/net/"
cd $info
while true; do
for interface in eth*
do
  eval rx1$interface=`cat $info$interface/statistics/rx_bytes`
  eval tx1$interface=`cat $info$interface/statistics/tx_bytes`
done
  sleep $intervalo
for interface in eth*
do
  eval rx2$interface=`cat $info$interface/statistics/rx_bytes`
  eval tx2$interface=`cat $info$interface/statistics/tx_bytes`
done
clear
date
for interface in eth*             
do
  echo $interface
  echo ---- 
  rx1=$(eval echo \${rx1$interface})
  rx2=$(eval echo \${rx2$interface})
  tx1=$(eval echo \${tx1$interface})
  tx2=$(eval echo \${tx2$interface})
  echo RX: $((($rx2-$rx1)/($intervalo*1024))) Kbps
  echo RX: $((($rx2-$rx1)/($intervalo*1048576))) Mbps
  echo TX: $((($tx2-$tx1)/($intervalo*1024))) Kbps
  echo TX: $((($tx2-$tx1)/($intervalo*1048576))) Mbps
done
done
Bandbreite anzeigen

HTML emails mit mail.mailutils

Das Programm mail.mailutils (meist einfach mail) ist auf vielen Linux-Distributionen installiert. Man kann damit über die Shell Emails verschicken, was sehr praktisch ist für scripte und cron-jobs, die den Admin per Mail informieren sollen.

Manchmal will man auch mal ein wenig mit HTML spielen und sei es auch nur, um um den Text ein <pre> zu setzen, damit er mit einer Monospace-Schrift angezeigt wird und ASCII-Art und Tabellen ordentlich dargestellt werden.

So schickt man HTML:

# generiere random boundary
BOUNDARY=$(date | md5sum | head -c8)
echo "
--_$BOUNDARY_
Content-Type: text/html; charset=\"us-ascii\"
Content-Transfer-Encoding: quoted-printable

<html><pre>
Meine Tabelle

+-------+----------+
| id    | analysis |
+-------+----------+
| 36190 |      219 |
| 36192 |      219 |
| 36273 |      219 |
| 36275 |       -1 |
| 36276 |       -1 |
| 36289 |      219 |
| 36293 |      122 |
| 36371 |      219 |
| 36388 |      219 |
| 36393 |      219 |
| 36394 |       -1 |
| 36395 |       -1 |
| 36708 |      122 |
+-------+----------+
</pre></html>
--_$BOUNDARY_--

" | mail.mailutils -s "Mein Subject" \
-a Content-Type:"multipart/alternative; boundary="\"_$BOUNDARY_\"" \
-a From:mein@absender.tld mein@empfaenger.tld

Diese Tabelle sieht jetzt sauber aus, wenn sie den Empfänger erreicht. Die Leerzeilen vor und nach dem Boundary und/oder Headern sind wichtig!

Wenn ich ein Bild in das HTML einbetten will, wird es komplizierter. Das mail-Tool kann zwar mit -A Anhänge verschicken, aber die kann ich dann nicht im HTML nutzen, da ich die dazugehörige ID nicht kenne.
Also muss ich das händisch machen, damit ich alle Parameter kontrollieren kann. Da Email ein 7-Bit Medium ist, muss ich aus dem Binär-Bild ASCII machen und dann noch dem Mailprogramm erklären, wie es das ASCII wieder in eine Binär-Bilddatei zurückverwandelt. Ich benutze hier base64 dafür, da das standardmässig installiert ist und so ziemlich jedes Mailprogramm das versteht. Ausserdem muss ich dem Bild einen Namen geben, denn der Dateiname geht verloren beim encoden in base64, und dazu eine ID, mit der ich darauf verweisen kann im HTML-Code der Mail. So gehts:

# generiere random boundary und Image ID
BOUNDARY=$(date | md5sum | head -c8)
IMAGEID=$(date | md5sum | head -c10 | rev)
# meine Bilddatei
IMAGEFILE=graph.png
( echo "
--_$BOUNDARY_
Content-Type: text/html; charset=\"us-ascii\"
Content-Transfer-Encoding: quoted-printable

<html>
Mein Graph als Bild:<br>
<img contenttype=3D\"image/png\" src=3D\"cid:$IMAGEID\"><br>
Sieht gut aus!

</pre></html>

--_$BOUNDARY_
Content-Type: image/png;name=\"$IMAGEFILE\"
Content-Disposition: attachment;filename=\"$IMAGEFILE\"
Content-ID: <$IMAGEID>
Content-Transfer-Encoding: base64 
"
base64 $IMAGEFILE 
echo "
--_$BOUNDARY_--" ) |  mail.mailutils -s "Mein Subject" \
-a Content-Type:"multipart/related; boundary="\"_$BOUNDARY_\""; type="\"multipart/alternative\""" \
-a MIME-Version:"1.0" \
-a From:mein@absender.tld mein@empfaenger.tld

Es gibt ausser mail.mailutils noch viele andere Commandline-Mailer wie etwa mutt oder mailx. Mit Hilfe der Beispiele oben kann man auch diese Mailer dazu bringen, HTML zu verschicken. Einfach mal gucken, welche Tools auf dem System installiert sind. Auch OS X kann das.

HTML emails mit mail.mailutils

bye bye, netstat

Mit netstat kann man sehen, welche Verbindungen gerade aktiv sind und auf welchen Ports ein Prozess horcht.

Aber netstat ist langsam. Ich habe einen Server, auf dem einige 10k TCP Verbindungen offen sind und der trotz fantastischer CPU und RAM Ausstattung geschlagene 2 Minuten braucht, um netstat auszuführen (mit -n, also ohne Namensauflösung!).

Deutlich schneller, nämlich unter einer Sekunde, schafft das der Nachfolger ss, was wohl eine Abkürzung von socket stat oder so ähnlich sein soll. Die manpage sagt nur „another utility to investigate sockets“.

Das Gute ist, es versteht alle Optionen von netstat. Wer also historisch bedingte Schwierigkeiten hat, ss zu tippen oder bloss ein Gewohnheitstier ist, macht sich einfach ein alias in der .bashrc:

alias netstat=ss
bye bye, netstat

Sekunden umrechnen und anzeigen

Ich habe ein Script mit einer Schleife geschrieben, das ziemlich lange läuft und auch vorher weiss, wie lange es ungefähr laufen wird. Ich will dem Anwender sagen, wie viel Geduld er noch aufbringen muss.

Erst schreibe ich eine Funktion, die die Umrechnung in Tage, Stunden und Minuten macht:

function DisplayTime {
  local T=$1
  local D=$((T/60/60/24))
  local H=$((T/60/60%24))
  local M=$((T/60%60))
  local S=$((T%60))
  [[ $D > 0 ]] && printf '%d days ' $D
  [[ $H > 0 ]] && printf '%d hours ' $H
  [[ $M > 0 ]] && printf '%d minutes ' $M
  [[ $D > 0 || $H > 0 || $M > 0 ]] && printf 'and '
  printf '%d seconds\n' $S
}

Ich nutze in der Benamsung von Funktionen immer eine Kombination aus Groß- und Kleinbuchstaben, damit ich sie als Funktion erkenne und sie nicht mit einem Systembefehl oder einer Variable verwechsle.

Dann schreibe ich einen Echo-Befehl in die Schleife, der seinen Output selbst ersetzt und benutze dafür die obige Funktion. Voraussetzung ist natürlich, dass ich die Variable $SEKUNDEN vorher im Skript irgendwie errechnet habe.

echo -ne "time remaining: $(DisplayTime $SEKUNDEN)                        \r"

Das „\r“ mit dem -e zusammen sorgt dafür, dass die Zeile überschrieben wird. Die Leerzeichen sind nötig, da der Output von der Funktion mit der Zeit kürzer wird und die Zeile sonst nicht  komplett überschrieben wird. Wenn jemand eine bessere Lösung als die Leerzeichen hat, dann her damit!

Sekunden umrechnen und anzeigen

snmp unter Ubuntu precise

Wie nervig. Nach einem (überfälligen) Update von Ubuntu lucid auf Ubuntu precise ging SNMP nicht mehr. Warum? Die Antwort steht in /etc/snmp/snmp.conf:

#
# As the snmp packages come without MIB files due to license reasons, loading
# of MIBs is disabled by default. If you added the MIBs you can reenable
# loaging them by commenting out the following line.
mibs :

Um die MIBs wieder zu bekommen, installiert man das Packet snmp-mibs-downloader und kommentiert die Zeile mibs : in oben genannter Datei aus. Danach geht alles wieder, wie es soll.

snmp unter Ubuntu precise