Einführung
Das Bereitstellen von diskreten Komponenten in Ihrer Anwendungsarchitektur auf verschiedenen Knoten ist eine gängige Methode, um die Last zu verringern und eine horizontale Skalierung zu ermöglichen. Ein typisches Beispiel ist die Konfiguration einer Datenbank auf einem separaten Server von Ihrer Anwendung. Obwohl es eine Reihe von Vorteilen mit dieser Konfiguration gibt, birgt die Verbindung über ein Netzwerk eine neue Reihe von Sicherheitsbedenken.
In diesem Leitfaden zeigen wir Ihnen, wie Sie auf jedem Ihrer Server eine Firewall einrichten können, wenn Sie eine verteilte Architektur verwenden. Wir werden unsere Richtlinie so konfigurieren, dass der beabsichtigte Datenverkehr zwischen unseren Komponenten zugelassen wird, während anderer Datenverkehr abgelehnt wird.
Sie können auch DigitaleOzean’s Cloud Firewalls konfigurieren, die als zusätzliche externe Schicht auf den DigitalOcean-Servern ausgeführt werden. Auf diese Weise müssen Sie keine Firewall auf Ihren eigenen Servern konfigurieren.
Für die Demonstration in diesem Leitfaden verwenden wir zwei Ubuntu 22.04-Server. Auf einem Server wird eine Webanwendung mit Nginx bereitgestellt und der andere wird die MySQL-Datenbank für die Anwendung hosten. Obwohl wir dieses Setup als Beispiel verwenden werden, sollten Sie in der Lage sein, die verwendeten Techniken auf Ihre eigenen Serveranforderungen zu übertragen.
Voraussetzungen
Um loszulegen, benötigen Sie zwei frische Ubuntu 22.04-Server. Fügen Sie auf jedem einen regulären Benutzer mit sudo
-Berechtigungen hinzu. Folgen Sie dazu unserer Anleitung zur anfänglichen Einrichtung des Ubuntu 22.04-Servers.
Die Anwendungseinrichtung, die wir absichern werden, basiert auf dieser Anleitung. Wenn Sie dem Beispiel folgen möchten, richten Sie Ihre Anwendungs- und Datenbankserver gemäß dieser Anleitung ein. Andernfalls können Sie diesen Artikel als allgemeine Referenz verwenden.
Schritt 1 – Einrichten einer Firewall
Sie beginnen mit der Implementierung einer Grundkonfiguration der Firewall für jeden Ihrer Server. Die Richtlinie, die wir implementieren werden, folgt dem Sicherheitsprinzip „Sicherheit zuerst“. Wir werden fast alles außer dem SSH-Verkehr einschränken und dann Löcher in die Firewall für unsere spezifische Anwendung bohren.
In dieser Anleitung wird die Syntax von iptables
verwendet. iptables
ist auf Ubuntu 22.04 automatisch mit einem nftables
-Backend installiert, daher müssen Sie keine zusätzlichen Pakete installieren.
Öffnen Sie mit nano
oder Ihrem bevorzugten Texteditor die Datei /etc/iptables/rules.v4
:
- sudo nano /etc/iptables/rules.v4
Fügen Sie die Konfiguration aus der Vorlagendatei für die Firewall ein:
*filter
# Standardmäßig alle ausgehenden Pakete erlauben, eingehende und weitergeleitete Pakete jedoch blockieren
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
# Benutzerdefinierte pro-Protokoll-Ketten
:UDP - [0:0]
:TCP - [0:0]
:ICMP - [0:0]
# Akzeptierter UDP-Verkehr
# Akzeptierter TCP-Verkehr
-A TCP -p tcp --dport 22 -j ACCEPT
# Akzeptierter ICMP-Verkehr
# Grundlegende Akzeptanzrichtlinie
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -i lo -j ACCEPT
# Ungültige Pakete verwerfen
-A INPUT -m conntrack --ctstate INVALID -j DROP
# Datenverkehr an protokollspezifische Ketten weiterleiten
## Nur neue Verbindungen zulassen (etablierte und verwandte Verbindungen sollten bereits behandelt werden)
## Für TCP zusätzlich nur neue SYN-Pakete zulassen, da dies die einzige gültige
## Methode ist, um eine neue TCP-Verbindung herzustellen
-A INPUT -p udp -m conntrack --ctstate NEW -j UDP
-A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP
-A INPUT -p icmp -m conntrack --ctstate NEW -j ICMP
# Alles ablehnen, was bis zu diesem Punkt durchgefallen ist
## Versuchen, protokollspezifisch mit Ablehnungsnachricht zu sein
-A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
-A INPUT -p tcp -j REJECT --reject-with tcp-reset
-A INPUT -j REJECT --reject-with icmp-proto-unreachable
# Änderungen übernehmen
COMMIT
*raw
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT
*security
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT
Datei speichern und schließen. Wenn Sie nano
verwenden, drücken Sie Strg+X
, um zu beenden, dann, wenn Sie dazu aufgefordert werden, J
und dann Enter drücken.
Wenn Sie dies in einer Live-Umgebung implementieren, laden Sie Ihre Firewall-Regeln noch nicht neu. Das Laden des hier beschriebenen Regelwerks führt sofort zur Trennung der Verbindung zwischen Ihrer Anwendungs- und Datenbankserver. Sie müssen die Regeln an unsere betrieblichen Anforderungen anpassen, bevor Sie neu laden.
Schritt 2 – Ermitteln Sie die von Ihren Diensten verwendeten Ports
Um eine Kommunikation zwischen Ihren Komponenten zu ermöglichen, müssen Sie die verwendeten Netzwerkports kennen. Sie könnten die richtigen Netzwerkports finden, indem Sie Ihre Konfigurationsdateien überprüfen, aber eine anwendungsunabhängige Methode, um die richtigen Ports zu finden, besteht darin, zu überprüfen, welche Dienste auf jeder unserer Maschinen auf Verbindungen warten.
Sie können das Tool netstat
verwenden, um dies herauszufinden. Da Ihre Anwendung nur über IPv4 kommuniziert, werden wir das Argument -4
hinzufügen, aber Sie können es entfernen, wenn Sie auch IPv6 verwenden. Die anderen Argumente, die Sie für die Suche nach Ihren laufenden Diensten benötigen, sind -p
, -l
, -u
, -n
und -t
, die Sie als -plunt
angeben können.
Diese Argumente können wie folgt aufgeschlüsselt werden:
p
: Show the PID and name of the program to which each socket belongs.l
: Show only listening sockets.u
: Show UDP traffic.n
: Show numeric output instead of service names.t
: Show TCP traffic.
- sudo netstat -4plunt
Auf Ihrem Webserver könnte Ihre Ausgabe so aussehen:
OutputActive Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1058/sshd
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 4187/nginx
Die erste markierte Spalte zeigt die IP-Adresse und den Port, auf dem der Service am Ende der Zeile hört. Die spezielle Adresse 0.0.0.0
bedeutet, dass der betreffende Dienst auf allen verfügbaren Adressen hört.
Auf Ihrem Datenbankserver könnte Ihre Ausgabe so aussehen:
- sudo netstat -4plunt
OutputActive Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1097/sshd
tcp 0 0 192.0.2.30:3306 0.0.0.0:* LISTEN 3112/mysqld
Sie können diese Spalten genauso lesen. In diesem Beispiel repräsentiert die Adresse 192.0.2.30
die private IP-Adresse des Datenbankservers. In der Voraussetzungstutorial haben Sie MySQL aus Sicherheitsgründen auf die private Schnittstelle beschränkt.
Beachten Sie die Werte, die Sie in diesem Schritt finden. Dies sind die Netzwerkdetails, die Sie für die Anpassung Ihrer Firewall-Konfiguration benötigen.
Auf Ihrem Webserver müssen Sie sicherstellen, dass die folgenden Ports erreichbar sind:
- Port 80 auf allen Adressen
- Port 22 auf allen Adressen (bereits in den Firewall-Regeln berücksichtigt)
Ihr Datenbankserver muss sicherstellen, dass die folgenden Ports erreichbar sind:
- Port 3306 auf der Adresse
192.0.2.30
(oder die damit verbundene Schnittstelle) - Port 22 auf allen Adressen (bereits in den Firewall-Regeln berücksichtigt)
Schritt 3 – Anpassen der Firewall-Regeln des Webservers
Jetzt, da Sie die benötigten Portinformationen haben, werden Sie die Firewall-Regelsatz Ihres Webservers anpassen. Öffnen Sie die Regeldatei in Ihrem Editor mit sudo
-Berechtigungen:
- sudo nano /etc/iptables/rules.v4
Auf dem Webserver müssen Sie Port 80 zu Ihrer Liste zulässigen Datenverkehrs hinzufügen. Da der Server auf allen verfügbaren Adressen hört – Webserver erwarten normalerweise von überall erreichbar zu sein – werden Sie die Regel nicht durch Schnittstelle oder Zieladresse einschränken.
Ihre Webbesucher werden das TCP-Protokoll verwenden, um eine Verbindung herzustellen. Ihr Framework hat bereits eine benutzerdefinierte Kette namens TCP
für TCP-Anwendungs-Ausnahmen. Sie können Port 80 zu dieser Kette hinzufügen, direkt unter der Ausnahme für Ihren SSH-Port:
*filter
. . .
# Akzeptabler TCP-Datenverkehr
-A TCP -p tcp --dport 22 -j ACCEPT
-A TCP -p tcp --dport 80 -j ACCEPT
. . .
Ihr Webserver wird die Verbindung mit Ihrem Datenbankserver initiieren. Ihr ausgehender Datenverkehr ist in Ihrer Firewall nicht eingeschränkt und eingehender Datenverkehr, der mit etablierten Verbindungen verbunden ist, ist erlaubt, daher müssen wir auf diesem Server keine zusätzlichen Ports öffnen, um diese Verbindung zu ermöglichen.
Speichern und schließen Sie die Datei, wenn Sie fertig sind. Ihr Webserver verfügt nun über eine Firewall-Richtlinie, die den gesamten legitimen Datenverkehr erlaubt und alles andere blockiert.
Testen Sie Ihre Regel-Datei auf Syntaxfehler:
- sudo iptables-restore -t < /etc/iptables/rules.v4
Wenn keine Syntaxfehler angezeigt werden, laden Sie die Firewall neu, um die neue Regel-Sammlung zu implementieren:
- sudo service iptables-persistent reload
Schritt 4 – Anpassen der Firewall-Regeln des Datenbankservers
Auf Ihrem Datenbankserver müssen Sie den Zugriff auf Port 3306
an die private IP-Adresse Ihres Servers erlauben. In diesem Fall war diese Adresse 192.0.2.30
. Sie können den Zugriff auf diese Adresse spezifisch begrenzen oder den Zugriff anhand der Schnittstelle beschränken, der diese Adresse zugeordnet ist.
Um die Netzwerkschnittstelle zu finden, die dieser Adresse zugeordnet ist, führen Sie ip -4 addr show scope global
aus:
- ip -4 addr show scope global
Output2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
inet 203.0.113.5/24 brd 104.236.113.255 scope global eth0
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
inet 192.0.2.30/24 brd 192.0.2.255 scope global eth1
valid_lft forever preferred_lft forever
Die markierten Bereiche zeigen, dass die Schnittstelle eth1
mit dieser Adresse verbunden ist.
Als nächstes passen Sie die Firewall-Regeln auf dem Datenbankserver an. Öffnen Sie die Regel-Datei mit sudo
-Privilegien auf Ihrem Datenbankserver:
- sudo nano /etc/iptables/rules.v4
Erneut werden Sie eine Regel zu unserer TCP
-Kette hinzufügen, um eine Ausnahme für die Verbindung zwischen Ihren Web- und Datenbankservern zu bilden.
Um den Zugriff basierend auf der tatsächlichen Adresse einzuschränken, fügen Sie die Regel wie folgt hinzu:
*filter
. . .
# Akzeptierter TCP-Verkehr
-A TCP -p tcp --dport 22 -j ACCEPT
-A TCP -p tcp --dport 3306 -d 192.0.2.30 -j ACCEPT
. . .
Wenn Sie die Ausnahme lieber basierend auf der Schnittstelle zulassen möchten, die diese Adresse enthält, können Sie stattdessen eine ähnliche Regel hinzufügen:
*filter
. . .
# Akzeptierter TCP-Verkehr
-A TCP -p tcp --dport 22 -j ACCEPT
-A TCP -p tcp --dport 3306 -i eth1 -j ACCEPT
. . .
Speichern und schließen Sie die Datei, wenn Sie fertig sind.
Überprüfen Sie auf Syntaxfehler mit diesem Befehl:
- sudo iptables-restore -t < /etc/iptables/rules.v4
Wenn Sie bereit sind, laden Sie die Firewall-Regeln neu:
- sudo service iptables-persistent reload
Beide Ihrer Server sollten nun geschützt sein, ohne den erforderlichen Datenfluss zwischen ihnen einzuschränken.
Zusammenfassung
Das Implementieren einer ordnungsgemäßen Firewall sollte immer Teil Ihres Bereitstellungsplans sein, wenn Sie eine Anwendung einrichten. Obwohl wir diese Konfiguration anhand von zwei Servern, die Nginx und MySQL ausführen, demonstriert haben, sind die oben gezeigten Techniken unabhängig von Ihren spezifischen Technologieentscheidungen anwendbar.
Um mehr über Firewalls und insbesondere iptables
zu erfahren, werfen Sie einen Blick auf die folgenden Anleitungen: