Script zum Setzen der Linux Timezone

Es gibt dafür durchaus Pakete und Binaries, die das übernehmen, aber warum nicht selbst machen und ein wenig mit select spielen?

#!/bin/bash
# Simple script to set system TZ for any linux system
echo "Setting the TZ of this system:"
if [ "$EUID" -ne 0 ]; then
  echo "Please run as root"
  exit
fi
cd /usr/share/zoneinfo/posix
PS3="Select the Region: "
select WREG in $(find . -type d | sed "s/\.\///g" | fgrep -v ".") other; do
	if [ -z $WREG ]; then
		echo "Not changing TZ"
		break
	fi
	echo "Selected the Region: $WREG "
	PS3="Select the TZ: "
	if [ $WREG == "other" ]; then
		mapfile -t WREGLIST < <( find . -maxdepth 1 -not -type d | sed "s/\.\///g" )
		WREG=""
	else
		cd $WREG
		mapfile -t WREGLIST < <( find . -not -type d | sed "s/\.\///g" )
	fi
	select WREGTZ in ${WREGLIST[*]}; do
		if [ -z $WREGTZ ]; then
			echo "Not changing TZ"
		break
		fi
	echo "Selected the TZ: $WREGTZ "
	ln -s /usr/share/zoneinfo/$WREG/$WREGTZ /etc/localtime
	echo "Current date is: $(date)"
	break
	done
break
done
Script zum Setzen der Linux Timezone

Aus CIDR-Schreibweise eine Maske berechnen

Es gibt Python-Scripte dafür und Perl-Bibliotheken auch, aber was, wenn man in Bash eine IPv4-Prefixlänge in eine Maske umrechnen will? Etwa /24 in 255.255.255.0? Das geht so:

#!/bin/bash

function PFLcalc () {
        let NN=32-$1
        PL=$(printf -- '1%.0s' $(seq 1 $1))
        PR=$(printf -- '0%.0s' $(seq 1 $NN))
        PF=$PL$PR
        OCT1=$(echo "ibase=2; ${PF:0:8}" | bc)
        OCT2=$(echo "ibase=2; ${PF:8:8}" | bc)
        OCT3=$(echo "ibase=2; ${PF:16:8}" | bc)
        OCT4=$(echo "ibase=2; ${PF:24:8}" | bc)
        MASK=$OCT1.$OCT2.$OCT3.$OCT4
}
PFLcalc $1 # call script with int le 24
echo "Mask is $MASK"

Ja, bei einer Länge von 0 verrechnet sich das Script. Pech.

Aus CIDR-Schreibweise eine Maske berechnen

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

Time Machine like backups

If you backup your data to a remote server or a local disk with a script (I strongly recommend rsync to do so, otherwise this script will most probably not work), you should also keep old versions of that data to be able to rollback to an earlier version. But if you have a LOT of data, your HD will run full. So better just keep the deltas.

In my case its half a terabyte of email data from a mail server. Many files change every day. Get deleted, added, moved.
To keep old versions (you could call them snapshots) of the backups, I use hardlinks, that will just create a new link to an existing file in the filesystem. It is not a symbolic link, meaning the actual file will only get deleted, when the last hardlink will be deleted from the filesystem. In my case, this will happen 10 days after the original file was deleted.

#!/bin/bash

BASEDIR="/var/bkp/" # base directory
BDIR=mybackup # directory to backup
DAYS=10
cp -al $BASEDIR/$BDIR/ $BASEDIR/$BDIR$(date +%Y%m%d)
if [ -d $BASEDIR/$BDIR$(date +%Y%m%d --date="-$DAYS day") ]; then
 echo "deleting old backup $BDIR$(date +%Y%m%d --date="-$DAYS day")..."
 rm -rf $BASEDIR/$BDIR$(date +%Y%m%d --date="-$DAYS day")
else
 echo -n "No old backups at "
 date +%Y%m%d --date="-$DAYS day"
fi
Time Machine like backups

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

copy-paste mit base64

Ich arbeite oft auf Systemen, die ich nur über einen virtuellen Screen erreiche (Remote Desktop etwa) und zu dem ich zwar eine gemeinsame Zwischenablage habe (copy-paste), aber keine einfache Möglichkeit, Dateien zu kopieren.

Wenn ich etwa eine Logdatei kopieren will, die in den Scrollback-Buffer des Terminalfensters auf der remote Maschine nicht komplett reinpasst, dann muss ich stückeln und bereichsweise kopieren. Das nervt tierisch.

Oder ich nutze gzip und base64:

% cp grosselogdatei.log /tmp
% cd /tmp
% gzip grosselogdatei.log
% base64 grosselogdatei.log.gz
H4sICLiyGlkAA29wZW5maWxlcy5jc3YAbZ3dlsSoCkbvz7PUmVWg+DPv/2BTnZgIuG/3oo35AoiS
VMv3n6/9o1/p/5f+77d+mv5PApPvp1li+v30llj52fXE6o99E7Mfk8DGv98fG4md1x1/1z3+toDd
33VLYn/XjXOef9eVKgkKGO [...]
B5SBkg6GOjwNjG+maEv+4PabgVJ+aBgX7iekIn32poGSDg3zQ8O4aKhDR39wL8wFSutmRx06+oP7
CalAKT/0/YJUoKRDx7jo+8XBSEGzgevFQH8YmCfH/mnWQMkfBsbFQH8YGBf3frOle7v3m9drf4FS
XAz0h4k6TMwPE/3BNTACVbg39xNSgb7nUZGiLfnDxLi495tZHbff3FS+tF7I/sni/wA6SY/SD4IA
AA==

Diesen ASCII Output kann ich locker per copy-paste in einem Rutsch kopieren und in eine Datei lokal kopieren, etwa grosselogdatei.log.gz.b64, und wieder auspacken:

% base64 -d grosselogdatei.log.gz.b64 > grosselogdatei.log.gz
% gunzip grosselogdatei.log.gz
copy-paste mit base64

Filtern in Bash

Manchmal will man aus einer Variable oder einem Text alle Zahlen herausziehen oder verstecken. Das geht natürlich mit sed ganz gut, aber noch einfacher in der Bash selbst.

% VAR=1a2b3c4d
% echo "${VAR//[!0-9]/}"
1234
% echo "${VAR//[0-9]/}"
abcd

Man kann auch nur Buchstaben filtern, damit man Sonderzeichen mit herausfiltert. Oder oder…

% VAR="1a,2b.3c;4d"
% echo "${VAR//[!0-9]/}"
1234
% echo "${VAR//[!a-z]/}"
abcd
% echo "${VAR//[!a-z 0-9]/}"
1a2b3c4d
% echo "${VAR//[a-z 0-9]/}"
,.;
Filtern in Bash

Mehrere Variablen in bash gleichzeitig setzen

Wenn ich mit den Output von einem Befehl mehrere Variablen setzen will, kann ich dafür read und die <<< Dreieckklammern nutzen. So gehts:

% EINS=2
% ZWEI=1
% read EINS ZWEI <<< $({ echo 1; echo 2 ; })
% echo $EINS $ZWEI
1 2

Wenn man auf Google oder Bing nach <<< sucht, findet man nichts. Also, entweder bin ich der erste (!!!11!1!!elf!!11), der darüber schreibt, oder wahrscheinlicher, nach diesem String darf man aus technischen Gründen einfach nicht suchen.

Mehrere Variablen in bash gleichzeitig setzen