Schlagwort-Archive: linux

Warum entwickelt/testet ihr (nicht) auf CentOS Stream, Fedora oder RHEL?

Hi, mein Name ist Jörg. Ich arbeite seit März 2023 als Senior Technical Account Manager für Red Hat und mir schwirren derzeit folgende Fragen im Kopf herum:

  • Wer sind die Menschen, die Open Source Software auf bzw. für CentOS Stream, Fedora und/oder RHEL entwickeln?
    • Sind es Menschen, die dies ausschließlich in ihrer Freizeit tun?
    • Arbeiten sie in Unternehmen, welche nach dem Open Source Entwicklungsmodell arbeiten?
  • Warum habt ihr euch für oder gegen die eine oder andere Distribution entschieden?
  • Was hindert euch daran, eine der genannten Distributionen zu verwenden?
  • Aus welchem Grund bevorzugt ihr andere Distributionen und welche sind dies?

Hinsichtlich dieser Fragen habe ich selbst offensichtlich einen Interessenskonflikt und bin darüber hinaus zu einem hohen Grad betriebsblind. Deshalb bin ich umso mehr daran interessiert, eure Antworten auf diese Fragen zu lesen.

Ich freue mich, wenn ihr euch die Zeit nehmt, um mir zu antworten und mir zu erläutern, wie ihr dazu steht. Eure Nachrichten nehme ich gern auf folgenden Kanälen entgegen:

  • Als Kommentar unter diesem Artikel
  • Als E-Mail an jkastning+distribution (at) my-it-brain (dot) de
  • Als Chat-Nachricht in #my-it-brain:matrix.org

Es freut mich, wenn daraus eine freundliche und konstruktive Diskussion entsteht. Sollte es dabei allerdings zu Trolling oder unangemessenen Äußerungen kommen, werde ich die Kommentare schließen und die Kommunikation einstellen. Bitte geht daher höflich miteinander um und behandelt einander so, wie ihr selbst auch behandelt werden möchtet.

RHEL/CentOS 7 – Root Passwort zurücksetzen

In diesem Tutorial wird beschrieben, wie das root-Passwort bei RHEL/CentOS 7 mit Bordmitteln zurückgesetzt werden kann. Mit Bordmitteln bedeutet, dass hierbei keine externen Bootmedien verwendet werden.

Aufgepasst! Macht man bei der Ausführung der folgenden Prozedur Fehler, können diese dazu führen, dass man jeglichen Zugang zu einem System verliert. Möchte man die Vorgehensweise üben, empfehle ich, dies ausschließlich auf Testsystemen zu tun.

Die Ausgangssituation

Das root-Passwort eines RHEL/CentOS 7 Servers ist unbekannt und muss zurückgesetzt werden. Es ist keine geöffnete root-Shell vorhanden und es ist kein Benutzer am System angemeldet, welcher über vollständige sudo-Berechtigungen verfügt.

Das Passwort soll ohne Einsatz externer Bootmedien zurückgesetzt werden.

Update 2019-12-17: Alternativ zur unten beschriebenen Vorgehensweise, kann bei Systemen, mit sehr großen Dateisystemen die in KB4661061 beschriebene Vorgehensweise genutzt werden.

Vorgehensweise

Schritt 1: Es wird ein Neustart des Systems durchgeführt. Sobald das Grub2-Boot-Menü erscheint, wird der Bootloader-Countdown durch Drücken einer beliebigen Taste unterbrochen.

Nun wählt man den gewünschten Eintrag (dies ist meist der erste in der Liste) aus und wechselt mit Drücken der Taste ‚e‘ in den Bearbeitungsmodus (siehe Abbildung 1).

abb1-grub2-menu

Abbildung 1: Grub2-Bootmenü

Schritt 2: Der Cursor wird zur Kernel-Kommandozeile bewegt. Diese beginnt üblicherweise mit dem Wort linux16. Hier wird, wie in Abbildung 2, ein „rd.break“ an das Ende der Zeile angefügt.

edit-kernel-command-line

Abbildung 2: Bearbeitete Startparameter für den Kernel

Die Bearbeitung wird durch Drücken der Tastenkombination Strg+x beendet und der Bootvorgang fortgesetzt. Der Bootvorgang wird an der Stelle angehalten, an der man sich in der Initial-Ram-Disk befindet, direkt bevor das eigentliche System gestartet wird. An dieser Stelle findet man den Inhalt des eigentlichen /-Dateisystems unterhalb von /sysroot.

Schritt 3: Nach Schritt 2 befindet man sich nun in einer root-Shell. Da das eigentliche Dateisystem unterhalb von /sysroot schreibgeschützt eingehängt wurde, muss dieses zunächst remountet werden. Dies geschieht mit dem folgenden Kommando (vgl. Abbildung 3):

switch_root:/# mount -oremount,rw /sysroot

remount-sysroot

Abbildung 3: Remount /sysroot

Schritt 4: In diesem Schritt wechselt man mittels chroot[1. chroot – wiki.ubuntuusers.de] in das /sysroot-Verzeichnis und setzt ein neues Passwort für den Benutzer root.

switch_root:/# chroot /sysroot
sh-4.2# passwd root
Changing password for user root.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.

Wichtig: SELinux[2. Einführung in das grundlegende Konzept von SELinux] ist zu diesem Zeitpunkt noch nicht aktiv. Dies bedeutet, dass alle neuen Dateien ohne einen entsprechenden SELinux-Kontext erstellt werden. Das Programm passwd arbeitet so, dass es erst eine neue Datei erstellt und mit dieser anschließend die alte Datei überschreibt. Die neue Datei /etc/shadow besitzt damit keinen SELinux-Kontext.

Um sicherzustellen, dass alle Dateien (inkl. der /etc/shadow) während des Bootvorgangs mit einem SELinux-Label versehen werden, muss die Datei autorelabel im aktuellen Verzeichnis erstellt werden:

sh-4.2# touch /.autorelabel

Wichtig: Sämtliche Dateien und Verzeichnisse werden erneut mit einem SELinux-Label versehen. Dies kann bei großen Dateisystemen einige Zeit dauern. Um Zeit zu sparen, können Dateisysteme (außer dem Dateisystem auf dem sich die /etc/shadow befindet) in der /etc/fstab auskommentiert werden. Nachdem das SELinux-Relabeling durchgeführt und das System gestartet wurde, können diese wieder eingehängt werden.

Abschließend verlässt man durch zweimalige Eingabe von exit zuerst die chroot-Umgebung und anschließend die Debug-Shell der Ram-Disk (vgl. Abbildung 4). Das System setzt den Bootvorgang an der Stelle fort, an der dieser unterbrochen wurde.

autrelabel

Abbildung 4

Es werden zunächst sämtliche Dateien von SELinux relabelt und anschließend ein Neustart ausgeführt.

Hinweis: Vergisst man die Datei .autorelabel zu erstellen und wird SELinux im Modus „Enforcing“ ausgeführt, kann man sich nach einem Neustart nicht am System anmelden. Man muss dann erneut booten und obige Schritte ausführen, um die Datei erstellen zu können.

Wurden die oben aufgeführten Schritte erfolgreich angewendet, kann man sich nun mit dem vergebenen Passwort am System anmelden und hat damit die Kontrolle zurückgewonnen.

Auch wenn ich diese Anleitung mehrere Male erfolgreich getestet habe, wünsche ich uns allen, dass wir sie möglichst niemals brauchen werden.

Einführung in das grundlegende Konzept von SELinux

SELinux[1. SELinux – Wikipedia] (Security-Enhanced Linux; engl. „sicherheitsverbessertes Linux“) ist eine Erweiterung des Linux-Kernels, mit der eine zusätzliche Sicherheitsschicht in das Betriebssystem eingezogen wird.

In dieser Einführung wird das zu Grunde liegende Konzept kurz vorgestellt und erläutert, welche Modi SELinux besitzt, wie diese angezeigt und umgeschaltet werden können. An einem einfachen Beispiel mit dem Apache Webserver wird dargestellt, wie SELinux in der Praxis wirkt. Des Weiteren wird mit Hilfe des Beispiels das Vorgehen bei einer Fehleranalyse erläutert.

SELinux ist dazu gedacht, das bestehende Berechtigungskonzept unter Linux zu erweitern, um zu verhindern, dass kompromittierte Dienste auf Daten zugreifen, auf die kein Zugriff erforderlich ist.

Als einfaches Beispiel mag hier der Webserver Apache[2. Apache HTTP Server – Wikipedia] dienen.

Dieser liest die Dateien, welche er ausliefern soll, in der Standardkonfiguration aus dem dem Verzeichnis /var/www/html. Daneben darf der Dienst z.B. auch alle Verzeichnisse und Dateien unterhalb der Verzeichnisse /tmp und /var/tmp lesen.

user@host:~$ ls -ld /tmp
drwxrwxrwt. 5 root root 4096 Nov 27 19:05 /tmp
user@host:~$ ls -ld /var/tmp
drwxrwxrwt. 2 root root 4096 Nov 10 06:34 /var/tmp
user@host:~$

Grundsätzlich hat der Dienst Zugriff auf alle Verzeichnisse und Dateien, welche den Lese- bzw. Schreibzugriff für alle Benutzer erlauben.

SELinux kann nun dazu genutzt werden, um genau diesen Zugriff zu unterbinden. Die folgende Abbildung soll dies verdeutlichen:

selinux-example-apache

Mögliche Zugriffe mit und ohne SELinux am Beispiel des Apache

SELinux stellt einfach gesprochen ein Regelwerk dar, nach welchem bestimmt wird, auf welche Verzeichnisse, Dateien, Prozesse und Ports ein Dienst zugreifen darf. Dazu erhalten diese Objekte ein Label mit einem Security Context. Nur wenn das SELinux-Regelwerk den Zugriff von einer Ressource auf eine weitere Ressource explizit erlaubt, ist der Zugriff gestattet. Andernfalls wird die Interaktion unterbunden. Dabei unterscheidet SELinux die verschiedenen Label nach Kontexten. Diese sind:

  • user
  • role
  • type
  • sensitivity

Im Folgenden wird ausschließlich der Kontext „type“ weiter betrachtet. So besitzt z.B. der Dienst Apache das Label httpd_t, während Dateien unterhalb von /var/www/html das Label httpd_sys_content_t tragen. Da SELinux eine Regel besitzt, welche den Zugriff des Kontext httpd_t auf Dateien und Verzeichnisse mit dem Kontext httpd_sys_content_t gestattet, kann der Apache die Datei index.html aus dem Verzeichnis /var/www/html anzeigen:

output-index-html

Ausgabe der Datei /var/www/html/index.html

Obiger Screenshot enthält neben dem obligatorischen „Hallo Welt“ auch noch die Ausgabe des Verzeichnis-Listings, welches neben den üblichen Angaben wie Berechtigungen, Benutzer und Gruppe auch den SELinux-Kontext mit ausgibt. Wie man SELinux steuert und sich den Kontext von Verzeichnissen, Dateien und Prozessen anzeigen lässt, wird im Folgenden Abschnitt erläutert.

Steuerung von SELinux

SELinux kennt drei Modi:

  • Enforcing
  • Permissive
  • Disabled

In welchem Status sich SELinux befindet, kann mit dem Kommando getenforce abgefragt werden:

[root@centos ~]# getenforce
Enforcing
[root@centos ~]#

