Zum Inhalt springen

Ausfallssichere Firewall – die einfache Variante…

Ich habe eine ähnliche Lösung bereits vor mehreren Jahren mal installiert, mit einem kleinen selbstgebastelten Script versehen und bis heute läuft das ganze eigentlich ohne nennenswerte Probleme!

Zwei identische Rechner die beide mit dem Internet und dem internen Netzwerk verbunden sind, bei der alten Variante haben sich die beiden Rechner einfach mittels ssh gegenseitig geprüft und falls einer nicht erreichbar war hat der andere seinen Job übernommen.

Nachdem ich aber jetzt eine ähnliche Lösung erneut aufbauen muss, habe ich mich entschlossen mal mit Heartbeat bzw. Linux-HA zu experimentieren. Der heutige Vormittag ging vorbei und Heartbeat lief, allerdings nicht so ganz zu meiner Zufriedenheit.

Während meiner Mittagspause habe ich mich dann entschlossen etwas Zeit in ein paar Zeilen Bash-Code zu stecken und die Lösung auf eigene Beine zu stellen.
Klar man soll das Rad nicht neu erfinden, aber in dem Fall erscheint es mir mehr Arbeit mich in Linux-HA vernünftig einzuarbeiten – da bin ich mit der Bash dann doch schneller…

Folgendes Szenario:

Firewall 1
eth0 – IP Adresse 192.168.100.241/24 (internes Netzwerk)
eth0:1 – IP-Adresse 192.168.100.240/24 (Gateway für internes Netzwerk)
eth1 – IP Adresse 192.168.33.1/24 (Management Netzwerk via Cross-Over-Kabel)
eth2 – IP Adresse öffentliche-IP (externe Adresse – Internet)

Firewall 2
eth0 – IP Adresse 192.168.100.242/24 (internes Netzwerk)
eth0:1 – IP-Adresse 192.168.100.240/24 (Gateway für internes Netzwerk)
eth1 – IP Adresse 192.168.33.2/24 (Management Netzwerk via Cross-Over-Kabel)
eth2 – IP Adresse öffentliche-IP (externe Adresse – Internet)

eth0:1 und eth2 wechseln von Firewall 1 zu Firewall 2 – das ganze sollte mehr oder weniger dynamisch sein, mir ist es im Grunde egal auf welchem Rechner die 192.168.100.240 bzw. eth2 aktiv sind – wichtig ist nur dass sie aktiv sind! 🙂

Also habe ich mir zwei identische Ubuntu Server aufgesetzt die sich jetzt „elmex“ und „aronal“ nennen und in der /etc/hosts jeweils den anderen Rechner mit dem Management Interface eingetragen haben.

/etc/hosts – auf aronal:

127.0.0.1 localhost

192.168.100.242 aronal
192.168.33.1 elmex

/etc/hosts – auf elmex:

127.0.0.1 localhost

192.168.100.241 elmex
192.168.33.2 aronal

Anschliessend habe ich ein Config File für beide Rechner erstellt und unter /etc abgelet.

/etc/cluster.cfg auf aronal

# zu ueberpruefendes interface bzw. ip-adresse
INT=192.168.100.240
INTNM=255.255.255.0
INTETH=eth0:1

OUT=85.65.222.31
OUTNM=255.255.255.248
OUTETH=eth1

# anderer management rechner
MGM=192.168.33.1
HOSTS=“192.168.100.200 192.168.100.201 192.168.100.218″

/etc/cluster.cfg auf elmex

# zu ueberpruefendes interface bzw. ip-adresse
INT=192.168.100.240
INTNM=255.255.255.0
INTETH=eth0:1

OUT=85.65.222.31
OUTNM=255.255.255.248
OUTETH=eth1

# anderer management rechner
MGM=192.168.33.2
HOSTS=“192.168.100.200 192.168.100.201 192.168.100.218″

Die in HOSTS eingetragenen IP-Adressen sind Server aus meinem Netzwerk von denen eigentlich zumindest einer immer aktiv ist. Die werden zusätzlich kontaktiert falls die 192.168.100.240 nicht aktiv ist, damit nicht aus Versehen eine Firewall in den Master Modus geht und dann beide als Master sich gegenseitig stören.

Das eigentliche Script habe ich check_cluster.sh benannt und in /usr/local/sbin/ abgelegt.

#!/bin/bash

. /etc/cluster.cfg

DATE=$(date)

# pruefen ob am eigenen rechner ip adresse laeuft

IPUP=$(ifconfig $INTETH | grep „$INT“)
if [ „$IPUP“ ]; then
echo „$DATE>> $INT ist auf Schnittstelle $INTETH aktiv“
sleep 30
exit
else
FAIL=1
fi

# pruefen ob ip adresse im netzwerk erreichbar ist

IPREACH=$(ping -c1 -w1 -W1 -n $INT|grep „from“)
if [ „$IPREACH“ ]; then
echo „$DATE>> $INT ist erreichbar“
else
FAIL=$((FAIL+1))
fi

# sind rechner im internen netzwerk erreichbar?

for IP in $HOSTS; do
IPREACH=$(ping -c1 -w1 -W1 -n $IP|grep „from“)
if [ „$IPREACH“ ]; then
HOOK=$((HOOK+1))
fi

done

# gegenueber via management interface erreichbar?

IPREACH=$(ping -c1 -w1 -W1 -n $MGM|grep „from“)
if [ „$IPREACH“ ]; then
echo „$DATE>> $MGM ist erreichbar“
MFAIL=0
else
MFAIL=1
fi

# FAIL = 1 –> INT laeuft nicht auf meinem rechner (bin slave)
# FAIL = 2 –> INT laeuft nicht im netzwerk!!! (slave to master)
# HOOK > 0 –> es sind andere rechner aus dem netzwerk erreichbar
# MFAIL = 0 –> partner erreichbar
# MFAIL = 1 –> partner via management interface nicht erreichbar! (ev. partner off -> master)

# MFAIL = 1 && FAIL = 2 && HOOK > 0
# partner scheint abgeschaltet zu sein, hosts im netz erreichbar
# slave to master, ich muss alle dienste uebernehmen

if [ „$MFAIL“ == „1“ ] && [ „$FAIL“ == „2“ ] && [ „$HOOK“ -gt 0 ]; then
echo „$DATE>> 12>0 wechsel von salve zu master…“
GOMASTER=1
fi

# MFAIL = 0 && FAIL = 2 && HOOK > 0
# partner laeuft aber INT nicht erreichbar, hosts im netz erreichbar
# koennte ein startup des partner sein, eventuell nach ein paar sekunden erneut checken

if [ „$MFAIL“ == „0“ ] && [ „$FAIL“ == „2“ ] && [ „$HOOK“ -gt 0 ]; then
if [ -f „/var/tmp/cluster.startup“ ]; then
rm /var/tmp/cluster.startup
echo „$DATE>> wechsel von slave zu master…“
GOMASTER=1
else
echo 1 > /var/tmp/cluster.startup
RAND=$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d“ „)
RAND=${RAND:0:1}
echo „$DATE>> 02>0 warte auf master… (erneuter check in $RAND sekunden)“
sleep $RAND
exit
fi
fi

# MFAIL = 0 && FAIL = 1 && HOOK > 0
# partner laeuf, INT aktiv, hosts im netzwerk erreichbar
# alles bestens, so soll es sein!

if [ „$MFAIL“ == „0“ ] && [ „$FAIL“ == „1“ ] && [ „$HOOK“ -gt 0 ]; then
echo „$DATE>> 01>0 status ok, partner lauscht auf $INT“
fi

# MFAIL = 1 && FAIL = 2 && HOOK = 0
# partner nicht erreichbar, INT nicht erreichbar, hosts im netzwerk nicht erreichbar!
# totalausfall – manueller eingriff auf jeden fall notwendig…

if [ „$MFAIL“ == „1“ ] && [ „$FAIL“ == „2“ ] && [ „$HOOK“ == „0“ ]; then
echo „$DATE>> 12=0 status nicht loesbar -> manueller eingriff notwendig!!!“
GOSLAVE=1
# sicherheitshalber auf slave wechseln damit
# beim reconnect des netzwerks der master nicht
# abgeschossen wird…
fi

# MFAIL = 0 && FAIL = 2 && HOOK = 0
# partner erreichbar, INT nicht erreichbar, hosts im netz nicht erreichbar
# mittels ssh checken ob partner INT am laufen hat
# wenn ja: hosts vom partner aus checken
# wenn nein: haendischer eingriff notwendig PROBLEM

if [ „$MFAIL“ == „0“ ] && [ „$FAIL“ == „2“ ] && [ „$HOOK“ == „0“ ]; then
echo „$DATE>> 02=0 kann hosts und INT nicht erreichen! partner ok – ev. ssh check“
fi

# MFAIL = 1 && FAIL = 1 && HOOK > 0
# partner nicht erreichbar, INT laeuft im netz aber nicht auf meinem rechner!!!, hosts erreichbar
# gibts eigentlich nicht – fremder host hat sich die adresse geschnappt oder
# management verbindung ist ausgefallen

if [ „$MFAIL“ == „0“ ] && [ „$FAIL“ == „1“ ] && [ „$HOOK“ -gt 0 ]; then
echo „$DATE>> 11>0 status ok, partner lauscht auf $INT“
fi

############ aktionen setzen ############

if [ „$GOMASTER“ == „1“ ]; then
if [ „$MFAIL“ == „0“ ]; then
ssh $MGM ifconfig $INTETH $INT netmask $INTNM down
ssh $MGM ifconfig $OUTETH $OUT netmask $OUTNM down
fi
# netzwerk interface aktivieren
ifconfig $INTETH $INT netmask $INTNM up
ifconfig $OUTETH $OUT netmask $OUTNM up
# firewall (re-)starten
/etc/init.d/firewall restart
# openvpn (re-)starten
/etc/init.d/openvpn restart
fi

if [ „$GOSLAVE“ == „1“ ]; then
# INT abdrehen damit kein anderer master gestoert wird!
ifconfig $INTETH $INT netmask $INTNM down
# OUT abdrehen damit kein anderer master gestoert wird!
ifconfig $OUTETH $OUT netmask $OUTNM down
fi

# script mittels sleep verzoegern damit es nicht zu oft durchlaeuft…
sleep 30

Und damit das ganze jetzt auch noch wirklich läuft muss es auf beiden Rechnern permanent laufen, dieses kleine Problem löst man am einfachsten mit einem kleinen Eintrag in der /etc/inittab auf beiden Rechnern.

# check_cluster aktivieren
C1:23:respawn:/usr/local/sbin/check_cluster.sh >> /var/log/cluster

Die Ausgaben des Script landen alle in der Datei /var/log/cluster – da sammelt sich mit der Zeit ziemlich was an, also sollte da noch bei gelegenheit die größe des Logfiles geprüft und entsprechend die Datei verkleinert werden. Aber Rom wurde auch nicht an einem Tag erbaut…

Jetzt noch mit dem Befehl „init q“ die /etc/inittag einlesen lassen und dann mit „tail -f /var/log/cluster“ zuschauen was passiert.

So lange alles läuft sieht das wie folgt aus:

/var/log/cluster auf aronal:

Thu Sep 13 18:00:56 CEST 2007>> 192.168.100.240 ist auf Schnittstelle eth3:1 aktiv
Thu Sep 13 18:01:26 CEST 2007>> 192.168.100.240 ist auf Schnittstelle eth3:1 aktiv
Thu Sep 13 18:01:56 CEST 2007>> 192.168.100.240 ist auf Schnittstelle eth3:1 aktiv
Thu Sep 13 18:02:26 CEST 2007>> 192.168.100.240 ist auf Schnittstelle eth3:1 aktiv
Thu Sep 13 18:02:57 CEST 2007>> 192.168.100.240 ist auf Schnittstelle eth3:1 aktiv

/var/log/cluster auf elmex:

Thu Sep 13 17:04:01 CEST 2007>> 192.168.100.240 ist erreichbar
Thu Sep 13 17:04:01 CEST 2007>> 192.168.33.2 ist erreichbar
Thu Sep 13 17:04:01 CEST 2007>> 01>0 status ok, partner lauscht auf 192.168.100.240
Thu Sep 13 17:04:01 CEST 2007>> 11>0 status ok, partner lauscht auf 192.168.100.240
Thu Sep 13 17:04:31 CEST 2007>> 192.168.100.240 ist erreichbar
Thu Sep 13 17:04:31 CEST 2007>> 192.168.33.2 ist erreichbar
Thu Sep 13 17:04:31 CEST 2007>> 01>0 status ok, partner lauscht auf 192.168.100.240
Thu Sep 13 17:04:31 CEST 2007>> 11>0 status ok, partner lauscht auf 192.168.100.240

Im Fehlerfall schreibt elmex folgendes ins Logfile:

Thu Sep 13 15:53:20 CEST 2007>> 192.168.33.2 ist erreichbar
Thu Sep 13 15:53:20 CEST 2007>> 02>0 warte auf master… (erneuter check in 33 sekunden)
Thu Sep 13 15:53:54 CEST 2007>> 192.168.33.2 ist erreichbar
Thu Sep 13 15:53:54 CEST 2007>> wechsel von slave zu master…

und übernimmt anstandslos die IP-Adressen.

Da das ganze heute erst frisch geschrieben wurde, stelle ich nicht den Anspruch dass es perfekt ist – aber im Moment funktioniert es recht gut!

Mir ist klar dass es für den selben Zweck ettliche fertige Projekte gibt, aber mir ist manchmal eine einfache selbst erstellte Lösung wesentlich lieber. Bei dem Script habe ich den Überblick und fall’s mal was schief geht finde ich den Fehler schnell und zuverlässig.
Da nehm ich schon mal gerne etwas fehlenden Komfort in kauf.

Ich hoffe ich hab’s für alle jene die es mal probieren wollen halbwegs verständlich zusammengeschrieben – viel Erfolg!

Schlagwörter:

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert