IP-Connection-Track erkennt keine stehenden Verbindungen

Wer das Problem hat, dass bei iptables-Rules wie der folgenden keine (oder nur vereinzelt) Verbindungen nach draußen zustande kommen hat das gleiche Problem wie ich es hatte:

iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -i eth1 -o pppoe0               -j ACCEPT

Die Regel besagt, alles von „Drinnen“ (eth1) nach „Draußen“ (pppoe0) zuzulassen und alle Pakete der Verbindungstatus als ESTABLISHED oder RELATED erkannt wird zuzulassen. Kurz: eine StateFull-Firewall die alles von drinnen nach draußen routet aber von draußen nur angefragtes rein lässt.

Wer auf etwas „stabilere“ (oder ältere) Software setzt (in Worten: debian squeeze) ist noch mit einem 2.6er Kernel unterwegs. Bei diesem sind die Module fürs ConnectionTracking wohl noch etwas schärfer/unbrauchbarer.

Erst mit folgendem Befehl verhält sich das ConnectionTracking wie man es erwarten würde:

echo 1 > /proc/sys/net/netfilter/nf_conntrack_tcp_be_liberal

Ab Kernel 2.6.22 ist dies nicht mehr notwendig.

Quelle: http://conntrack-tools.netfilter.org/manual.html

Linux Bandbreiten-Monitor/Traffic-Accounting

Wenn man unter Linux einen Rechner betreibt findet man einen Haufen an Möglichkeiten den Bandbreitenverbrauch zu ermitteln. Betreibt man hingegen einen Router und will nicht nur den eigenen Verbrauch, sondern den Transfer-Traffic aufgeschlüsselt nach gewissen Attributen, wird es dünn im Lösungsumfeld. Soll das ganze dann noch auf einem handelsüblichen SOHO-Router laufen, wird es richtig kniffelig.

Das erste Problem ist, dass man wissen muss nach,nach was man sucht. Unter „Bandbreiten-Monitor“ verstehen die meisten Lösungsvorschläge das Monitoring des eigenen Verbrauchs/Traffic oder nur das „Gesamtaufkommen“ an einem Interface. Kurzum: Werte die sich leicht ermitteln lassen. Das aufschlüsseln des Transfertraffics bezeichnet man häufig als IP-Accounting. Danach teilt sich das Feld in folgende Lösungen auf:

  • Promiscuous Mode Basiert: Hierbei wird ein Interface in den Promiscuous-Mode geschaltet und der komplette Netzwerktraffic mitgeschnitten. Die Lösungen unterscheiden sich dann darin, wo und wie die Daten ausgewertet werden. Im einfachsten Fall werden die Pakete vorverarbeitet und an eine Sammelstelle weiter geleitet. Bekannteste Vertreter sind NTop, NProbe und bandwidthd.
  • IPTables/NetFilter Basiert: Hierbei wird ausgenutzt, dass IPTables mit protokolliert wie viele Pakete/Bytes über eine Filter-Regel gelaufen sind. Dies kann man dann asyncron über Bash-Befehle auslesen.

Der Promiscuous Mode basierten Lösungen sind die allumfassenden Lösungen. Sie schneiden alles mit und können die detailliertesten Auswertungen bieten. Sie haben jedoch immer zwei massive Nachteile. Erstens ist die benötigte CPU-Leistung enorm. Selbst die schlanke NPrope schaffte bei einem 600MHz Prozessor nicht mal Ansatzweise den zu erwartenden Transfertraffic (100Mbit/s). Auch wird das Analyseziel stark belastet. Sowohl was die Bandbreite als auch die CPU-Last betrifft. Das ist für eine einfache Bandbreitenanalyse ein wenig übertrieben. Des weiteren ist es mehr als fraglich ob das ganze rechtlich überhaupt zulässig ist. Immerhin handelt es sich hierbei um eine DPI,  jeh nach Ausführung sogar mit ungesicherter Ausleitung.

Die IPTables -Variante hat den Vorteil, dass die Pakete sowieso durch IPTables verarbeitet werden müssen. Der Overhead für das zusätzliche Monitoring hält sich in Grenzen. Auf besagtem 600 MHz Prozessor war keine Leistungsabfall durch Zu- oder Abschalten der Filter-Regeln zu messen, noch gab es eine nennenswerte CPU-Last. Der Nachteil ist jedoch, dass man wissen muss was man Monitoren will. Wenn man nur wissen will, wie viel Traffic auf ein Netzsegment entfällt, ist das einfach umsetzbar. Will man jedoch wissen auf welchen Client im Netzsegment der Traffic aufgelaufen ist, wird es schwierig. Besonders wenn es sich um ein Netzsegment mit ständig wechselnden Clients handelt wird es nahezu unmöglich das mit IPTables alleine zu bewerkstelligen.

