Server Tools: Technische Details
Technische Details zu Server Tools: Schichten und Module, Programmablauf, Webserver-Dispatch, erzeugte Apache- und Nginx-Konfigurationen, Logrotate, Versionserkennung, Fehlerbehandlung, Tests und CI.
Technische Details
Aufbau
Server Tools besteht aus einem Einstiegsscript und Libraries nach dem Muster "Building Blocks": Kleine Funktionen erledigen je eine Aufgabe, übergeordnete Funktionen setzen sie zu Abläufen mit Validierung, Rückfrage, Rollback und Audit-Log zusammen.
graph TD
A["bin/server-tools"] --> B["core.sh, config.sh, security.sh"]
A --> C["backup.sh, install.sh"]
A --> D["database.sh, vhost.sh, ssl.sh, cron.sh, firewall.sh, fail2ban.sh, log.sh, status.sh, user.sh"]
A --> E["cli.sh, menu.sh"]
D --> F["_ws_dispatch"]
F --> G["webserver/apache.sh"]
F --> H["webserver/nginx.sh"]
| Datei | Aufgabe |
|---|---|
bin/server-tools |
Einstieg: Library-Pfad bestimmen, Flags auswerten, Befehl verteilen |
lib/core.sh |
Logging, die, confirm, Abhängigkeitsprüfung, Versionsbestimmung |
lib/config.sh |
Standardwerte, Config laden und prüfen, --config-Ausgabe |
lib/security.sh |
Eingabevalidierung, Escaping, Audit-Log, Passwort-Erzeugung, sicheres Schreiben |
lib/backup.sh |
Backups vor dem Löschen, Backup-Übersicht |
lib/install.sh |
install und uninstall |
lib/database.sh |
MySQL-Operationen |
lib/vhost.sh |
VHost-Abläufe, Logrotate, Audit |
lib/webserver/apache.sh, nginx.sh |
Konfigurationserzeugung und Site-Mechanik je Webserver |
lib/ssl.sh, cron.sh, firewall.sh, fail2ban.sh, log.sh, status.sh, user.sh |
Fachmodule |
lib/cli.sh |
Argument-Verarbeitung und Verteilung der CLI-Befehle |
lib/menu.sh |
Interaktives Menü |
conf/server-tools.conf.example |
Config-Vorlage |
Jede Library beginnt mit einem Guard (_<NAME>_SOURCED), der doppeltes Laden verhindert.
Programmablauf
bin/server-toolssetztset -euo pipefail.- Es sucht die Libraries in
../librelativ zur Binary, danach in/usr/local/lib/server-tools. - Es lädt alle Libraries in fester Reihenfolge:
core,config,security,backup,database,vhost,ssl,cron,status,log,firewall,fail2ban,user,install,cli,menu. - Die globalen Flags
--yesund--quietwerden aus der Argumentliste entfernt.--yessetztST_AUTO_CONFIRM=true. - Der erste Parameter wählt den Befehl. Die meisten Zweige führen nacheinander
check_root,load_config,check_dependenciesund die CLI-Funktion aus. - Ohne Argument startet das Menü.
Die Version bestimmt core.sh aus der Datei .version neben den Libraries, sonst per git describe --tags --always im Verzeichnis der Libraries, sonst dev.
Fehlerbehandlung
Das Entry-Script läuft mit set -e. Eine Funktion, die mit Status 1 endet, beendet damit den ganzen Aufruf mit Exit-Code 1. Das Menü fängt das ab: Jede Aktion läuft dort mit || true, ein Fehler führt zurück ins Menü.
Meldungen schreiben log_info, log_warn und log_error auf stderr. die schreibt eine Fehlermeldung und beendet mit 1.
Webserver-Dispatch
vhost.sh ruft webserver-abhängige Funktionen über _ws_dispatch <name> <argumente> auf. Je nach ST_WEBSERVER wird daraus apache_<name> oder nginx_<name>:
| Name | Aufgabe |
|---|---|
vhost_config_path |
Pfad der Konfigurationsdatei |
vhost_exists, vhost_enabled |
Existenz, Aktivierung |
generate_vhost_config, generate_proxy_config, generate_redirect_config |
Konfiguration erzeugen |
generate_www_redirect_snippet, generate_https_redirect_snippet |
Weiterleitungs-Blöcke |
get_vhost_docroot, get_vhost_php_version, get_vhost_type, get_vhost_backend, websocket_enabled |
Werte aus einer Konfiguration lesen |
change_php_version_in_config |
Socket-Pfad ersetzen |
enable_site, disable_site, reload |
Aktivieren, Deaktivieren, Test und Reload |
enable_modules_php, enable_modules_proxy, rewrite_module_ensure |
Apache-Module, bei Nginx ohne Wirkung |
Typ, DocumentRoot, PHP-Version und Backend eines VHosts liest das Tool aus der Konfigurationsdatei, es gibt keine eigene Datenbank.
Erzeugte Konfigurationen
Die Beispiele zeigen die Vorlagen mit example.com, einem Alias und PHP 8.3.
Apache, PHP-VHost
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
ServerAdmin webmaster@localhost
DocumentRoot /var/www/example.com/html
<Directory /var/www/example.com/html>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>
# Security Headers
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
# Forwarded Headers
RequestHeader set X-Forwarded-Proto expr=%{REQUEST_SCHEME}
RequestHeader set X-Forwarded-Port expr=%{SERVER_PORT}
ServerSignature Off
ErrorLog /var/www/example.com/logs/error.log
CustomLog /var/www/example.com/logs/access.log combined
</VirtualHost>
Apache, Reverse Proxy
Statt DocumentRoot, Directory und FilesMatch enthält der Block:
ProxyPreserveHost On
ProxyPass / http://localhost:3000/
ProxyPassReverse / http://localhost:3000/
Mit --websocket folgt ein Block RewriteEngine On mit zwei RewriteCond (Upgrade: websocket, Connection: upgrade) und RewriteRule ^/?(.*) ws://localhost:3000/$1 [P,L]. Security- und Forwarded-Header sowie die Log-Pfade entsprechen dem PHP-VHost.
Nginx, PHP-VHost
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com/html;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
location ~ /\.ht {
deny all;
}
# Security Headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
access_log /var/www/example.com/logs/access.log;
error_log /var/www/example.com/logs/error.log;
}
Der Nginx-Proxy-Block enthält ein location / mit proxy_pass, proxy_http_version 1.1, proxy_set_header Host $host (außer bei --preserve-host false), X-Real-IP, X-Forwarded-For, X-Forwarded-Proto und X-Forwarded-Port, mit --websocket zusätzlich Upgrade und Connection "upgrade". Die Block-Struktur muss mit einer einzelnen Zeile } enden, weil Weiterleitungs-Blöcke vor dieser Zeile eingefügt werden.
Logrotate
/etc/logrotate.d/vhost-<domain> enthält für /var/www/<domain>/logs/*.log: weekly, missingok, rotate <ST_LOGROTATE_ROTATE>, maxage <ST_LOGROTATE_DAYS>, compress, delaycompress, notifempty, create 640 www-data www-data, sharedscripts und ein postrotate, das den Webserver neu lädt, wenn dessen PID-Datei existiert. Welcher Webserver das ist, steht beim Erzeugen fest im Text.
Dateien, die das Tool anlegt
| Pfad | Zweck |
|---|---|
/etc/server-tools/config |
Konfiguration |
<ST_CREDENTIAL_DIR>/<db>.txt |
Zugangsdaten je Datenbank |
<ST_BACKUP_DIR>/*.tar.gz |
Backups vor dem Löschen |
<ST_DB_BACKUP_DIR>/*.sql.gz |
Datenbank-Dumps |
<ST_AUDIT_LOG> |
Audit-Log |
/etc/logrotate.d/vhost-<domain> |
Logrotate je VHost |
/var/www/<domain>/logs/, html/ |
Verzeichnisse einer Domain |
/etc/cron.d/<name> |
Cronjobs |
/etc/cron.d/ssl-renewal, /usr/local/bin/ssl-renewal.sh, /var/log/ssl-renewal.log |
Zertifikats-Erneuerung |
/var/www/<domain>/.ssh/authorized_keys |
Schlüssel des Domain-Users |
Entwicklung und Tests
make setup-tests # BATS und Hilfsbibliotheken nach tests/libs klonen
make test # alle Tests
make lint # ShellCheck
make format-check # shfmt -i 4 -bn -ci
make check # lint, format-check und test
Die Tests liegen in tests/unit/*.bats (mehr als 500 Tests in 17 Dateien). Jeder Test läuft in einem eigenen temporären Verzeichnis, Systembefehle werden durch Mocks ersetzt (mock_command in tests/test_helper.bash). Kein Test berührt das echte System.
Die CI führt bei Push und Pull Request auf main drei Jobs aus: ShellCheck und shfmt, die BATS-Tests und einen Install-Smoke-Test, der make install, make uninstall, ./bin/server-tools install und uninstall gegen die echten /usr/local-Pfade ausführt.
Code-Stil: Bash 4.4 oder neuer, vier Leerzeichen Einrückung, set -euo pipefail nur im Entry-Script, Commits nach Conventional Commits. Die Vorgaben stehen in CONTRIBUTING.md des Repositories.