nftables soweit fertig

This commit is contained in:
Alfred Krohmer 2014-03-12 01:30:35 +01:00
parent 86698f08c1
commit b055f49ec2
8 changed files with 106 additions and 11 deletions

View File

@ -0,0 +1,36 @@
\documentclass{standalone}
\usepackage[utf8]{inputenc}
\usepackage{pgfplots}
\pgfplotsset{compat=1.3}
\usepgfplotslibrary{units}
\usepgfplotslibrary{dateplot}
\usepackage{tikz}
\usepackage{pgfplotstable}
\usepackage{csquotes}
\usepackage{float}
\begin{document}
\pgfplotstableread{nft/ipt-nft-simple-drop.txt}{\nftsimpledrop}
\begin{tikzpicture}
\begin{axis}[
yshift=-0.5cm,
xtick={5000,10000,15000,20000,25000,30000},
xmin=200, xmax=30000,
ymin=0, ymode=log,
xlabel={Anzahl der Firewall-Regeln}, ylabel={Pakete pro Sekunde},
tick label style={/pgf/number format/fixed,/pgf/number format/1000 sep = \thinspace},
width=15cm, height=10cm,
legend entries={iptables,nftables},
scaled ticks=false
]
\addplot[smooth,blue] table [x={rules}, y={iptpps}] {\nftsimpledrop};
\addplot[smooth,red] table [x={rules}, y={nftpps}] {\nftsimpledrop};
\end{axis}
\end{tikzpicture}
\end{document}

View File

@ -20,7 +20,7 @@
yshift=-0.5cm,
xtick={5000,10000,15000,20000,25000,30000},
xmin=200, xmax=30000,
ymin=0, ymode=log,
ymin=0,
xlabel={Anzahl der Firewall-Regeln}, ylabel={Pakete pro Sekunde},
tick label style={/pgf/number format/fixed,/pgf/number format/1000 sep = \thinspace},
width=15cm, height=10cm,

View File

@ -0,0 +1,7 @@
\subsubsection{Schlussfolgerung / Fazit}
\texttt{nftables} schneidet bezüglich Datendurchsatz bei einer mittleren Regelanzahl bis maximal 25 000 Regeln noch deutlich schlechter als \texttt{iptables} ab. Mehr als diese 25 000 Regeln werden in der Praxis eher selten benötigt. Um z.B. mehrere IP-Adressen gleichzeitig zu blockieren könnte man diese in ein \texttt{ipset} (\texttt{iptables}) oder in eine »(un)named set« (\texttt{nftables}) speichern und bräuchte dann nur noch eine Regel, um alle Pakete von diesen Adressen zu verwerfen.
Zwar schneidet \texttt{iptables} bezüglich der Antwortzeit bei einer sehr großen Regelanzahl schlechter ab als \texttt{nftables}, dies hält sich mit ca. 12 ms bei 100 000 Regeln aber dennoch in Grenzen.
Dieser Performance-Aspekt und die Tatsache, dass es bisher keine vollständige Dokumentation zu \texttt{nftables} gibt, führt zur Schlussfolgerung, dass \texttt{nftables} bisher \textbf{noch nicht} für den Produktiveinsatz bereit ist und es noch einige Zeit dauern wird, bis es \texttt{iptables} komplett ersetzen kann.

View File

@ -0,0 +1,15 @@
\subsubsection{Dokumentation}
Da \texttt{iptables} schon recht lange in Verwendung ist, gibt es hierzu ein breites Spektrum an Dokumentation, Tutorials, usw. \\
Bei \texttt{nftables} sieht es hingegen eher rar aus. Bis vor einigen Wochen gab es als einzige Dokumentation ein Tutorial:
\begin{itemize}
\item \url{https://home.regit.org/netfilter-en/nftables-quick-howto/}
\end{itemize}
Inzwischen gibt es ein Wiki, das die verschiedenen Regeltypen usw. besser kategorisiert:
\begin{itemize}
\item \url{http://wiki.nftables.org/wiki-nftables}
\end{itemize}
Abgesehen von diesen beiden Quellen gibt es bisher jedoch kaum weitere auffindbare Dokumentation, eine komplette Referenz zu allen möglichen Befehlen und Regeln fehlt z.B. noch.

View File

@ -15,6 +15,10 @@ Der Firewall-Rechner soll nun über \texttt{iptables} und \texttt{nftables} den
Zur Überwachung der Messdaten (Datenrate und PPS) habe ich auf allen vier Netzwerkschnittstellen das Tool \texttt{ifpps} aus dem \texttt{netsniff-ng}-Bundle laufen lassen.
\paragraph{Testablauf} Die Benchmarks habe ich einigen Bash- und Python-Scripts realisiert. Vor Beginn jedes Tests loggt sich der steuernde Rechner mit SSH auf der Firewall ein (über das äußere Konfigurationsnetzwerk) und generiert dort mit einem Python-Script die entsprechende Anzahl an Regeln für das jeweilige Firewall-System. Anschließend werden die Regeln mit dem entsprechenden Tool angewendet.
Danach wird der Paketgenerator \texttt{pktgen} beim Sender gestartet und zunächst zwei Sekunden laufen gelassen. Anschließend werden per SSH über \texttt{ifpps} die Netzwerkauslastung und mit \texttt{top} die Prozessorauslastung bzw. mit \texttt{ping} die Antwortzeit gemessen.
\pagebreak
\paragraph{Testeinstellung} Zunächst habe ich die Datenrate und die PPS in Abhängigkeit der Ethernet Frame-Größe gemessen um festzustellen, mit welcher Paketgröße die Messungen am besten durchzuführen sind. \\ \\
@ -33,7 +37,7 @@ Wie aus den Diagrammen ersichtlich wird, wird bei Frame-Größen, die an die MTU
Beim Sender beginnt jedoch bei Frame-Größe kleiner 300 Byte die Datenrate linear zu sinken. Die PPS stagnieren dann bei ca. 450000 Paketen. Dies ist mit der Leistungsfähigkeit der Atom-Prozessoren zu erklären, da diese ab dieser Grenze komplett ausgelastet sind und die Pakete nicht schneller erzeugen können. \\
Beim Empfänger geschieht dieser Einbrauch schon bei ca. 450 Byte Frame-Größe. Hier stagniert die PPS bei ca. 250000. Auch dies lässt sich mit der Auslastung des Prozessors erklären. Der Unterschied zum Sender besteht darin, dass bei diesem die Pakete lediglich statisch zusammengesetzt und losgeschickt werden müssen. Beim Empfänger hingegen muss das Betriebssystem die Pakete zunächst auswerten, was den Bearbeitungszeit verlängert. Dadurch kommen beim Empfänger wesentlich weniger Pakete an, als der Sender losschickt. (Die übrigen Pakete werden gedroppt.) \\
Beim Empfänger geschieht dieser Einbrauch schon bei ca. 450 Byte Frame-Größe. Hier stagniert die PPS bei ca. 250000. Auch dies lässt sich mit der Auslastung des Prozessors erklären. Der Unterschied zum Sender besteht darin, dass bei diesem die Pakete lediglich statisch zusammengesetzt und losgeschickt werden müssen. Beim Empfänger hingegen muss das Betriebssystem die Pakete zunächst auswerten, was den Bearbeitungszeit verlängert. Dadurch kommen beim Empfänger wesentlich weniger Pakete an, als der Sender losschickt. Die übrigen Pakete können nicht länger im Ringpuffer der Netzwerkkarte gespeichert werden und über PAUSE-Frames des Ethernet-Protokolls wird der Firewall mitgeteilt, weniger Pakete zu senden (mit \texttt{tcpdump} zu messen). \\
Um eine größtmögliche Auslastung des Gigabit-Netzwerkes zu ermöglichen und gleichzeitig die maximale Anzahl an PPS zu erhalten, habe ich also für die folgenden Benchmarks die Ethernet Frame-Größe auf 450 festgesetzt.
@ -43,8 +47,28 @@ Letztendlich habe ich dazu entschieden, auf der Empfängerseite mit \texttt{ipta
\pagebreak
\paragraph{Einfache Drop-Regeln} Für den nachfolgenden Benchmark habe ich entsprechend viele Regeln generieren lassen, die besagen, dass von (zufällig gewählten) IP-Adressen alle Pakete gedropt werden sollen. \\
\paragraph{Paketdurchsatz} Für den nachfolgenden Benchmark habe ich entsprechend viele Regeln generieren lassen, die besagen, dass von (zufällig gewählten) IP-Adressen alle Pakete gedropt werden sollen. \\
\includegraphics{benchmarks/nft-ipt-drop.pdf}
Die Datenrate und die Anzahl an PPS liegen initial bei den bereits zuvor gemessenen ca. 980 MBit/s und 250000 Paketen pro Sekunde. Jedoch bricht die Leistungsfähigkeit bei beiden Firewall-Lösungen schnell erheblich ein. So kann \texttt{iptables} bei 5000 Regeln nur noch ca. 100 MBit/s bei 28000 Paketen verarbeiten, \texttt{nftables} schafft hier nur ca. 50 MBit/s bei 14000 Paketen.
Zur besseren Anschaulichkeit auch in logarithmischer Ordinatenskalierung: \\
\includegraphics{benchmarks/nft-ipt-drop-log.pdf}
\pagebreak
Die Datenrate und die Anzahl an PPS liegen initial bei den bereits zuvor gemessenen ca. 980 MBit/s und 250000 Paketen pro Sekunde. Jedoch bricht die Leistungsfähigkeit bei beiden Firewall-Lösungen schnell erheblich ein. So kann \texttt{iptables} bei 5000 Regeln nur noch ca. 100 MBit/s bei 28000 Paketen verarbeiten, \texttt{nftables} schafft hier nur ca. 50 MBit/s bei 14000 Paketen. Im weiteren Verlauf wird jedoch in der logarithmischen Skalierung sichtbar, dass \texttt{nftables} bei mehr als ca. 25000 Regeln beginnt besser zu skalieren.
\paragraph{Antwortzeit} Unter Verwendung der vorherigen Regeln habe ich nun die Antwortzeit gemessen. \\
\includegraphics{benchmarks/nft-ipt-drop-response.pdf}
In diesem Diagramm erkennt man, dass hier \texttt{nftables}, im Gegensatz zum Paketdurchsatz, bei der Antwortzeit bei Skalierung auf sehr viele Firewall-Regeln besser abschneidet als \texttt{iptables}. Bis ca. 20000 Regeln reagieren beide Firewall-Lösungen in etwa gleich schnell, danach entwickeln sich die Reaktionszeiten linear auseinander. Bei 100000 Regeln hat hier \texttt{nftables} einen Vorteil von ca. 4 ms.
\paragraph{Weitere Regeln} Ursprünglich wollte ich für weitere Benchmarks noch andere Regeltypen verwenden. Dies scheiterte jedoch grundsätzlich an zwei Punkten:
\begin{itemize}
\item \texttt{nftables} ist bisher sehr schlecht dokumentiert. Es ist mir bspw. nicht gelungen eine Möglichkeit zu finden, um komplette Pakete (nicht nur den Header) nach bestimmten Strings zu durchsuchen. Während dies auf Kernel-Seite durch die virtuelle Maschine eigentlich ohne Weiteres möglich sein sollte, ist diese Möglichkeit im Userspace-Tool \texttt{nft} offenbar noch nicht enthalten (oder nirgends dokumentiert).
\item NAT-Regeln (oder generell modifizierende Regeltypen) lassen sich nicht ohne weiteres mit der Skalierung auf eine große Anzahl von Regeln benchmarken, da die Paketbearbeitung nach der ersten, den Filterkriterien entsprechenden Regel abbricht.
\end{itemize}
\pagebreak

View File

@ -1,5 +1,5 @@
\subsubsection{Verfügbare Tools}
Da \texttt{nftables} erst vor kurzem Einzug in den Linux-Kernel gehalten hat, gibt es in noch keiner Linux-Distribution entsprechende Tools für die Konfiguration der neuen Firewall-Lösung. Man hat also nur die Möglichkeit, sich die Userland-Programme selbst zu kompilieren. (Bei Arch Linux waren diese bereits im AUR vorhanden, nicht jedoch im offizielen Repository.)
Für \texttt{iptables} heißt das primäre Tool wie die Firewall selbst: \texttt{iptables}. Inzwischen gibt es für \texttt{iptables} noch weitere Tools, die auf einer höheren Abstraktionsebene arbeiten, z.B. \texttt{ufw} (uncomplicated firewall) oder diverse Frontends von distributionsspezifischen Systemkonfigurationstools. Zumindest \texttt{iptables} selbst ist in fast jeder Linux Distribution enthalten, \texttt{ufw} und Konsorten sind oft ebenfalls verfügbar.
Das zentrale Tool zur Konfiguration stellt hier \texttt{nft} dar. Die Syntax wurde im vorherigen Abschnitt bereits demonstriert.
Da \texttt{nftables} erst vor kurzem Einzug in den Linux-Kernel gehalten hat, gibt es in kaum einer Linux-Distribution entsprechende Tools für die Konfiguration der neuen Firewall-Lösung. Man hat also fast nur die Möglichkeit, sich das Userland-Programm \texttt{nft} selbst zu kompilieren. Bei Arch Linux bspw. gibt es ein Paket aus dem AUR, seit einigen Tagen aber auch im Community-Repository.

View File

@ -21,21 +21,32 @@ Wie man sieht mussten sich Linux-Nutzer, die eine Firewall benötigten, seit 199
\item \textbf{\texttt{ebtables}} für Ethernet Bridging
\end{itemize}
Alle diese Tools haben im Linux-Kernel eine Entsprechung in der Netfilter-Infrastruktur. An dieser Stelle sind für jedes Protokoll alle möglichen Filterkriterien und -aktionen einzeln im Kernel (meist als Modul ladbar) implementiert. Da diese Implementierungen alle sehr protokollspezifisch sind, sorgte dies dafür, dass enorm viel Code für jedes Protokoll repliziert werden musste.
Alle diese Tools haben im Linux-Kernel eine Entsprechung in der Netfilter-Infrastruktur. An dieser Stelle sind für jedes Protokoll alle möglichen Filterkriterien und -aktionen einzeln im Kernel (meist als Modul ladbar) implementiert, der Code kann auf jedes einzelne Feld eines Pakets über eine Datenstruktur zugreifen. Da diese Implementierungen alle sehr protokollspezifisch sind, musste zunächst der Code für alle Protokolle repliziert werden und wurde dann getrennt erweitert und gewartet.
\paragraph{\texttt{nftables}} soll alle Implementierungen für die verschiedenen Protokolle zusammenfassen und damit die Schnittstelle der Konfigurationstools zum Kernel vereinfachen, die Code-Replikation verringern und ein effizienteres Abarbeiten der Regeln gewährleisten.
Dies wird durch den Einsatz einer kleinen virtuellen Maschine im Kernel gewährleistet. Im Gegensatz zu \texttt{iptables}, wo für jedes Filterkriterium einer Regel eine eigene Funktion im Kernel aufgerufen wurde, wird nun das zu verarbeitende Paket geladen und die zu Byte-Code kompilierten Regeln in der virtuellen Maschine angewendet. Dieser Code kann nun einzelne Felder und Bits eines Pakets betrachten und vergleichen, arithmetische Operationen darauf ausführen und das Paket ändern.
Dies wird durch den Einsatz einer kleinen virtuellen Maschine im Kernel gewährleistet. Im Gegensatz zu \texttt{iptables}, wo für jedes Filterkriterium einer Regel eine eigene Funktion im Kernel aufgerufen wurde, wo also der Kernel jedes Protokoll mit allen Einzelheiten kennt, wird nun stattdessen das zu verarbeitende Paket geladen und bestimmter Byte-Code in der virtuellen Maschine angewendet. Dieser Byte-Code entsteht, indem die Firewall-Regeln noch im Userspace geparst, kompiliert und über die Netlink-Schnittstelle in den Kernel geladen werden. Dieser Code kann nun Operationen auf einzelnen Felder und Bits eines Pakets ausführen:
\begin{itemize}
\item betrachten und vergleichen: IP-Adressen können z.B. direkt mit einer festgelegten Adresse verglichen werden
\item arithmetische Operationen darauf ausführen: bestimmte Bereiche von IP-Adressen können maskiert werden, um bspw. nur zu untersuchen, ob das Paket aus einem bestimmten Subnetz kommt
\item das Paket ändern: DNAT (Destination Network Address Translation) und SNAT (Source NAT) um Ziel- oder Absenderaddressen und -ports zu manipulieren
\end{itemize}
Darüber hinaus soll nun ein atomares Austauschen der Firewall-Regeln in einer einzelnen Netlink-Transaktion möglich sein. (Netlink ist die Schnittstelle zum Kernel, mit der die Netfilter-Infrastruktur angesprochen wird; dies ist die bisher schnellste Schnittstelle zum Kernel, besonders wenn große Mengen an Daten übertragen werden sollen) Dies funktioniert bisher allerdings noch nicht effektiv, weil das Userspace-Tool \texttt{nft} noch keine Möglichkeit bietet, alle bereits existenten Regeln zu entfernen und gleichzeitig die aktualisierte Fassung der Regeln zu laden. (Man müsste hier für jede Regel-Aktualisierung, die man durchführen will, ein eigenes nft-Script schreiben, dass zunächst die alte Regel entfernt und eine neue hinzufügt. Diese Aktualisierung ist dann atomar.)
Darüber hinaus soll nun ein atomares Austauschen der Firewall-Regeln in einer einzelnen Netlink-Transaktion möglich sein. (Netlink ist die Schnittstelle zum Kernel, mit der die Netfilter-Infrastruktur angesprochen wird; dies ist die bisher schnellste Schnittstelle zum Kernel, besonders wenn große Mengen an Daten übertragen werden sollen) Dies funktioniert bisher allerdings noch nicht effektiv, weil das Userspace-Tool \texttt{nft} noch keine Möglichkeit bietet, alle bereits existenten Regeln zu entfernen und gleichzeitig die aktualisierte Fassung der Regeln zu laden. Zur Lösung dieses Problems gäbe es zunächst zwei Möglichkeiten:
\begin{itemize}
\item Man könnte hier für jede Regel-Aktualisierung, die man durchführen will, ein eigenes nft-Script schreiben, dass zunächst die alte Regeln entfernt und einen neuen Regelsatz hinzufügt. (Eine Art Migrations-Skript.) Diese Aktualisierung ist dann atomar. Dies könnte auch automatisiert werden.
\item Man könnte zunächst in einem ersten Skript alle Regeln entfernen und noch in der gleichen Transaktion eine einzige Regel hinzufügen, die sämtlichen Datenverkehr verbietet. In einem zweiten Skript könnte man dann innerhalb einer zweiten Transaktion diese Regel wieder entfernen und gleichzeitig den gewünschten neuen Regelsatz einfügen. Auch hier hätte man dann kein Zeitfenster mehr, indem das System »offen« ist. Es besteht aber die Gefahr, dass man hier einen Fehler in den neuen Regeln hat, so dass diese gar nicht angewendet werden und man sich somit aus dem System aussperren könnte.
\end{itemize}
\subsubsection{table, chain, hook}
Während \texttt{iptables} noch vordefinierte Tabellen (\texttt{filter}, \texttt{nat}, \texttt{mangle}, etc.) und mehrere Chains (\texttt{PREROUTING}, \texttt{INPUT}, \texttt{FORWARD}, \texttt{OUTPUT}, \texttt{POSTROUTING}) in diesen Tabellen hatte, gibt es in \texttt{nftables} keine vorgefertigen Tabellen und Chains mehr. Stattdessen gibt es sogenannte Hooks, in die man sich in selbst erstellten Chains »einhängen« kann. Dabei kann man eine Priorität angeben, die die Reihenfolge der Abarbeitung der Chains angibt. Diese Hooks entsprechen dabei größtenteils den Tabellen und Chains aus \texttt{iptables}.
Während \texttt{iptables} noch vordefinierte Tabellen (\texttt{filter}, \texttt{nat}, \texttt{mangle}) und mehrere Chains (\texttt{PREROUTING}, \texttt{INPUT}, \texttt{FORWARD}, \texttt{OUTPUT}, \texttt{POSTROUTING}) in diesen Tabellen hatte, gibt es in \texttt{nftables} keine vorgefertigen Tabellen und Chains mehr. Stattdessen gibt es sogenannte Hooks, in die man sich in selbst erstellten Chains »einhängen« kann. Dabei kann man eine Priorität angeben, die die Reihenfolge der Abarbeitung der Chains angibt. Diese Hooks entsprechen dabei größtenteils den Tabellen und Chains aus \texttt{iptables}.
\pagebreak
\subsubsection{Syntax}
Für \texttt{iptables} könnte man bspw. folgenden Aufruf verwende, um eingehende TCP-Pakete auf Port 22 zunächst zu loggen und anschließend zu verwerfen: \\
Für \texttt{iptables} könnte man bspw. folgenden Aufruf verwenden, um eingehende TCP-Pakete auf Port 22 zunächst zu loggen und anschließend zu verwerfen: \\
\shellcmd{iptables -A INPUT -p tcp ---dport 22 -j LOG} \\
\shellcmd{iptables -A INPUT -p tcp ---dport 22 -j DROP} \\

View File

@ -3,3 +3,5 @@
\input{nftables/nftables-vs}
\input{nftables/nftables-tools}
\input{nftables/nftables-perf}
\input{nftables/nftables-doc}
\input{nftables/nftables-conc}