IP-Sets überarbeitet
Es gibt bereits eine Antwort, in der IP-Sätze erwähnt werden. Es ist jedoch insofern eindimensional, als es sich auf die Leistungsverbesserungen gegenüber klassischen Regeln und die Tatsache konzentriert, dass IP-Sätze das Problem mit vielen individuellen IP-Adressen mildern, die in der CIDR-Notation nicht einfach als Subnetz ausgedrückt werden können.
Notation unten verwendet
Denn ipset
ich werde die Notation verwenden, die von gelesen ipset restore
und von geschrieben wurde ipset save
.
Entsprechend werde ich für iptables
(und ip6tables
) Regeln die Notation so verwenden, wie sie gelesen iptables-restore
und geschrieben wurde iptables-save
. Dies führt zu einer kürzeren Schreibweise und ermöglicht es mir, potenzielle Nur-IPv4- Regeln (mit Präfix -4
) oder Nur-IPv6- -6
Regeln (mit Präfix ) hervorzuheben .
In einigen Beispielen leiten wir den Paketfluss in eine andere Kette um. Es wird davon ausgegangen, dass die Kette zu diesem Zeitpunkt vorhanden ist, sodass die Linien zum Erstellen der Ketten nicht erstellt werden (auch der Tabellenname oder die COMMIT
am Ende angeführten Befehle werden nicht erwähnt ).
Erweiterte IP-Sätze
IP-Sets können viel mehr als in der anderen Antwort erwähnt, und Sie sollten auf jeden Fall die IP-Set-Dokumentation ( ipset(8)
) zusammen mit iptables-extensions(8)
diesem kurzen Eintrag hier lesen .
Zum Beispiel werde konzentriere ich mich hauptsächlich auf drei Satztypen: hash:ip
, hash:net
und list:set
, aber es gibt mehr als diejenigen , und sie alle gültigen Anwendungsfälle haben.
Sie können zum Beispiel auch Portnummern abgleichen, nicht nur IP-Adressen .
Speichern und Wiederherstellen von IP-Sets wie mit iptables-save
undiptables-restore
Sie können IP-Satzdeklarationen in großen Mengen erstellen und durch Weiterleiten in importieren ipset restore
. Wenn Sie Ihren Befehl widerstandsfähiger gegenüber bereits vorhandenen Einträgen machen möchten, verwenden Sie ipset -exist restore
.
Wenn sich Ihre Regeln in einer Datei mit dem Namen befinden default.set
, würden Sie Folgendes verwenden:
ipset -exist restore < default.set
Eine solche Datei kann Einträge zu create
Sets und zu add
Einträgen darin enthalten . Im Allgemeinen scheinen die meisten Befehle von der Befehlszeile eine entsprechende Version in den Dateien zu haben. Beispiel (Erstellen einer Reihe von DNS-Servern):
create dns4 hash:ip family inet
create dns6 hash:ip family inet6
# Google DNS servers
add dns4 8.8.8.8
add dns4 8.8.4.4
add dns6 2001:4860:4860::8888
add dns6 2001:4860:4860::8844
Hier wird ein Satz für IPv4 ( dns4
) und ein Satz für IPv6 ( dns6
) erstellt.
Zeitüberschreitungen bei IP-Sätzen
Zeitüberschreitungen in IP-Sätzen können sowohl pro Satz als auch pro Eintrag als Standard festgelegt werden. Dies ist sehr nützlich für Szenarien, in denen Sie jemanden vorübergehend blockieren möchten (z. B. zum Scannen von Ports oder zum Versuch, Ihren SSH-Server zu brachialisieren).
Dies funktioniert folgendermaßen (Standardeinstellung bei der Erstellung von IP-Sets):
create ssh_loggedon4 hash:ip family inet timeout 5400
create ssh_loggedon6 hash:ip family inet6 timeout 5400
create ssh_dynblock4 hash:ip family inet timeout 1800
create ssh_dynblock6 hash:ip family inet6 timeout 1800
Wir werden im Folgenden auf diese speziellen Sets zurückkommen und die Gründe dafür erläutern, warum sie so eingestellt sind, wie sie sind.
Wenn Sie Ihr Timeout für eine bestimmte IP-Adresse festlegen möchten, können Sie einfach sagen:
add ssh_dynblock4 1.2.3.4 timeout 7200
So blockieren Sie IP 1.2.3.4 für zwei Stunden anstelle der (festgelegten) Standard-halben Stunde.
Wenn Sie sich das ipset save ssh_dynblock4
nach kurzer Zeit ansehen würden, würden Sie etwas in etwa so sehen:
create ssh_dynblock4 hash:ip family inet hashsize 1024 maxelem 65536 timeout 1800
add ssh_dynblock4 1.2.3.4 timeout 6954
Timeout-Vorbehalte
- Zeitüberschreitungen sind eine Funktion in jedem Satz. Wenn sich das Gerät nicht wurde erstellt mit Timeout - Unterstützung wird ein Fehler (zB erhalten
Kernel error received: Unknown error -1
).
- Zeitüberschreitungen werden in Sekunden angegeben. Verwenden Sie Bash-arithmetische Ausdrücke, um beispielsweise von Minuten zu Sekunden zu gelangen. Z.B:
sudo ipset add ssh_dynblock4 1.2.3.4 timeout $((120*60))
Überprüfen, ob ein Eintrag in einem bestimmten IP-Satz vorhanden ist
In Ihren Skripten kann es hilfreich sein, zu überprüfen, ob bereits ein Eintrag vorhanden ist. Dies kann erreicht werden ipset test
, indem null zurückgegeben wird, wenn der Eintrag vorhanden ist, andernfalls ungleich null. So können die üblichen Prüfungen in einem Skript angewendet werden:
if ipset test dns4 8.8.8.8; then
echo "Google DNS is in the set"
fi
In vielen Fällen möchten Sie jedoch lieber den -exist
Schalter verwenden, ipset
um ihn anzuweisen, sich nicht über vorhandene Einträge zu beschweren.
IP-Sätze aus iptables
Regeln auffüllen
Dies ist meiner Meinung nach eine der Killerfunktionen von IP-Sets. Sie können nicht nur mit den Einträgen eines IP-Sets abgleichen, sondern auch einem vorhandenen IP-Set neue Einträge hinzufügen.
Zum Beispiel haben Sie in dieser Antwort auf diese Frage:
-A INPUT -p tcp -i eth0 -m state --state NEW --dport 22 -m recent --update --seconds 15 -j DROP
-A INPUT -p tcp -i eth0 -m state --state NEW --dport 22 -m recent --set -j ACCEPT
... mit der Absicht, Verbindungsversuche zu SSH (TCP-Port 22) einzuschränken. Das verwendete Modul recent
protokolliert die letzten Verbindungsversuche. Anstelle des state
Moduls bevorzuge ich jedoch das conntrack
Modul.
# Say on your input chain of the filter table you have
-A INPUT -i eth+ -p tcp --dport ssh -j SSH
# Then inside the SSH chain you can
# 1. create an entry in the recent list on new connections
-A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
# 2. check whether 3 connection attempts were made within 2 minutes
# and if so add or update an entry in the ssh_dynblock4 IP set
-4 -A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock4 src --exist
-6 -A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock6 src --exist
# 3. last but not least reject the packets if the source IP is in our
# IP set
-4 -A SSH -m set --match-set ssh_dynblock4 src -j REJECT
-6 -A SSH -m set --match-set ssh_dynblock6 src -j REJECT
In diesem Fall leite ich den Fluss in die SSH
Kette um, sodass ich mich nicht -p tcp --dport ssh
für jede einzelne Regel wiederholen muss .
Wiederholen:
-m set
macht darauf iptables
aufmerksam, dass wir Switches des set
Moduls verwenden (das IP-Sets verarbeitet)
--match-set ssh_dynblock4 src
Weist iptables
an, die source ( src
) -Adresse mit der benannten set ( ssh_dynblock4
)
-Adresse abzugleichen
- dies entspricht
sudo ipset test ssh_dynblock4 $IP
(wo $IP
enthält die Quell-IP-Adresse für das Paket)
-j SET --add-set ssh_dynblock4 src --exist
Fügt die source ( src
) -Adresse aus dem Paket zur IP-Gruppe hinzu oder aktualisiert sie ssh_dynblock4
. Wenn ein Eintrag existiert ( --exist
), wird er einfach aktualisiert.
- dies entspricht
sudo ipset -exist add ssh_dynblock4 $IP
(wo $IP
enthält die Quell-IP-Adresse für das Paket)
Wenn Sie stattdessen die Ziel- / Zieladresse abgleichen möchten, verwenden Sie dst
statt src
. Weitere Optionen finden Sie im Handbuch.
Sätze von Sätzen
IP-Sets können andere Sets enthalten. Wenn Sie dem Artikel bis hierher gefolgt sind, haben Sie sich gefragt, ob es möglich ist, Sets zu kombinieren. Und natürlich ist es das auch. Für die IP - Sets von oben wir zwei Gelenksätze erstellen können ssh_dynblock
und ssh_loggedon
jeweils die enthalten nur IPv4-und IPv6-Sets:
create ssh_loggedon4 hash:ip family inet timeout 5400
create ssh_loggedon6 hash:ip family inet6 timeout 5400
create ssh_dynblock4 hash:ip family inet timeout 1800
create ssh_dynblock6 hash:ip family inet6 timeout 1800
# Sets of sets
create ssh_loggedon list:set
create ssh_dynblock list:set
# Populate the sets of sets
add ssh_loggedon ssh_loggedon4
add ssh_loggedon ssh_loggedon6
add ssh_dynblock ssh_dynblock4
add ssh_dynblock ssh_dynblock6
Und die nächste Frage , die in Ihrem Kopf auftauchen sollten , ist , ob dies uns erlaubt zu passen und und IP - Sets in einer IP - Version unabhängige Art und Weise zu manipulieren.
Und die Antwort darauf lautet: JA! (Leider wurde dies nicht explizit dokumentiert, als ich das letzte Mal nachgesehen habe)
Infolgedessen können die Regeln aus dem vorherigen Abschnitt folgendermaßen umgeschrieben werden:
-A INPUT -i eth+ -p tcp --dport ssh -j SSH
-A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
-A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock src --exist
-A SSH -m set --match-set ssh_dynblock src -j REJECT
Das ist viel prägnanter. Und ja, das ist bewährt und wirkt wie ein Zauber.
Alles zusammen: SSH-Brute-Force-Verteidigung
Auf meinen Servern wird ein Skript als cron
Job ausgeführt, der eine Reihe von Hostnamen aufnimmt, diese in IP-Adressen auflöst und dann in die IP-Gruppe für "vertrauenswürdige Hosts" eingibt. Die Idee ist, dass vertrauenswürdige Hosts mehr Versuche erhalten, sich am Server anzumelden, und nicht unbedingt so lange gesperrt werden, wie andere.
Umgekehrt haben ganze Länder die Verbindung zu meinem SSH-Server gesperrt, mit der (potenziellen) Ausnahme von vertrauenswürdigen Hosts (dh die Reihenfolge der Regeln ist von Bedeutung).
Dies ist jedoch eine Übung für den Leser. Hier möchte ich eine saubere Lösung hinzufügen, die die im Set enthaltenen Sätze verwendet, ssh_loggedon
um zu ermöglichen, dass nachfolgende Verbindungsversuche weitergeleitet werden und nicht der gleichen Prüfung unterliegen wie die anderen Pakete.
Es ist wichtig, die Standard-Zeitüberschreitungen von 90 Minuten für ssh_loggedon
und 30 Minuten für ssh_dynblock
die folgenden iptables
Regeln zu berücksichtigen:
-A INPUT -i eth+ -p tcp --dport ssh -j SSH
-A SSH -m set --match-set ssh_loggedon src -j ACCEPT
-A SSH -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
-A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock src --exist
-A SSH -m set --match-set ssh_dynblock src -j REJECT
Inzwischen sollten Sie sich fragen, wie die IP-Adresse der Verbindung in die ssh_loggedon
Untergruppen gelangt. Also lesen Sie weiter ...
Bonus: Hinzufügen der IP, mit der Sie sich bei der SSH-Anmeldung anmelden
Wenn Sie mit sshrc
Freunden experimentiert haben , haben Sie von seinen Mängeln erfahren. Aber PAM kommt zur Rettung. Ein Modul namens pam_exec.so
ermöglicht es uns, ein Skript während der SSH-Anmeldung an einem Punkt aufzurufen, an dem wir wissen, dass der Benutzer zugelassen ist.
Fügen Sie /etc/pam.d/sshd
unter den Einträgen pam_env
und pam_selinux
die folgende Zeile hinzu:
session optional pam_exec.so stdout /path/to/your/script
und stellen Sie sicher, dass Ihre Version des Skripts ( /path/to/your/script
oben) vorhanden und ausführbar ist.
PAM verwendet Umgebungsvariablen, um zu kommunizieren, was gerade vor sich geht, sodass Sie ein einfaches Skript wie das folgende verwenden können:
#!/bin/bash
# When called via pam_exec.so ...
SETNAME=ssh_loggedon
if [[ "$PAM_TYPE" == "open_session" ]] && [[ -n "$PAM_RHOST" ]]; then
[[ "x$PAM_RHOST" != "x${PAM_RHOST//:/}" ]] && SETNAME="${SETNAME}6" || SETNAME="${SETNAME}4"
ipset -exist add $SETNAME "$PAM_RHOST"
fi
Leider ipset
scheint das Dienstprogramm nicht die eingebauten Smarts von Netfilter zu haben. Daher müssen wir beim Hinzufügen unseres Eintrags zwischen IPv4- und IPv6-IP-Sätzen unterscheiden. Andernfalls ipset
wird davon ausgegangen, dass wir dem Satz von Sätzen anstelle der IP einen weiteren Satz hinzufügen möchten . Und natürlich ist es unwahrscheinlich, dass ein Set nach einer IP benannt wird :)
In diesem Fall überprüfen wir :
die IP-Adresse und hängen sie 6
an den festgelegten Namen an 4
.
Das Ende.