SSH ist das Eintrittstor zu so gut wie jedem Linux-basierten Server. Umso wichtiger ist es, diesen Dienst so gut es geht abzusichern. Mit Knockd können wir ihn vor unberechtigten Zugriffen schützen, indem wir ihn verstecken.

Mit einem eigenen Server ist vieles machbar. Schnell sind alle gewünschten Anwendungen bzw. Services installiert und es kann losgehen.
Auf dem überwiegenden Teil aller Server, die über das Internet erreichbar sind, läuft eine Linux-Distribution. Für Administratoren ist insbesondere der Schutz solcher Systeme von höchster Priorität. Deshalb empfiehlt sich u. a. der Einsatz einer Firewall. Damit können wir einen Server nach außen abschotten. Natürlich müssen wir dennoch ein paar "Löcher" bzw. Ports offen lassen. Wollen wir eine Webseite betreiben, ist der Einsatz eines Webservers erforderlich. Damit von außen auf die Seite zugegriffen werden kann, müssen Anfragen an den Port 80 (für HTTPS auch 443) in der Firewall erlaubt werden.

Jeder fängt einmal klein an. So vergessen gerade Einsteiger, überhaupt eine Firewall einzusetzen. Viel gefährlicher ist es allerdings, sich nach der Aktivierung einer Solchen entspannt zurückzulehnen. Einfach schnell einer Anleitung aus dem Internet folgen und ein paar Ports freischalten - fertig. Wäre es doch nur so einfach.
Um sich nicht selber auszusperren, muss bei Linux-Servern der Port 22 freigeschaltet werden. Unter diesem ist der SSH-Daemon erreichbar. Über ihn kann einfach und per Kommandozeile auf den Server zugegriffen werden.

Vor Kurzem habe ich im Rahmen einer Präsentation einen Server aufgesetzt. Geschockt hat mich, dass es keine fünf Minuten gedauert hat, bis in den Logs erste fehlgeschlagene Anmeldeversuche auf den Port 22 (also den SSH-Service) zu sehen waren. Diese waren natürlich nicht von mir. Jedenfalls war ich zu diesem Zeitpunkt nicht in China unterwegs. Tatsächlich ist es allseits bekannt, dass große Botnetze ganze IP-Adressräume prüfen. Hin und wieder versuchen sie, sich auf Maschinen über interessante Ports, wie 22 für SSH, aufzuschalten. Leider haben sie damit auch das ein oder andere Mal Erfolg.

Unter dem Radar fliegen

Eine Möglichkeit, sich vor solchen Angriffen zu schützen, ist es, die Standardeinstellungen von wichtigen Diensten wie SSH etwas anzupassen. So könnte beispielsweise der Port 22 geändert werden. Ein angepasster Port könnte zwischen 1024 und 49151 liegen. Die Ports 0 bis 1023 sind "Well-Known Ports" und sollten für derartige Anpassungen nicht genutzt werden.

Diese Maßnahme stellt ein zusätzliches Hindernis dar, welches potenzielle Angreifer überwinden müssten, um unseren Server zu übernehmen. Allerdings haben sie diverse Möglichkeiten, unseren Täuschungsversuch zu umgehen. Mit einem Port-Scan könnten sie herausfinden, unter welchem Port der SSH-Dienst tatsächlich erreichbar ist. Ein solcher Scan ist z.B. mit dem Tool Nmap rasch durchgeführt. Einen fremden Server ohne das "Okay" des Administrators auf offene Ports zu untersuchen, ist eine rechtliche Grauzone. Spätestens mit dem Ausnutzen einer Sicherheitslücke oder der Überlastung des Zielsystems handelt es sich um eine Straftat. Administratoren haben viele Möglichkeiten, einen Port-Scan zu enttarnen, während er noch läuft - z.B. indem die Logs der Firewall laufend auf verdächtige Anfragen überprüft werden.

Port Knocking mit Knockd

Eine Firewall kann uns vor Angriffen gut abschirmen. Allerdings müssen neben den Ports der öffentlich zugänglichen Dienste (wie 80 und 443 für einen Webserver) auch einige andere wichtige Ports offen sein (Bsp.: 22 für SSH).

Grundlegende Erklärung

