endeavour: .env-Rechte, Socket-Prüfung und HTTPS-Proxy korrigiert

- deploy.sh setzt die .env auf root:mareike-docker 0640 und prüft DB_SOCKET/Socket vor dem Build
- optionale shared/deploy.conf für server-spezifische Einstellungen (z. B. MAREIKE_DB_SOCKET_DIR)
- nginx: absolute_redirect off, damit keine http://-Redirects entstehen
- Anleitung: Proxy per mod_rewrite nur unter HTTPS, keine KeyHelp-Weiterleitung, lv auf der Hauptdomain

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-26 14:30:42 +02:00
co-authored by Claude Opus 5.5
parent 985c9ce07e
commit c4ca101594
4 changed files with 115 additions and 24 deletions
+75 -22
View File
@@ -51,7 +51,8 @@ Browser ──HTTPS──▶ KeyHelp-Webserver (Apache/nginx, Let's Encrypt, ein
/opt/mareike/
├── bin/deploy.sh Kopie von docker/endeavour/deploy.sh
├── src/ Git-Checkout (wird bei jedem Deploy aktualisiert)
├── shared/.env Konfiguration mit Zugangsdaten (nur root lesbar)
├── shared/.env Konfiguration mit Zugangsdaten (root, Gruppe mareike-docker lesbar, 0640)
├── shared/deploy.conf optional: Server-Einstellungen für deploy.sh (z. B. MAREIKE_DB_SOCKET_DIR)
└── storage/ Laravel-storage/ (Logs, Sessions, erzeugte PDFs/ZIPs) – persistent
```
@@ -152,8 +153,18 @@ install -m 0750 /opt/mareike/src/docker/endeavour/deploy.sh /opt/mareike/bin/dep
mysql -e "SELECT @@socket;" # erwartet: /run/mysqld/mysqld.sock
```
Liegt der Socket woanders, in `/opt/mareike/bin/deploy.sh` die Variable `MAREIKE_DB_SOCKET_DIR` auf das Verzeichnis
setzen und `DB_SOCKET` in der `.env` anpassen.
Liegt der Socket woanders, z. B. unter `/var/lib/mysql/mysql.sock`, das **Verzeichnis** in
`/opt/mareike/shared/deploy.conf` eintragen:
```bash
echo 'MAREIKE_DB_SOCKET_DIR=/var/lib/mysql' > /opt/mareike/shared/deploy.conf
```
In der `.env` bleibt das Verzeichnis `/run/mysqld`, denn so heißt der Mount-Punkt im Container. Nur der Dateiname
wird übernommen, im Beispiel also `DB_SOCKET=/run/mysqld/mysql.sock`.
`deploy.sh` prüft vor jedem Build, ob `DB_SOCKET` gesetzt ist und der Socket auf dem Host existiert. Wenn nicht,
bricht es mit einem Hinweis ab.
Weil die Datenbank von KeyHelp verwaltet wird, ist sie automatisch im **KeyHelp-Backup** enthalten.
@@ -162,7 +173,7 @@ Weil die Datenbank von KeyHelp verwaltet wird, ist sie automatisch im **KeyHelp-
## 5. `.env` anlegen
```bash
install -m 0600 /opt/mareike/src/docker/endeavour/env.production.example /opt/mareike/shared/.env
install -m 0640 -o root -g mareike-docker /opt/mareike/src/docker/endeavour/env.production.example /opt/mareike/shared/.env
echo "base64:$(openssl rand -base64 32)" # Ergebnis als APP_KEY eintragen
nano /opt/mareike/shared/.env
```
@@ -225,7 +236,7 @@ angelegt wird.
App\Models\Tenant::create([
'slug' => 'lv',
'name' => '<Name des Verbands>',
'url' => '<lv-subdomain>.meine-anmeldung.digital',
'url' => 'meine-anmeldung.digital',
'email' => '<kontakt@…>',
'email_finance' => '<finanzen@…>',
'account_name' => '<Kontoinhaber>',
@@ -249,7 +260,7 @@ global und werden für jeden aktiven Tenant ausgeführt.
### 6.4 Erste Administratorin / ersten Administrator
1. Auf `https://<lv-subdomain>.meine-anmeldung.digital` ganz normal registrieren. Voraussetzung: Abschnitt 7 ist
1. Auf `https://meine-anmeldung.digital` ganz normal registrieren. Voraussetzung: Abschnitt 7 ist
erledigt und der Mailversand funktioniert.
2. Das Konto zur Administration hochstufen:
@@ -261,20 +272,36 @@ global und werden für jeden aktiven Tenant ausgeführt.
## 7. Tenant-Domain in KeyHelp einrichten (je Tenant)
Diesen Ablauf für den Dach-Tenant `lv` und später für jede weitere Gruppe wiederholen.
Domain-Schema:
### 7.1 Einmalig: Proxy-Module aktivieren (Apache)
- Dach-Tenant `lv`: die **Hauptdomain** `meine-anmeldung.digital`. Sie existiert in KeyHelp bereits samt
Let's-Encrypt-Zertifikat.
- Gruppen: `<gruppe>.meine-anmeldung.digital`, jede als eigene Domain in KeyHelp mit eigenem Zertifikat.
Den folgenden Ablauf für die Hauptdomain und später für jede Gruppe wiederholen.
> **Wichtig:** Nicht die KeyHelp-Funktion **„Weiterleitung“** benutzen. Sie schickt den *Browser* auf
> `http://127.0.0.1:8080`, und das kann nicht funktionieren. Gebraucht wird ein **Proxy**: KeyHelp holt die Seite
> intern vom Container ab und liefert sie selbst per HTTPS mit seinem Zertifikat aus. Das geschieht ausschließlich über
> die Webserver-Direktiven unten.
### 7.1 Einmalig: Module aktivieren (Apache)
```bash
a2enmod proxy proxy_http headers
a2enmod proxy proxy_http headers rewrite
systemctl reload apache2
```
### 7.2 Domain anlegen
### 7.2 Domain einrichten
1. KeyHelp → **Domains** → neue Domain für das mareike-Konto, z. B. `wilde-moehre.meine-anmeldung.digital`.
Den DNS-Eintrag (A/AAAA) auf endeavour setzen, falls KeyHelp den DNS nicht selbst verwaltet.
2. **SSL/TLS:** Let's Encrypt aktivieren und **HTTPS erzwingen** einschalten.
1. **Hauptdomain:** In KeyHelp die vorhandene Domain `meine-anmeldung.digital` öffnen. Eine eventuell eingerichtete
*Weiterleitung* entfernen.
**Gruppe:** KeyHelp → **Domains** → neue Domain für das mareike-Konto anlegen, z. B.
`wilde-moehre.meine-anmeldung.digital`. Den DNS-Eintrag (A/AAAA) auf endeavour setzen, falls KeyHelp den DNS nicht
selbst verwaltet.
2. **SSL/TLS:** Let's Encrypt aktiv und **HTTPS erzwingen** eingeschaltet. Bei der Hauptdomain muss
`www.meine-anmeldung.digital` im Zertifikat enthalten sein, sonst gibt es beim www-Redirect eine
Zertifikatswarnung.
3. PHP wird für diese Domain nicht gebraucht.
4. In den Domain-Einstellungen im Feld für **individuelle Webserver-Direktiven** (nur als Admin sichtbar, die
Bezeichnung variiert je nach KeyHelp-Version) Folgendes eintragen:
@@ -282,17 +309,34 @@ systemctl reload apache2
**Apache** (KeyHelp-Standard):
```apache
RewriteEngine On
# Let's Encrypt von KeyHelp nicht anfassen
RewriteRule ^/?\.well-known/acme-challenge/ - [L]
# www → ohne www
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^/?(.*)$ https://%1/$1 [R=301,L,NE]
# alles ohne TLS auf https umleiten
RewriteCond %{HTTPS} !=on
RewriteRule ^/?(.*)$ https://%{HTTP_HOST}/$1 [R=301,L,NE]
# unter https an den Container durchreichen
RewriteRule ^/?(.*)$ http://127.0.0.1:8080/$1 [P,L,NE]
ProxyPreserveHost On
ProxyRequests Off
RequestHeader set X-Forwarded-Proto "https"
ProxyPass /.well-known/acme-challenge/ !
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
RequestHeader set X-Forwarded-Proto "https"
Header always set Strict-Transport-Security "max-age=31536000" "expr=%{HTTPS} == 'on'"
```
Die Regeln funktionieren unabhängig davon, ob KeyHelp die Direktiven in den Port-80- oder den Port-443-vHost
schreibt. Ohne TLS wird immer auf `https://` umgeleitet, nur unter HTTPS wird an den Container weitergereicht. Das
`http://127.0.0.1:8080` ist dabei die **interne** Verbindung zum Container, der Browser sieht sie nie.
**nginx** (falls KeyHelp auf nginx läuft):
```nginx
if ($scheme != "https") {
return 301 https://$host$request_uri;
}
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
@@ -301,9 +345,6 @@ systemctl reload apache2
client_max_body_size 70m;
}
```
Die Ausnahme für `/.well-known/acme-challenge/` sorgt dafür, dass KeyHelp die Zertifikate weiter selbst verlängern
kann.
5. Speichern und die Domain aufrufen. Solange der Tenant noch nicht existiert, zeigt mareike **404 „Tenant not
found“**. Das ist ein gutes Zeichen, denn der Proxy funktioniert.
@@ -324,7 +365,7 @@ Tenants:
```bash
cat > /etc/cron.d/mareike <<'EOF'
* * * * * root curl -fsS -o /dev/null -H "Host: <lv-subdomain>.meine-anmeldung.digital" http://127.0.0.1:8080/execute-crons
* * * * * root curl -fsS -o /dev/null -H "Host: meine-anmeldung.digital" http://127.0.0.1:8080/execute-crons
EOF
```
@@ -378,14 +419,26 @@ Enthält ein Release Datenbankänderungen, deshalb vorher ein DB-Backup ziehen.
| Symptom | Ursache / Lösung |
|---|---|
| `Database file at path […database.sqlite] does not exist` | Die `.env` wird im Container nicht gelesen, Laravel fällt dann auf SQLite zurück. Rechte prüfen (0640, Gruppe `mareike-docker`) und ob `MAREIKE_ENV_FILE` auf die richtige Datei zeigt. |
| Browser landet auf `http://` | In KeyHelp ist eine *Weiterleitung* statt der Proxy-Direktiven eingerichtet, oder es sind noch die alten `ProxyPass`-Direktiven ohne HTTPS-Umleitung aktiv (Abschnitt 7.2). Woher die http-Umleitung kommt, zeigen die drei `curl`-Befehle darunter. |
| 404 „Tenant not found“ | Der Host passt nicht zu `tenants.url` (Tippfehler, `https://` oder `/` im Eintrag), der Tenant ist nicht `has_active_instance`, oder der Proxy reicht den Host nicht durch (`ProxyPreserveHost On` fehlt). |
| 502/503 vom KeyHelp-Webserver | Container laufen nicht (`deploy.sh compose ps`) oder die Firewall wurde neu geladen, dann hilft `systemctl restart docker`. |
| Seite lädt ohne CSS, Mixed Content | `ASSET_URL` ist gesetzt oder die nginx-Config im Container wurde nicht übernommen (`fastcgi_param HTTPS on`). |
| `SQLSTATE[HY000] [2002]` | Socket nicht erreichbar. `SELECT @@socket` prüfen, `MAREIKE_DB_SOCKET_DIR` bzw. `DB_SOCKET` anpassen, nach einem MariaDB-Neustart den Stack mit `deploy.sh compose restart app` neu starten. |
| `SQLSTATE[HY000] [2002] No such file or directory` | Im Container liegt kein Socket unter `DB_SOCKET`. Entweder fehlt `DB_SOCKET` in der `.env`, oder der Socket liegt auf dem Host nicht in `/run/mysqld`. Pfad mit `mysql -NBe "SELECT @@socket;"` prüfen und ggf. `MAREIKE_DB_SOCKET_DIR` in `shared/deploy.conf` setzen (Abschnitt 4). Nach einem MariaDB-Neustart hilft oft `deploy.sh compose restart app`. |
| `Permission denied` in storage/ | Besitzer von `/opt/mareike/storage` muss `mareike-docker` sein, der nächste `deploy.sh`-Lauf korrigiert das. |
| Upload bricht ab | Grenzen prüfen: `MAX_INVOICE_FILE_SIZE` in der `.env`, 64 MB in `php.ini`, 70 MB in `nginx.conf`. |
| Mails kommen nicht an | `MAIL_*` prüfen, Log in `storage/logs/`. Absenderadresse muss zum Postfach passen (SPF/DKIM der Domain in KeyHelp). |
Woher kommt eine Umleitung auf `http://`?
```bash
curl -sI http://meine-anmeldung.digital/ | grep -iE '^(HTTP|Location)' # erwartet: 301 → https://meine-anmeldung.digital/
curl -sI https://meine-anmeldung.digital/ | grep -iE '^(HTTP|Location)' # erwartet: 302 → https://meine-anmeldung.digital/login
curl -sI -H 'Host: meine-anmeldung.digital' http://127.0.0.1:8080/ | grep -i '^Location' # Container direkt: https://…/login
```
Zeigt der dritte Befehl `https://`, der zweite aber `http://`, liegt es an den KeyHelp-Einstellungen, nicht an mareike.
Logs:
```bash