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:
@@ -13,6 +13,13 @@
|
||||
set -euo pipefail
|
||||
|
||||
MAREIKE_BASE="${MAREIKE_BASE:-/opt/mareike}"
|
||||
|
||||
# Server-spezifische Einstellungen, z. B. MAREIKE_DB_SOCKET_DIR=/var/lib/mysql
|
||||
if [[ -f "$MAREIKE_BASE/shared/deploy.conf" ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
source "$MAREIKE_BASE/shared/deploy.conf"
|
||||
fi
|
||||
|
||||
MAREIKE_USER="${MAREIKE_USER:-mareike-docker}"
|
||||
SRC="$MAREIKE_BASE/src"
|
||||
|
||||
@@ -48,6 +55,28 @@ if [[ ! -f "$MAREIKE_ENV_FILE" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Der Container läuft als $MAREIKE_USER und muss die eingebundene .env lesen können.
|
||||
# Ist sie unlesbar, ignoriert Laravel sie still und fällt z. B. auf SQLite zurück.
|
||||
chown "root:$MAREIKE_GID" "$MAREIKE_ENV_FILE"
|
||||
chmod 0640 "$MAREIKE_ENV_FILE"
|
||||
|
||||
# Der Container erreicht die KeyHelp-MariaDB nur über den Socket im eingebundenen Verzeichnis /run/mysqld.
|
||||
DB_SOCKET="$( (grep -E '^DB_SOCKET=' "$MAREIKE_ENV_FILE" || true) | tail -n 1 | cut -d= -f2- | tr -d "\"' \r")"
|
||||
if [[ -z "$DB_SOCKET" ]]; then
|
||||
echo "DB_SOCKET fehlt in $MAREIKE_ENV_FILE (erwartet: DB_SOCKET=/run/mysqld/mysqld.sock)." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$(dirname "$DB_SOCKET")" != "/run/mysqld" ]]; then
|
||||
echo "DB_SOCKET muss in /run/mysqld/ liegen (Mount-Punkt im Container), ist aber: $DB_SOCKET" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -S "$MAREIKE_DB_SOCKET_DIR/$(basename "$DB_SOCKET")" ]]; then
|
||||
echo "Kein MariaDB-Socket unter $MAREIKE_DB_SOCKET_DIR/$(basename "$DB_SOCKET")." >&2
|
||||
echo "Pfad prüfen mit: mysql -NBe 'SELECT @@socket;'" >&2
|
||||
echo "Liegt er woanders, MAREIKE_DB_SOCKET_DIR in $MAREIKE_BASE/shared/deploy.conf setzen." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> Quellcode auf '$REF' bringen"
|
||||
git -C "$SRC" fetch --tags --prune --force origin
|
||||
if git -C "$SRC" show-ref --verify --quiet "refs/remotes/origin/$REF"; then
|
||||
@@ -70,6 +99,11 @@ done
|
||||
echo "==> Images bauen"
|
||||
compose build --pull
|
||||
|
||||
if ! compose run --rm --no-deps app php -r 'exit(is_readable(".env") ? 0 : 1);'; then
|
||||
echo ".env ist im Container nicht lesbar – Besitzer/Rechte von $MAREIKE_ENV_FILE prüfen." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> Datenbank migrieren"
|
||||
compose run --rm --no-deps app php artisan migrate --force
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ APP_ENV=production
|
||||
APP_DEBUG=false
|
||||
# Erzeugen mit: echo "base64:$(openssl rand -base64 32)" – danach NIE mehr ändern (verschlüsselte Daten!)
|
||||
APP_KEY=
|
||||
# Domain des Dach-Tenants "lv"
|
||||
APP_URL=https://<lv-subdomain>.meine-anmeldung.digital
|
||||
# Die Hauptdomain ist der Dach-Tenant "lv"; Gruppen laufen als <gruppe>.meine-anmeldung.digital
|
||||
APP_URL=https://meine-anmeldung.digital
|
||||
# Kein ASSET_URL: Assets kommen von der jeweiligen Tenant-Domain selbst.
|
||||
|
||||
APP_LOCALE=de
|
||||
|
||||
@@ -17,6 +17,10 @@ server {
|
||||
index index.php;
|
||||
|
||||
server_tokens off;
|
||||
|
||||
# nginx sieht hinter dem Proxy nur http. Eigene Redirects (z. B. /tinymce → /tinymce/) daher relativ halten,
|
||||
# sonst landet der Browser auf http://.
|
||||
absolute_redirect off;
|
||||
client_max_body_size 70m;
|
||||
|
||||
location / {
|
||||
|
||||
+75
-22
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user