Tag Archive: php

Web-Root und temporäre Dateien auf restriktivem Dateisystem

Web-Root noexec

In diesem Artikel beschreibe ich das Anlegen und Einbinden eines Images auf Basis des ext2-Dateisystems.
Dieses möchte ich als Web-Root sowie Verzeichnis für temporäre Dateien meiner PHP-Installation verwenden.
Eingebunden wird das Dateisystem unter Angabe diverser Sicherheitsoptionen, die etwa die Ausführung von Dateien unterbindet. Mehr dazu im Verlauf…

Mit einer Blockgröße von 1024 Byte, erstelle ich 5242880 Blocks gefüllt mit „0en“ in einem Image, das künftig die virtuelle Festplatte ist. Multipliziert erhalte ich 5120MB, 5GB:

dd if=/dev/zero of=/usr/local/noexec.img bs=1024 count=5242880

Nun erstelle ich ein einfaches ext2 Dateisystem, die Vorteile von ext3,4 sind nicht relevant. Journaling übernimmt bereits das übergeordnete Dateisystem, falls aktiv.

mke2fs /usr/local/noexec.img

Die Warnung mit „y“ bestätigen:

/usr/local/noexec.img is not a block special device.
Proceed anyway? (y,n) y

Da es sich nicht um ein „block device“ handelt, ist das okay.
Um das Dateisystem zukünftig automatisch und unter Angabe der Sicherheitsoptionen „nodev“, „nosuid“ und „noexec“ einzubinden, wird nun ein Eintrag in die Datei „/etc/fstab“ erstellt.
„loop“ wird benötigt, da das „Gerät“ auf sich selbst zurück zeigt, ergo kein eigenständiges Gerät ist.

echo "/usr/local/noexec.img /mnt/noexec ext2 loop,nodev,nosuid,noexec 0 0" >> /etc/fstab

Zu den „noXYZ“-Optionen eine kleine Erklärung:

  • nodev: Untersagt den Zugriff auf „device nodes“ aus – üblicherweise – „/dev“
  • noexec: Untersagt die Ausführung von Dateien
  • nosuid: Untersagt „setuid“, um Privilegien abzuändern

Zuerst erstelle ich das Zielverzeichnis, in welches das Dateisystem sofort eingebunden wird („mount -a“), anschließend wird die Verzeichnisstruktur erstellt:

mkdir /mnt/noexec
mount -a
mkdir /mnt/noexec/{tmp,www}

Nun kann eine rekursive Kopie des aktuellen Web-Roots angelegt werden. Darauf folgen Anpassungen der Rechte/Besitzer, in diesem Fall gehört das Web-Root dem Benutzer „www-data“:

cp -R /altes/www-verzeichnis/* /mnt/noexec/www
chown -R www-data: /mnt/noexec/www/
chmod 777 /mnt/noexec/tmp

Wird ein FPM-Worker für PHP eingesetzt, kann in der jeweiligen Konfiguration, etwa „/etc/php5/fpm/pool.d/www.conf“, Folgendes eingefügt werden. Diese Einträge haben gegenüber jenen via „php.ini“ höhere Priorität:

php_admin_value[open_basedir] = /mnt/noexec
php_admin_value[upload_tmp_dir] = /mnt/noexec/tmp
php_admin_value[session.save_path] = /mnt/noexec/tmp
php_admin_value[sys_temp_dir] = /mnt/noexec/tmp
php_admin_value[disable_functions] = exec,system,passthru,shell_exec,popen,escapeshellcmd,proc_open,proc_nice,ini_restore

Oder direkt in der Konfiguration des entsprechenden PHP-Moduls, z.B. „/etc/php5/cgi/php.ini“ oder „/etc/php5/cli/php.ini“:

open_basedir = /mnt/noexec
upload_tmp_dir = /mnt/noexec/tmp
session.save_path = /mnt/noexec/tmp
sys_temp_dir = /mnt/noexec/tmp
disable_functions = exec,system,passthru,shell_exec,popen,escapeshellcmd,proc_open,proc_nice,ini_restore

„disable_functions“ beinhaltet Funktionen, die zur Auführung von Befehlen benutzt werden können. Diese sollten generell deaktiviert werden.
Zudem hat „open_basedir“ keinen Einfluss auf den Pfad von Binaries, die etwa durch exec(„/bin/ls“) ausgeführt werden!
Auch befindet sich „ls“ nicht auf dem abgesicherten Dateisystem, weshalb die Ausführung nicht blockiert würde.

Zum Abschluss den Web-Server auf das neue Web-Root „/mnt/noexec/www“ zeigen lassen.

Dash: Live System-Monitoring im Web-Browser

Dash Preview

Dash ist ein optisch ansprechendes Web-Dashboard für live System-Monitoring eines Computers im Browser. Es unterscheidet sich vor allem durch seine moderne Optik und Benutzerfreundlichkeit.
Zur Zeit befindet es sich noch im Beta-Status, verrichtet seine Dienste auf unterstützten Systemen aber bereits sehr gut. Eine Aufzeichnung der Aktivität findet nicht statt.

Voraussetzung für die Installation

Zu den unterstützten Systemen zählen bislang:

  • Arch
  • Debian 6, 7
  • Ubuntu 11.04+
  • Linux Mint 16+

Offiziell unterstützte Web-Server sind Nginx und Apache.

Benutzer von Debian-Derivaten installieren vorab das Paket „php5-json“:

sudo apt-get install php5-json

Womöglich ist „php5-json“ durch das Meta-Paket „php5-common“ bereits installiert. Neuere Ubuntu-Versionen trennten das Modul jedoch aus diesem Paket.

Bei der Einrichtung gehe ich vom Web-Root „/var/www/linux-dash-master“ aus. „www-data“ ist ausführender Besitzer des Web-Servers.

Installation

cd /var/www
wget https://github.com/afaqurk/linux-dash/archive/master.zip && unzip master.zip && rm master.zip
chown -R www-data: linux-dash-master

Achtung: Dash kann „dnsmasq“ auf aktive Leases überwachen. Hierzu liest es die Datei „/var/lib/misc/dnsmasq.leases“. Falls „open_basedir“ in PHP gesetzt ist, bitte „/var/lib/misc“ als Pfad ergänzen.

Im Anschluss kann Dash über den Web-Browser aufgerufen werden.

Falls der Zugriff beschränkt werden soll, kann mit Hypertext Access gearbeitet werden. Unter dem Punkt „CGP absichern“, habe ich hier bereits ausführlich den Vorgang beschrieben. Abgesehen vom Namen, lässt sich die Einrichtung so übernehmen.
Sollte Aufzeichnung der Systemaktivität eine Anforderung sein, empfehle ich den gesamten Artikel.

Update

Da Dash sich in aktiver Entwicklung befindet, kann ein Blick in das Git-Repository von Zeit zu Zeit nicht schaden.
Der Update-Vorgang entspricht eins zu eins der Installation. Vorausgesetzt ihr habt euch an den Artikel gehalten.

Anhang

Dash: Live System-Monitoring im Web-Browser
Dash

dpkg.log mit PHP auswerten

PHP

phpdpkg
dpkg.log mit PHP auswerten
Ein kleines quick-and-dirty Script, um die Datei dpkg.log mit PHP auszuwerten.
Zu sehen sind die X zuletzt installierten- sowie entfernten Pakete.

Die Logs rotieren in der Regel jeden Monat!

Getestet unter Debian Wheezy und Ubuntu 12.04 mit DPKG 1.16.x sowie PHP >= 5.3.

Falls „open_basedir“ via PHP-Option gesetzt ist, muss „/var/log“ freigegeben werden.

Einstellungsmöglichkeiten sind in den ersten 3 Variablen gegeben.








Installierte Pakete (max. )

"; echo ""; echo ""; echo ""; $at++; } } ?>
Installiert amNameArch.Version
", $date->format('d.m.Y H:i:s'), "", $pkgdetails[0], "", $pkgdetails[1], "", $lines_ex[5], "

Entfernte Pakete (max. )

"; echo ""; echo ""; echo ""; $at++; } } ?>
Entfernt amNameArch.Version
", $date->format('d.m.Y H:i:s'), "", $pkgdetails[0], "", $pkgdetails[1], "", $lines_ex[4], "

How-To: nginx, PHP-FPM (multiple Sockets), MySQL und Subdomains in Debian Wheezy/Jessie

Im Gegensatz zu dem Klassiker „LAMP“ (Linux, Apache, MySQL und PHP), beschreibe ich hier die Methode „LNMP“, also weniger Apache, mehr nginx. Als kleine Zugabe soll jeder Server (jede Site/Subdomain) einen eigenen PHP Socket zugeteilt bekommen und durch „open_basedir“ der Zugriff auf die eigene Site limitiert werden.

Changelog

  • 07.06.2015 – PHP: Fix für Debian Jessie (danke an Kevin für den Hinweis!)
  • 03.08.2014 – „date.timezone“ und „listen.allowed_clients“ gesetzt
  • 17.01.2014 – Kritische Funktionen deakvieren
  • 18.05.2014 – „listen.*“-Parameter für PHP5-FPM, welche nun Pflicht sind

Das Szenario

Um alles ein wenig Anschaubarer zu machen, gehe ich von folgendem Umstand aus:

Das Szenario
Das Szenario

Es wird davon ausgegangen, dass für die Domäne „domain.tld“ ein A-Record „*“ besteht, welcher alle Anfragen auf die öffentliche IP des Webservers leitet. Während des Schreibens habe ich meine lokale „hosts“ Datei („/etc/hosts“) abgeändert, um die Konfiguration zu testen, wobei „192.168.67.129“ die IP meines Test-Servers ist. Zum Ausprobieren ohne Domäne sehr hilfreich:

192.168.67.129 domain.tld
192.168.67.129 www.domain.tld
192.168.67.129 notavailable.domain.tld
192.168.67.129 custom1.domain.tld
192.168.67.129 custom2.domain.tld

Wie bereits beschrieben erhalten alle Sites einen eigenen PHP Socket mit zusätzlichem Attribut für das „open_basedir“.

Installation

Es reicht grundsätzlich die Installation folgender Pakete aus:

apt-get install nginx mysql-server mysql-client php5-fpm php5-mysql

Während der Installation von MySQL wird ein Passwort für den root-Benutzer abgefragt, welches frei wählbar ist aber sich von dem des lokalen root-Benutzers Unterscheiden sollte.

Natürlich gibt es verschiedene nginx Pakete mit unterschiedlichen Funktionen. Das Paket „nginx“ installiert z.B. „nginx-full“. Eine Übersicht gibt es hier

Abhängig von der später eingesetzten Software (Drupal, Joomla, WordPress, Webmail etc.), können noch weitere PHP-Module benötigt werden. Diese Abhängigkeiten werden natürlich in den Dokumenten der Anbieter beschrieben. „php5-mysql“ ist das Modul zur Anbindung an die die MySQL Datenbank.

Konfiguration

Ein kleines bisschen Sicherheit

Es ist immer zu empfehlen, die Version des Webservers und PHP zu verstecken. Selbstverständlich werden so keine Angriffe verhindert, allerdings kann das Auffinden von anfälligen (veralteten) Versionen erschwert werden. Folgende zwei Dateien dazu editieren:

/etc/nginx/nginx.conf:

server_tokens off;

/etc/php5/fpm/php.ini:

expose_php = Off

Für alle Sockets sollten zudem kritische Funktionen zum Ausführen von Anwendungen deaktiviert werden. Diese können für einzelne Sockets bei Bedarf freigegeben werden:

/etc/php5/fpm/php.ini:

disable_functions=phpinfo,exec,shell_exec,system,passthru

Auch MySQL kann schnell minimal abgesichert werden, dazu „mysql_secure_installation“ ausführen:

Enter current password for root (enter for none): Passwort von der Installation des MySQL eingeben
Change the root password? [Y/n] n
Alles Weitere mit Enter bestätigen

Einrichtung der Sites

Zuerst die Sites bereinigen:

rm /etc/nginx/sites-*/*

Und im Anschluß vier neue Sites anlegen (z.B. via „nano“):

/etc/nginx/sites-available/default:

server {
       listen         80;
       rewrite        ^ http://www.domain.tld permanent;
}

/etc/nginx/sites-available/www:

server {
        listen 80;

        root /usr/share/nginx/www;
        access_log /var/log/nginx/access_log_www;
        index index.html index.htm index.php;

        server_name domain.tld www.domain.tld;

        location / {
        }

        location ~ \.php$ {
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass unix:/var/run/php5-fpm-www.sock;
                fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name;
                fastcgi_index index.php;
                include fastcgi_params;
        }
}

/etc/nginx/sites-available/custom1:

server {
        listen 80;

        root /usr/share/nginx/custom1;
        access_log /var/log/nginx/access_log_custom1;
        index index.html index.htm index.php;

        server_name custom1.domain.tld;

        location / {
        }

        location ~ \.php$ {
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass unix:/var/run/php5-fpm-custom1.sock;
                fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name;
                fastcgi_index index.php;
                include fastcgi_params;
        }
}

/etc/nginx/sites-available/custom2:

server {
        listen 80;

        root /usr/share/nginx/custom2;
        access_log /var/log/nginx/access_log_custom2;
        index index.html index.htm index.php;

        server_name custom2.domain.tld;

        location / {
        }

        location ~ \.php$ {
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass unix:/var/run/php5-fpm-custom2.sock;
                fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name;
                fastcgi_index index.php;
                include fastcgi_params;
        }
}

Hinweis: Durch „server_name“ reagieren die Sites nur auf eben ihren Namen.

Nun die Sites für nginx sichtbar machen:

cd /etc/nginx/sites-enabled
ln -s /etc/nginx/sites-available/* .

Wichtig: Die Site „default“ ist die Anlaufstelle für Aufrufe der IP-Adresse oder unbekannten Sub-Domains. Für unsere Konfiguration werden diese Aufrufe auf www.domain.tld umgeleitet, dafür steht der „redirect“.

Einrichtung der PHP Sockets

Der PHP FastCGI Process Manager (php-fpm) durchsucht das Verzeichnis „/etc/php5/fpm/pool.d“ nach zu erstellenden Sockets. Alle vorhandenen vorab löschen:

rm /etc/php5/fpm/pool.d/*

Drei neue Konfigurationen werden hier erstellt, die „Listener“ entsprechen denen aus den Sites:

/etc/php5/fpm/pool.d/www.conf:

[www]
listen = /var/run/php5-fpm-www.sock
listen.backlog = 4096
user = www-data
group = www-data
listen.owner = www-data
listen.group = www-data
listen.allowed_clients = 127.0.0.1
listen.mode = 0660
pm = dynamic
pm.max_children = 10
pm.start_servers = 3
pm.min_spare_servers = 3
pm.max_spare_servers = 5
pm.max_requests = 40
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp
php_admin_value[open_basedir] = /usr/share/nginx/www/:/tmp/
php_admin_value[date.timezone] = Europe/Berlin

/etc/php5/fpm/pool.d/custom1.conf:

[custom1]
listen = /var/run/php5-fpm-custom1.sock
listen.backlog = 4096
user = www-data
group = www-data
listen.owner = www-data
listen.group = www-data
listen.allowed_clients = 127.0.0.1
listen.mode = 0660
pm = dynamic
pm.max_children = 10
pm.start_servers = 3
pm.min_spare_servers = 3
pm.max_spare_servers = 5
pm.max_requests = 40
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp
php_admin_value[open_basedir] = /usr/share/nginx/custom1/:/tmp/
php_admin_value[date.timezone] = Europe/Berlin

/etc/php5/fpm/pool.d/custom2.conf:

[custom2]
listen = /var/run/php5-fpm-custom2.sock
listen.backlog = 4096
user = www-data
group = www-data
listen.owner = www-data
listen.group = www-data
listen.allowed_clients = 127.0.0.1
listen.mode = 0660
pm = dynamic
pm.max_children = 10
pm.start_servers = 3
pm.min_spare_servers = 3
pm.max_spare_servers = 5
pm.max_requests = 40
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp
php_admin_value[open_basedir] = /usr/share/nginx/custom2/:/tmp/
php_admin_value[date.timezone] = Europe/Berlin

Das Limit der Backlogs kann nicht einfach beliebig erhöht werden, das System hat hierfür ein globales Limit (Standard: 1000), welches in der Datei /etc/sysctl.conf eingestellt werden kann:

echo "net.core.netdev_max_backlog=4096" >> /etc/sysctl.conf
echo "net.core.somaxconn=4096" >> /etc/sysctl.conf
echo "net.ipv4.tcp_max_syn_backlog=4096" >> /etc/sysctl.conf
sysctl -p # Änderungen einlesen

Die „pm“ Attribute haben großen Einfluss auf die Leistung und Resourcenverwendung von PHP auf das System. Damit auch auf die Website. Zur besseren Konfiguration nach Bedarf einfach Google bemühen, da eine Erklärung zu komplex für dieses How-To wäre. Läuft auf der Site „custom1“ beispielsweise eine weitaus komplexere Anwendung (~Joomla) als auf „custom2“ (~Postfixadmin), sollten die Einstellungen natürlich angepasst werden.
Kleiner Hinweis: Bei sehr, sehr vielen Sites mit vielen Prozessen, könnte irgendwann auch die Einstellung „fs.file-max“ in der Datei „/etc/sysctl.conf“ wichtig werden.

Die „php_admin_value“ Attribute können ebenfalls eingesetzt werden, um die globalen Einstellungen aus der Datei „/etc/php5/fpm/php.ini“ zu überschreiben. Zum Beispiel lassen sich eigene memory_limits einrichten.

Verzeichnisse und Dateien anlegen

Die Verzeichnisse der Sites dieses Beispiels liegen unter „/usr/share/nginx“.
Achtung: Möchte man einen anderen Ort hierfür wählen, müssen die Sockets („open_basedir“) sowie die Site Konfigurationen angepasst werden!

Das Webroot bereinigen und die neuen Ordner für die Sites anlegen, abschließend den Besitzer auf www-data abändern:

rm -r /usr/share/nginx/*
mkdir /usr/share/nginx/{www,custom1,custom2}
chown -R www-data:www-data /usr/share/nginx/*

Access- und Error-Log

Der Speicherort ist selbstverständlich frei wählbar, allerdings muss das Verzeichnis exisiteren, damit die Dateien von nginx automatisch erstellt werden.

In den Site Konfigurationen ist der Speicherort „/var/log/nginx“ für die Access Logs jedoch gezielt gewählt, da hier die automatisch mit der Installation von nginx erzeugte Regel für „logrotate“ greift. Die Regel befindet sich in der Datei „/etc/logrotate.d/nginx“. Wird der Speicherort also geändert, sollte man sich Gedanken über das rotieren der Logs machen, damit diese nicht den Speicherplatz unnötig belegen. Auch die Übersicht leidet nach einigen Monaten stark. Die vorhandene Regel kann hierzu als Beispiel dienen.

Es ließe sich zusätzlich das „error_log“ per Site definieren, worauf ich in diesem Beispiel verzichtet habe. Die Fehler werden in die global via „/etc/nginx/nginx.conf“ definierte Datei geschrieben. Der Pfad lautet in der Standard-Installation „/var/log/nginx/error_log“.

Der Site „default“ wurde kein solches Attribut erteilt, weshalb hierfür auch die Zugriffe in die globale Datei „/var/log/nginx/access_log“ geschrieben werden.

Fertigstellung

Die Dienste durchstarten

Abschließend die Änderungen übernehmen, indem die zugehörigen Dienste neugestartet werden:

service php5-fpm restart
service nginx restart

Konfiguration testen

Um ganz sicher zu gehen, dass nun alles funktioniert, kann in den Webroots jeweils eine Datei „index.php“ angelegt werden, die die PHP Konfiguration ausgibt („phpinfo“) sowie den Sitename anzeigt:

echo "Sitename CUSTOM1 

" > /usr/share/nginx/custom1/index.php echo "Sitename CUSTOM2

" > /usr/share/nginx/custom2/index.php echo "Sitename WWW

" > /usr/share/nginx/www/index.php

Sieht nicht schön aus, aber erfüllt seinen Zweck.

Zusätzlich die „Worker“ überprüfen:

ps aux | grep php-fpm

ps aux | grep php-fpm
ps aux | grep php-fpm
Die Ausgabe zeigt, dass die definierten Worker auch tatsächlich arbeiten. Da die Konfiguration gerade neugestartet wurde und die Prozesse noch nicht ausgelastet sind, entspricht die Anzahl dem Attribut „pm.start_servers“, nämlich 3.

Viel Erolg!