Obige Ausgabe bedeutet, dass SELinux im Modus „Enforcing“ ausgeführt wird. In diesem Modus verhindert SELinux Zugriffe, welche nicht explizit durch das Regelwerk erlaubt werden. Im Unterschied dazu werden diese Zugriffe im Modus „Permissive“ nicht verhindert, sie werden jedoch protokolliert. Dieser Modus eignet sich daher hervorragend, um das aktuelle Verhalten von Diensten zu analysieren und die Konfiguration ggf. anzupassen. Die Umschaltung zwischen den Modi „Enforcing“ und „Permissive“ kann zur Laufzeit erfolgen:

[root@centos ~]# getenforce
Enforcing
[root@centos ~]# setenforce 0
[root@centos ~]# getenforce
Permissive
[root@centos ~]# setenforce 1
[root@centos ~]# getenforce
Enforcing
[root@centos ~]#

Der Standard-Modus wird in der Datei /etc/selinux/config festgelegt.

Anzeige der SELinux-Kontexte

Der kleine Punkt (im Screenshot rot markiert) hinter den Berechtigungen zeigt an, dass ein SELinux-Kontext für eine Datei bzw. ein Verzeichnis existiert:

Der Punkt zeigt einen vorhandenen SELinux-Kontext an

Der Punkt zeigt einen vorhandenen SELinux-Kontext an

Welchen Kontext eine Datei bzw. ein Prozess besitzt, kann angezeigt werden, indem bekannte Kommandos mit der Option „-Z“ benutzt werden. Das folgende Listing zeigt einige Beispiele:

[root@centos ~]# ps -eZ | grep httpd
system_u:system_r:httpd_t:s0     2876 ?        00:00:00 httpd
system_u:system_r:httpd_t:s0     2920 ?        00:00:00 httpd
system_u:system_r:httpd_t:s0     2921 ?        00:00:00 httpd
system_u:system_r:httpd_t:s0     2922 ?        00:00:00 httpd
system_u:system_r:httpd_t:s0     2923 ?        00:00:00 httpd
system_u:system_r:httpd_t:s0     2924 ?        00:00:00 httpd
system_u:system_r:httpd_t:s0     2925 ?        00:00:00 httpd
system_u:system_r:httpd_t:s0     3152 ?        00:00:00 httpd
[root@centos ~]# ls -lisaZ /var/www/html/
total 12
drwxr-xr-x. root root system_u:object_r:httpd_sys_content_t:s0 .
drwxr-xr-x. root root system_u:object_r:httpd_sys_content_t:s0 ..
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 index.html
[root@centos ~]# ls -lisaZ /var/tmp
total 28
drwxrwxrwt. root root system_u:object_r:tmp_t:s0       .
drwxr-xr-x. root root system_u:object_r:var_t:s0       ..
drwxr-xr-x. abrt abrt system_u:object_r:abrt_var_cache_t:s0 abrt
drwx------. root root system_u:object_r:tmp_t:s0       systemd-private-f844adcb52b14995b675d9aa065e925c-colord.service-MczkBW
drwx------. root root system_u:object_r:tmp_t:s0       systemd-private-f844adcb52b14995b675d9aa065e925c-cups.service-c3Aouq
drwx------. root root system_u:object_r:tmp_t:s0       systemd-private-f844adcb52b14995b675d9aa065e925c-httpd.service-oAqhlb
drwx------. root root system_u:object_r:tmp_t:s0       systemd-private-f844adcb52b14995b675d9aa065e925c-rtkit-daemon.service-xpSsO2
[root@centos ~]# ls -lisaZ /tmp |head -n4
total 96
drwxrwxrwt. root      root      system_u:object_r:tmp_t:s0       .
dr-xr-xr-x. root      root      system_u:object_r:root_t:s0      ..
-rw-r--r--. root      root      system_u:object_r:tmp_t:s0       anaconda.log
[root@centos ~]#

Die Datei index.html hat bei ihrer Erstellung im Verzeichnis /var/www/html automatisch den korrekten Kontext httpd_sys_content_t erhalten und kann daher im Webbrowser angezeigt werden. Wird eine Datei im Verzeichnis /tmp erstellt, erhält diese automatisch den Kontext user_tmp_t:

[root@centos ~]# echo 'TEST' > /tmp/test.txt
[root@centos ~]# ls -lisaZ /tmp/test.txt
-rw-r--r--. root root unconfined_u:object_r:user_tmp_t:s0 /tmp/test.txt
[root@centos ~]#

Was tun, wenn’s klemmt?

Um zu erläutern, wie man einem Fehler auf die Spur kommt, baue ich zuerst einen ein. Dazu verschiebe ich die im vorangegangenen Abschnitt erzeugte Datei in das DocumentRoot des Apache und versuche, diese im Browser aufzurufen.

forbidden

Forbidden

SELinux verhindert den Zugriff des Dienstes „httpd“ auf die Datei „text.txt“, da keine Regel existiert, welche den Zugriff vom Kontext httpd_t auf user_tmp_t explizit erlaubt. Doch wie kann man dies feststellen, wenn man nicht bereits zu Beginn um den falschen Kontext weiß?

Zuerst wirft man einen Blick auf die Dateiberechtigungen, welche aber keinen Fehler erkennen lassen:

[root@centos ~]# ls -lZ /var/www/html/
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 index.html
-rw-r--r--. root root unconfined_u:object_r:user_tmp_t:s0 test.txt
[root@centos ~]#

Da die Dateiberechtigungen als Fehlerquelle ausscheiden und in diesem Beispiel keine POSIX-ACL[3. ACL – wiki.ubuntuusers.de] verwendet werden, bleibt nur noch SELinux als Fehlerquelle übrig. Daher suchen wir einmal in /var/log/audit/audit.log nach Ereignissen vom Typ „AVC“:

[root@centos ~]# tail /var/log/audit/audit.log | grep AVC
type=AVC msg=audit(1480277043.225:537): avc:  denied  { open } for  pid=2922 comm="httpd" path="/var/www/html/test.txt" dev="sda1" ino=924457 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_tmp_t:s0 tclass=file

Treffer! Um die Sache weiter zu analysieren, untersuchen wir als nächstes /var/log/messages (Ausgabe gekürzt). Hinweis: Unter RHEL 7 muss das Paket ’setroubleshoot-server‘ installiert sein, damit SELinux-Ereignisse auch in /var/log/messages geloggt werden (siehe Abschnitt 4.2. Which Log File is Used).

Nov 27 21:29:13 centos setroubleshoot: SELinux is preventing /usr/sbin/httpd from open access on the file /var/www/html/test.txt. For complete SELinux messages. run sealert -l 7a6417b8-06e2-4499-8f0f-ed0bb16f2b2a

Hier findet sich eine Bestätigung, dass SELinux den Zugriff blockiert hat und ein Vorschlag, wie dieser Vorfall genauer analysiert werden kann. Damit lassen sich umfassende Informationen abrufen, welche sogar die Lösung mit ausgeben (Ausgabe gekürzt):

[root@centos ~]$ sealert -l 7a6417b8-06e2-4499-8f0f-ed0bb16f2b2a
SELinux is preventing /usr/sbin/httpd from open access on the file /var/www/html/test.txt.

*****  Plugin restorecon (92.2 confidence) suggests   ************************

If you want to fix the label. 
/var/www/html/test.txt default label should be httpd_sys_content_t.
Then you can run restorecon.
Do
# /sbin/restorecon -v /var/www/html/test.txt

*****

Der Bericht gibt an, wie das Standard-Label für die Datei text.txt gesetzt sein sollte. Darüber hinaus ist dem Bericht der Befehl zu entnehmen, mit dem die Label wieder auf den Standardwert zurückgesetzt werden können. Nachdem dieser Befehl ausgeführt wurde, kann die Datei text.txt im Webbrowser geöffnet werden.

[root@centos ~]# /sbin/restorecon -v /var/www/html/test.txt
/sbin/restorecon reset /var/www/html/test.txt context unconfined_u:object_r:user_tmp_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0
[root@centos ~]#

correct-selinux-context

Aufruf von test.txt mit korrektem SELinux-Kontext

Schlusswort

Es wurde in das grundlegende Konzept von SELinux und die möglichen Betriebsmodi eingeführt. Darüber hinaus wurde erläutert, wie man sich die zu einem Objekt gehörenden SELinux-Kontexte anzeigen lassen kann und wie diese auf einen Standard-Kontext zurückgesetzt werden können. Abschließend wurde ein kleiner Einblick in das Vorgehen zur Fehleranalyse gegeben.

Damit bin ich am Ende dieser kleinen Einführung angekommen, wobei das Thema SELinux damit noch lange nicht abschließend behandelt ist. Für weiterführende Informationen sei an dieser Stelle auf die Dokumentation der einzelnen Distributionen verwiesen.[4. RHEL 7: SELinux User’s and Administrator’s Guide: Basic and advanced configuration of Security-Enhanced Linux (SELinux) {en}] [5. SELinux – CentOS Wiki {en}] [6. SELinux – Fedora Project {en}]

Quellen und weiterführende Links

Standardgateway mit dem Linux-Kommando „route“ hinzufügen

Im Mai brachte mir ein DSM-Update auf meiner Synology Diskstation DS213air einigen Ärger ein. Nach dem Update fehlte meiner Diskstation das Standardgateway, so dass diese ihren Weg in das Internet nicht mehr finden konnte. Zum Glück lässt sich das Standardgateway mit dem folgenden Befehl schnell und einfach zur Routingtabelle hinzufügen. Und dies funktioniert nicht nur auf der Diskstation, sondern prinzipiell unter jedem Linux-Betriebssystem.

Auf der Diskstation muss man sich zuerst via SSH als root anmelden. Anschließend wir das Standardgateway wie folgt wieder hinzugefügt:

route add default gw IP-ADRESSE SCHNITTSTELLE

In meinem Fall wurde als Schnittstelle das Interface „wlan0“ verwendet. Der Befehl sieht dann konkret wie folgt aus:

route add default gw 192.168.178.188 wlan0

Leider verliert die Diskstation das Standardgateway bei jedem Neustart wieder. Ich habe daher auch eine Support-Anfrage an Synology gestellt. Leider verlief die Bearbeitung nicht meinen Wünschen entsprechend. Da ich das Standardgateway nicht nach jedem Neustart manuell hinzufügen möchte, habe ich mir daher etwas anderes einfallen lassen.

Als Workaround dient folgendes Skript. Dieses prüft, ob in der Routingtabelle ein Standardgateway vorhanden ist. Fehlt dieses, wird es durch das Skript hinzugefügt. Die IP-Adresse des Standardgateways und die verwendete Schnittstelle können dem Skript dabei über Parameter mitgeteilt werden.

Dieses Skript habe ich auf meiner Diskstation abgelegt. In den Dateieigenschaften habe ich unter „Genehmigungen“ die Berechtigung zum Ausführen der Datei erteilt. Anschließend habe ich das Skript in den Aufgabenplaner eingetragen und lasse es von nun an stündlich ausführen.

Diese Lösung ist nicht schön, aber sie stabilisiert den Betrieb meines NAS. Ich hoffe, mit diesem Beitrag meinen Leidensgenossen helfen zu können, die ebenfalls mit diesem Problem zu kämpfen haben.

Update 2015-07-05T14:05
Da das Skript im Feed nicht mit angezeigt wird, reiche ich hiermit den Link zum Skript auf GitHub nach.

Logging und logrotate mit NGINX

In diesem Artikel wird beschrieben, wie ich das Logging und logrotate meines NGINX-Servers konfiguriert habe. Dabei gehe ich kurz auf die beiden verwendeten Direktiven error_log und ngx_http_log_module ein.

Damit dient dieser Artikel meiner Dokumentation und evtl. euch als Anregung, ein eigenes Logging zu konfigurieren.

Logging

Informationen zum Logging findet man in der offiziellen NGINX-Dokumentation.[1. NGINX-Dokumentation] Im folgenden werden die Direktiven error_log[2. NGINX core module error_log] und ngx_http_log_module[3. NGINX – ngx_http_log_module] verwendet.

Mein Server liefert mehrere Webseiten aus. Ich möchte gern für jede Webanwendung ein separates Error-Log und Access-Log schreiben. Dabei wird folgendes Muster verwendet:

  • Log-Verzeichnis: /var/www//logs
  • Name für error_log: _error.log
  • Name für access_log: _access.log

Konfiguration des Error-Log

Die Error_log-Syntax ist denkbar einfach:

error_log log_file [ log_level ]

log_file gibt den Pfad zur Log-Datei an. Mit log_level wird bestimmt, wie viele Informationen protokolliert werden sollen.

Log-Level[4. How To Configure Logging and Log Rotation in Nginx on an Ubuntu VPS]

  • emerg: Notfall, in dem sich das System in einem nicht nutzbaren Zustand befindet
  • alert: Ernste Störung. Sofortiger Eingriff ist erforderlich
  • crit: Kritische Probleme, um die man sich kümmern sollte
  • error: Ein Fehler ist aufgetreten. Hier funktioniert etwas nicht
  • warn: Ein ungewöhnliches Ereignis ist aufgetreten. Dies ist jedoch kein Grund zur Sorge
  • notice: Normale Vorgänge werden ebenfalls protokolliert
  • info: Unnützes Wissen – Nice to know
  • debug: Debugging-Informationen, welche helfen, ein Problem näher zu analysieren

Die Log-Level sind nach Priorität angeordnet. Wird das Level auf „error“ gesetzt, so werden alle Events der Level error, crit, alert und emerg protokolliert.

Möchte man rein gar nichts protokollieren, muss das Log nach /dev/null umgeleitet werden.

error_log /dev/null crit;

Konfiguration des Access-Log

Das Modul ngx_http_log_module besteht aus den Direktiven access_log, log_format und open_log_file_cache, von denen ich hier nur die ersten beiden verwenden werde.

Mit der Direktive log_format kann das Format der Log-Dateien konfiguriert werden. Die einzelnen Formate werden über einen Bezeichner ausgewählt. Dies kann z.B. wie folgt aussehen:

log_format compression '$remote_addr - $remote_user [$time_local] '
                       '"$request" $status $bytes_sent '
                       '"$http_referer" "$http_user_agent" "$gzip_ratio"';

access_log /spool/logs/nginx-access.log compression buffer=32k;

Eine detaillierte Beschreibung aller verfügbaren Parameter kann der offiziellen Dokumentation entnommen werden.[5. NGINX -log_format]

Ich selbst verwende aktuell ausschließlich das Format combined. Dieses ist bereits in der Standardinstallation enthalten. Es sieht wie folgt aus:

log_format combined '$remote_addr - $remote_user [$time_local]  '
		    '"$request" $status $body_bytes_sent '
		    '"$http_referer" "$http_user_agent"';

Für die Protokollierung meiner Webanwendungen wird daher folgendes in die jeweiligen Server-Direktiven eingetragen:

server {
...
access_log /var/www//logs/_access.log combined;
...
}

Falls man das Access-Log deaktivieren möchte, kann man dies durch den folgenden Eintrag erreichen:

access_log off;

Nun werden schon mal alle Log-Dateien nach Webanwendungen getrennt in das Verzeichnis /var/www//logs geschrieben.

Im nächsten Abschnitt gehe ich darauf ein, wie man verhindert, dass die Festplatte mit Log-Dateien vollgeschrieben wird.

Rotation der NGINX Log-Dateien

Zum Rotieren der Logs verwende ich die Anwendung logrotate. Diese ist bei Ubuntu bereits in der Standardinstallation enthalten.

Es wird ein Skript im Verzeichnis /etc/logrotate.d erstellt und folgender Inhalt eingefügt.

/var/www//logs/*.log {
        daily
        missingok
        rotate 31
        compress
        delaycompress
        notifempty
        sharedscripts
        postrotate
                [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
        endscript
}

Mit diesem Skript wird logrotate angewiesen, die Log-Dateien im Verzeichnis /var/www//logs/ täglich zu rotieren und die letzten 31 Log-Dateien zu behalten. Die Log-Datei wird nicht rotiert, falls sie leer ist, also keine Einträge enthält. Die älteren Dateien werden dabei komprimiert, um Speicherplatz zu sparen.

Die generelle Beschreibung von logrotate würde den Rahmen dieses Artikels sprengen. Weitere Informationen sind in der Manpage zu finden.[6. logrotate(8) – Linux man page]

Damit ist der verspätete Frühjahrsputz auf diesem Server beendet.

SSL-Schwachstelle bedroht Web- und Mail-Server

Diverse Seiten im Internet berichten über eine neue SSL-Schwachstelle, die durch eine sogenannte Logjam-Attacke ausgenutzt werden kann.[1. Logjam-Attacke: Verschlüsselung von zehntausenden Servern gefährdet] [2. Tausende Web-Server betroffen: Neue HTTPS-Sicherheitslücke aufgetaucht] [3. Guide to Deploying Diffie-Hellman for TLS]

