Unterschiede zwischen den Revisionen 19 und 20
Revision 19 vom 2021-05-31 15:34:55
Größe: 23354
Autor: anonym
Kommentar: Typo
Revision 20 vom 2023-06-30 15:15:44
Größe: 0
Autor: phil
Kommentar:
Gelöschter Text ist auf diese Art markiert. Hinzugefügter Text ist auf diese Art markiert.
Zeile 1: Zeile 1:
Einige [[https://riseup.net/en/security/resources/radical-servers|Technik-Kollektive]] bieten Wordpress-Hosting auf Basis von [[https://wordpress.org/support/article/create-a-network/|Wordpress-Multisite]] an. Beispiele dafür sind die tollen Angebote von [[https://blackblogs.org/|blackblogs.org]] und [[https://noblogs.org/|noblogs.org]]. In der Regel ist die Funktionalität von Wordpress beschränkt, so dass sich nur eine Auswahl von Themes und Plugins installieren lassen. Ein anderer Ansatz ist der Betrieb einzener Wordpress-Seiten in einem abgesichertem Hosting.

Diese Seite beschreibt die Einrichtung eines solchen Hostings. Entgegen anderen Anleitungen verwenden wir Apache, um mittels [[http://httpd.apache.org/docs/2.4/mod/mod_macro.html|Macros]] die Seiteneinrichtung zu vereinfachen. Zur Absicherung verwenden wir ein WikiPediaDe:chroot, basierend auf PHP-FPM.

<<TableOfContents>>

= Einführung =
Dieser Anleitung basiert auf anderen sehr gut Howtos. Für ein grundlegendes Verständnis solltest du diese auch lesen:
 * [[https://nickyreinert.de/blog/2019/04/12/mehrere-virtuelle-server-mit-nginx-und-php-fpm-fur-wordpress-teil-1-3/|nickyreinert.de]] für einen allgemeinen Überblick
 * [[https://blog.kthx.at/2015/09/23/php-fpm-chroot/|blog.kthx]] zum Setup-Skript
 * [[https://www.vennedey.net/resources/3-Secure-webspaces-with-NGINX-PHP-FPM-chroots-and-Lets-Encrypt|vennedey.net]] zu den Sicherheitimplikation von NSCD und opcache
 * [[https://binary-butterfly.de/artikel/das-perfekte-php-wordpress-setup/|binary-butterfly.de]] zur Verwendung von `mknod` statt den mount-binds

== Variablen, Versionen und Pfade ==
Zur Veranschaulichung werden die nachfolgenden Variablen und Werte in der Anleitung genutzt:

|| Beispiel-Domain || example.org ||
|| Benutzerkonto || katja ||
|| PHP-Version || 7.3 ||
|| Quota-Partition (ext4) || /data ||
|| Skript-Verzeichnis || /usr/local/bin ||
|| Wordpress-Speicherort || /data/wordpress ||
|| IP des Reverse-Proxys || 192.168.0.1 ||

Bitte beachte, dass sich einzelnen Pfadangaben teilweise in mehreren Konfigurationsdateien benutzt werden. Bei Änderungen eines Pfades musst du also alle Vorkommen ändern.

= Benutzerkonto zur PHP-Ausführung =
 * Ein Systemnutzer mit eingeschränkten Rechten ist für die Auführung des PHP-Prozesses zuständig. Die Anmeldung per Passwort<<FootNote(Die Anmeldung per ssh-Schlüssel ist weiterhin möglich)>> und das Anlegen eines Home-Verzeichnissen werden deaktiviert:{{{
adduser --disabled-login --disabled-password --no-create-home --gecos katja}}}
 * Im späteren Verlauf wird das Wordpress-Verzeichnis diesem neuen Benutzerkonto übergeben.

= Quotas einrichten =
 * Disk-Quotas sorgen dafür, dass eine einzelne Wordpress-Instanz nicht den gesamten Speicherplatz auf einer Partition aufbrauchen kann. Läuft Wordpress in einem KVM-Gast muss ein Kernel installiert werden<<FootNote(Damit die Quota-Kernelmodule verfügbar sind)>>:{{{
apt install linux-image-amd64 quota}}}
 * Anschließend werden der ext4-Partition in `/etc/fstab` die notwendigen Parameter übergeben:{{{
/dev/vdb /data auto noatime,nodiratime,usrjquota=aquota.user,jqfmt=vfsv1 0 0}}}
 * Die Partition mit den neuen Werten remounten:{{{
mount -vo remount /data}}}
 * Den Quota-Index erstellen und Quotas aktivieren<<FootNote(Falls `quotaon` den Fehler `Devive or ressource busy anzeigt`, Quota deaktivieren und erneut aktivieren)>>:{{{
quotacheck -cum

quotaon -v /data}}}
 * Nun können für den neuen Nutzer die Quota-Regeln erstellt werden:{{{
edquota katja}}}
 * Weitere Hinweise dazu im [[https://wiki.archlinux.org/index.php/Disk_quota|Arch-Wiki]]

= Apache: Seitenkonfiguration mit Macros =
 * Durch Apache-Macros ist es möglich, nur eine Seitenkonfiguration für alle Wordpress-Instanzen zu erstellen.
 * Die Seitenkonfiguration `/etc/apache/sites-available/wordpress.conf` basiert auf dem später näher erläuterten Verzeichnisaufbau. Die Konfiguration verhindert durch Rewriting die Installation von eigenen Themes und Modulen über das Dashboard<<FootNote(Die Installation über das Wordpress Theme- und Pluginverzeichnis ist weiterhin möglich)>>. Zudem wird eine optionale htaccess-Datei eingebunden. Damit lassen sich einzelnen Wordpress-Seiten per htacess zusätzlich schützen:{{{
<Macro WPSite $domain $pool>
<VirtualHost *>
        ServerName $domain
        SetEnv HTTPS on
        DocumentRoot /data/wordpress/$domain/htdocs
        ErrorLog /var/log/apache2/$domain.error
        IncludeOptional /data/wordpress/_htaccess.d/$domain.conf
        Include /etc/apache2/conf-available/wordpress-cache.conf

        # Installation von eigenen Themes und Plugins verhindern
        <IfModule mod_rewrite.c>
                RewriteEngine On
                RewriteCond %{REQUEST_URI} ^/wp-admin/update.php
                RewriteCond %{QUERY_STRING} action=upload-(plugin|theme)
                RewriteRule (.*) /wp-admin/plugin-install.php [QSD,R=302,L]
        </IfModule>
        <Directory /data/wordpress/$domain/htdocs>
                Require all granted
                Options SymLinksIfOwnerMatch MultiViews IncludesNoExec
                AllowOverride AuthConfig FileInfo Indexes Limit Options

                # https://wordpress.org/support/article/hardening-wordpress/#securing-wp-includes
                <IfModule mod_rewrite.c>
                        RewriteEngine On
                        RewriteBase /
                        RewriteRule ^wp-admin/includes/ - [F,L]
                        RewriteRule !^wp-includes/ - [S=3]
                        RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
                        RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
                        RewriteRule ^wp-includes/theme-compat/ - [F,L]
                </IfModule>
        </Directory>
        <IfModule proxy_fcgi_module>
                <FilesMatch ".+\.ph(ar|p|tml)$">
                        SetHandler "proxy:unix:/run/php/php-fpm-$pool.sock|fcgi://$domain"
                </FilesMatch>
                <FilesMatch ".+\.phps$">
                        Require all denied
                </FilesMatch>
                <FilesMatch "^\.ph(ar|p|ps|tml)$">
                        Require all denied
                </FilesMatch>
                <Files xmlrpc.php>
                        Require all denied
                </Files>
                <Files wp-config.php>
                        Require all denied
                </Files>
        </IfModule>
</VirtualHost>
</Macro>
# Includieren der Variablen
Include /etc/apache2/conf-available/wordpress-sites.conf

UndefMacro WPSite}}}
 * Alle Variablen werden in `/etc/apache2/conf-available/wordpress-sites.conf` definiert. Die `pool`-Variable bezeichnet den Namen des PHP-FPM-Pools:{{{
# Muster: Use WPSite $domain $pool

Use WPSite example.org katja}}}
 * Anschließend werden alle benötigten Module und die Seite aktiviert:{{{
a2enmod macros

a2ensite wordpress.conf

systecmtl reload apache2}}}

= Chroot-Verzeichnis-Struktur =
 * Oberste Ebene eines jeden chroots ist das jeweilige Domain-Verzeichnis. Für die Beispieldomain ist es `data/wordpress/example.org`.
 * Das `tmp`-Verzeichnis hat eine doppelte Funktion: Es markiert ein chroot-Verzeichnis. Das ist für das später vorgestellte chroot-setup-Skript wichtig. Zudem werden dort temporäre Daten gespeichert.
 * Wordpress selbst liegt unter `htdocs`. Daraus ergibt sich folgende Struktur:{{{
├── data/wordpress/example.org
│ ├── dev/
│ ├── etc/
│ ├── htdocs/
│ │ └── index.php
│ ├── lib/
│ ├── run/
| ├── sessions/
| ├── tmp/
| ├── usr/
| └── var/}}}
 * `htdocs`, `tmp` und `sessions` gehören dem oben erstellten Benutzerkonto `katja:katja`. Alle anderen Verzeichnisse `root:root`.

= Chroot-Setup-Skript =
 * Das Skript `/usr/local/bin/php-fpm-chroot-setup` nimmt die Ersteinrichtung des chroots und der Verzeichnisstruktur vor. Es kopiert zudem die notwendigen Daten in das chroot.
 * Im Wesentlichen handelt es sich um Daten für die Namensauflösung, das Überprüfen von Zertifikaten und den mysql-Socket:{{{
#!/bin/sh

set -eu

BASE_DIR="/data/wordpress"
CHROOT_DIRS=$(find "$BASE_DIR" -mindepth 1 -maxdepth 1 -type d)
COPY_SOURCES="
    /etc/hosts
    /etc/resolv.conf
    /etc/ssl/certs
    /lib/x86_64-linux-gnu/libnss_dns.so.2
    /usr/lib/ssl/openssl.cnf
    /usr/share/zoneinfo"
MOUNTS="/var/run/mysqld"
MKNOD_DIRS="
    dev
    logs
    sessions
    tmp"

create_mounts() {
    local chroot_dir="$1"
    shift
    local mount_dir
    for mount_dir in "$@"; do
        if [ -d "$mount_dir" ]; then
            # $mount_dir ist ein Pfad zu einem Verzeichnis
            mkdir -p "$chroot_dir/$mount_dir"
            mount --bind -o ro "$mount_dir" "$chroot_dir/$mount_dir"
        else
            # $mount_dir ist ein Pfad zu einer Datei
            mkdir -p "$chroot_dir/$(dirname "$mount_dir")"
            touch "$chroot_dir/$mount_dir"
            mount --bind -o ro "$mount_dir" "$chroot_dir/$mount_dir"
        fi
    done
}

remove_chroot() {
    local chroot_dir="$1"
    shift
    if [ -d "$chroot_dir/tmp" ]; then
        echo "Loese $chroot_dir auf..."
        for f in "$@"; do
            umount "$chroot_dir/$f" || continue
            if [ -d "$chroot_dir/$f" ]; then
                # Leeren Ordner loeschen
                rmdir "$chroot_dir/$f"
            elif [ -f "$chroot_dir/$f" ]; then
                # Datei loeschen
                rm "${chroot_dir}${f}"
            fi
        done
    fi
}

if [ $# -ge 1 ]; then
        ACTION=$1
        shift
else
        ACTION=help
fi

case "$ACTION" in
    init)
        [ $# -ne 1 ] && echo "action 'init' requires one parameter: domain - e.g. 'example.org'" && exit 1
        DOMAIN="$1"
        CHROOT_SITE_DIR="$BASE_DIR/$DOMAIN"

        for i in $MKNOD_DIRS; do
            mkdir -p "$CHROOT_SITE_DIR/$i"
        done
        for i in $COPY_SOURCES; do
            cp -urpL --parents "$i" "$CHROOT_SITE_DIR"
        done
        mknod -m 666 "$CHROOT_SITE_DIR/dev/null" c 1 3
        mknod -m 444 "$CHROOT_SITE_DIR/dev/random" c 1 8
        mknod -m 444 "$CHROOT_SITE_DIR/dev/urandom" c 1 9
        mknod -m 666 "$CHROOT_SITE_DIR/dev/zero" c 1 5
        mkdir -p "$CHROOT_SITE_DIR/tmp"
        create_mounts "$CHROOT_SITE_DIR" "$MOUNTS"
        ;;

    start)
        for chroot_dir in $CHROOT_DIRS; do
            # Nur in Ordnern mit eigenem /tmp Verzeichnis als Markierung einen Chroot aufsetzen
            if [ -d "$chroot_dir/tmp" ]; then
                # Berechtigungen von /tmp korrigieren
                chmod 777 "$chroot_dir/tmp"
                chmod +t "$chroot_dir/tmp"

                echo "Setting up ${chroot_dir} ..."
                create_mounts "$chroot_dir" $MOUNTS
         for i in $COPY_SOURCES; do
             cp -urpL --parents "$i" "$chroot_dir"
         done
            fi
        done
    ;;

    stop)
        for chroot_dir in $CHROOT_DIRS; do
            remove_chroot "$chroot_dir" "$MOUNTS"
        done
    ;;

    restart)
 "$0" stop 2>/dev/null
 "$0" start
    ;;

    remove)
        DOMAIN="$1"
        CHROOT_SITE_DIR="$BASE_DIR/$DOMAIN"
        remove_chroot "$CHROOT_SITE_DIR" "$MOUNTS"
    ;;

    help|--help)
        echo "Usage: $(basename "$0") { start | stop | restart | init | remove }"
        echo
        exit 1
    ;;
    *)
        "$0" help >&2
        exit 1
    ;;
esac
}}}
 * Nähere Erläuterungen zu dem Skript findest du [[https://blog.kthx.at/2015/09/23/php-fpm-chroot/|hier]].
 * Zudem ist [[https://www.vennedey.net/blog/3-NSCD-socket-in-a-PHP-FPM-chroot-leaks-user-database|hier]] erklärt, weshalb die Verwendung von NSCD zur Namensauflösung keine gute Idee ist. Als Alternative kopiert das Skript `/etc/resolv.conf` und `libnss_dns.so.2`.

== Initieren beim Start ==
 * Einige Daten (im Beispiel der mysql-Socket) werden durch das Skript per bind-mount in das chroot gemountet. Diese müssen bei jedem Start wieder hergestellt werden. Dies übernimmt der systemd-Service `/etc/systemd/system/php-fpm-chroot.service`:{{{
[Unit]
Description=Set up PHP-FPM chroot mounts
After=network.target php7.3-fpm.service mysqld.service

[Service]
Type=forking
ExecStart=/usr/local/bin/php-fpm-chroot-setup start
ExecStop=/usr/local/bin/php-fpm-chroot-setup stop
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target}}}
 * Den Service aktivieren und den Deamon neu laden:{{{
systemctl enable php-fpm-chroot.service

systemctl daemon-reload}}}

= Konfiguration des PHP-FPM-Pools mit Chroot =
 * Die Konfigurarion des PHP-FPM-Pools basiert auf den Pfadangaben, die in der [[#Apache: Seitenkonfiguration mit Macros|Apachekonfiguration]] und in der [[#Chroot-Verzeichnis-Struktur|Verzeichnisstruktur]] verwenden werden.
 * Damit das PHP-FPM-Socket gefunden wird:{{{
php_admin_value[doc_root] = /htdocs
php_admin_value[cgi.fix_pathinfo] = 0}}}
 * [[https://bugs.php.net/bug.php?id=69090|Sicherheitseinstellugnen]] für opcache:{{{
php_admin_value[opcache.validate_permission] = 1
php_admin_value[opcache.validate_root] = 1}}}
 * Angabe des Cert-Pfades, damit die Validierung klappt:{{{
php_admin_value[openssl.capath] = /etc/ssl/certs}}}
 * Alles in allem die vollständige FPM-Konfiguration:{{{
[katja]
prefix = /data/wordpress/example.org
user = $pool
group = www-data
listen = /run/php/php-fpm-$pool.sock
listen.owner = $pool
listen.group = www-data
listen.mode = 0660
listen.allowed_clients = 127.0.0.1
pm = ondemand
pm.max_children = 5
pm.start_servers = 2
pm.process_idle_timeout = 10s;
pm.max_requests = 100
chroot = $prefix
chdir = /
security.limit_extensions = .php .php3 .php4 .php5
php_admin_value[doc_root] = /htdocs
php_admin_value[cgi.fix_pathinfo] = 0
php_admin_value[opcache.validate_permission] = 1
php_admin_value[opcache.validate_root] = 1
php_admin_value[session.save_path] = /sessions
php_admin_value[openssl.capath] = /etc/ssl/certs
php_admin_value[disable_functions] = mail,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_exec,passthru,system,proc_get_status,proc_close,proc_nice,proc_terminate,proc_open,curl_ini,parse_ini_file,show_source,dl,symlink,system_exec,exec,shell_exec,phpinfo}}}

= Verwaltung mit wpcli =
 * [[https://wp-cli.org/de/|Wpcli]] ist ein Werkzeug für die Kommandozeile, um Wordpress-Seiten zu verwalten.
 * Die Installation geht recht [[https://wp-cli.org/de/#installation|einfach]].
 * Zu beachten ist, dass `wpcli` stehts als der angelegte Wordpress-Nutzer ausgeführt wird (und nicht als root).

= Seitengeschwindigkeit optimieren =
Mittels Caching sollen Seitenaufrufe beschleunigt werden.

== Webserver-Caching ==
 * Wir weisen den Browser an, Mediendateien, css und javascript zu cachen. Dafür wirf in `/etc/apache2/conf-available/wordpress-cache.conf` ein globaler Cache definiert:{{{
<IfModule mod_expires.c>
  ExpiresActive on

  # whitelist expires rules
  ExpiresDefault "access 1 month"

  # Favicon (cannot be renamed)
  ExpiresByType image/x-icon "access plus 1 week"

  # Media: images, video, audio
  ExpiresByType image/gif "access plus 1 month"
  ExpiresByType image/png "access plus 1 month"
  ExpiresByType image/jpg "access plus 1 month"
  ExpiresByType image/jpeg "access plus 1 month"
  ExpiresByType video/ogg "access plus 1 month"
  ExpiresByType audio/ogg "access plus 1 month"
  ExpiresByType video/mp4 "access plus 1 month"
  ExpiresByType video/webm "access plus 1 month"

  # Webfonts
  ExpiresByType application/x-font-ttf "access plus 1 year"
  ExpiresByType font/opentype "access plus 1 year"
  ExpiresByType application/x-font-woff "access plus 1 year"
  ExpiresByType image/svg+xml "access plus 1 year"

  # CSS and JavaScript
  ExpiresByType text/css "access plus 1 month"
  ExpiresByType text/javascript "access plus 1 month"
  ExpiresByType application/javascript "access plus 1 month"

  <IfModule mod_headers.c>
    Header append Cache-Control "public"
  </IfModule>
</IfModule>}}}
 * Nun noch das entsprechende Modul neu laden und den Webserver neu laden:{{{
a2enmod macros expires

systemctl reload apache2}}}

== Redis-Caching ==
 * Redis ist ein persistenter Objekt-Cache zur Zwischenspeicherung von Daten
 * In der `wp-config.php` muss der Cache_Salt angegeben werden. Das ist wichtig, damit bei der Verwendung von nur einer Redis-Datenbank mit mehreren Wordpress-Seiten die Inhalte des Caches korrekt zugeordnet werden können:{{{
wp config set WP_CACHE_KEY_SALT example.org_}}}
 * Die Nutzung setzt ein Plugin voraus, bspw. [[https://wordpress.org/plugins/wp-redis|WP-Redis]]:{{{
wp plugin install wp-redis

wp plugin acticate wp-redis}}}
 * Anschließend lässt sich die Redis-Nutzung per `wpcli` aktivieren:{{{
wp redis enable

wp redis info}}}
 * Wird Redis per TCP-Verbindung angesprochen, muss nichts weiter konfiguriert werden. Soll die Verbindung über das Socket hergestellt werden, müssen die Serverangaben in `wp-config.php` ergänzt werden:{{{
$redis_server = array(
  'host' => '/run/redis/redis-server.sock',
  'port' => 0,
);}}}
 * Zudem muss der Wordpress-Nutzer zur Redis-Gruppe hinzugefügt werden:{{{
usermod -a -G redis katja}}}

== Varnish-Caching ==
Alternativ zu Redis kann [[http://varnish-cache.org|Varnish]] als Caching-Lösung eingesetzt werden. Dadurch lassen sich die Caching-Einstellungen ziemlich detailliert anpassen. Die nachfolgenden Punkte orientieren sich an den Blog-Artikeln [[https://www.getpagespeed.com/server-setup/varnish/varnish-virtual-hosts|Varnish Virtual Hosts. The Right Way]] sowie [[https://info.varnish-software.com/blog/failure-to-purge-a-story-about-client.ip-and-proxies|Purge and proxies, a love-hate affair]]. In der Wordpress-[[https://codex.wordpress.org/Varnish|Dokumentation]] ist ein umfangreiches Konfigurationsbeispiel für Varnish enthalten. Dort werden unnötigerweise viele Sachen gedoppelt, die Varnish durch seine ''Built-in vcl_recv'' sowieso macht<<FootNote(Siehe [[https://info.varnish-software.com/the-varnish-book|The Varnish Book]], Kapitel 7.8 "Built-in vcl_recv")>>.
 * Installation über die Paketquellen:{{{
apt install varnish}}}
 * Varnish lauscht anschließend auf Port `6081` auf Verbindungen.
 * Da die freie Version keine SSL-Terminierung unterstützt, muss Varnish hinter einem Reverse-Proxy laufen
 * Die Konfiguration in `/etc/varnish/default.vcl` sieht so aus:{{{
vcl 4.0;

acl local {
    "localhost";
    "192.168.0.2";
    "192.168.0.1";
}

import std;

backend default {
    .host = "127.0.0.1";
    .port = "80";
}

sub vcl_recv {
}

sub vcl_backend_response {
}

sub vcl_deliver {
    unset resp.http.server;
    unset resp.http.via;
    unset resp.http.x-powered-by;
    unset resp.http.x-runtime;
    unset resp.http.x-varnish;
}

include "vhosts.vcl";}}}
 * Hier werden folgende Sachen konfiguriert:
  * Die ACL-Regel ''local'' besagt, dass der Cache nur von berechtigten Adresse gelöscht werden kann. ''192.168.0.1'' ist in diesem Fall der Reverse-Proxy.
  * Das Modul ''std'' wird importiert - es wird für die ACL-Regel benötigt.
  * Unter '' sub vcl_deliver'' wir die Auslieferung verschiedener Header unterdrückt.
  * Die Konfiguration von einzelnen Seiten erfolgt über die Einbindung der Datei `/etc/varnish/hosts.vcl`.
 * In `/etc/varnish/hosts.vcl` werden wiederum lediglich weitere Seiten inkludiert:{{{
include "sites.d/example.org.vcl";}}}
 * Im anzulegenden Verzeichnis `/etc/varnish/sites.d` befinden sich dann die spezielle Seitenkonfiguration `example.org.vcl`:{{{
sub vcl_recv {
    if (req.http.host == "example.org") {
        set req.backend_hint = default;
    }
    include "sites.d/wordpress.vcl";
}

sub vcl_backend_response {
    if (beresp.ttl == 120s) {
        set beresp.ttl = 1h;
    }
}
}}}
 * Hier werden folgende Sachen festgelegt:
  * Inkludieren der allgemeinen Wordpress-Konfiguration
  * Konfigurieren der Haltbarkeit der gecachten Sachen
 * Letzendlich sieht die `sites.d/wordpress.vcl` so aus:{{{
if (req.http.Upgrade ~ "(?i)websocket") {
    return (pipe);
}

if (req.url ~ "wp-admin|wp-login|login") {
    return (pass);
}

set req.http.cookie = regsuball(req.http.cookie, "wp-settings-\d+=[^;]+(; )?", "");
set req.http.cookie = regsuball(req.http.cookie, "wp-settings-time-\d+=[^;]+(; )?", "");
set req.http.cookie = regsuball(req.http.cookie, "wordpress_test_cookie=[^;]+(; )?", "");
    if (req.http.cookie == "") {
        unset req.http.cookie;
    }

if (req.method == "PURGE") {
    if (std.ip(req.http.x-real-ip, "0.0.0.0") ~ local) {
        return (purge);
    } else {
        return (synth(403));
    }
}
}}}
 * Damit die ACL-Regel funktioniert und nur berechtigte IP-Adressen den Cache löschen können, muss der Reverse-Proxy den Header ''X-Real-IP'' setzen.
 * Zum automatischen Löschen der Caches bei Änderungen an der Seite kann das Plugin [[https://it.wordpress.org/plugins/varnish-http-purge/|Proxy Cache Purge]] verwendet werden.

= Abschließende Hinweise =
 * In der obigen Apachekonfiguration wird mittels rewrite die Installation von eigenen Themes und Plugins unterbunden. Ergänzend dazu kann die Bearbeitung vorhandener Themes und Plugins im Dashboard verhindert werden:{{{
# wp-config.php
...
define('DISALLOW_FILE_EDIT', true)}}}
 * Ein paar Abweichungen von den gängigen Anleitungen zur Einrichtung von PHP-FPM seien noch erwähnt:
  * Das Mounten oder Kopieren von `sendmail` in das chroot für Mailfunktionalität: Das funktioniert so nicht. Sendmail hat weitere Abhängigkeiten und braucht Zugriff auf verschiedene Verzeichnisse. Als Aternative wird [[https://www.acme.com/software/mini_sendmail/|mini_sendmail]] vorgeschlagen. Mittels der oben dargestellten PHP-FPM-Konfiguration wird `mail()` eh deaktiviert. Sendmail ist also in diesem Setup nicht notwendig. Der Mailversand aus Wordpress erfolgt mittels eines SMTP-Plugins.
  * Das Mounten oder Kopieren von `/etc/ssl/certs` in das chroot: Die Zertikate alleine reichen für die volle Funktionalität nicht aus. PHP benötigt zudem Zugriff auf `/usr/lib/ssl/openssl.cnf`.

----
'''Hinweise und Links'''

 * [[https://unix.wroclaw.pl/igort/en/php-fpm-chroot-fopen-url-problems.html|Validierung von Zertifikaten]] im chroot debuggen
 * Anpassen der [[https://www.getpagespeed.com/server-setup/nginx-and-php-fpm-what-my-permissions-should-be|Benutzerrechte]] für PHP-PFM
 * [[https://blog.kernl.us/2020/02/should-i-use-memcached-or-redis-for-wordpress-caching/|Performancevergleich]] zwischen Memcached und Redis
 * [[https://github.com/pantheon-systems/wp-redis|Doku]] zum WP-Redis-Plugin


CategorySystemausfall

'''Fussnoten'''

Creative Commons Lizenzvertrag
This page is licensed under a Creative Commons Attribution-ShareAlike 2.5 License.