Beim Webserver gibt es sehr viel Optimierungspotential.
Wir werden hier erst mal apache mit einer übersichtlichen und einfach zu erweiternden Konfiguration erstellen und danach suExec mit FastCGI einsetzten um die Sicherheit auch bei mehreren Benutzern auf dem System (sog. „ISP Hosting Setup“) zu gewährleisten.
Damit dann die User ihre Daten auch hochladen können, wird ein FTP-Server (proftpd) installiert.
Danach werden wir noch einen MySQL-Datenbankserver erstellen.
Inhalt
apache Webserver
Zuerst werden mal die notwendigen Komponenten installiert:
yum install ImageMagick unzip mailx httpd mod_fcgid mod_ssl php php-cli php-common php-devel php-gd php-imap php-mbstring php-mcrypt php-mysql php-pdo php-pear chkconfig httpd on |
Gegegebnfalls kann man auf einem Server noch unerwünschte Software entfernen: 😉
yum remove firewalld firewalld-filesystem NetworkManager NetworkManager-libnm |
Damit müssten wir alles haben um auch komplexere Applikationen wie typo3 oder gallery2 laufen zu lassen.
[stextbox id=“tip“ caption=“Mit dem SCL an neue PHP Versionen kommen“]
Hat man eine „Enterprise“ Linux Distribution wie RHEL/CentOS bringt das einige Vorteile, wenn es um die Stabilität des Systems geht – So laufen auch alte Webapplikationen ohne Probleme, zumindest so lange man kein Major Release Upgrade macht. Doch das bringt auch Nachteile: Während mehreren Jahren wird beispielsweise PHP nicht updatet. Zwar fliessen Security Patches nach wie vor noch in rein (sog. „Backports“), doch neue Funktionen gibt es keine mehr. Eine kleine Abhilfe gibt es mit dem EPEL-Repository, doch auch da hinken die (etwas neueren) Pakete hinterher. Und neuere Web-Applikation fordern relativ neue PHP Versionen.
Eine Abhilfe gibt es mit dem RedHat Software Collections ( SCL ) Repository. Dies erstellt in /opt/rh/ eine komplett neue Umgebung und stellt dort auch die neuesten PHP Versionen zur Verfügung welche man sogar getrennt von der „alten“ PHP Version benutzen kann. So lässt man die alten Web-Applikationen mit dem alten PHP und neue mit dem neuen laufen.
Installieren kann man dies einfach mit yum:
yum install centos-release-scl |
und danach z.B. PHP 7 installieren:
yum install rh-php70 |
Und dazu die Module:
yum install rh-php70 rh-php70-php-bcmath rh-php70-php-cli rh-php70-php-common rh-php70-php-devel rh-php70-php-gd rh-php70-php-imap rh-php70-php-intl rh-php70-php-mbstring rh-php70-php-mysqlnd rh-php70-php-pdo rh-php70-php-pear rh-php70-php-pecl-apc rh-php70-php-process rh-php70-php-pspell rh-php70-php-xml rh-php70-php-mcrypt |
[/stextbox]
Die Problematik mit mod_php
Bei den meisten apache Webservern läuft PHP als Modul: mod_php. Dies ist sehr einfach einzubinden, allerdings auch sehr suboptimal. Denn dann werden sämtliche PHP Scripte mit dem Benutzernamen und Gruppe des Webservers ausgeführt (z.B. apache). Dies bedingt, dass „apache“ auf alle Web-Verzeichnisse zumindest lese, bei manchen sogar Schreibberechtigung erfordert. -So kann also jeder Benutzer auf dem Server sämtliche Inhalte der anderen Benutzer lesen und viele sogar beschreiben!
Workarounds gibts bei php mit dem sog. „save_mode“, was aber nur eine halbe Lösung ist.
Lässt man hingegen PHP als (fast-)CGI laufen, so werden alle PHP-Skripte mit dem tatsächlichen Web-Benutzer ausgeführt. Dabei gehts es sogar soweit, dass jeder Benutzer ein eigenes PHP und sogar eine eigene php.ini haben könnte.
Deshalb installierten wir oben apache mit der CGI-Variante und suEXEC, anstatt „mod_php“.
[stextbox id=“comment“ caption=“FastCGI vs. mpm-itk/mpm-peruser“]Neben FastCGI gibt es noch andere Ansätze um vhosts auf unterschiedlichen usern laufen zu lassen, eine davon mpm-itk und mpm-peruser.
Die letzteren beiden mpm-* arbeiten als apache-module und haben den Vorteil, dass die ganze apache-Instanz mit dem jeweiligen user ausgeführt wird; somit kann man mod_php wieder verwenden und hat das Problem auch schon bei anderen Script sprachen wie Perl gelöst; zudem muss man dem apache-user keine Leserechte auf den Web-Verzeichnissen mehr geben.
Diese, auf den ersten blick „elegante“ Lösung hat jedoch einen grossen Haken: Damit der apache die einzelnen vhost-Instanzen als andere user starten kann, muss der Kernprozess selber als root laufen!
Theoretisch könnte somit ein Hacker, falls er eine Sicherheitslücke ausnutzen kann bevor der apache den vhost initiiert (gemäss des Autors von mpm-itk am ehesten bei mod_ssl) root rechte auf dem server erlangen!
Und sollte man einmal vergessen bei einem vhost explizit einen user anzugeben würde dieser vhost ebenfalls mit root-rechten(!) laufen.
Natürlich ist das „Risiko“, dass das passiert sehr klein, mir ist jedoch trotzdem nicht wohl dabei einen Prozess welcher derart nach aussen offen und bekannt ist als root laufen zu lassen. – Es muss letztendlich jeder admin selber bestimmen, was ihm wichtiger ist. Eine Anleitung für mpm-itk gibts im Blog-Beitrag: Hosting absichern: Apache2 mit ITK-MPM.
Ausserdem: Wenn man wirklich auf „highest security“ aus ist, würde man einen reverse-proxy server vor den apache stellen, was zwar die aufwändigste, aber auf jeden Fall die sicherste Lösung ist.[/stextbox]
Konfiguration
Als erstes müssen wir nun sicherstellen, dass die Datei: /etc/httpd/conf.d/php.conf entweder leer ist, oder nicht existiert. Ist die Datei da, einfach alles darin löschen und abspeichern.
Alternativ lässt sich das auch mit diesem Kommando machen:
test -f /etc/httpd/conf.d/php.conf && echo "# This module is disabled">/etc/httpd/conf.d/php.conf |
Wo das php binary nun liegt finden wir heraus mit:
which php-cgi |
Bei Debian und CentOS ist dies normalerweise: /usr/bin/php-cgi.
Nun müssen wir testen ob es sich auch um die fcgi-Version handelt:
/usr/bin/php-cgi -v |
Hier müsste so was wie hier ausgegeben werden (siehe das „fcgi“ in den Klammern):
PHP 5.2.10 (cgi-fcgi) (built: Nov 13 2009 11:39:02) Copyright (c) 1997-2009 The PHP Group Zend Engine v2.2.0, Copyright (c) 1998-2009 Zend Technologies |
[stextbox id=“note“ caption=“Hinweis“]Wir könnten nun grundsätzlich php auch selbst kompilieren und dies verwenden. Dabei müsste man einfach darauf achten, dass php mit der fcgi-option kompiliert wird.
Der Nachteil wäre aber dann, dass wir php nicht mehr automatisch mit dem Paketmanager upgraden könnten.[/stextbox]
Nun noch überprüfen, ob FCGID aktiviert ist, dazu müsste im file: /etc/httpd/conf.d/fcgid.conf oder in der httpd.conf so etwas stehen:
LoadModule fcgid_module modules/mod_fcgid.so AddHandler fcgid-script fcg fcgi fpl AddHandler fcgid-script php SocketPath run/mod_fcgid SharememPath run/mod_fcgid/fcgid_shm |
Anlegen der Benutzer und Gruppen
Nun werden wir für jeden Benutzer einen Account ohne shell Zugriff, sowie die gemeinsame Benutzergruppe „users“ anlegen und den user apache in die Gruppe des Web-Users aufnehmen.
Es ist hier übersichtlich wenn man pro Domain einen Benutzer macht; ausser man hat Benutzer mit mehreren Domains. Wir legen hier einen Benutzer pro Domain an:
mkdir -v /srv/www groupadd users useradd testdomain.tld -G users -c "Test domain user" -d /srv/www/testdomain.tld usermod -a -G testdomain.tld apache |
So erhält jeder Benutzer einen system account in der Form: „domain.tld“ und ist sowohl in seiner eigenen Gruppe („domain.tld“), wie auch in der Zusatzgruppe: „users“.
[stextbox id=“note“ caption=“Hinweis“]Die Zusatzgruppe „users“ wird benötigt, wann immer alle Benutzer auf ein Verzeichnis Zugreifen können sollen, z.B. das das PHP-Session Verzeichnis (/var/lib/php/session/) oder auch gemeinsam genutzte Dateien und Verzeichnisse.[/stextbox]
Der apache selbst hat den gleichnamigen Benutzernamen und ist in der Gruppe: apache (bei debian systemen: www-data), so wie in den Gruppen aller Web-Benutzer (z.B.: „domain.tld“)
[stextbox id=“comment“ caption=“Hinweis“]Der apache user muss in den Gruppen aller Web-Benutzer sein, da er sonst die Web-Verzeichnisse der Bentzer gar nicht lösen könnte, was jedoch trotz php-fcgi notwendig ist. Denn php-fcgi gilt nur in PHP-Scripten.
Sicherheitstechnisch ist dies jedoch unbedenklich, da umgekehrt keiner der Web-User in der apache Gruppe ist.[/stextbox]
Verzeichnisstruktur
Die Domain Verzeichnisse sind nach domain-tld/subdomain gegliedert, d.h. der Virtualhost: subdomain.domain.tld hätte den document root: /srv/www/domain.tld/subdomain/public_html
Eine Skelettstruktur sieht dann entsprechend so aus:
/srv/www/domain.tld/ `-- www |-- log | |-- access_log | |-- combined.log | `-- error_log `-- public_html |-- index.html |
Wir haben hier also alle Homepages im Verzeichnis /srv/www/ und jede Domain hat ein Verzeichnis „domain.tld“, so wie die Unterverzeichnisse: „log“ und „public_html“. – Im Verzeichnis „log“ werden die Logfiles pro (sub-)Domain gespeichert und in „public_html“ die öffentlichen Seiten.
Berechtigungen
Die Berechtigungen werden dann wie folgt gesetzt:
drwx--x--x root apache /srv/www/ dr-xr-x--x domain.tld domain.tld /srv/www/domain.tld/ dr-xr-x--x domain.tld domain.tld /srv/www/domain.tld/www/ dr-xr-x--- domain.tld domain.tld /srv/www/domain.tld/www/log/ drwxr-x--- domain.tld domain.tld /srv/www/domain.tld/www/public_html/ -rw-r----- domain.tld domain.tld /srv/www/domain.tld/www/public_html/index.php |
Dies müsste sicher stellen, dass der User wirklich nur in „seinem“ home-Verzeichnis lesen kann; des weiteren darf er auch dort z.B. sein log-Verzeichnis nicht löschen, da dies vom apache verwaltet wird.
Weiter darf die Gruppe (welche der apache user benutzt) Verzeichnisse innerhalb von /srv/www/ lesen (execute), sich diese aber nicht auflisten lassen (read).
[stextbox id=“comment“ caption=“Info: Globales Ausführrecht aufs domain/www Verzeichnis“]Das „World-Excecute“ Recht (551) muss nicht unbedingt so gesetzt sein. Möchte man aber später z.B. zentralisierte Tools wie z.B. Log-Analyzer einsetzen kann es nötig werden, dass alle User in die Verzeichnisse wechseln können. Anzeigen lassen kann man sich aber nur mit dem Ausführ-Recht nichts.
Alternativ kann man jedoch auch die ACL Erweiterung des Filesystems benutzen:
setfacl -m u:webstats:x /srv/www/domain.tld /srv/www/domain.tld/www |
Dies würde dem User webstats erlauben die Verzeichnisse innerhalb von: /srv/www/domain.tld/www zu erreichen.[/stextbox]
Anpassen des Filesystems
suEXEC bedingt standardmässig, dass der PHP-Starter unter /var/www liegt; um dies zu ändern müsste man suEXEC selbst/neu kompilieren, deshalb lassen wir es so.
Hinweis: Es geht dabei nur um das script, dass PHP-CGI startet; wo die Webseiten liegen ist egal!
Zuerst brauchen wir jedoch ein dediziertes Verzeichnis für den Web-User wo die PHP-Session files (server cookies) abgelegt werden können:
mkdir -pv /var/local/lib/php/session/testdomain.tld chown -v testdomain.tld:testdomain.tld /var/local/lib/php/session/testdomain.tld chmod -v 750 /var/local/lib/php/session/testdomain.tld |
[stextbox id=“note“ caption=“Änderung des Standard-Session Verzeichnisses nötig“]Wenn man das PHP Standard-Verzeichnis für sessions nimmt (/var/lib/php/session/), welches die Rechte:
drwxrwx---. 2 root apache /var/lib/php/session/ |
hat, so muss man dieses Verzeichnis für die gemeinsame Gruppe „users“ lesbar machen:
chown -v root:users /var/lib/php/session/ |
Da jedoch unterhalb dieses Verzeichnisses jeder Web-User noch sein eigenes Verzeichnis bekommt, ist sichergestellt, dass die Web-User untereinander die Session-Files nicht lesen können.[/stextbox]
Nun legen wir das fcgi-verzeichnis an:
mkdir -v /var/www/fcgi |
Die Idee ist, dass hier für jeden web-user ein eigens Verzeichnis erstellt wird und ein startscript darin, dass „sein“ php-cgi startet wenn die Webseite dieses Benutzers aufgerufen wird. Es ist dabei wichtig, dass jeder Benutzer ein Verzeichnis hat, auch wenn darin normalerweise nur ein script liegt – Ansonsten verweigert suEXEC aus Sicherheitsgründen den Dienst. Das Verzeichnis muss den Benutzer- und Gruppennamen des Webbenutzer haben.
Nun machen wir das mal für unsere testdomain:
mkdir -v /var/www/fcgi/testdomain.tld |
Darin erstellen wir das Starterscript:
/var/www/fcgi/testdomain.tld/php-fcgi.sh
#!/bin/sh . /var/www/fcgi/php-fcgi-wrapper.inc exec /usr/bin/php-cgi $OPTIONS |
und machen es ausführbar und setzen Benutzer/Rechte:
chmod -v 755 /var/www/fcgi/testdomain.tld/php-fcgi.sh chown -vR testdomain.tld:testdomain.tld /var/www/fcgi/testdomain.tld |
Und damit wir die Konfiguration für alle user zentral ändern können, binden wir noch ein include-file /var/www/fcgi/php-fcgi-wrapper.inc ein:
USERNAME=$(cat /etc/passwd |grep $(whoami) |cut -d: -f1) WEBROOT="/srv/www/$USERNAME" BASEDIR="/tmp:/usr/share/php:/usr/share/pear:$WEBROOT" SESSION_DIR="/var/local/lib/php/session/$USERNAME" PHPRC=/etc/php.ini export PHPRC OPTIONS="-d open_basedir=${BASEDIR} -d session.save_path=$SESSION_DIR" |
Einzelne Werte können hier pro user überschrieben werden, indem sie unterhalb des include files definiert werden.
Dieses Script ist ein sog. „Wrapper“ und wird nun jeweils gestartet wenn die Webseite des Benutzers zum ersten mal aufgerufen wird.
In diesem Script könnte man nun eine ganze Umgebung konfigurieren: Individuelles php binary, eigene php.ini, Optionen voraussetzen, Umgebungsvariablen definieren., usw.
Wir beschränken uns hier aber auf das Standard php/php.ini und konfigurieren nur das open_basedir für den Benutzer.
Nun braucht der Benutzer aber noch ein Webverzeichnis; diese legen wir unter /srv/www an:
mkdir -v /srv/www mkdir -pv /srv/www/testdomain.tld/www/{log,public_html} chown -vR testdomain.tld:testdomain.tld /srv/www/testdomain.tld/ chmod -v 551 /srv/www/testdomain.tld chmod -v 751 /srv/www/testdomain.tld/www chmod -v 570 /srv/www/testdomain.tld/www/log chmod -v 750 /srv/www/testdomain.tld/www/public_html |
Zu beachten ist hier, dass die Webverzeichnisse dem Domain-Benutzer und der Domain-Gruppe (zu welcher der Apache-User (apache) gehört) gehören. apache bekommt damit aber nur lese- und ausführ rechte. Dies ist nötig, weil sonst der apache ja selbst nicht mehr auf die Seiten zugreifen könnte. Ein Auslesen fremder Daten per PHP wird so aber unmöglich, da PHP mit der Gruppe: „testdomain.tld“ ausgeführt wird.
Des weiteren sollten wir noch sicherstellen, dass vom Benutzer angelgte Dateien/Verzeichnisse auch die entsprechenden Berechtigungen erhalten, die machen wir mit „umask“:
echo "umask 027" >> /srv/www/testdomain.tld/.bashrc |
und dann am besten auch im skel-Verzeichnis; so wird das beim anlegen neuer Benutzer automatisch gesetzt:
echo "umask 027" >> /etc/skel/.bashrc |
Apache Konfigurieren
Nun erstellen wir eine saubere apache config und für jede Datei eine Virtualhosts Datei. (Kommentare wurden hier der besseren Unübersichtlichkeit halber bewusst weggelassen)
Bestehende Dateien werden hier überschrieben und überflüssige gelöscht. Zum Schluss sollte das Verzeichnis /etc/httpd etwa so aussehen:
drwxr-xr-x 6 root root 1.0K Feb 23 00:31 . drwxr-xr-x 66 root root 5.0K Mar 7 19:41 .. drwxr-xr-x 2 root root 1.0K Feb 26 16:10 conf drwxr-xr-x 2 root root 1.0K Mar 7 22:45 conf.d lrwxrwxrwx 1 root root 19 Feb 22 05:09 logs -> ../../var/log/httpd lrwxrwxrwx 1 root root 29 Feb 22 05:09 modules -> ../../usr/lib64/httpd/modules lrwxrwxrwx 1 root root 13 Feb 22 05:09 run -> ../../var/run drwxr-xr-x 2 root root 1.0K Apr 15 2008 ssl drwxr-xr-x 2 root root 1.0K Mar 3 02:23 vhosts.d |
Nun gehts an die Dateien:
/etc/httpd/conf/httpd.conf:
Diese Datei kann man gröstenteils beim Standard belassen; lediglich zu unterst muss noch die Zeile:
Include vhosts.d/*
eingefügt werden:
echo "Include vhosts.d/*" >> /etc/httpd/conf/httpd.conf |
[stextbox id=“tip“ caption=“httpd.conf mit override-file“]Grosse, unübersichtliche Konfigurationsdateien wie die httpd.conf lässt man am besten unverändert. So kann man bei einem release-upgrade die Änderungen sehr gut zurückverfolgen.
Falls man trotzdem mal was an dieser Konfigurationsdatei ändern will, bietet sich der Trick mittels eines „override-files“ an, welches auch z.B. beim CSS-Styling genutzt wird: Man included eine zweite Datei am Schluss, in der man dann alle Optionen die man ändern will einträgt. Diese werden so automatisch „überschrieben“, sollten sie bereits existieren:
echo "Include conf/httpd-override.conf" >> /etc/httpd/conf/httpd.conf |
# Hier wird alles konfiguriert, was sonst in der httpd.conf eingetragen werden würde. Beispiel:
DirectoryIndex index.html index.htm index.php index.xhtml |
[/stextbox]
SSL-Konfiguration
Grundkonfiguration
apache ist dafür bekannt, das mod_ssl Modul nicht mit zeitgemässer Konfiguration auszuliefern, deshalb muss diese zuerst ein wenig angepasst werden.
Deshalb sollten in /etc/httpd/conf.d/ssl.conf folgende Zeilen auskommentiert (deaktiviert) werden: SSLProtocol und SSLCipherSuite.
Dann zuunterst, wie schon bei der httpd.conf ein ssl.conf.override einbinden:
Include conf.d/ssl.conf.overrides |
Nun die Datei ssl.conf schliessen und eine neue Datei /etc/httpd/conf.d/ssl.conf.overrides Datei mit folgendem Inhalt erstellen:
SSLProtocol all -SSLv2 -SSLv3 SSLCipherSuite ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS SSLHonorCipherOrder on Header always set Strict-Transport-Security "max-age=31536000;" |
Dies deaktivert die unsicheren Protokolle SSLv2/SSlv3 und bietet nur noch „sichere“ ciphers an.
Die letzte Header
Anweisung schaltet HSTS ein und bringt damit den Browser des Webseiten-Besuchers dazu beim nächsten Mal automatisch die HTTPS Version der Webseite aufzurufen.
[stextbox id=“note“ caption=“Tipp: Ciphers gelegentlich überprüfen“]
Da es in bestimmten ciphers immer wieder Sicherheitslücken geben kann, sollte die obige Liste gelegentlich überprüft werden.
Dazu gibt es einen SSL Configuration Generator, welche die aktuelle Konfiguration für verscheiedene Webserver Kombinationen und Versionen erzeugt.
[/stextbox]
Zertifikate
Nun erstellen wir noch ein eigenes (selbst signiertes) SSL-Zertifikat:
mkdir -pv /usr/local/share/ssl/localhost/{certs,keys} chmod -v 755 /usr/local/share/ssl/localhost/certs chmod -v 700 /usr/local/share/ssl/localhost/keys openssl req -x509 -newkey rsa:2048 -keyout /usr/local/share/ssl/localhost/keys/apache.key -out /usr/local/share/ssl/localhost/certs/apache.crt -days 3650 -nodes |
[stextbox id=“tip“ caption=“Vom Browser akzeptierte SSL Zertifikate“]
Mit dem selbst-signierten SSL Zertifikat wird zwar die die Übertragung der Seite verschlüsselt, doch der Besucher bekommt von seinem Browser jeweils eine Warnung, dass das Zertifikat „unsicher“ ist. Dies, weil die die Browser wie Firefox, Chrome und co. nur Zertifikate als „sicher“ einstufen, wenn sie von einer bekannten Stelle signiert sind.
Als Ausweg kann man sich bei letsencrypt.org kostenlos ein Zertifikat ausstellen lassen, welches vom Browser des Besuchers ohne Warnung akzeptiert- und als „sicher“ eingestuft wird.
Hier ein Beispiel für letsencrypt und die Domain example.com:
yum install certbot mkdir -pv /usr/local/share/ssl/example.com/{certs,keys}/ chmod -v 755 /usr/local/share/ssl/example.com/certs chmod -v 700 /usr/local/share/ssl/example.com/keys certbot certonly ln -sv /etc/letsencrypt/live/example.com/cert.crt /usr/local/share/ssl/example.com/certs/letsencrypt.crt ln -sv /etc/letsencrypt/live/example.com/privkey.key /usr/local/share/ssl/example.com/keys/letsencrypt.key systemctl reload httpd |
[/stextbox]
Signiertes Zertifikat mit letsencrypt
Das schnelle Beispiel, welches oben vorgestellt wurde ist gut zum testen. In einem professionellen Webhosting Server will man jedoch etwas mehr Kontrolle haben. 😉 Deshalb hier noch eine erweiterte Variante.
Letsencrypt implementiert zum automatischen generieren von SSL Zertikaten das ACME-Protokoll.
Für dieses stellt letsencrypt mit dem
Denn da das „offizielle“ certbot Script möglichst alle Fälle abdecken will ist es ziemlich aufgebläht. Der inoffizielle client acme-tiny ist mit nur ca. 200 Zeilen code besser geignet, da er nur das nötigste enthält und dazu gemacht ist als nicht-root Benutzer zu laufen.
Zuerst wird das Script heruntergeladen:
wget -O - "https://raw.githubusercontent.com/diafygi/acme-tiny/master/acme_tiny.py" > /usr/local/bin/acme_tiny.py chmod -v 755 /usr/local/bin/acme_tiny.py |
Dann wird ein Benutzer erstellt, welcher das ACME-Script ausführt mit entsprechenden Rechten:
useradd -c "letsencrypt ACME client" -d /srv/www/letsencrypt/ letsencrypt echo "umask 0022" >> /srv/www/letsencrypt/.bashrc chown -v letsencrypt:apache /srv/www/letsencrypt/ chmod -v 750 /srv/www/letsencrypt/ |
Damit letsencrypt funktioniert, muss der Client eine Datei im Verzeichnis .well-known/acme-challenge/ anlegen, welche von der Domain für die man ein Zertifikat beantragt aufrufbar sein muss, also beispielsweise: www.example.com/.well-known/acme-challenge/.
Damit nicht in jedem Domain-Webroot das Verzeichnis angelegt werden muss, wird ein globales Alias in /etc/httpd/conf.d/letsencrypt.conf angelegt:
Alias /.well-known/acme-challenge/ "/srv/www/letsencrypt/.well-known/acme-challenge/" <Directory "/srv/www/letsencrypt/.well-known/acme-challenge/"> AllowOverride None Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec </Directory> |
Nun noch das Verzeichnis anlegen:
mkdir -pv /srv/www/letsencrypt/.well-known/acme-challenge/ chown -vR letsencrypt:apache /srv/www/letsencrypt/.well-known/ chmod -vR 750 /srv/www/letsencrypt/.well-known/ |
Dann noch den Webserver neu starten damit die Änderung wirksam wird:
systemctl reload httpd |
Erstellen der SSL Verzeichnisse
mkdir -pv /usr/local/share/ssl/letsencrypt/{requests,certs,keys}/ chown -vR letsencrypt:root /usr/local/share/ssl/letsencrypt/ chmod -v 755 /usr/local/share/ssl/letsencrypt/ chmod -v 755 /usr/local/share/ssl/letsencrypt/{requests,certs} chmod -v 700 /usr/local/share/ssl/letsencrypt/keys |
Dazu braucht es noch das folgende Intermediate-Zertifikat von letsenrypt: Let’s Encrypt Authority X3 (IdenTrust cross-signed)
Dieses wird am besten unter: /usr/local/share/ssl/letsencrypt/intermediate.crt abgelegt:
wget 'https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt' mv -v lets-encrypt-x3-cross-signed.pem.txt /usr/local/share/ssl/letsencrypt/intermediate.crt |
Nun braucht der letsencrypt Benutzer noch sudo rechte um den Webserver reloaden zu können:
visudo -f /etc/sudoers.d/letsencrypt |
letsencrypt ALL=(root) NOPASSWD: /etc/init.d/httpd reload, /usr/bin/systemctl reload httpd |
Dann wird der Benutzer- und dann ins SSL Verzeichnis gewechselt:
su - letsencrypt cd /usr/local/share/ssl/letsencrypt/ |
Für die erstmalige Verwendung muss man einen privaten Schlüssel erstellen mit dem man sich bei letsencrypt anmelden kann, den sogennanten „Account Key“.
Dieser muss nur einmal erstellt- und kann für alle Domains benutzt werden; es ist sozusagen das „Login“ bei letsencrypt:
openssl genrsa 4096 > /usr/local/share/ssl/letsencrypt/account.key chmod -v 600 /usr/local/share/ssl/letsencrypt/account.key |
Nun wird ein Key und CSR für die Domain „example.com“ erstellt.
Da später sowohl „example.com“, wie auch „www.example.com“ aufrufbar sein sollen wird daraus ein sogenanntens SAN-Zertifikat gemacht.
Dazu muss erst eine (sehr rudimentäre) Konfigurationsdatei für openssl erstellt werden:
/usr/local/share/ssl/letsencrypt/openssl.cnf
[ req ] distinguished_name = req_distinguished_name [ req_distinguished_name ] |
Dann wird der CSR für das Zertifikat erstellt:
openssl genrsa 4096 > /usr/local/share/ssl/letsencrypt/keys/example.com.key openssl req -new -sha256 -key /usr/local/share/ssl/letsencrypt/keys/example.com.key -subj "/" -reqexts SAN -config <(cat /usr/local/share/ssl/letsencrypt/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:example.com,DNS:www.example.com")) > /usr/local/share/ssl/letsencrypt/requests/example.com.csr |
Und die Berechtigungen gesetzt:
chmod -v 600 /usr/local/share/ssl/letsencrypt/keys/example.com.key chmod -v 640 /usr/local/share/ssl/letsencrypt/requests/example.com.csr |
Nun wird das Zertifikat mit acme-tiny von letsencrypt signiert:
/usr/local/bin/acme_tiny.py --account-key /usr/local/share/ssl/letsencrypt/account.key --csr /usr/local/share/ssl/letsencrypt/requests/example.com.csr --acme-dir /srv/www/letsencrypt/.well-known/acme-challenge/ > /usr/local/share/ssl/letsencrypt/certs/example.com.crt |
Und der Webserver reloaded:
sudo /usr/bin/systemctl reload httpd |
Damit nun nicht für jede Domain das Script aufgerufen werden muss, wird noch ein Wrapper-Script erstellt, welches das automatisch für jede Datei im CSR Verzeichnis macht:
mkdir -v /srv/www/letsencrypt/bin/ chmod -v 700 /srv/www/letsencrypt/bin/ |
/srv/www/letsencrypt/bin/renew-certs
#!/bin/bash ARG_DOMAIN="$1" DIR_SSL="/usr/local/share/ssl/letsencrypt" DIR_ACME="/srv/www/letsencrypt/.well-known/acme-challenge" FILE_ACCOUNTKEY="/usr/local/share/ssl/letsencrypt/account.key" CMD_ACME="/usr/local/bin/acme_tiny.py" CMD_WEB_RELOAD="/usr/bin/sudo /etc/init.d/httpd reload" # If a single domain is entered only create/renew the certificate for that domain if [[ ! -z "$ARG_DOMAIN" ]]; then if [[ -f "$DIR_SSL/requests/$ARG_DOMAIN.csr" ]]; then $CMD_ACME --account-key $FILE_ACCOUNTKEY --csr $DIR_SSL/requests/$ARG_DOMAIN.csr --acme-dir $DIR_ACME > $DIR_SSL/certs/$ARG_DOMAIN.crt.new if [ -s $DIR_SSL/certs/$ARG_DOMAIN.crt.new ] then mv -v $DIR_SSL/certs/$ARG_DOMAIN.crt.new $DIR_SSL/certs/$ARG_DOMAIN.crt echo -e "Certificate signed! Check it with:\nopenssl x509 -in $DIR_SSL/certs/$ARG_DOMAIN.crt -text -noout" else echo "Error creating certificate." exit 1 fi else echo "A domain specified, but the corresponding CSR does not exist!" exit 1 fi # Without arguments do it for all domains which have a existing CSR file else for csr in $DIR_SSL/requests/*.csr do domain=$(basename "${csr%.*}") $CMD_ACME --quiet --account-key $FILE_ACCOUNTKEY --csr $DIR_SSL/requests/$domain.csr --acme-dir $DIR_ACME > $DIR_SSL/certs/$domain.crt.new if [ -s $DIR_SSL/certs/$domain.crt.new ] then mv $DIR_SSL/certs/$domain.crt.new $DIR_SSL/certs/$domain.crt fi done fi $CMD_WEB_RELOAD > /dev/null |
chmod -v 700 /srv/www/letsencrypt/bin/renew-certs |
Als letztes muss man noch einen cronjob erstellen, welche einmal monatlich die Zertifikate erneuert:
crontab -e |
0 0 1 * * /srv/www/letsencrypt/bin/renew-certs |
[stextbox id=“warning“ caption=“Achtung startssl“]
Früher benutze man oft startssl.com, welches auch kostenlose Zertifikate bot. Da sich jedoch startssl nicht an die Regeln hielt, vertrauen die Brwoser den Zertifikaten von startssl nicht mehr.
Und da letsencrypt als Inititative der grossen Browserhersteller selber enstanden ist und getragen wird, sollte auf jedenfall letsencrypt für kostenlose Zertifikte genommen werden.
[/stextbox]
HTTPS testen
Zum Abschluss kann man seine fertig Konfigurierte Domain mit einem SSL Anaylzer, z.B. mit dem ssllabs.com: SSL Server Test überprüfen ob alles ok ist.
Neue Domain hinzufügen
Soll nun eine neue SSL-Domain hinzugefügt werden, reicht es einfach einen Key/CSR zu erstellen und im Webserver zu konfigurieren; der Rest macht das renew-certs
Script automatisch.
/etc/httpd/vhosts.d/example.com
<VirtualHost *:80> ServerName example.com ServerAlias www.example.com DocumentRoot /srv/www/example.com/www/public_html </VirtualHost> |
Achtung: Zuerst darf nur der HTTP VirtualHost erstellt werden, der HTTPS VirtualHost kann erst hinzugefügt werden, wenn das Zertifikat vorhanden ist!
systemctl reload httpd |
su - letsencrypt openssl genrsa 4096 > /usr/local/share/ssl/letsencrypt/keys/example.com.key openssl req -new -sha256 -key /usr/local/share/ssl/letsencrypt/keys/example.com.key -subj "/" -reqexts SAN -config <(cat /usr/local/share/ssl/letsencrypt/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:example.com,DNS:www.example.com")) > /usr/local/share/ssl/letsencrypt/requests/example.com.csr chmod -v 600 /usr/local/share/ssl/letsencrypt/keys/example.com.key chmod -v 640 /usr/local/share/ssl/letsencrypt/requests/example.com.csr |
[stextbox id=“tip“ caption=“commonName (CN) setzen“]
Im oben gezeigten Beispiel wird kein „commonName“ gesetzt (-subj „/“).
Für letsencrypt ist das nicht wichtig, es wird dann einfach die erste Domain beim [SAN] Abschnitt als commonName eingesetzt.
Dies kann jedoch suboptimal sein, denn der commonName wird bei jedem Seitenaufruf als „Webseite“ angezeigt. Und so kann es vorkommen, dass die erste Seite nicht „example.com“, sondern „komische.subpage.example.com“ ist, die dann bei jeder Domain im Zertifikat gezeigt wird.
In diesem Fall kann man im CSR den commonName direkt setzten, indem man die Anstelle von -subj "/"
, beispielsweise folgendes schreibt:
-subj /C=CH/ST=MeinOrt/L=MeinOrt/O=Meine Firma/OU=Meine Abteilung/CN=example.com
bzw. im Script:
openssl req -new -sha256 -key /usr/local/share/ssl/letsencrypt/keys/example.com.key -subj "/C=DE/ST=MeinOrt/L=MeinOrt/O=Meine Firma/OU=Meine Abteilung/CN=example.com" -reqexts SAN -config <(cat /usr/local/share/ssl/letsencrypt/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:example.com,DNS:www.example.com")) > /usr/local/share/ssl/letsencrypt/requests/example.com.csr |
Hinweis: letsencrypt wird aus dem subject sämtliche Angaben bis auf „CN=“ löschen, es kommt also nicht darauf an, was man bei C=, ST=, L=, O= und OU= angibt.
[/stextbox]
Der erstellte CSR lässt sich anschliessend Online mit dem CSR Decoder überprüfen.
renew-certs |
/etc/httpd/vhosts.d/example.com
<VirtualHost *:443> ServerName example.com ServerAlias www.example.com DocumentRoot /srv/www/example.com/www/public_html SuexecUserGroup example.com example.com SSLEngine On SSLCertificateFile /usr/local/share/ssl/letsencrypt/certs/example.com.crt SSLCertificateKeyFile /usr/local/share/ssl/letsencrypt/keys/example.com.key SSLCertificateChainFile /usr/local/share/ssl/letsencrypt/intermediate.crt </VirtualHost> |
systemctl reload httpd |
Webseite 100% auf SSL Umstellen
Hat man SSL richtig eingerichtet, möchte man die Seite gleich „voll“ auf SSL Umstellen, so dass unverschlüsseltes HTTP gar nicht mehr nötig wäre.
Dazu muss man meist in der Web-Applikation (CMS) das noch entsprechend umstellen, so dass alle links mit https:// erzeugt werden; bei wordpress beispielsweise in den Einstellungen und bei Drupal in der Konfigurationsdatei.
Weiter kann man dann noch in der Datenbank nach http:// links suchen und diese entsprechend ändern. Allerdings ist diese Aktion nicht gerade ungefährlich, als Datenbank vorher sichern!
Um zu überprüfen ob die Seite wirklich 100% SSL ist, hilft der Online-Checker auf whynopadlock.com weiter.
Web-Konfiguration
Nun erstellen wir das „Basis-Verzeichnis“:
mkdir -v /srv/www/ chown -v root:apache /srv/www/ chmod -v 750 /srv/www |
/etc/httpd/conf.d/vhosts.conf:
#========================== Global Section ==================================== # Include the virtual host configurations: NameVirtualHost *:80 NameVirtualHost *:443 #========================== Global directory settings ========================= <Directory /> Order Deny,Allow Deny from all Options None AllowOverride None </Directory> <Directory "/srv/www"> AllowOverride All Options +SymLinksIfOwnerMatch +Includes Order Allow,Deny Allow from all </Directory> #========================== Logging settings ================================== # Global log formats and settings LogFormat "%V %h %l %u %t \"%r\" %s %b" vcommon LogFormat "%V %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" vcombined #========================== Default VirtualHosts ============================== # Here we are catching any request which does not go to a configured VirtualHost # See: # * http://httpd.apache.org/docs/current/vhosts/name-based.html#defaultvhost # * http://httpd.apache.org/docs/current/vhosts/name-based.html#using #-------------------------- HTTP default -------------------------------------- # Default settings for HTTP <VirtualHost *:80> ServerName localhost DocumentRoot /srv/www/localhost/www CustomLog /srv/www/localhost/www/log/access.log common ErrorLog /srv/www/localhost/www/log/error.log </VirtualHost> #-------------------------- HTTPS default ------------------------------------- # Default settings for HTTPS <VirtualHost *:443> ServerName localhost DocumentRoot /srv/www/localhost/www ErrorLog /srv/www/localhost/www/log/ssl_error_log TransferLog /srv/www/localhost/www/log/ssl_access_log CustomLog /srv/www/localhost/www/log/ssl_request.log ssl SSLEngine on SSLCertificateFile /etc/pki/tls/certs/localhost.crt SSLCertificateKeyFile /etc/pki/tls/private/localhost.key #SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt #SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt </VirtualHost> #========================== VirtualHosts ====================================== # Include VirtualHosts from directory Include vhosts.d/* |
Nun noch das Web-Verzeichnis für localhost anlegen:
mkdir -pv /srv/www/localhost/www/log chown -vR apache:apache /srv/www/localhost/ chmod -vR 750 /srv/www/localhost/ |
[stextbox id=“note“ caption=“Hinweis: Zertifikate“]
Falls wie oben beschrieben ein „richtiges“ Zertifikat mit letsencrypt erstellt wurde, müssen SSLCertificateFile, SSLCertificateKeyFile und SSLCertificateChainFile entsprechend angepasst werden.
[/stextbox]
Nun noch für jede Domain ein Virtualhostsfile (jeder Host erhällt auch ein SSL-Virtualhost; damit kann man alle Webseiten auch per SSL abrufen); 0.0.0.0 ist wiederum mit der IP des Servers zu ersetzen:
/etc/httpd/vhosts.d/testdomain.tld
################################################################################ # Virtualhosts File for Domain testdomain.tld ################################################################################ <Directory /srv/www/testdomain.tld/www/> AllowOverride All Options +SymLinksIfOwnerMatch +Includes +ExecCGI AddHandler fcgid-script .php FCGIWrapper /var/www/fcgi/testdomain.tld/php-fcgi.sh .php </Directory> <VirtualHost *:80> ServerName testdomain.tld ServerAlias www.testdomain.tld DocumentRoot /srv/www/testdomain.tld/www/public_html CustomLog /srv/www/testdomain.tld/www/log/combined.log combined CustomLog /srv/www/testdomain.tld/www/log/access_log common ErrorLog /srv/www/testdomain.tld/www/log/error_log SuexecUserGroup testdomain.tld testdomain.tld </VirtualHost> <Directory /srv/www/testdomain.tld/www/> AllowOverride All Options +SymLinksIfOwnerMatch +Includes +ExecCGI AddHandler fcgid-script .php FCGIWrapper /var/www/fcgi/testdomain.tld/php-fcgi.sh .php </Directory> <VirtualHost *:443> ServerName testdomain.tld ServerAlias www.testdomain.tld DocumentRoot /srv/www/testdomain.tld/www/public_html CustomLog /srv/www/testdomain.tld/www/log/combined.log combined CustomLog /srv/www/testdomain.tld/www/log/access_log common ErrorLog /srv/www/testdomain.tld/www/log/error_log SuexecUserGroup testdomain.tld testdomain.tld SSLEngine On SSLCertificateFile /usr/local/share/ssl/localhost/certs/apache.crt SSLCertificateKeyFile /usr/local/share/ssl/localhost/keys/apache.key #SSLCertificateChainFile /usr/local/share/ssl/localhost/chain.pem </VirtualHost> |
[stextbox id=“tip“ caption=“Dynamische VirtualHosts“]Bei apache können mittels „VirtualDocumentRoot“ Verzeichnisse mit Laufzeit-Informationen als DocumentRoot dienen.
Vor allem bei massen hosting ist dies sehr praktisch.
Wenn man z.B. einen zentralen log-analyzer installiert hat, der log statistiken generiert, kann man einen Virtuewllen host machen, bei dem stats.example.com automatisch auf /srv/www/shared/stats/public_html/stats/example.com zeigt:
### Public vhost for all domains ### ServerName stats.hauptserver.tld ServerAlias stats.* SuexecUserGroup web-shared web-shared VirtualDocumentRoot /srv/www/shared/stats/public_html/stats/%2+ Alias /icon/ "/srv/www/shared/stats/public_html/icon/" Options +Indexes |
Weitere Verwendung dazu findet man in der apache Dokumentation: Dynamically configured mass virtual hosting und Apache Module mod_vhost_alias.[/stextbox]
[stextbox id=“note“ caption=“Hinweis: Wildcard domains“]Wenn man im apache sogenannte „wildcard“ domains (*.example.com) macht ist folgendes zu beachten:
- Wildcard subdomains funktionieren nur in der ServerAlias-Direktive, nicht mit ServerName
- Ist ein wildcard (*) gesetzt, werden alle nachfolgenden subdomains, auch wenn sie definiert sind ignoriert
Es ist dabei zu beachten, dass man zuerst alle subdomains (sub1.example.com, sub2.example.com, …) setzten muss, bevor man den wildcard (*.example.com) setzt.[/stextbox]
Domain / Benutzer hinzufügen
Um nun eine neue Domain, bzw. Benutzer hinzuzufügen muss man folgendes tun:
- Benutzer erstellen:
adduser domain.tld -G users usermod -a -G domain.tld apache |
- Verzeichnis und Starterscript erstellen:
mkdir -v /var/www/fcgi/domain.tld /var/lib/php/session/domain.tld vi /var/www/fcgi/domain.tld/php-fcgi.sh chown -vR domain.tld:domain.tld /var/www/fcgi/domain.tld /var/lib/php/session/domain.tld chmod -v 755 /var/www/fcgi/domain.tld/php-fcgi.sh chmod -v 750 /var/lib/php/session/domain.tld |
- Webverzeichnis erstellen:
mkdir -p /srv/www/domain.tld/www/{log,public_html} chown -vR domain.tld:domain.tld /srv/www/domain.tld/ chmod -v 550 /srv/www/domain.tld chmod -v 750 /srv/www/domain.tld/www chmod -v 570 /srv/www/domain.tld/www/log chmod -v 750 /srv/www/domain.tld/www/public_html |
- Virtualhost-file erstellen:
vi /etc/httpd/vhosts.d/domain.tld |
- Apache Neu starten:
/etc/init.d/httpd restart |
Fehlersuche
Internals Server Error
Wahrscheinlich stimmen die Rechte des Wrappers oder des Verzeichnisses in dem der Wrapper liegt nicht. Prüfen Sie die suEXEC Logdatei (/var/log/httpd/suexec.log) sowie die Apache Error Logdatei (/var/log/httpd/error.log).
mysql Datenbankserver
yum install mysql mysql-server chkconfig mysqld on /etc/init.d/mysqld start |
Die default Werte von /etc/my.cnf sind hier schon passend.
Möchte man UTF8 verwenden muss man evtl. unter dem Punkt [client] noch folgendes anfügen:
default-character-set = utf8 |
und unter: [mysqld]
init-connect="SET NAMES utf8" |
FTP-Server
Als letztes installieren wir den FTP-Server:
yum install ftp proftpd chkconfig proftpd on |
Nun noch in: /etc/proftpd.conf für jede Homepage ein Verzeichnis: /srv/www/domain.tld erstellen:
Und dein Webserver läuft!
Erweiterungen
Quota
Ob Quota auf filesystem-ebene heute noch Sinn macht, muss jeder für sich entscheiden. In der Regel wird man das Quota ohnehin zusätzlich noch in den Software-Komponenten (ftp-server, mailserver, usw.) konfigurieren.
So installieren wir das File System Quota:
Installieren:
yum install quota |
Initialisieren:
Zuerst muss man bei jeder Partition, für die man quota Einsetzen will in der /etc/fstab bei den Options: „usrquota,grpquota“ angefügt werden, z.B.:
defaults,usrquota,grpquota |
Danach die Partition remounten, z.B.:
mount -o remount /srv |
Nun Initialisert man noch das Quota-File:
quotacheck -cug /srv quotacheck -avug |
Setzen kann man das Quota dann mit:
edquota username |
Verwaltung / Administration
Logrotation
Da wir nun keine zentralen logfiles mehr haben müssen wir ein neues logrotate script anlgegen, damit die logs nicht irgendwann das filesysteme zumüllen:
/etc/logrotate.d/httpd-vhosts
/srv/www/*/*/log/*log { monthly rotate 12 compress delaycompress missingok notifempty sharedscripts postrotate /sbin/service httpd reload > /dev/null 2>/dev/null || true endscript } |
Logfile-Analyzer
Um die Zugriffe auf die Webseite auszuwerten installiert man am besten einen Log-Anlayzer wie AWstats oder goAccess.
Neue Domain erstellen automatisiert
Da es nun ziemlich mühsam wäre bei jeder neuen Domain diese kompletten obigen Schritte durch zu gehen, bietet es sich an ein entsprechend Script zu erstellen, dass dies automatisiert; dieses kann entweder unter /root/bin oder etwa /usr/local/bin liegen.
Dazu können wir gleich das Verzeichnis: /etc/skel/ benutzen; denn sobald ein neuer user im System angelegt wird, wird der Inhalt dieses Verzeichnisses automatisch ins User Verzeichnis kopiert.
Zuerst legt man also die benötigte Verzeichnisstruktur für einen „WebUser“ in diesem Verzeichnis an:
mkdir -pv /etc/skell/www/{log,private,public_html} touch /etc/skell/.bash_history /etc/skell/.viminfo |
Zu beachten ist hier, dass auch die Dateien angelegt werden müssen, die normalerweise beim ersten login automatisch erzeugt würde (z.B. .bash_history), da der WebUser auf der ersten ebene seines HOME-Verzeichnisses keine Schreibberechtigung haben wird.
Weiter werden nun jeweils „template-files“ angelegt um einen VirtualHost zu erzeugen, eine Datenbank aufzusetzen und natürlich der php-fcgi wrapper:
/etc/skel/vhost.template
################################################################################ # Virtualhosts File for Domain domain.tld ################################################################################ #========================== PROD domain ======================================= ServerName domain.tld ServerAlias www.domain.tld DocumentRoot /srv/www/domain.tld/www/public_html CustomLog /srv/www/domain.tld/www/log/combined.log combined CustomLog /srv/www/domain.tld/www/log/access_log common ErrorLog /srv/www/domain.tld/www/log/error_log SuexecUserGroup domain.tld domain.tld AllowOverride All Options +SymLinksIfOwnerMatch +Includes +ExecCGI AddHandler fcgid-script .php FCGIWrapper /var/www/fcgi/domain.tld/php-fcgi.sh .php #========================== SSL =============================================== # # ServerName domain.tld # ServerAlias www.domain.tld # DocumentRoot /srv/www/domain.tld/www/public_html # CustomLog /srv/www/domain.tld/www/log/combined.log combined # CustomLog /srv/www/domain.tld/www/log/access_log common # ErrorLog /srv/www/domain.tld/www/log/error_log # SuexecUserGroup domain.tld domain.tld # # # AllowOverride All # Options +SymLinksIfOwnerMatch +Includes +ExecCGI # AddHandler fcgid-script .php # FCGIWrapper /var/www/fcgi/domain.tld/php-fcgi.sh .php # # |
/etc/skel/db.sql.template
CREATE DATABASE domain_tld; GRANT ALL PRIVILEGES ON `domain_tld`.* TO 'domain.tld'@'%' IDENTIFIED BY 'setpass'; FLUSH PRIVILEGES; |
/etc/skel/php-fcgi.sh.template
#!/bin/sh PHPRC=/srv/www/conf.generic/ export PHPRC exec /var/www/fcgi/php-fcgi |
Nun können wir das domainadd/domaindel Script erstellen; die vorhin erzeugten template files werden von diesem script dann entsprechend weiter verarbeitet:
/root/bin/domainadd
#!/bin/sh ################################################################################ # domainadd / domaindel # CREATED BY: Steven Varco # # Adds or deletes a domain. # The directory structure of the domain can be configured in /etc/skel # For the creation of virtualhosts and DB you can create the follwing templates # in /etc/skel: # - vhost.template # - db.sql.template # - php-fcgi.sh.template # - www/public_html/index.html # in these files the string "domain.tld" (or "domain_tld" for DB Names) will be # replaced with the actual domain. # In the DB template, the string "setpass" will be replaced with a random generated # password. # # HISTORY: #============================================================================== # 29.11.2011 Steven Varco - Created Script # 22.09.2012 Steven Varco - added FTP directory creation # 23.04.2013 Steven Varco - Changed user group settings # 25.10.2013 Steven Varco - changed log-directory (user, permissions) # for use with log-statisics tools (awstats) ################################################################################ #========================== Configuration ===================================== DOMAIN=$1 DOMAINBASEDIR="/srv/www" DOMAINDIR="$DOMAINBASEDIR/$DOMAIN" FTPBASEDIR="/srv/ftp" SESSION_DIR="/var/local/lib/php/session" PATH_VHOSTS=/etc/httpd/vhosts.d PATH_FCGI=/var/www/fcgi GROUPID=100 WEBGROUP="apache" USERGROUP="users" LOGGROUP="web-log" FTPUSERGROUP="ftpusers" PASSWORD=$(> /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c16) VERB="-v" #========================== Creation ========================================== if [ -z "$DOMAIN" ]; then echo "usage: domainadd " exit 1 fi if [ "$DOMAIN" != "" ] then if [ ! -d $DOMAINDIR ] then echo "creating domain: $DOMAIN in: $DOMAINDIR" # User creation and permissions useradd -b $DOMAINBASEDIR -m $DOMAIN -G $USERGROUP # WWW-Directory creation # If /etc/skel is not configured properly, create needed directories mkdir -p $VERB $DOMAINDIR/www/{log,private,public_html} touch $DOMAINDIR/.bash_history chown $VERB $DOMAIN:$DOMAIN $DOMAINDIR/.bash_history chmod $VERB 600 $DOMAINDIR/.bash_* chown -R $VERB $DOMAIN:$DOMAIN $DOMAINDIR |grep changed chgrp -R $VERB $LOGGROUP $DOMAINDIR/*/log |grep changed chmod $VERB 551 $DOMAINDIR chmod $VERB 551 $DOMAINDIR/www chmod $VERB 550 $DOMAINDIR/*/log |grep changed find $DOMAINDIR/*/{private,public_html}/ -type d -print0 |xargs -0 chmod $VERB 750 |grep changed find $DOMAINDIR/*/{private,public_html}/ -type f -print0 |xargs -0 chmod $VERB 640 |grep changed if [ -f "$DOMAINDIR/www/public_html/index.html" ]; then perl -p -i -e "~s|domain.tld|$DOMAIN|" $DOMAINDIR/www/public_html/index.html fi # (optional) FTP_directory creation mkdir $VERB -p $FTPBASEDIR/$DOMAIN/ftp chown -R $VERB $DOMAIN:$FTPUSERGROUP $FTPBASEDIR/$DOMAIN chmod $VERB 2550 $FTPBASEDIR/$DOMAIN chmod $VERB 2750 $FTPBASEDIR/$DOMAIN/ftp ### TODO: Add user in mysql table ### # FCGI stuff mkdir $VERB $PATH_FCGI/$DOMAIN chown $VERB $DOMAIN:$DOMAIN $PATH_FCGI/$DOMAIN/ chmod 550 $VERB $DOMAIN:$DOMAIN $PATH_FCGI/$DOMAIN/ if [ -f "$DOMAINDIR/php-fcgi.sh.template" ]; then mv $VERB $DOMAINDIR/php-fcgi.sh.template $PATH_FCGI/$DOMAIN/php-fcgi.sh chown $VERB $DOMAIN:$DOMAIN $PATH_FCGI/$DOMAIN/php-fcgi.sh chmod 500 $VERB $DOMAIN:$DOMAIN $PATH_FCGI/$DOMAIN/php-fcgi.sh fi mkdir $VERB $SESSION_DIR/$DOMAIN chown $VERB $DOMAIN:$DOMAIN $SESSION_DIR/$DOMAIN chmod $VERB 750 $SESSION_DIR/$DOMAIN # VirtualHost creation if [ -f "$DOMAINDIR/vhost.template" ]; then perl -p -i -e "~s|domain.tld|$DOMAIN|" $DOMAINDIR/vhost.template mv $VERB $DOMAINDIR/vhost.template $PATH_VHOSTS/020-$DOMAIN chown $VERB root:root $PATH_VHOSTS/020-$DOMAIN fi # DB creation if [ -f "$DOMAINDIR/db.sql.template" ]; then DBNAME=${DOMAIN//./_} perl -p -i -e "~s|domain.tld|$DOMAIN|" $DOMAINDIR/db.sql.template perl -p -i -e "~s|domain_tld|$DBNAME|" $DOMAINDIR/db.sql.template perl -p -i -e "~s|setpass|$PASSWORD|" $DOMAINDIR/db.sql.template fi # Activating if [ -f "$PATH_VHOSTS/020-$DOMAIN" ]; then #/etc/init.d/httpd configtest && /etc/init.d/httpd reload echo "to activate changes, execute: /etc/init.d/httpd configtest && /etc/init.d/httpd reload" fi if [ -f "$DOMAINDIR/db.sql.template" ]; then echo "To create a corresponding DB, execute: mysql -uroot -p < $DOMAINBASEDIR/$DOMAIN/db.sql.template && rm -v /srv/web/$DOMAIN/db.sql.template" fi else echo "Domain already exists!" exit 1 fi else echo "No domain given!" exit 1 fi |
/root/bin/domaindel
#!/bin/sh DOMAIN=$1 DOMAINBASEDIR="/srv/www" DOMAINDIR="$DOMAINBASEDIR/$DOMAIN" GROUPID=100 WEBGROUP="apache" USERGROUP="users" PASSWORD=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c32) #VERB="-v" if [ -z "$DOMAIN" ]; then echo "usage: domainadd example.com" exit 1 fi if [ "$DOMAIN" != "" ] then if [ -d $DOMAINDIR ] then rm -f $VERB /etc/httpd/vhosts.d/020-$DOMAIN userdel -r $VERB $DOMAIN rm -rf $VERB /var/www/fcgi/$DOMAIN echo "to activate changes, execute: /etc/init.d/httpd configtest && /etc/init.d/httpd reload" echo "NOTE: Any user databases and the corresponding DB user have to be removed manually!" else echo "Domain does not exist!" exit 1 fi else echo "No domain given!" exit 1 fi |
Nun lässt sich mittels:
domainadd |
eine neue Domain erstellen
.
Related Links
- The Software Collections ( SCL ) Repository: Mit dem SCL lassen sich neuere Progammiersprachen in ein RHEL/CentOS hinzufügen
- Implementing Disk Quotas: Der RHEL/Centos Guide um quotas zu implementieren
- Dynamically configured mass virtual hosting: Tipps und Tricks fürs Massen-Domain-Hosting
- Apache Module mod_vhost_alias: Erklärt das Modul vhost_alias
- letsencrypt.org: Kostenlose (vom Browser akzeptierte) SSL Zertifikate
- startssl.com: Kostenlose (vom Browser akzeptierte) SSL Zertifikate
- Letsencrypt howto von archlinux
- Mozilla SSL Configuration Generator: Generiert aktuelle SSL-Konfigurationen für verschiedene Webserver
- ssllabs.com: SSL Server Test: Mit diesem Online-Tool kann man die SSL-Verbindung seiner Webseite überprüfen lassen
- heinlein-support.de: HSTS: Header Strict Transport Security für Webserver einrichten: Infos zu HSTS
- admin-magazin.de: Der PHP-Interpreter PHP-FPM
- GoAccess: Schöner Logfile-Analyzer, sowohl für die Kommandozeile wie auch HTML output (siehe auch: Web-Stats mit GoAccess)
- whynopadlock.com: Überprüft die Webseite ob noch unverschlüsselte Inhalte ausgelifert werden, z.B. durch http:// Links
- CSR Decoder: Damit lässt sich einfach und übersichtlich ein SSL Certificate Request (CSR) anschauen
- WordPress auf 100% HTTPS-only umstellen, HTTP vollständig deaktivieren: Informationen zur SSL-Umstelung von wordpress Seiten
- freenom.com: freenom.com bitet gratis domainnamen an, ideal um z.B. einen Webserver zu testen.
2 thoughts on “WWW-Server”