commit 1064a4d8e8b7659985821b20e75968ca1152fed9 Author: Christoph Date: Sun Jul 19 08:33:01 2026 +0200 inmitial commit diff --git a/Installationsanleitung_Euro-Office.docx b/Installationsanleitung_Euro-Office.docx new file mode 100644 index 0000000..ed7059c Binary files /dev/null and b/Installationsanleitung_Euro-Office.docx differ diff --git a/Installationsanleitung_Euro-Office.md b/Installationsanleitung_Euro-Office.md new file mode 100644 index 0000000..7b904d1 --- /dev/null +++ b/Installationsanleitung_Euro-Office.md @@ -0,0 +1,411 @@ +# Installationsanleitung: Euro-Office DocumentServer mit Nextcloud-Anbindung + +Diese Anleitung beschreibt die Installation des Euro-Office DocumentServers (Docker-Variante) +auf einem Debian-13-Server und die Anbindung an eine bestehende Nextcloud-Instanz über Apache2 +als Reverse-Proxy. + +Projekt: https://github.com/Euro-Office/DocumentServer +Dokumentation: https://euro-office.github.io/documentation/ + +--- + +## 1. Parameter dieser Installation + +Alle im Dokument verwendeten Platzhalter sind hier zentral aufgeführt. Für eine abweichende +Installation genügt es, diese Tabelle anzupassen und die Werte in den Befehlen entsprechend +zu übernehmen. + +| Platzhalter | Beschreibung | Beispielwert (diese Installation) | +|-----------------------------|------------------------------------------------------------|------------------------------------------------------| +| `` | Hostname des Euro-Office DocumentServers | `eo-ds.ndnaht.de` | +| `` | Hostname der bestehenden Nextcloud-Instanz | `cloud.ndnaht.de` | +| `` | Öffentliche IPv4-Adresse des Servers | `135.181.90.132` | +| `` | Öffentliche IPv6-Adresse des Servers | `2a01:4f9:c014:1ba8::1` | +| `` | Arbeitsverzeichnis für Docker-Compose-Stack | `/opt/euro-office` | +| `` | Verzeichnis der Apache-vHost-Konfigurationsdateien | `/usr/local/apache2/conf/vhosts` | +| `` | Lokaler Port, auf den der Container gemappt wird | `8080` | +| `` | Fester Name der Docker-Netzwerk-Bridge | `br-euroffice` | +| `` | Verzeichnis mit TLS-Zertifikat (hier: dehydrated) | `/var/lib/dehydrated/certs/` | +| `` | Docroot-Verzeichnis der Nextcloud-Installation | `/var/www/cloud.ndnaht.de/htdocs` | +| `` | Pfad der Nextcloud-Logdatei | `/var/www/cloud.ndnaht.de/logs/cloud.log` | +| `` | Pfad zum PHP-CLI-Binary | `/usr/local/php/bin/php` | +| `` | Gemeinsames Secret zwischen DocumentServer und Nextcloud | wird generiert (siehe Schritt 3) | + +--- + +## 2. Voraussetzungen + +- **Betriebssystem:** Debian 13 (getestet), amd64 oder arm64 +- **Docker Engine** (Community Edition) inkl. **Compose-Plugin v2**, installiert über das offizielle Docker-Repository: + ``` + deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian trixie stable + ``` +- **Mindestens 4 GB RAM** (8 GB empfohlen für Mehrbenutzerbetrieb), 10 GB freier Plattenplatz +- **Bestehende Nextcloud-Instanz** (getestet mit Nextcloud 34), erreichbar über Apache2, mit SSH-/`occ`-Zugriff +- **DNS-Eintrag** für ``, zeigt auf `` (bzw. ``) +- **Gültiges TLS-Zertifikat** für `` (hier über [dehydrated](https://github.com/dehydrated-io/dehydrated) verwaltet) +- **Apache2** bereits installiert und aktiv, mit folgenden Modulen aktiviert: + `proxy`, `proxy_http`, `proxy_wstunnel`, `ssl`, `headers`, `rewrite` +- **Firewall-Hinweis:** Falls der Server eine eigene iptables/nftables-Firewall betreibt + (Eigenbau-Skript, CSF, ufw o. ä.), die *nicht* automatisch mit Docker zusammenarbeitet, + siehe Abschnitt 8 ("Firewall-Anpassung für Docker-Bridge-Netzwerke"). Ohne diesen Schritt + ist der Container u. U. nicht einmal vom Host selbst erreichbar. + +--- + +## 3. Arbeitsverzeichnis und Secrets anlegen + +```bash +sudo mkdir -p +cd +``` + +JWT-Secret generieren und in `.env` ablegen (wird von Docker Compose automatisch eingelesen): + +```bash +echo "EO_JWT_SECRET=$(openssl rand -hex 32)" | sudo tee .env +sudo chmod 600 .env +``` + +> Diesen Wert später 1:1 in die Nextcloud-Konfiguration übernehmen (Schritt 10). + +--- + +## 4. docker-compose.yaml + +Der Container wird **nicht** direkt öffentlich exponiert, sondern nur auf `127.0.0.1` +gebunden — Apache übernimmt als Reverse-Proxy die öffentliche Erreichbarkeit inkl. TLS. +Das Docker-Netzwerk bekommt einen **festen Bridge-Namen**, damit Firewall-Regeln, die +auf den Interface-Namen verweisen, auch nach einem Neuaufbau des Netzwerks gültig bleiben. + +```yaml +services: + euro-office: + image: ghcr.io/euro-office/documentserver:latest + container_name: euro-office-ds + restart: unless-stopped + environment: + - JWT_ENABLED=true + - JWT_SECRET=${EO_JWT_SECRET} + # EXAMPLE_ENABLED bewusst NICHT gesetzt (=false) -> kein offenes Beispiel-App in Produktion + ports: + - "127.0.0.1::80" + volumes: + - eo_data:/var/lib/euro-office/documentserver + - eo_logs:/var/log/euro-office/documentserver + - eo_config:/etc/euro-office/documentserver + networks: + - euroffice + healthcheck: + test: ["CMD-SHELL", "curl -sf http://localhost/healthcheck || exit 1"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 60s + +volumes: + eo_data: + eo_logs: + eo_config: + +networks: + euroffice: + driver: bridge + driver_opts: + com.docker.network.bridge.name: +``` + +Datei anlegen: + +```bash +cd +sudo tee docker-compose.yaml > /dev/null << 'EOF' +# --- Inhalt wie oben, Platzhalter durch tatsächliche Werte ersetzt --- +EOF +``` + +Konfiguration prüfen, bevor gestartet wird (zeigt das aufgelöste `JWT_SECRET`): + +```bash +sudo docker compose config +``` + +--- + +## 5. Container starten + +```bash +cd +sudo docker compose up -d +sudo docker compose ps +``` + +Erwartet: Status `Up ... (healthy)` nach ca. 60–90 Sekunden (erste Fontgenerierung dauert etwas). + +Lokalen Health-Check testen: + +```bash +curl -i --max-time 5 http://127.0.0.1:/healthcheck +``` + +Erwartet: `HTTP/1.1 200 OK` + +> **Falls dieser Test hängt/fehlschlägt (Timeout oder Connection Reset), obwohl +> `docker compose ps` bereits `healthy` zeigt:** Das deutet auf ein Firewall-Problem hin — +> siehe Abschnitt 8. + +--- + +## 6. Apache-Module prüfen/aktivieren + +```bash +sudo a2enmod proxy proxy_http proxy_wstunnel ssl headers rewrite +sudo systemctl restart apache2 +sudo systemctl status apache2 --no-pager +``` + +--- + +## 7. Apache-vHost anlegen + +Datei `/.conf`: + +```apache +:80 []:80> + ServerName + RewriteEngine on + RewriteCond %{HTTPS} !=on + RewriteRule (.*) https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L] + CustomLog /var/log/apache2/-access.log combined + ErrorLog /var/log/apache2/-error.log + + +:443 []:443> + ServerName + + SSLEngine on + SSLCertificateFile /fullchain.pem + SSLCertificateKeyFile /privkey.pem + + ProxyPreserveHost On + ProxyRequests Off + + # WebSocket-Verbindungen (Co-Editing) an proxy_wstunnel weiterreichen + RewriteEngine On + RewriteCond %{HTTP:Upgrade} =websocket [NC] + RewriteRule ^/(.*) ws://127.0.0.1:/$1 [P,L] + RewriteCond %{HTTP:Upgrade} !=websocket [NC] + RewriteRule ^/(.*) http://127.0.0.1:/$1 [P,L] + + # Wichtig: DocumentServer muss wissen, dass er hinter TLS läuft, + # sonst generiert er interne Editor-/Cache-URLs mit http:// statt https:// + # ("Mixed Content", Editor kann Dokumente nicht laden/herunterladen). + RequestHeader set X-Forwarded-Proto "https" + RequestHeader set X-Forwarded-Host "" + RequestHeader set X-Forwarded-Port "443" + + ProxyPass / http://127.0.0.1:/ retry=0 + ProxyPassReverse / http://127.0.0.1:/ + + # Große Uploads (Dokumente) erlauben + LimitRequestBody 0 + + CustomLog /var/log/apache2/-access.log combined + ErrorLog /var/log/apache2/-error.log + +``` + +Aktivieren und testen: + +```bash +sudo apachectl configtest +sudo systemctl reload apache2 +``` + +Erreichbarkeit von außen prüfen: + +```bash +curl -i https:///healthcheck +``` + +Erwartet: `HTTP/1.1 200 OK` + +--- + +## 8. Firewall-Anpassung für Docker-Bridge-Netzwerke + +**Nur relevant, wenn eine eigene Firewall-Lösung eingesetzt wird, die den Datenverkehr über +Docker-Bridge-Interfaces nicht automatisch zulässt** (z. B. eigene iptables/nftables-Skripte +mit einer restriktiven `OUTPUT`/`FORWARD`-Policy). + +### Symptom + +- `docker compose ps` zeigt den Container als `healthy` (interner Check funktioniert) +- Ein `curl` **von außerhalb des Containers** auf `127.0.0.1:` oder die + Container-IP hängt oder liefert `Connection reset by peer` / `Operation timed out` +- `ping` auf die Container-IP funktioniert (ICMP ist oft separat erlaubt), TCP jedoch nicht + +### Ursache + +Docker bindet Container-Ports über einen `docker-proxy`-Prozess bzw. NAT-Regeln, die +Datenverkehr über das jeweilige Docker-Bridge-Interface (`br-...`) leiten. Eine restriktive +Firewall mit interface-spezifischen `ACCEPT`-Regeln (z. B. nur für `eth0`) und einer +abschließenden `DROP`-Regel lässt diesen Bridge-Verkehr nicht automatisch durch. + +### Lösung + +1. Docker-Netzwerk mit **festem Bridge-Namen** betreiben (siehe Abschnitt 4, + `com.docker.network.bridge.name: `) — verhindert, dass sich der + Interface-Name bei jedem Neuaufbau des Netzwerks ändert. +2. In der Firewall-Konfiguration eine **explizite Ausnahme für dieses eine Interface** + eintragen (kein Wildcard wie `br+`, das würde auch fremde/zukünftige Bridges ungefiltert + durchlassen). Je nach Firewall-Lösung z. B.: + - Eigenbau-Skript mit `unprotected_ifs`-Variable: `unprotected_ifs=""` + - iptables direkt: gezielte `ACCEPT`-Regeln für `-i ` / `-o ` + in den Chains `INPUT`, `OUTPUT` und `FORWARD` +3. Firewall neu laden und Docker-Daemon **danach** neu starten, damit Docker seine eigenen + iptables-Chains (`DOCKER`, `DOCKER-FORWARD`, `DOCKER-USER`, …) neu aufbaut: + ```bash + sudo systemctl restart + sudo systemctl restart docker + cd && sudo docker compose up -d + ``` +4. **Startreihenfolge dauerhaft absichern**, damit das Problem nach einem Server-Reboot nicht + erneut auftritt — Docker soll immer *nach* der Firewall starten: + ```bash + sudo mkdir -p /etc/systemd/system/docker.service.d/ + sudo tee /etc/systemd/system/docker.service.d/after-firewall.conf > /dev/null << 'EOF' + [Unit] + After=.service + Wants=.service + EOF + sudo systemctl daemon-reload + ``` + +### Restrisiko + +Ein manueller Neustart der Firewall **während Docker bereits läuft** stört bestehende, +schon aktive Container-Verbindungen in der Regel nicht. Wird jedoch **danach** ein neues +Docker-Netzwerk angelegt (z. B. `docker compose down && up`, oder ein neuer Compose-Stack), +kann die Regel-Einfügung fehlschlagen (`Failed to Setup IP tables: ... No chain/target/match +by that name`), weil Dockers eigene Chains beim Firewall-Neuaufbau entfernt wurden. In diesem +Fall genügt es, den Docker-Daemon einmal neu zu starten und den Stack erneut zu starten: + +```bash +sudo systemctl restart docker +cd && sudo docker compose up -d +``` + +### Verifikation nach einem vollständigen Server-Reboot + +```bash +sudo reboot +# nach dem Reboot, neu verbinden: +systemctl status --no-pager +systemctl status docker.service --no-pager +sudo docker compose -f /docker-compose.yaml ps +curl -i --max-time 5 https:///healthcheck +ip a | grep -A2 +sudo docker network ls +``` + +--- + +## 9. Nextcloud-Connector-App installieren + +Die Euro-Office-Integration ist im offiziellen Nextcloud App Store gelistet. + +**Über die Weboberfläche:** + +1. Als Administrator einloggen +2. Profilbild (oben rechts) → **Apps** +3. Kategorie **„Büro & Text"** auswählen +4. **„Euro-Office"** suchen → **„Download and enable"** + +**Alternativ über die Konsole (`occ`):** + +```bash +cd +sudo -u www-data occ app:install eurooffice +``` + +`app:install` lädt die App aus dem Nextcloud App Store herunter **und** aktiviert sie in +einem Schritt. Falls die App bereits heruntergeladen, aber nur deaktiviert ist, genügt +stattdessen: + +```bash +sudo -u www-data occ app:enable eurooffice +``` + +**Kontrolle per SSH:** + +```bash +sudo -u www-data /occ app:list | grep -i eurooffice +``` + +Erwartet: Eintrag unter *Enabled*, z. B. `eurooffice: 11.0.1` + +--- + +## 10. Nextcloud konfigurieren (DocumentServer-URL + JWT-Secret) + +Zuerst prüfen, welche Config-Keys die App verwendet: + +```bash +cd +sudo -u www-data occ config:list eurooffice +``` + +DocumentServer-URL setzen: + +```bash +sudo -u www-data occ config:app:set eurooffice DocumentServerUrl --value="https:///" +``` + +JWT-Secret setzen (identischer Wert wie in `/.env`, Variable `EO_JWT_SECRET`): + +```bash +sudo grep EO_JWT_SECRET /.env +sudo -u www-data occ config:app:set eurooffice jwt_secret --value="" +``` + +Kontrolle: + +```bash +sudo -u www-data occ config:list eurooffice +``` + +--- + +## 11. Funktionstest + +1. In Nextcloud ein Office-Dokument (`.docx`, `.xlsx`, `.pptx`) öffnen +2. Der Euro-Office-Editor sollte sich im Browser öffnen und das Dokument anzeigen + +**Falls die Meldung „Herunterladen ist fehlgeschlagen" erscheint:** + +Browser-Entwicklerkonsole (F12) prüfen. Eine Meldung wie +*„Laden von gemischten aktiven Inhalten … wurde blockiert"* (Mixed Content) weist darauf hin, +dass der DocumentServer interne URLs mit `http://` statt `https://` erzeugt. Prüfen, ob die +`X-Forwarded-*`-Header im vHost (Abschnitt 7) korrekt gesetzt sind, danach: + +```bash +sudo apachectl configtest +sudo systemctl reload apache2 +``` + +Browser-Hard-Reload (`Strg+Shift+R`) und Dokument erneut öffnen. + +--- + +## 12. Wartung / Hinweise + +- **Volumes:** `eo_data`, `eo_logs`, `eo_config` (benannte Docker-Volumes) enthalten + Konfiguration, Cache und Logs. Für Backups: `docker run --rm -v :/data -v $(pwd):/backup alpine tar czf /backup/.tar.gz -C /data .` +- **Updates:** `docker compose pull && docker compose up -d` +- **Logs im Container:** + ```bash + sudo docker compose exec euro-office find /var/log/euro-office -iname "*.log" + ``` +- **EXAMPLE_ENABLED:** bewusst nicht gesetzt/aktiviert — die Beispiel-App hat keine + Zugriffskontrolle und darf nicht öffentlich erreichbar sein.