Abhilfe schafft ein die NFLog Schnittstelle und ein Daemon aus dem pmacct.net-Project. IPTables führt das Decoding und Vorfilterung durch und leitet das Paket dann an den uacctd weiter. Dieser filtert, aggregiert und verarbeitet die Daten oder leitet sie an ein Sammelstelle weiter.

Das Ganze ist denkbar einfach zu konfigurieren. Als erstes braucht es nur IPTables-Regeln die die interessanten Pakete ausleiten.

iptables -I FORWARD -d 192.168.0.0/16 -j NFLOG --nflog-nlgroup 1
iptables -I FORWARD -s 192.168.0.0/16 -j NFLOG --nflog-nlgroup 1

Durch diese Regeln werden alle Pakete die das 192.168-Netz betreffen an die ULog-Schnittstelle weitergeleitet. Die weiteren Parameter bewirken, dass immer gewartet wird bis sich 50 Pakete gesammelt haben und nur die ersten 48 Bytes weitergeleitet werden. Die Group-Angabe ist später für die Auswertung wichtig.

Der entsprechende uacctd ist wie folgt konfiguriert:

daemonize: true
pidfile: /var/run/uacctd.pid
syslog: daemon

uacctd_group : 1
plugins: memory[host_in], memory[host_out]

aggregate[host_in]: dst_host
aggregate[host_out]: src_host

aggregate_filter[host_in]: dst net 192.168.0.0/16
aggregate_filter[host_out]: src net 192.168.0.0/16

imt_path[host_in]: /tmp/pmacct_host_in.pipe
imt_path[host_out]: /tmp/pmacct_host_out.pipe

Durch diese Konfiguration werden zwei Endpunkte angelegt, jeweils einen für ein- und ausgehendem Traffic und nach internem Client aggregiert. Der Speicherverbrauch und die Prozessorlast ist in meinem Einsatzgebiet zu vernachlässigen (selten mehr als 20 Clients gleichzeitig) kann jedoch je nach Einsatzart stark ansteigen. Ob das ganze bei 10k Clients und „portgenauem“ Monitoring noch skaliert habe ich nicht ausprobiert.

Auswerten kann man das ganze wie folgt:

pmactt -s -p /tmp/pmacct_host_in.pipe

Mit der eigenen Wunsch-Skriptsprache kann man nun die wildesten Skriptes zum überwachen des Traffics basteln.
Hier mein Beispiel für einen Perl-Skript/Munin-Plugin: munin-bandwidth.perl

Dynamische Firewall mit iptables

Einer der Gründe warum ich meinen Netgear-Router zum Switch/AP degradiert habe war, dass ich mehr Kontrolle über meine Datenströme haben will. Meine grundlegende Anforderungen sind:

  • Aufschlüsselung der Bandbreite nach Clients
  • unterwerfen von unbekannten Clients unter eine dynamische Bandbreiten Beschränkung
  • gute Wartbarkeit
  • gute Monitoring-Möglichkeit

Die ursprüngliche Anregung habe ich diesem Artikel entnommen. Man muss das Konzept nur noch um die Dynamik ergänzen, damit auch Clients verwaltet werden können die man vorher nicht kennt.

Unter Ubuntu hat man die Wahl zwischen der „uncomplicated Firewall“ (UFW) und dem direkten Zugriff mittels iptables. Ich hab mich für iptables entschieden. Zum einen bin ich unter UFW zu schnell in Bereich gestoßen, bei den ich quasi iptables-Befehle geschrieben habe (nur ohne iptables vorangestellt). Zum anderen lieferte mir UFW zu viele „default“-Regeln aus, die mit „nicht löschen“ markiert sind ohne zu Begründen warum. Hat man sich einmal in iptables eingearbeitet, so hat man die volle Freiheit die man braucht.

WICHTIGER HINWEIS:Beim Ausführen der folgenden Befehle sollte man in Reichweite des Rechners sitzen oder einen alternativen Zugang zum Rechner haben, da man sich u.U aussperrt.

Es empfiehlt sich für das einstellen der Firewall-Regeln ein eigenes Set an Scripts zu erstellen, z.B. unter /etc/firewall. So kann man mit einfacher die Firewall zu und abschalten.

Als erstes sollte man die default-Policy der Haupt-Regel-Ketten auf DROP setzen. Das ist zwar ein wenig Paranoid hilft aber ungemein, da man jedes Loch bewusst aufmachen muss.

Diese Kommandos verhindern das jegliches einloggen via Remote-Access-Tools. Es wird jeder Traffic gedropt.
iptables -L INPUT -p DROP
iptables -L FORWARD -p DROP
iptables -L OUTPUT -p DROP

Bei aller Regelwut sollte man eines bedenken, das Durchlaufen der Filterregeln kostet zeit. Je nachdem auf welchem Layer man die Entscheidung zum DROP/ACCEPT trifft, müssen die Pakete aufwändig entpackt werden. Einige Module beherrschen dabei die sogenannte DeepPackageInspection (DPI). Diese Filterregeln sollten jedoch nur sparsam zum Einsatz kommen da sie sonst den kompletten Netzwerktraffic drücken.

Als erstes sollte man eine Regel einrichten die jede Verbindung annimmt die schon mal geprüft wurde. Zu diesem Zweck nutzt man das „state“-Module bzw Connection-Tracking.

iptables -L INPUT -m state --state ESTABLISHED,RELEATED -j ACCEPT
iptables -L OUTPUT -m state --state ESTABLISHED,RELEATED -j ACCEPT

Nun sollte man die Input-Chain um statische Ausnahmen für die Fernwartung und eventuell benötigte Dienste erweitern. Vorzugsweise mit angaben aus welchem Netz diese Zugriffe erfolgen dürfen. Hier bietet es sich an eigene „Chains“ anzulegen und so die Wartbarkeit zu erhöhen.

Beispiel: Es ist sinnvoll den „eingehenden“ Traffic nach „Herkunft“ zu unterscheiden. Traffic aus dem eigenen Netz unterliegt einer anderen Vertrauenswürdigkeit als externer Traffic.

iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -i eth1 -j ACCEPT #vom lokalen netz zum server alles zulassen
iptables -A INPUT -i ppp0 -j server_incoming_traffic
iptables -A INPUT -j LOG --log-level 4 --log-prefix "Input dropped by firewall: "

Die ersten beiden regeln erlauben jeglichen Traffic des „localhost“ und „eth1“ Interfaces. Nur Traffic des ppp0 Interfaces wird einer Filter-Chain unterworfen. Führt in dieser „server_incoming_traffic“-Chain keine Regel zu einem DROP oder ACCEPT wird diese einfach verlassen und das LOG – Kommando wird ausgeführt, zum logging aber später mehr.

Die Filter-Chain kann dann so aussehen:

iptables -N server_incoming_traffic
iptables -A server_incoming_traffic -p tcp --dport 22 -j ACCEPT
iptables -A server_incoming_traffic -p tcp --dport 443 -j ACCEPT
iptables -A server_incoming_traffic -p tcp --dport 6880:6999 -j ACCEPT
iptables -A server_incoming_traffic -p udp --dport 6880:6999 -j ACCEPT

so kann man eine Reihe von Port-Ranges freigeben. Man kann auf Wunsch die Regeln auch noch ins unendliche erweitern. Aber immer dran denken, dass frisst Performance.

Beim ausgehenden Traffic kann man es ähnlich händeln, mit einer Ausnahme. Bei einigen Programmen weiß man im vornherein nicht ob und welche Ports genutzt werden. Hier führen DROP-Policys normalerweise dazu, dass die Programme nicht oder nur eingeschränkt funktionieren. Entweder reißt man für solche Programme große Lücken in seine Firewall oder nutzt das „owner“-Modul. Bei ausgehenden Traffic ist es möglich zu ermitteln unter welcher UID oder GID der Socket aufgemacht wurde. Auch darauf kann man filtern. Wenn man sich an den Standard hält jedem Dienst seinen eigenen User zu verpassen, kann man so schön auf Dienstebene freigeben, wer was darf.

Folgendes Beispiel gibt für rTorrent allen ausgehenden Traffic frei.

iptables -A server_outgoing_traffic -p tcp -m owner --uid-owner rtorrent -j ACCEPT
iptables -A server_outgoing_traffic -p udp -m owner --uid-owner rtorrent -j ACCEPT

Kommen wir nun zum Forward-Traffic. Hier wird es ein wenig kniffeliger. Zum ein landet hier alles was geroutet wird, zum anderen muss man hier mit dem „in“ und „out“ aufpassen. Am einfachsten fällt die Unterscheidung nach Zielnetzen.

iptables -A FORWARD -o ppp0 -s 192.168.99.0/24 -j outgoing_traffic
iptables -A FORWARD -o eth1 -d 192.168.99.0/24 -j incoming_traffic
iptables -A FORWARD -j LOG --log-level 4 --log-prefix "Forward dropped by firewall: "

