Den eigenen Server zähmen — Teil 3: Absichern
Endlich sind wir auf unserem Server und die Magie kann beginnen. Ab jetzt ist nahezu wirklich egal, welchen Anbieter ihr für den Server habt, oder auf welchem Betriebssystem ihr arbeitet. Wir sind jetzt auf dem frisch installierten Ubuntu und wollen jetzt folgende Schritte machen:
- Einen neuen Benutzer erstellen
- Den Benutzer Sudo-Rechte geben
- Den Root-Nutzer keinen SSH-Zugriff mehr gewähren
- Dafür sorgen, dass wir uns nur noch mit einem SSH-Key anmelden können
- Unseren Public-Key auf den Server senden (Hier gibt es wieder OS Unterschiede)
- Ubuntu dazu bringen, sich selber um Updates zu kümmern
- Dem Server einen Namen gegeben
- Eine Firewall aktivieren
Danach haben wir einen brauchbaren Server, den wir für viele Dinge verwenden können. Das Tutorial wird dann noch ein wenig weiter gehen und wir Installieren noch zusätzlich Docker und für einen Webserver dann sogar noch Traeffik.
Aber fangen wir an. Wir befinden uns also gerade auf unseren Server und das als Root-Benutzer. Dieser ist der erste und mächtigste Benutzer auf Unix-ähnlichen Systemen, der nahezu alles darf. Ich sage extra nahezu, weil auch ihm sind Schranken gesetzt. Er kann aber viel und ehrlich gesagt - zu viel.
Aus großer Kraft folgt große Verantwortung (Ben Parker)
Jeder der sich als Root-Benutzer auf unser System anmelden kann, kann viel ändern; was zwar beim Einrichten Komfort bedeutet, das System aber angreifbar macht.
Die Lösung liegt in einem anderen Benutzer, der sich zwar immer wieder die Berechtigungen holen kann, um (nahezu) alles machen zu können, der nach seiner Anmeldung aber erst einmal weniger Möglichkeiten besitzt.
Dieses machen wir mit dem Befehl
adduser {username}wobei wir natürlich {usernamen} durch einen von uns gewählten Namen ersetzen. Verwenden wir für unseren Server einfach 'alex', also adduser alex. Danach folgen ein paar Eingaben
New password:
Retype new password:
passwd: password updated successfully
Changing the user information for amlor
Enter the new value, or press ENTER for the default
Full Name []: Alex
Room Number []: 221B
Work Phone []: 555 1337
Home Phone []: 555 4711
Other []: I'm the master of desaster
Is the information correct? [Y/n] Y
Wichtig hierbei ist natürlich, dass wir für das Passwort ein anderes verwenden, als für unseren Root-Benutzer, da wir ansonsten nicht viel für unsere Sicherheit machen. Auch sollte das Passwort natürlich mindestens 12 Zeichen verwenden - evt. sogar 16 und aus Zahlen, Sonderzeichen, kleinen und großen Buchstaben bestehen. Ein Passwortmanager kann uns natürlich dabei helfen.
Die anderen Einträge sind auf so kleinen Server, wo wir uns in den meisten Fällen allein drauf austoben nicht wirklich wichtig.
Haben wir den Benutzer eingetragen, kann er sehr wenig. Damit wir mit ihm aber doch wieder ein wenig mehr machen können, weisen wir ihm der Gruppe 'sudo' zu:
usermod -aG sudo {username}Hier auch natürlich wieder den {usernamen} tauschen - in unserem Beispiel also usermod -aG sudo alex.
Vertrauen ist gut, Kontrolle ist besser. Um jetzt zu überprüfen, ob der Benutzer auch wirklich in die Gruppe aufgenommen worden ist, können wir folgenden Aufruf nehmen
grep '^sudo' /etc/groupAls Ausgabe sollte dann so etwas in der Art erscheinen:
sudo:x:27:{username}
Dieses stellt ein indirekter Test dar, da wir lediglich gucken, ob es einen Eintrag in der Gruppe 'sudo' gibt, in dem unser Benutzer auftaucht. Wer es direkt testen möchte, der kann folgendes probieren
su - alex
sudo ls -lah
Mit dem ersten Befehl wechseln wir zum Benutzer 'alex', sind dann also nicht mehr root. Das sehen wir auch daran, dass im terminal jetzt nicht mehr root@XXX sondern alex@XXX steht. Das XXX steht für den Rechner-Namen, der bei Netcup mit einem v startet und dann aus einer langen Zahlenreihe besteht - da kümmern wir uns gleich drum.
Die zweite Zeile kennen wir schon. 'ls -lah' listet alle Ordner und Verzeichnisse auf und zwar auch die unsichtbaren in einer bestimmten Schreibweise (untereinander, mit allen zusätzlichen Daten). Das sudo davor steht dafür, dass wir das als 'Super-User' machen wollen, d.h. mit sämtlichen Berechtigungen. Wir können dadurch also potentiell mehr sehen. In unserem jetzigem Fall ist das gar nicht mal so viel; viel wichtiger ist aber, dass wir aufgefordert werden, das Passwort für den Benutzer 'Alex' einzugeben. Das zeigt schon, dass wir auch wirklich in der Gruppe 'sudo' sind, denn ansonsten bekämen wir einen Hinweis, dass wir etwas verbotenes versuchen und dass der Administrator in Kenntnis gesetzt wird.
Wer den direkten Test gemacht hat, kann jetzt wieder mit 'exit' zum Root-Benutzer wechseln.
Der nächste Schritt dient dann dazu, den Root-Benutzer die Möglichkeit zu entziehen, sich über ssh anmelden zu können - andere Möglichkeiten bleiben aber bestehen, was wichtig sein kann, wenn man sich über Notfallkonsolen noch mal direkt als Root-Benutzer anmelden möchte.
Viele der wichtigsten Dateien, die unser System beschreiben und einrichten finden wir unter /etc. In diesem Fall geht es um Einstellungen bezüglich des SSH-Zuganges, also müssen wir in /etc/ssh/ gucken. Die genaue Datei ist /etc/ssh/sshd_config.
Jetzt werden wir so richtig wild und versetzen den letzten normalsterblichen in staunen und nutzen 'vi'. Ja ich weiß, der ein oder andere wird jetzt fragen, ob ich von allen guten Geistern verlassen bin und anmerken, dass wir da nie wieder raus kommen werden - aber so schlimm wird es nicht - versprochen.
Der Vorteil an VI(m) ist, dass es auf den meisten Unix-Systemen mit drauf ist. Nano, Pico und Co können schon mal fehlen, VI ist aber wie in guter Freund - immer an der richtigen Stelle da.
VI(m) ist ein textbasierter Editor für das Terminal. Es gibt ein paar Punkte, die es zu beachten gilt, danach ist der Einsatz aber eigentlich schon selbstverständlich. Öffnet man eine Datei, dann ist man im Lesemodus. Wir können also hoch und runter scrollen und nicht wirklich viel mehr - zumindest scheint es erst einmal so.
Drückt man in diesem Moment die : taste, erscheint das Symbol auch unten links und wenn wir jetzt q und Enter drücken, kommen wir wieder raus. War doch gar nicht so schlimm, oder?
Ok, fair enough - wir haben noch nichts verändert; und wie schreibt man denn jetzt etwas? Auch gar nicht so schwierig. Drücken wir i, so landen wir in dem -- INSERT -- Modus, was wir unten links jetzt auch sehen können. Aufpassen hierfür dürfen wir natürlich nicht erst : gedrückt haben, ansonsten erscheint jetzt natürlich :i. Wenn das der Fall ist, erst einmal esc drücken und danach i. Auf die gleiche Art und Weise, kommen wir auch wieder aus dem -- INSERT -- Modus raus… wobei wir dann natürlich mit : und dann q VI wieder verlassen könnten.
Es braucht evt. seine Zeit, bis man sich daran gewöhnt hat. Ehrlich gesagt sind Pico/Nano aber gar nicht so weit davon entfernt und der Aufwand lohnt sich, ist man spätestens jetzt der IT-King auf jeder Party… nun ja, fast ;)
Kehren wir zurück zu unserem Server und der Einrichtung von SSH. Wir öffnen jetzt also die Datei 'sshd_config' im Ordner '/etc/ssh'. Da wir aktuell noch der Root-Benutzer sind, brauchen wir kein 'sudo'. Ich schreibe es aber trotzdem mit hinzu, da wir als anderer Benutzer ansonsten später die Änderungen nicht speichern könnten.
sudo vi /etc/ssh/sshd_config
Wir scrollen jetzt bis an das Ende der Datei, bis wir folgende Einträge finden:
# Example of overriding settings on a per-user basis
#Match User anoncvs
# X11Forwarding no
# AllowTcpForwarding no
# PermitTTY no
# ForceCommand cvs server
PasswordAuthentication yes
PermitRootLogin yes
Wenn wir diese gefunden haben, drücken wir i um in den -- INSERT -- Modus zu gelangen und ändern die letzten beiden Zeilen, indem wir aus dem 'yes' jeweils ein 'no' machen
# Example of overriding settings on a per-user basis
#Match User anoncvs
# X11Forwarding no
# AllowTcpForwarding no
# PermitTTY no
# ForceCommand cvs server
PasswordAuthentication no
PermitRootLogin no
Jetzt verlassen wir den -- INSERT -- Modus mit Esc, drücken :wq und bestätigen das mit 'Enter'. Das w steht in dem Fall für 'Write' und q für Quit.
Wird gemeckert, dass wir keine Berechtigungen haben, dann sind wir aktuell kein Root-Benutzer und haben das 'sudo' vergessen - sollte aktuell an dieser Stelle des Tutorials zwar nicht der Fall sein, aber ab und an kommt man ja unter anderen Vorraussetzungen auf diese Zeilen.
In dem Fall schreiben wir nicht :wq sondern :q! und befinden uns wieder im normalen Terminal außerhalb von VI(m) - leider ohne Änderungen gemacht zu haben. Das ! hinter dem q zeigt einfach an, dass Meldungen ignoriert werden sollen. Vergessen wir dieses, merkt VI(m) an, dass die Datei geändert worden ist und daher das Programm nicht verlassen werden kann.
Die Kritik, dass man VI(m) also nicht so einfach verlassen kann stimmt also - es ist aber keine berechtigte, da die Hürden aus gutem Grund vorhanden sind. Wenn man aber alles betrachtet, dann sieht man, dass es gar nicht so viele Dinge gibt, auf die man achten muss - und nur, weil man alles erklären kann, geht die Magie nicht verloren ;)
Was haben jetzt die beiden Zeilen aber gemacht? Nun ja, die erste besagt, dass sich kein Benutzer mehr mit seinem Passwort anmelden kann und die zweite, dass der Root-Benutzer sich gar nicht mehr über SSH anmelden kann.
Jetzt kommt ein brenzliger Schritt. Wir müssen noch schnell dafür Sorge tragen, dass wir uns auch in Zukunft trotzdem noch anmelden können. Wir öffnen hierfür ein neues Terminal! In diesem tippen wir folgendes ein:
ssh-copy-id {benutzername}@xxx.xxx.xxx.xxx
Hier müsst ihr beachten: Ihr müsst im Step2 einen neuen SSH-Key angelegt haben; ihr müsste im Terminal-Tab sein, mit dem ihr nicht auf dem Server angemeldet seit und ihr müsste natürlich euren Benutzernamen und die IP für euren Server verwenden. Ist eure Domain schon richtig eingetragen (denkt an den Ping-Test), könnt ihr alternativ natürlich auch diese verwenden. Es sollten dann bei euch ähnliche Ausgaben erscheinen:
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/Users/{benutzername}/.ssh/id_rsa.pub"/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys{benutzername}@xxx.xxx.xxx.xxx's password:Hier müsste ihr das Passwort des Benutzers auf dem Server verwenden. Nicht das Passwort, welches ihr für den SSH-Schlüssel verwendet habt!
Als Erfolg sollte folgendes erscheinen.
Number of key(s) added: 1Now try logging into the machine, with: "ssh '{benutzername}@xxx.xxx.xxx.xxx'"and check to make sure that only the key(s) you wanted were added.Somit haben wir schon eine ganze Menge für die momentane Sicherheit getan… was ist aber mit der Zukunft? Hierfür müssen wir kurz erklären, was 'APT' ist. APT steht für Advanced Packaging Tool und ist eine Möglichkeit, neue Programme auf ein Unix-System zu bekommen. Vergleichen können wir es evt. mit einem App-Store für das Terminal. Der Vorteil daran ist, dass auf dem Rechner eine Liste entsteht, welche Programme installiert sind und somit weiß APT auch, ob es Updates für selbige gibt. Das Problem aktuell ist: Wir müssen selber dafür sorgen, dass diese Installiert werden. Aber auch dafür gibt es noch etwas.
APT Silent update
Wenn man etwas mit APT installieren möchte, gibt es 2 Zeilen, die man immer erst einmal ausfähren sollte:
sudo apt-get updatesudo apt-get upgradeDa wir gerade noch mit dem Root-Benutzer angemeldet sind, ist das sudo eigentlich wieder überflüssig - wir besitzen ja gerade noch alle Rechte. Um uns daran aber gewöhnen, fangen die beiden Befehle damit wieder an.
Auch müssten wir uns eigentlich in diesem Moment noch nicht um Updates kümmern, da das System ja ganz frisch ist - aber vielleicht kam ja doch ein Update in den letzten 5 Minuten und auch hier ist nicht falsch, dass wir uns direkt daran gewöhnen, diese kleine Extraarbeit zu leisten.
Die nächsten beiden Zeilen sorgen dafür, dass Silent Update installiert wird
sudo apt-get install unattended-upgrades apt-listchangessudo dpkg-reconfigure -plow unattended-upgradesBei der Installation müssen wir noch ein mal bestätigen, dass wir das Programm auch wirklich installieren wollen… und danach haben wir auch in Zukunft einen Server, der alle notwendigen Updates automatisch bezieht.
Bleiben zwei Dinge. Einen kosmetischen und einen für die Sicherheit.
sudo vi /etc/hostnameWie immer gilt: das sudo ist aktuell noch nicht notwendig, da wir gerade noch Root-Benutzer sind. Mit diesem Befehl kommen wir zumindest in die Datei, die für den Rechner-Name zuständig ist. Bei Netcup finden wir hier die vHierEineLangeNummer Eintrag, den wir die ganze Zeit bei unserer Eingabezeile hinter root@ sehen. Wir können hier eintragen, was wir wollen - wobei es klug ist, sich auf das lateinische Alphabet und Zahlen zu beschränken. Ob wir hier jetzt 'Deep-Thought', 'Hal9000' oder 'Hugo' schreiben bleibt uns überlassen. Ich verwende gerne den domain-name ohne TLD (Top Level Domain: z.B. .de oder .net). Das heißt bei dulldaydudes.de heißt der Rechner dulldaydues. So kann ich bei mehreren Servern schnell sehen, auf welchen ich gerade eingeloggt bin.
Denkt dran - editieren könnt ihr bei VI(m), indem ihr mit 'i' in den -- INSERT -- Modus wechselt. Beenden könnt ihr diesen mit 'ESC' und mit :wq verlasst ihr VI(m) und speichert.
Wundert euch nicht, wenn jetzt noch nicht der Server-Name angezeigt wird. Wir müssen jetzt aber noch schnell eine weitere Datei bearbeiten
sudo vi /etc/hostsDiese Datei ist dafür zuständig, dass der Server sich selbst kennt und im Netzwerk (und auch darüber hinaus) findet. Hier können sich die Einträge auch wieder ein wenig von Hoster zu Hoster unterscheiden
127.0.0.1 localhost
127.0.1.1 vXXXXXXXXXXXXXXX.hotsrv.de vXXXXXXXXXXXXXXX
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
xxx.xxx.xxx.xxx vXXXXXXXXXXXXXXX.hotsrv.de vXXXXXXXXXXXXXXX
Sollte die Datei leer sein, dann legt sie am besten an. Bei anderen Hostern als Netcup kann es sein, dass vXXXXXXXXXXXXXXX.hotsrv.de fehlen oder stark anders aussehen. Wichtig ist hier, dass wir die Datei wie folgt ändern
Netcup
127.0.0.1 localhost
127.0.1.1 vXXXXXXXXXXXXXXX.hotsrv.de NeuerServerName
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
xxx.xxx.xxx.xxx vXXXXXXXXXXXXXXX.hotsrv.de NeuerServerName
Andere Hoster
127.0.0.1 localhost
127.0.1.1 NeuerServerName
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
xxx.xxx.xxx.xxx NeuerServerName
Hier bitte bei den anderen Hostern beachten, sollte dort ein Eintrag mit komplizierter-name.tolle-domain.TLD stehen, so muss diese erhalten bleiben.
Wenn ihr die Einträge geändert/eingefügt habt, dann mit ESC und dann :wq speichern und VI(m) verlassen.
Final Countdown
Vielen ist der Begriff 'Firewall' schon ein mal unter gekommen und die meisten wissen, dass sie etwas mit Sicherheit zu tun haben… aber reicht dieses Wissen für den nächsten Schritt. Eindeutiges Jain.
Die IP-Adresse haben wir jetzt schon häufiger verwendet und wir wissen, damit kann man einen Rechner finden. Sie steht für so etwas wie Postleitzahl, Ort, Strasse und Hausnummer. Dieses Gleichnis hinkt jetzt natürlich an vielen Punkten, aber sie kommt der Wahrheit nahe genug.
Auf einem Server laufen viele kleine Programme, die für verschiedene Dinge zuständig sind. Damit die Vorgänge auf dem Server aber etwas geordneter ablaufen, gibt es so genannte Ports, die für die Anfragen zuständig sind.
Bleiben wir eben bei dem Vergleich mit der Adresse. Der Server verhält sich also wie eine Person; lebt in einer Stadt, in einer Strasse, in einem bestimmten Haus. Die einzelnen Programme sind jetzt die Bewohner des Hauses und ein Port ist so etwas, wie eine Türklingel.
Jetzt sind einige Wohnungen nicht bewohnt. Damit mit diesen keinen Schabernack betrieben werden kann, wollen wir die Schalusie vor den Fenster lassen - dieses ist unsere Firewall.
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install ufw
So langsam verstehen wir diese Befehle. Sudo ist noch immer ein wenig überflüssig. Die ersten beiden Zeilen sind dafür da, das System auf den neusten Stand zu bringen - die letzte installiert etwas und zwar UFW.
Ubuntu FireWall
In der Regel sollten wir folgende Ausgabe sehen
ufw is already the newest version (0.xx-x).
Wenn nicht, sollten wir spätestens jetzt UFW installiert haben. Jetzt gibt es mehrere Ansätze, wie man mit einer FireWall umgehen kann. Wir verfolgen den mit der maximalen Sicherheit. Wir führen also folgende Zeilen aus:
sudo ufw default deny incoming
sudo ufw default deny outgoing
Diese beiden lassen erst einmal alle Schalusien runter. Jetzt wird der aufmerksame Beobachter sicher sagen: Aber wir haben doch gerade noch eine Verbindung - und zwar die über SSH (Port 22). Das ist richtig, wir haben UFW aber noch nicht aktiviert. Bevor wir das aber machen, öffnen wir aber wieder ein paar, damit wir auch in Zukunft mit dem Server kommunizieren können. Daher führen wir als nächstes folgende Befehle aus
sudo ufw allow ssh
sudo ufw limit ssh
sudo ufw allow out http
sudo ufw allow in http
sudo ufw allow out https
sudo ufw allow in https
sudo ufw logging on
Hier erlauben wir wieder einige Dinge und zwar ssh, http und https, also die Ports 22, 80 und 443. Wir könnten auch die Zahlen direkt eintragen - aber wer kann sich wirklich merken, welche Services welche Ports nutzen ;)
Als nächstes kommt der spannende Teil; wir aktivieren die Firewall mit dem nächsten Befehl
sudo ufw enable
Wir bestätigen mit einem y und haben den nächsten Schritt für ein sichereres System gemacht
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
Wir sind natürlich neugierig und wollen wissen, ob die Firewall auch wirklich aktiv ist und überprüfen das mit
sudo ufw status
und sehen, dass alles so weit richtig eingerichtet ist
To Action From
-- ------ ----
22/tcp LIMIT Anywhere
80/tcp ALLOW Anywhere
443/tcp ALLOW Anywhere
22/tcp (v6) LIMIT Anywhere (v6)
80/tcp (v6) ALLOW Anywhere (v6)
443/tcp (v6) ALLOW Anywhere (v6)
80/tcp ALLOW OUT Anywhere
443/tcp ALLOW OUT Anywhere
80/tcp (v6) ALLOW OUT Anywhere (v6)
443/tcp (v6) ALLOW OUT Anywhere (v6)
Zu einem krönenden Abschluss kommen wir mit diesem Befehl
reboot
Wir werden ausgeloggt, der Server startet neu und alle Änderungen werden wirklich aktiv. Dieses beutet
- Der Root-Benutzer kann sich nicht mehr per SSH verbinden
- Unser Benutzer kann sich nicht mehr mit seinem Passwort anmelden
- Die Anmeldung erfolgt über unseren SSH-Key. Wenn dieser ein Passwort besitzt,
dann müssen wir jetzt dieses bei der Anmeldung verwenden
- Beim nächsten anmelden hat der Rechner einen schönen Namen
- Die Firewall ist aktiv
- Updates werden automatisch in der Nacht gezogen und installiert
Natürlich kann man immer alles besser machen. Diese Schritte sollten reichen, um zumindest den gröbsten Ärger zu umgehen. Der Einsatz als Spiele-Server oder als kleiner Web-Server spricht nichts entgegen. Wer ein wenig mehr machen möchte, kann bei SSH noch einen anderen Port angeben. Wie oben benannt verwendet SSH eigentlich den Port 22. Mit ssh benutzername@domain.tld:port kann man aber eine Anfrage auf einen anderen starten. In der Datei /etc/ssh/sshd_config, die wie hier im Tutorial auch bearbeitet habt, gibt es den Block
Include /etc/ssh/sshd_config.d/*.conf
#Port 22
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::
In den meisten Config-Dateien steht das # für ein Kommentar, d.h. für eine Zeile die vom Programm ignoriert wird. Löschen wir dieses Zeichen und tragen dann als Zahl z.B. die 32000 ein, so werden in Zukunft unsere SSH-Verbindungen über diesen Port abgehandet. Das heißt natürlich, dass man :32000 auch wirklich angeben muss. Fehlt dieses, wird wieder auf Port 22 versucht und dort nimmt der Server in Zukunft keine Anfragen mehr an.
Warum das 'versetzen' von Port-Adressen mehr Sicherheit bedeutet? Hierfür muss man bedenken, dass kein Programm mit einer entsprechenden Komplexität existiert, welches nicht 'unsicher' ist. D.h. man sollte erst einmal immer davon ausgehen, das ein Programm unsicher ist und Lücken aufweist. Da in den meisten Fällen über ssh kommuniziert wird, lohnen sich also auch Angriffe auf dieses Programm. Befindet sich das Programm aber nicht hinter dem Port, hinter dem es vermutet wird, muss danach erst gesucht werden. Das bedeutet, der Angreifer braucht mehr Zeit und Zeit ist die mächtigste Waffe was die Sicherheit angeht - hierzu aber mehr in einem zukünftigen Beitrag, wo wir über Teergruben sprechen werden.