Dieser Artikel beschreibt, wie sich NGINX, Postfix und Dovecot härten lassen. Damit sind diese Dienste nicht mehr verwundbar für die aktuelle SSL-Schwachstelle.

Ich beschreibe die drei genannten Dienste, da ich diese selbst auf mehreren Ubuntu-Servern betreibe. Für weitere Dienste schaut bitte in der Quelle unter Punkt „3.“ nach.

Zu Beginn wird eine Diffie-Hellman-Group für den Server generiert:

openssl dhparam -out dh_params.pem 2048

Anschließend folgt die Härtung der einzelnen Dienste.

NGINX

Im server Block der jeweiligen Webpräsenz werden die zu verwendenden „Cipher Suites“ und Diffie-Hellman-Parameters angegeben:

ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

ssl_prefer_server_ciphers on;

ssl_dhparam /pfad/zur/dh_params.pem;

Anschließend wird der Dienst zur Übernahme der Einstellungen neu geladen:

sudo service nginx reload

Postfix

Beim MTA Postfix werden beide Parameter in der Datei /etc/postfix/main.conf eingetragen:

smtpd_tls_mandatory_exclude_ciphers = aNULL, eNULL, EXPORT, DES, RC4, MD5, PSK, aECDH, EDH-DSS-DES-CBC3-SHA, EDH-RSA-DES-CDC3-SHA, KRB5-DE5, CBC3-SHA
smtpd_tls_dh1024_param_file = /pfad/zur/dh_params.pem

Auch hier werden die Einstellungen anschließend neu eingelesen:

sudo service postfix reload

Dovecot

Die beiden Parameter werden in die Datei /etc/dovecot/conf.d/10-ssl.conf eingetragen:

ssl_cipher_list=ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
ssl_prefer_server_ciphers = yes

#regenerates every week
ssl_dh_parameters_length = 2048

Und auch hier werden die Änderungen anschließend eingelesen:

sudo doveadm reload

Damit sollte man vor dieser Schwachstelle vorerst geschützt sein.

Cisco AnyConnect VPN Client unter Ubuntu installieren

Dieser Artikel beschreibt die Installation des Cisco AnyConnect VPN Client unter Ubuntu Trusty Tahr.

Laut einem Dokument[1. AnyConnect – Unterstützte Betriebssysteme und Systemvoraussetzungen], dass ich bei der Universität Bielefeld gefunden habe, werden Ubuntu 9.x und 10.x unterstützt. Ich konnte den Client jedoch auch problemlos unter Ubuntu 14.01.1 LTS installieren.

Dazu habe ich einfach das Installationsskript vpnsetup.sh heruntergeladen, ausführbar gemacht und ausgeführt.

:~$ wget https://github.com/Tronde/Schriftrolle/blob/master/vpnsetup.sh
:~$ sudo chmod u+x vpnsetup.sh
:~$ sudo ./vpnsetup.sh

Ist das Setup durchgelaufen, kann der Client aus dem Anwendungsmenü heraus gestartet werden.

cisco anyconnect secure mobility client

Cisco AnyConnect Secure Mobility Client

In das Feld „Connect to“ müsst ihr nun nur noch eureb Verbindungsserver eintragen und schon kann es losgehen.

Die Installation ist damit abgeschlossen. Das war es schon. :-)

Das lokale Paketarchiv aufräumen

Während ich im Artikel „Automatische Installation von Sicherheitsupdates“ beschrieben habe, wie man Sicherheitsupdates unter Ubuntu automatisch installieren lassen kann, werde ich hier kurz beschreiben, wie das lokale Paketarchiv automatisch bereinigt werden kann.

In der Datei /etc/apt/apt.conf.d/10periodic gibt es unter anderem den folgenden Eintrag:

APT::Periodic::AutocleanInterval "0";

Dieser Wert gibt an, in welchem Intervall (in Tagen) das lokale Archiv der Paketquellen bereinigt werden soll. Dieser Wert steht standardmäßig auf „0“. Damit wird das Paketarchiv gar nicht automatisch bereinigt. Um dies zu ändern trägt man hier einfach einen Wert seiner Wahl ein. So lasse ich das Paketarchiv bspw. alle 28 Tage bereinigen:

APT::Periodic::AutocleanInterval "28";

Viele Grüße
Tronde

Testinstanz für einen WordPress Blog erstellen

Um Änderungen an der Konfiguration und den Dateien meines Blogs gefahrlos testen zu können, möchte ich eine Testinstanz meines Blogs einrichten. Die dazu erforderlichen Schritte werde ich in diesem Artikel dokumentieren.

Dieser Artikel ist keine Schritt-für-Schritt-Anleitung und kein Tutorial, welches man blind befolgen kann. So gehe ich hier z.B. nicht auf die allgemeine Webserverkonfiguration ein. Falls ihr hierzu Hilfe benötigt, schaut bitte in der offiziellen Dokumentation zu eurem Webserver nach.

Voraussetzungen

Ich betreibe meinen Blog bei einem deutschen Webhosting-Provider. Bei diesem habe ich über ein Kundencenter via FTP-Zugang Zugriff auf die Konfiguration und die Dateien meines Blogs.

Bei einem anderen deutschen Unternehmen betreibe ich noch einen Linux-Root-Server, welcher sich hervorragend eignet, um darauf eine Testinstanz einzurichten.

Daten sichern

Um die spätere Testinstanz mit den Daten des Live-Blogs füttern zu können, werden die Daten aus dem aktuellen Blog zuerst einmal gesichert.

Die Sicherung umfasst die Datenbank und das DocumentRoot des Blogs.

Zur Sicherung der Datenbank verwende ich das Tool MySQLDumper.[1. http://www.mysqldumper.de/] Mit diesem Werkzeug kann man über eine einfach zu bedienende Weboberfläche ein Backup seiner Datenbank erstellen. Dieses Backup wird anschließend zur weiteren Verwendung auf den lokalen Rechner heruntergeladen.

Im zweiten Schritt wird das DocumentRoot-Verzeichnis des Blogs gesichert. Hierzu nutze ich den FTP-Client FileZilla.[2. http://www.filezilla.de/]

Serverkonfiguration

Auf dem Linux-Root-Server laufen Ubuntu Server 14.04 LTS und der Webserver NGINX.[3. http://nginx.org/]

Für meine Webseiten verwende ich eine einheitliche Verzeichnisstruktur:

example.org/
├── logs
│   ├── access.log
│   └── error.log
└── public
    └── index.html

2 directories, 3 files

So wird für jede Webseite in separate Log-Dateien geschrieben, was eine Fehleranalyse deutlich erleichtern kann. Die Dateien der Webseite liegen im Verzeichnis public. Die Datei index.html stellt dabei aktuell nur einen Platzhalter dar, bis die gesicherten Daten des Live-Blogs eingespielt werden.

Den Platzhalter kann man nutzen, um zu testen, ob der Webserver korrekt konfiguriert ist und die im Verzeichnis public abgelegte Seite auch ausliefert.

Um die spätere Testinstanz über die gleiche URL aufrufen zu können, wie den Live-Blog, wird ein Eintrag in die /etc/hosts eingefügt. So erreicht man, dass die URL des Live-Blogs nun auf die IP-Adresse des Servers mit der Testinstanz zeigt.

Hat man alles richtig konfiguriert, wird man vom Webserver mit einem „Hallo Welt!“ begrüßt.

Zugriff beschränken

Während man eine neue Konfiguration testet, können Fehler passieren. Diese können zu Folge haben, dass der Blog nicht korrekt ausgeliefert wird. Für gewöhnlich ist nicht gewünscht, dass ein Benutzer dies sieht. Evtl. möchte man auch einfach nicht, dass Neuerungen zufällig schon von Besuchern entdeckt werden, bevor sie in den Live-Blog überführt wurden.

Daher scheint es sinnvoll den Zugriff auf die Testinstanz einzuschränken und einen Aufruf der Testinstanz erst nach erfolgreicher Authentifizierung mit Benutzername und Passwort zu erlauben.

Dazu eignet sich für NGINX-Benutzer das Modul ngx_http_auth_basic_module.[4. http://nginx.org/en/docs/http/ngx_http_auth_basic_module.html] Nutzer von Apache können .htaccess-Dateien[5. .htaccess – Wikipedia] verwenden. Da ich selbst einen NGINX betreibe, gehe ich im Folgenden auf das zuerst genannte Modul ein.

Zuerst wird die .htpasswd-Datei erstellt, welche Benutzernamen und Passwort für die Authentifizierung enthält.

sudo touch /etc/nginx/conf.d/.htpasswd

Um das Passwort in verschlüsselter Form speichern zu können wird das Paket apache2-utils benötigt. Dieses enthält das Programm htpasswd, mit welchem Benutzername und Passwort erzeugt werden.

:~$ sudo htpasswd -c /etc/nginx/conf.d/.htpasswd BENUTZERNAME
New password: 
Re-type new password: 
Adding password for user BENUTZERNAME

Der Benutzer, unter dessen Kontext der Webserver (NGINX) läuft, muss Zugriff auf die .htpasswd erhalten.

:~$ sudo chown www-data:www-data /etc/nginx/conf.d/.htpasswd

Nun öffnet man die Konfigurationsdatei für die Testseite, welche für gewöhnlich unterhalb von /etc/nginx/sites-available/ liegt. Hier ist folgende Konfiguration zu integrieren:

location / {
    auth_basic           "Authentifizierung erforderlich!";
    auth_basic_user_file conf.d/.htpasswd;
}

auth_basic aktiviert die Überprüfung von Benutzername und Passwort. auth_basic_user_file gibt den Pfad an, wo die .htpasswd-Datei liegt, gegen die geprüft wird.

Mit einem „configtest“ kann man seine Konfiguration auf syntaktische Fehler überprüfen und bei Bestehen der Prüfung die Konfiguration neu laden.

:~$ sudo service nginx configtest 
 * Testing nginx configuration                    [ OK ] 
jkastning@rs212997:~$ sudo service nginx reload
 * Reloading nginx configuration nginx            [ OK ]

Ab jetzt fragt NGINX nach einem Benutzernamen und Passwort, bevor er die Webseite mit dem „Hallo Welt.“-Slogan ausliefert.

Daten einspielen

Wenn der Webserver prinzipiell mit dem weiter oben erstellten vHost funktioniert und die „Hallo Welt.“-Seite ausliefert, werden als nächstes die Daten aus dem Live-Blog eingespielt.

Dazu werden der Datenbank-Dump und das im ersten Schritt gesicherte Verzeichnis auf den Linux-Root-Server hochgeladen. Ich habe dazu wieder FileZilla und das SFTP-Protokoll genutzt.

DocumentRoot

Die gesicherten Dateien und Verzeichnisse, müssen in das DocumentRoot-Verzeichnis der Testinstanz kopiert werden. In meinem Beispiel ist das das Verzeichnis /var/www/example.org/public/. Mit folgendem Befehl wird sichergestellt, dass der Webserver auch alle Dateien lesen kann.

sudo chgrp -R www-data /var/www/example.org/public

Die Datenbank

Bevor die Datenbanksicherung eingespielt werden kann, muss eine Datenbank und ein Datenbankbenutzer angelegt werden.

Wer hierbei Hilfe zur Syntax benötigt, kann im Artikel „Häufig verwendete MySQL-Befele“ nachlesen.

Ist dies erledigt, kann die Datenbanksicherung mit folgendem Befehl eingespielt werden.

mysql -u root -p db_name < db_sicherung.sql

Ober der angelegte Benutzer auch wirklich Zugriff auf die Datenbank hat, kann man mit dem folgenden Befehl überprüfen. Wenn alles stimmt, kann man sich so an der Datenbank anmelden.

mysql -u db_benutzer -p db_name

Fertig. Mein Testblog läuft und ich kann mich jetzt daran machen, mit der WordPress-Konfiguration zu experimentieren. :-)

Update vom 24.12.2016

Bei der gestrigen Aktualisierung der Testinstanz musste ich feststellen, dass zwar die Startseite des Blogs geladen wird, jedoch alle Artikelaufrufe in einen 404-Fehler laufen.

Die Ursache für diese Fehler liegt darin begründet, dass ich mit Nginx einen anderen Webserver verwendet, als mein Webhoster. Dieser behandelt die Permalinks von WordPress anders, als z.B. ein Apache-Webserver mit dem Modul mod_rewrite.

Eine Lösung für dieses Problem habe ich ziemlich schnell in der Nginx Library[6. Nginx Library - WordPress Permalinks {en}] gefunden. Es wird die try_files-Direktive[7. Nginx try_files directive {en}] verwendet, um aufgerufene URLs zur weiteren Behandlung an die index.php von WordPress weiterzuleiten.

Die Einrichtung ist relativ einfach. Das Vorgehen unterscheidet sich, je nach dem ob WordPress direkt im Wurzelverzeichnis einer Domain oder in einem Unterverzeichnis installiert ist. Im Folgenden werden beide Fälle betrachtet.

WordPress im Wurzelverzeichnis

Liegt die WordPress-Installation im Wurzelverzeichnis der Domain z.B. unter http://www.example.com, sucht man in der Konfigurationsdatei der Seite nach dem Block location /. Diesem Block wird folgende Zeile hinzugefügt:

try_files $uri $uri/ /index.php?$args;

Der vollständige Block sollte anschließend wie folgt aussehen:

location / {
    index index.php index.html index.htm;
    try_files $uri $uri/ /index.php?$args;
}

Abschießend muss die Konfiguration von Nginx neu geladen werden. Nun sollten auch die 404-Fehler behoben sein.

WordPress in einem Unterverzeichnis

Liegt die WordPress-Installation in einem Unterverzeichnis der Domain z.B. unter http://www.example.com/wordpress, muss der Konfigurationsdatei ein Block nach dem Muster location /wordpress/ hinzugefügt werden, welcher wie folgt aussieht:

location /wordpress/ {
  try_files $uri $uri/ /wordpress/index.php?$args;
}

Abschießend muss die Konfiguration von Nginx neu geladen werden. Nun sollten auch die 404-Fehler behoben sein.

Häufig benötigte MySQL-Befehle

In diesem Artikel dokumentiere ich die von mir am häufigsten verwendeten und am schnellsten vergessenen MySQL-Befehle. So muss ich sie nicht jedes Mal in der offiziellen Dokumentation nachschlagen.[1. MySQL Documentation]

Der Vollständigkeit halber beginne ich mit dem Befehl, mit dem man sich mit einem MySQL-Server verbindet, welcher auf dem localhost läuft.

$ mysql -u BENUTZERNAME -p
Password: 

Eine neue Datenbank kann mit dem folgenden Befehl erstellt werden[2. CREATE DATABASE Syntax]:

CREATE {DATABASE | SCHEMA} [IF NOT EXISTS] db_name
    [create_specification] ...

create_specification:
    [DEFAULT] CHARACTER SET [=] charset_name
  | [DEFAULT] COLLATE [=] collation_name

Beispiel:

CREATE DATABASE db_name;

Einen MySQL-Benutzeraccount erstellt man mit dem Befehl[3. CREATE USER Syntax]:

CREATE USER user_specification [, user_specification] ...

user_specification:
    user [ identified_option ]

auth_option: {
    IDENTIFIED BY 'auth_string'
  | IDENTIFIED BY PASSWORD 'hash_string'
  | IDENTIFIED WITH auth_plugin
  | IDENTIFIED WITH auth_plugin AS 'hash_string'
}

Beispiel:

CREATE USER 'pusemuckel'@'localhost' IDENTIFIED BY 'password';

Mit dem folgenden Kommando werden dem erstellten Benutzer Zugriffsrechte auf die erstellte Datenbank gewährt.[4. GRANT Syntax] Mit diesen Berechtigungen kann der Benutzer neue Tabellen in der Datenbank erstellen, Daten in Tabellen einfügen oder auch die gesamte Datenbank löschen.

GRANT ALL PRIVILEGES ON db_name.* TO 'pusemuckel'@'localhost';

Die letzten beiden Schritte können auch verkürzt mit folgendem Kommando ausgeführt werden:

GRANT ALL ON db_name.* TO 'pusemuckel'@'localhost' IDENTIFIED BY 'password';

Hat man neue Benutzer angelegt, oder die Berechtigungen bestehendender Benutzer geändert, werden die neuen Berechtigungen mit dem folgenden Kommando geladen:

FLUSH PRIVILEGES;

Irgendwann möchte man in seiner Datenbank auch wieder aufräumen und nicht mehr benötigte Datenbanken und Benutzer löschen[5. DROP DATABASE Syntax] [6. DROP USER Syntax]. Dies wird mit den beiden folgenden Kommandos erledigt:

DROP DATABASE db_name;
DROP USER user;

Um eine SQL-Datei einzulesen, um z.B. ein SQL-Dump zu importieren ist folgender Befehl geeignet:

$ mysql -u USERNAME -p DATABASE_NAME < SQL-Datei

So, nun muss ich mich zukünftig nur noch daran erinnern, hier nachzuschauen, wenn mir mal wieder die Syntax entfallen ist. ;-)