Knockd erlaubt es uns, Dienste wie SSH gegenüber der Außenwelt zu verstecken. Es ist also nicht erkennbar, ob auf unserem Server ein SSH-Dienst läuft, geschweige denn, unter welchem Port er erreichbar ist. Knockd besteht aus dem Client-Programm "Knock" (läuft auf unserem Rechner) und dem zugehörigen Daemon "Knockd" (läuft auf unserem Server).

Standardmäßig lassen wir den Port des SSH-Dienstes von der Firewall blockieren. Von außen kann nun nicht über SSH auf unseren Server zugegriffen werden. Wollen wir uns verbinden, starten wir Knock auf unserem Computer. Das Programm versucht, auf im Vorhinein definierte Ports auf dem Zielsystem zuzugreifen.

Der Daemon Knockd auf unserem Server überprüft im Hintergrund durchgehend die Firewall-Logs. Wenn eine IP-Adresse in den konfigurierten Zeitabständen auf die definierten Ports zugreift (also anklopft = knock), wird der Port des SSH-Dienstes in der Firewall für den Anklopfenden geöffnet. Wir können uns anschließend mit dem Server über SSH verbinden. Haben wir unsere Aufgaben erledigt, wird der Port wieder geschlossen.

Einrichtung

Um Knockd verwenden zu können, benötigen wir eine Firewall. Hier nutzen wir "iptables". Anschließend installieren wir Knockd auf dem Server und Knock auf unserem Computer.

ACHTUNG
Änderungen der Firewall-Konfiguration und des SSH-Dienstes sollten mit Ruhe und Bedacht erfolgen. Es besteht die Gefahr, sich aus dem eigenen System auszusperren.

Iptables Konfiguration

Der Linux-Kernel bringt von Haus aus die Firewall "Netfilter" mit. Sie kann mit Tools wie "iptables" oder "ufw" konfiguriert werden. Iptables ist mächtiger als ufw - aber gleichzeitig komplexer. Unsere erstellten Regeln werden beispielsweise nicht über Neustarts hinweg gespeichert (Ein Programm bietet hier Abhilfe).

Die folgenden Befehle beziehen sich auf die Verwaltung von Firewall-Regeln für IPv4. Ist der eigene Server auch unter IPv6 erreichbar, müssen alle folgenden Befehle analog mit ip6tables ausgeführt werden!

Iptables & weitere Tools installieren (falls nicht bereits vorhanden)
sudo apt-get install iptables
Das Paket ip6tables ist in iptables enthalten. Es muss also nicht gesondert installiert werden.

Default ACCEPT Policy hinzufügen (solange Konfiguration nicht endgültig)
Die bereits vorhandene Konfiguration kann mit sudo iptables -S ausgegeben werden.

sudo iptables -P INPUT ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -F (bereits vorhandene Regeln löschen - außer die Default Policy)
Falls keine von uns im Folgenden festgelegte Regel zutrifft, werden Datenpakete angenommen. Das minimiert die Gefahr, sich aus dem eigenen System auszusperren.
Wenn die Konfiguration endgültig ist, sollte als Default Policy "DROP" gesetzt werden.

Localhost freischalten
sudo iptables -I INPUT 1 -i lo -j ACCEPT
Programme auf unserem Server müssen hin und wieder untereinander Daten austauschen. Wir müssen unserer Firewall also mitteilen, dass sie den Datenverkehr des Loopback Devices (lokalen Datenverkehr) nicht blockieren soll. Das ist sehr wichtig. Ohne diese Regel funktioniert unser Betriebssystem nicht korrekt und es kann zum Absturz der Maschine kommen.

ICMP freischalten
sudo iptables -A INPUT -p icmp -j ACCEPT
Falls IPv6 verwendet wird:
sudo ip6tables -A INPUT -p icmpv6 -j ACCEPT
Das Internet Control Message Protocol (ICMP) bietet wichtige Funktionalitäten wie Ping. Insbesondere der neue Standard IPv6 nutzt dieses Protokoll intensiv. Würden wir Anfragen für IPv6 nicht freischalten, kann es zu Störungen beim Routen von Datenpaketen kommen.

Aktuelle SSH-Verbindung zulassen
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
Alle bereits offenen Verbindungen und neue Verbindungen, die zu den bereits Offenen gehören, werden erlaubt.

