Deploy — Zielhosts
Für den Zielhost gilt immer dieselbe Regel: DocumentRoot/root = public/. Alles andere
(src/, bin/, database/, state/) ist per Verzeichnisstruktur nicht erreichbar.
Der Rest hängt vom Betriebssystem ab (Pfade, Benutzer, fpm-Socket, SELinux).
| Vorlage | Webserver |
|---|---|
apache-vhost.conf.example |
Apache 2.4 + PHP-FPM (proxy_fcgi) |
nginx.conf.example |
nginx + PHP-FPM |
Ablauf (jedes OS)
# 1. Code
git clone … /srv/tlsgate && cd /srv/tlsgate && git checkout v2.0.0
# 2. Laufzeitdaten ausserhalb des Checkouts, nur fuer den Web-User (<WEBUSER> siehe Tabelle)
install -d -o <WEBUSER> -g <WEBUSER> -m 0700 /var/lib/tlsgate
# 3. Wurzelschluessel getrennt von der DB (docs/SECURITY.md 1c, docs/DESIGN_TENANTS.md §2)
# Format: "<key_id>:<base64 von 32 Byte>". TOTP-Secrets (und spaeter SOAP-Zugaenge) werden daraus abgeleitet.
install -d -m 0750 -g <WEBUSER> /etc/tlsgate
( umask 0277; php -r 'echo "1:", base64_encode(random_bytes(32)), "\n";' > /etc/tlsgate/root.key )
chown root:<WEBUSER> /etc/tlsgate/root.key && chmod 0640 /etc/tlsgate/root.key
# 4. DB anlegen — als root/sudo (der CLI-Guard verlangt euid 0 oder dass dem Aufrufer alles gehört; "sudo -u <WEBUSER>" geht erst nach dem chown), mit denselben Pfaden
# wie der Webserver. Die CLI legt die Dateien als root an, deshalb danach dem Web-User uebergeben.
export TLS_GATE_STATE_DIR=/var/lib/tlsgate TLSGATE_ROOT_KEY_FILE=/etc/tlsgate/root.key
sudo -E php bin/user_cli.php migrate # nach jedem Update erneut: erst --dry-run, dann migrate (Sicherung liegt daneben)
sudo -E php bin/user_cli.php group add bwa "B.W.A" eigen
sudo -E php bin/user_cli.php user add admin@example.org bwa admin
sudo -E php bin/user_cli.php cred add bwa registrar-a https://…/soap.wsdl && sudo -E php bin/user_cli.php cred test bwa registrar-a
sudo chown -R <WEBUSER>:<WEBUSER> /var/lib/tlsgate # Web-Prozess braucht Schreibrecht (SQLite -wal/-journal)
# 5. Webserver-Vorlage anpassen (server_name, Zertifikat, fpm-Socket), einbinden, testen, neu laden
# 6. Gegenprobe mit denselben Pfaden
TLS_GATE_STATE_DIR=/var/lib/tlsgate APACHE_USER=<WEBUSER> bin/TLS-doktor.bash
curl -sI https://tlsgate.example.org/login.php # 200 + Sicherheits-Header
curl -so /dev/null -w '%{http_code}\n' --path-as-is https://tlsgate.example.org/../bin/user_cli.php # 404/400
FPM-Pool: Umgebung für PHP
SetEnv (Apache) bzw. fastcgi_param (nginx) reichen für Pfade. SOAP-Zugangsdaten stehen weder hier noch im Pool, sondern
verschlüsselt in der DB (php bin/user_cli.php cred add <gruppe> <label> <wsdl>, danach cred test). Im Pool bleiben nur Pfade:
; <pool.d>/tlsgate.conf
[tlsgate]
user = <WEBUSER>
group = <WEBUSER>
listen = <SOCKET>
listen.owner = <WEBUSER>
listen.group = <WEBUSER>
pm = ondemand
pm.max_children = 5
clear_env = yes
env[TLS_GATE_STATE_DIR] = /var/lib/tlsgate
env[TLSGATE_ROOT_KEY_FILE] = /etc/tlsgate/root.key
php_admin_value[open_basedir] = /srv/tlsgate:/var/lib/tlsgate:/etc/tlsgate:/tmp
php_admin_value[disable_functions] = exec,shell_exec,system,passthru,proc_open,popen
php_admin_flag[expose_php] = off
Hinweis zu open_basedir: Das Web braucht /srv/tlsgate (wegen src/, bootstrap.php und
resources/mock/), den State-Pfad und den Key-Pfad. disable_functions betrifft nur den Web-Pool.
bin/dashboard.sh und die CLIs laufen mit der CLI-php.ini.
Pro Betriebssystem
| Debian / Ubuntu | RHEL / Alma / Rocky | Alpine | FreeBSD | |
|---|---|---|---|---|
| Pakete | php-fpm php-sqlite3 php-soap (sodium ist im Kern) |
php-fpm php-pdo php-soap php-sodium |
php83-fpm php83-pdo_sqlite php83-sodium php83-soap php83-openssl |
php83 php83-pdo_sqlite php83-sodium php83-soap |
<WEBUSER> |
www-data |
Apache: apache · nginx: nginx |
nginx (bzw. apache) |
www |
<SOCKET> |
/run/php/php8.3-fpm.sock |
/run/php-fpm/www.sock |
/run/php-fpm83/php-fpm.sock (im Pool setzen) |
/var/run/php-fpm.sock (im Pool setzen, Standard ist TCP 127.0.0.1:9000) |
| FPM-Pool-Dir | /etc/php/8.3/fpm/pool.d/ |
/etc/php-fpm.d/ |
/etc/php83/php-fpm.d/ |
/usr/local/etc/php-fpm.d/ |
| Apache-vhost | /etc/apache2/sites-available/ + a2ensite, a2enmod proxy_fcgi ssl headers |
/etc/httpd/conf.d/ |
/etc/apache2/conf.d/ |
/usr/local/etc/apache24/Includes/ |
| nginx-Site | /etc/nginx/sites-available/ + Symlink |
/etc/nginx/conf.d/ |
/etc/nginx/http.d/ |
/usr/local/etc/nginx/ (include) |
| Log-Makro | ${APACHE_LOG_DIR} gibt es |
kein ${APACHE_LOG_DIR} → logs/tlsgate_error.log |
wie RHEL | /var/log/httpd-tlsgate_error.log |
| Besonderheit | — | SELinux, siehe unten | musl: PASSWORD_ARGON2ID prüfen (Doktor) |
Pfade unter /usr/local; bin/*.sh brauchen bash (pkg) |
PHP-Versionen in der Tabelle sind Beispiele. Mindestens PHP 8.1 wird gebraucht (composer.json). Der
Doktor zeigt, ob pdo_sqlite, sodium und Argon2id in der FPM-Version vorhanden sind. CLI und FPM
können unterschiedlich gebaut sein.
SELinux (RHEL-Familie)
semanage fcontext -a -t httpd_sys_content_t '/srv/tlsgate(/.*)?'
semanage fcontext -a -t httpd_sys_rw_content_t '/var/lib/tlsgate(/.*)?'
semanage fcontext -a -t httpd_sys_content_t '/etc/tlsgate(/.*)?'
restorecon -Rv /srv/tlsgate /var/lib/tlsgate /etc/tlsgate
setsebool -P httpd_can_network_connect on # ausgehender SOAP-Call zum Partnergate
Ohne httpd_can_network_connect fällt die App still auf den Mock zurück. Die Oberfläche zeigt dann
die Quelle „Mock-Fallback“, obwohl die Zugangsdaten stimmen.
Hinter einem Reverse-Proxy
Die IP-Sperre nutzt REMOTE_ADDR, X-Forwarded-For wertet sie nicht aus. Deshalb muss die echte
Client-IP am Webserver ankommen: bei nginx über set_real_ip_from + real_ip_header, bei Apache über
mod_remoteip. Sonst sperrt ein einziger Angreifer den Proxy und damit alle Benutzer.