Die regeln sind schnell erklärrt: alles was an ppp0 gerouted wird und vom Netz 192.168.99.0/24 kommt wird der Chain “ outgoing_traffic“ unterworfen. Gleichlaufend wird alles was an eth1 geroutet wird und an das gleiche Netz gesendet wird der Chain „incoming_traffic“ unterworfen. Alles andere wird geloggt und gedropt.

In diesen beiden Listen passiert nun das „magic“. Clients die ins „Internet“ wollen müssen durch diese Regeln durch. Hier muss also am ende jeder Client aufgeführt sein der Accepted werden soll. Deshalb sollte man erstmal jeden Port droppen den man nicht geroutet wissen will. Das dient weniger dazu, dass der User die entsprechenden Programme nicht nutzt (ein umbiegen von Ports, beherrscht wohl jeder …) sondern soll eher verhindern, dass gewisse Windows-Clients unwissend ihre Existenz in Netz hinaus posaunen…

Danach muss man nun die Clients dynamisch in der Firewall freigeben. Das geht am besten über den DHCP-Deamon. Jeder Client muss sich eine IP holen, tut er es auf korrektem weg, wird er in der Firewall freigeschaltet, ansonsten bekommt er nicht gerouted. Das einzige Einfallstor sind dann noch IP/Mac spoofing. Das kann man aber bei kommen. Der dhcpd3-Daemon bietet für diese Zwecke die Möglichkeit ein Script auszuführen. Mittels folgendem Eintrag in der dhcpd.conf kann man Scripte anstoßen wenn ein Client ein Lease bekommt oder freigibt.

on commit {
  set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
  set ClientMac = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));

  log(concat("Commit: IP: ", ClientIP, " Mac: ", ClientMac));

  execute("/etc/firewall/add-client", ClientIP);
}

on release {
  set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
  set ClientMac = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));

  log(concat("release: IP: ", ClientIP, " Mac: ", ClientMac));

  execute("/etc/firewall/del-client", ClientIP);
}

on expiry {
  set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
  set ClientMac = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));

  log(concat("expire: IP: ", ClientIP, " Mac: ", ClientMac));

  execute("/etc/firewall/del-client", ClientIP);
}

Sobald man diesen Code in die /etc/dhcp3/dhcpd.conf einfügt, werden zwei Scripte ausgeführt. in diesem Fall del-client und add-client. Wichtig dabei ist, dass man in apparmor die Ausführung dieser Scripte von dhcpd zulässt. Entweder über das unsichere „ohne eigene Regeln ausführen“ oder man legt schnell eine eigene Regel an.

In den Scripts wird einfach nur folgendes gemacht:

#!/bin/bash
RULEOCCURE=iptables -L -n|grep -c $1
if [ $RULEOCCURE -eq 0 ]; then
        /usr/bin/sudo /sbin/iptables -A outgoing_traffic -s $1 -j ACCEPT
        /usr/bin/sudo /sbin/iptables -A incoming_traffic -d $1 -j ACCEPT
fi
iptables -D outgoing_traffic -s $1 -j ACCEPT
iptables -D incoming_traffic -s $1 -j ACCEPT

Das $1 wird zur Laufzeit durch die gewünschte IP ersetzt.

Kommen wir nun zum Logging, Eine Firewall die nicht überwacht wird bringt recht wenig. Deswegen sollte man mitloggen was passiert. Rudimentär kann man mittels des iptables-Befehl selber auswerten.

iptables -L -v -n

Durch diesen Befehl wird für jede FilterChain aufgelistet wie oft sie angewendet wurde. (Counter und Bytes) So kann man auch raus finden welcher Client wie viel Traffic erzeugt. Geht der Counter für die Drops jedoch extrem hoch sollte man mal schauen was da eigentlich passiert. Für dieses anliegen braucht man die LOG – Rule.

iptables -A FORWARD -j LOG --log-level 4 --log-prefix "Forward dropped by firewall: "

Man braucht kein Präfix, es empfiehlt sich aber. So kann man mittels rsyslog dafür sorgen, dass alle Meldungen der Firewall in eine Datei geloggt werden. Dazu bedarf eines eines Eintrag in der rsyslog-config.

#
# FIREWALL logging
#
:msg, contains, "firewall"      /var/log/firewall
:msg, contains, "firewall"      ~

Mittels eines Scriptes kann man nun alle fünf Minuten diese Statistik und die Logs auswerten und ggf Gegenmaßnahmen einleiten. Zb „vollautomatisch“ eine DROP-Regel für eine IP anlegen die eine flood-Attacke durchführt und gleichzeitig eine Warn-Mail absetzen.