SSH-Port freischalten
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
Anfragen von außen auf den Port 22 werden erlaubt.
Mit diesem Befehl können natürlich auch weitere Dienste freigeschaltet werden. Einfach die Port-Nummer entsprechend anpassen und die Regel hinzufügen.

Alle konfigurierten Regeln anzeigen
sudo iptables -S
Es werden alle konfigurierten Regeln angezeigt. An dieser Stelle unbedingt auf eventuelle Tippfehler kontrollieren!

Funktion prüfen

Die aktuell geöffnete SSH-Session lassen wir offen. Um zu überprüfen, ob - wie gewünscht - neue Verbindungen aufgebaut werden können, versuchen wir, uns erneut mit unserem Server zu verbinden. Wenn das funktioniert, ist die Konfiguration korrekt. Sollte das nicht der Fall sein, sind wir über die noch offene Session mit dem Server verbunden und können vorhandene Fehler beheben.

Arbeitet die Firewall korrekt, setzen wir eine "Default DROP Policy". Eingehender und/oder ausgehender Datenverkehr wird, sofern keine von uns erstellte Regel zutrifft, abgewiesen. Der Einfachheit halber konfigurieren wir diese strikte Policy so, dass sie für den eintreffenden Datenverkehr gilt. Programme auf unserem Server können dadurch weiterhin nach außen kommunizieren. Um aber von außen auf sie zuzugreifen, ist eine entsprechende Regel für die Firewall (siehe SSH-Port freischalten) erforderlich.

Default DROP Policy hinzufügen
sudo iptables -P INPUT DROP
Ab dem Setzen dieser Policy ist Vorsicht geboten, wenn die Regeln der Firewall geändert werden. Bevor die Firewall-Einstellungen geändert werden, sollte die DROP zu einer ACCEPT Policy geändert werden:
sudo iptables -P INPUT ACCEPT
Danach unbedingt wieder die DROP Policy setzen! sudo iptables -P INPUT DROP

Ist der Server auch via IPv6 erreichbar?

Wenn das der Fall ist, müssen nun alle bisher aufgeführten Schritte mit dem Tool ip6tables ausgeführt werden. Wie oben erwähnt, ändert sich die Syntax der eigentlichen iptables-Befehle nicht. Die Regeln werden diesmal nicht mit iptables, sondern mit ip6tables erstellt.

Wenn die Befehle ausgeführt wurden, sollte die Bash-History wie folgt aussehen:

Bash History der iptables & ip6tables Konfiguration
Bash History der iptables & ip6tables Konfiguration

Regeln nach Neustart automatisch laden

sudo apt-get install iptables-persistent
Nach jedem Neustart des Systems erstellt der Dienst automatisch die konfigurierten Regeln. Das ist erforderlich, da iptables die erstellten Regeln nicht persistent abspeichert.

Während der Installation fragt das Programm, ob alle aktuell vorhandenen Regeln automatisch geladen werden sollen. Dem muss zugestimmt werden. Zusätzlich wird gefragt, ob auch IPv6-Regeln gespeichert werden sollen. Auch hier muss zugestimmt werden, sofern der Server unter IPv6 erreichbar ist und entsprechende Regeln im Vorhinein erstellt wurden. Der Dienst "iptables-persistent" wird automatisch nach einem Neustart gestartet und erstellt alle aktuell definierten Firewall-Regeln.

iptables-persistent IPv4 Konfiguration
iptables-persistent IPv4 Konfiguration
iptables-persistent IPv6 Konfiguration
iptables-persistent IPv6 Konfiguration

Sollten die iptables-Regeln einmal verändert werden, muss (nach den Änderungen der Firewall) das Tool iptables-persistent darüber informiert werden:
sudo dpkg-reconfigure iptables-persistent.
Es erscheinen die gleichen Abfragen wie bei der Installation. Beide müssen mit <Yes> beantwortet werden. Nun wurden die Änderungen gespeichert.

Bitte nach der Einrichtung bzw. Änderung unbedingt prüfen, ob alle Regeln wie gewünscht nach einem Neustart sudo reboot übernommen wurden!

Knockd Konfiguration

Installation mit: sudo apt-get install knockd.

Die Konfiguration des Daemons kann anschließend in der Datei /etc/knockd.conf angepasst werden.
Knockd wird nach einem Neustart des Systems automatisch gestartet. Sollten jedoch Änderungen an der Konfiguration des Daemons vorgenommen werden, muss er unbedingt neugestartet werden: sudo service knockd restart.

Beispiel 1

Die folgende Konfiguration weist Knockd an, den SSH-Port für die anklopfende IP zu öffnen und ihn nach Ausführen der zweiten Sequenz wieder zu schließen. Da die Firewall lediglich unsere IP-Adresse für den Port 22 freischaltet, kann sich kein anderer Rechner mit unserem Server verbinden. Sollte sich unsere IP-Adresse ändern (i.d.R. passiert das alle 24 Std.) könnte theoretisch jemand der unsere alte Adresse erhält, auf den Server zugreifen. Dieses Szenario ist sehr unwahrscheinlich, dennoch sorgt die zweite Konfiguration von Knockd (siehe Beispiel 2) hier für Abhilfe.

[options]
    UseSyslog
    Interface = eth0
    logfile = /var/log/knockd.log

[openSSH]
    sequence = 4000,5000,6000
    seq_timeout = 10
    tcpflags = syn
    command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT

[closeSSH]
    sequence = 7000,8000,9000
    seq_timeout = 10
    tcpflags = syn
    command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT

Beispiel 2

Wie im obigen Beispiel, lässt Knockd nach erfolgreichem Anklopfen den Port 22 für die anfragende IP-Adresse öffnen. Allerdings wird das wenige Sekunden danach (wir sind mittlerweile mit dem Server verbunden) wieder rückgängig gemacht. Unsere Firewall haben wir während der Einrichtung angewiesen, bereits bestehende Verbindungen nicht zu unterbrechen. Folglich können wir nun in aller Ruhe via SSH mit der Maschine arbeiten. Wenn wir uns abmelden/die Session schließen, ist wieder Funkstille. Solange bis wir wieder mit der richtigen Kombination anklopfen.
Im Folgenden der Inhalt der knockd.conf:

[options]
    UseSyslog
    Interface = eth0
    logfile = /var/log/knockd.log

[opencloseSSH]
    sequence = 4000,5000,6000
    seq_timeout = 15
    start_command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
    cmd_timeout = 30
    stop_command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
    tcpflags = syn

Nicht vergessen: Die Ports sollten unbedingt geändert werden, sofern der Einsatz auf einem Server beabsichtigt ist!
Wichtig: seq_timeout muss größer sein als cmd_timeout (Angabe in Sekunden).

Knockd aktivieren

Wenn wir die Konfiguration abgeschlossen haben, müssen wir Knockd einmalig aktivieren. Hierfür einfach in der Datei /etc/default/knockd den Wert von START_KNOCKD=0 ändern zu START_KNOCKD=1.

Nach erfolgter Konfiguration verwalten wir den Service mit:
sudo service knockd start/stop/restart/reload/status

Mit dem Server verbinden / anklopfen

Wenn wir uns nun mit dem Server per SSH verbinden wollen, müssen wir korrekt anklopfen. Mit dem Programm Knock auf unserem Computer können wir das sehr einfach handhaben. Der Befehl kann natürlich einzeln ausgeführt oder in ein Bash-Skript eingebunden werden. Hier ein funktionierendes Beispiel:

#!/bin/bash

### Folgende Variablen anpassen
SERVER_IP="123.123.123.123"
SERVER_USER="nutzername"

clear
echo "Anklopfen & verbinden zur Maschine: $SERVER_IP"
knock $SERVER_IP 4000 5000 6000
ssh SERVER_USER@$SERVER_IP

Einfach als eine Skript-Datei (Bsp.: knock.sh) abspeichern und anschließend ausführbar machen mit sudo chmod +x knock.sh. Nun einfach starten mit ./knock.sh.

Fehlerbehebung

Hat etwas nicht funktioniert? Selber ausgesperrt? Keine Panik! Die meisten Provider bieten für derartige Fälle eine Online-Konsole an. Einfach kurz nachfragen oder auf eigene Faust das Webinterface durchstöbern.
Über eine solche Konsole kann als Root-Nutzer auf die Maschine zugegriffen werden. Anschließend können die gerade erstellten Firewall-Regeln außer Kraft gesetzt werden: sudo iptables -P INPUT ACCEPT.