Alle Tutorials
Auf dieser Seite
Schritt 2: txAdmin hinter einem Apache-Reverse-Proxy
Das txAdmin-Panel über Apache 2.4 mit HTTPS auf einer eigenen Subdomain erreichbar machen, inklusive WebSocket-Weiche für die Live-Konsole und den Fallen, die dabei niemand bemerkt.
Das hier ist der zweite Teil der FiveM-Reihe. Nach Schritt 1 ist Port 40120 von außen gesperrt und das Panel nur über einen SSH-Tunnel erreichbar. Das ist sicher, aber unbequem, und für mehr als eine Person taugt es nicht.
Am Ende dieses Teils nimmt Apache die Anfragen unter einer eigenen Subdomain per HTTPS entgegen, die Live-Konsole bekommt eine echte WebSocket-Verbindung, und der Tunnel ist überflüssig. Port 40120 bleibt dabei geschlossen, das ist der ganze Punkt.
Voraussetzungen
- ein laufender FXServer nach Schritt 1, das
Panel ist lokal unter
127.0.0.1:40120erreichbar und von außen gesperrt - Apache 2.4 mit funktionierendem HTTPS, entweder über Certbot oder über ein Wildcard-Zertifikat
- eine Subdomain, die auf den Server zeigt, im Beispiel
tx.example.de
Die Apache-Version ist wichtiger als sie aussieht. Ab 2.4.47 gibt es die Option
upgrade=websocket an ProxyPass, und damit wird die WebSocket-Weiche zu einer
einzigen Zeile. Darunter braucht es den Umweg über mod_rewrite, beide
Varianten stehen unten.
apache2 -vSchritt 2.1: Apache-Module aktivieren
a2enmod ssl proxy proxy_http proxy_wstunnel headers rewrite
systemctl restart apache2proxy_wstunnel wird auch dann gebraucht, wenn Du die Variante mit
upgrade=websocket nimmst. Die Option an ProxyPass stellt nur die Weiche, das
Tunneln selbst macht weiterhin dieses Modul.
Schritt 2.2: vHost anlegen
Die Konfiguration unten gibt es als kommentierte Vorlage:
curl -fsSL https://uploads.musiker15.de/fivem/txadmin-vhost.conf \
-o /etc/apache2/sites-available/tx.example.de.conf
nano /etc/apache2/sites-available/tx.example.de.conf<VirtualHost *:80>
ServerName tx.example.de
Redirect permanent / https://tx.example.de/
</VirtualHost>
<VirtualHost *:443>
ServerName tx.example.de
ServerAdmin info@example.de
SSLEngine on
SSLCertificateFile /etc/apache2/ssl/example.de/fullchain.cer
SSLCertificateKeyFile /etc/apache2/ssl/example.de/example.de.key
# Security-Header
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
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 Cross-Origin-Opener-Policy "same-origin"
Header always set Cross-Origin-Resource-Policy "same-origin"
# Reverse Proxy inklusive WebSocket-Upgrade
ProxyPreserveHost On
ProxyTimeout 600
ProxyPass / http://127.0.0.1:40120/ upgrade=websocket
ProxyPassReverse / http://127.0.0.1:40120/
RequestHeader set X-Forwarded-Proto "https"
</VirtualHost>a2ensite tx.example.de.conf
apachectl -t && systemctl reload apache2Wenn Apache älter als 2.4.47 ist
Dann kennt ProxyPass die Option upgrade=websocket nicht und quittiert mit
Unknown parameter. Stattdessen übernimmt mod_rewrite die Weiche, und zwar
vor dem ProxyPass:
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule ^/?(.*) ws://127.0.0.1:40120/$1 [P,L]
ProxyPass / http://127.0.0.1:40120/
ProxyPassReverse / http://127.0.0.1:40120/Was der Live-Konsole sonst noch das Genick bricht
ProxyTimeout 600 ist kein Schmuck. Ohne die Zeile gilt der globale Timeout,
also 60 Sekunden. Eine Live-Konsole, auf der eine Minute lang nichts passiert,
zählt damit als tote Verbindung und wird gekappt. Sichtbar wird das als Panel,
das sich alle paar Minuten neu verbindet und dabei den Konsolenpuffer verliert.
Schritt 2.3: Zwei Fallen in typischen Konfigurationen
Beides sieht man häufig, beides funktioniert scheinbar, und beides ist falsch.
X-Forwarded-For nicht von Hand setzen
# Nicht so:
RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s"mod_proxy_http setzt X-Forwarded-For, X-Forwarded-Host und
X-Forwarded-Server von allein. Die RequestHeader-Zeile läuft davor, danach
hängt das Modul die Client-IP an den vorhandenen Wert an. Beim Backend kommt
dann 1.2.3.4, 1.2.3.4 an. Für txAdmin heißt das: die IP in den Logs und in der
Ban-Ansicht ist unbrauchbar, und Ratenbegrenzungen greifen an der falschen
Stelle.
X-Forwarded-Proto setzt das Modul dagegen nicht, diese Zeile bleibt also
stehen.
Die CSP muss zur eigenen Domain passen
Der Wert connect-src entscheidet, wohin der Browser die WebSocket-Verbindung
aufbauen darf. Steht dort eine Domain aus einer alten Konfiguration, blockiert
der Browser die Verbindung und die Live-Konsole bleibt leer.
# Aus einer anderen Installation uebernommen, passt hier nicht:
connect-src 'self' wss://irgendeine-andere-domain.de;Brauchst Du trotzdem eine eigene CSP, muss connect-src die eigene Subdomain
nennen:
Header always set Content-Security-Policy "connect-src 'self' wss://tx.example.de; frame-ancestors 'self';"Schritt 2.4: txAdmin die eigene Adresse mitteilen
Unter Settings → General die txAdmin-URL auf https://tx.example.de
setzen. Aus dieser Adresse baut txAdmin die Links, die es an Discord und in
Einladungen verschickt. Ohne die Angabe stehen dort
http://<server-ip>:40120-Adressen, die von außen nicht erreichbar sind.
Ab jetzt ist der SSH-Tunnel aus Schritt 1
überflüssig. Die ufw-Regel deny 40120/tcp bleibt selbstverständlich stehen.
Prüfen
# Ist der Port von aussen dicht (von einem anderen Rechner aus)
curl -m 5 http://<server-ip>:40120/ ; echo "exit: $?"
# Antwortet der Proxy
curl -sI https://tx.example.de/ | head -1Den WebSocket-Upgrade misst man im Browser, nicht mit curl
Richtig ist der echte Browser. Panel öffnen, Entwicklerwerkzeuge, Netzwerk, Filter WS. Dort muss eine Verbindung mit Status 101 stehen. Oder in der Konsole:
new WebSocket("wss://tx.example.de/socket.io/?EIO=4&transport=websocket").onopen = () =>
console.log("Upgrade steht");Bleibt es beim Polling, siehst Du im Netzwerk-Tab stattdessen alle paar Sekunden
neue transport=polling-Anfragen.
Wenn der Upgrade nicht durchgeht
upgrade=websocket ist die Standardantwort, und sie reicht nicht immer. Auf
einem meiner Server blieb es trotz upgrade=websocket, upgrade=ANY,
mod_proxy_wstunnel mit Rewrite-Weiche und abgeschaltetem h2c bei Status
400.
Die Messung, die dort weitergeholfen hat, ist der Vergleich der
Antwortgröße. Das Backend antwortet auf denselben Handshake mit 34 Bytes,
durch den Proxy kamen 1414. Der 400 stammte also von Apache selbst, nicht von
txAdmin:
# Direkt am Backend, auf dem Server
curl -s -o /dev/null -w '%{size_download}\n' \
"http://127.0.0.1:40120/socket.io/?EIO=4&transport=websocket"
# Durch den Proxy
curl -s -o /dev/null -w '%{size_download}\n' \
"https://tx.example.de/socket.io/?EIO=4&transport=websocket"Regel daraus: liefern Proxy und Backend denselben Statuscode, ist der
Statuscode kein Beweis. Erst Größe oder Rumpf sagen, wer geantwortet hat. Wer
weitersucht, beginnt bei LogLevel alert proxy:trace3.
Häufige Fehler
| Meldung oder Symptom | Ursache | Lösung |
|---|---|---|
503 Service Unavailable | FXServer läuft nicht | systemctl status fivem, ss -tlnp |
| Panel lädt, Live-Konsole nur traege (Polling) | WebSocket-Upgrade kommt nicht durch | Weiche aus Schritt 2.2 prüfen, 101 erwarten |
| Panel verbindet sich alle paar Minuten neu | ProxyTimeout zu kurz | ProxyTimeout 600 setzen |
CSP-Verletzung zu wss:// in der Browser-Konsole | falsche Domain in connect-src | eigene Subdomain eintragen oder CSP weglassen |
| Alle Zugriffe kommen mit derselben IP an | X-Forwarded-For doppelt gesetzt | manuelle RequestHeader-Zeile entfernen |
Unknown parameter: upgrade=websocket | Apache älter als 2.4.47 | Rewrite-Variante aus Schritt 2.2 nehmen |
| Zertifikatswarnung mit fremder Domain | kein passender :443-vHost, SNI-Fallback | ServerName prüfen, apache2ctl -S |
Discord-Links zeigen auf :40120 | txAdmin-URL nicht gesetzt | siehe Schritt 2.4 |
Weiter mit Schritt 3
Der Server läuft, das Panel ist sicher erreichbar. Bleibt die Frage, wer die Artefakte aktuell hält. Der letzte Teil automatisiert das: