[Tutorial] Nextcloud auf ZimaOS ohne Portfreigabe veröffentlichen

Nginx auf einem VPS + FRP über Tailscale

Mit dieser Anleitung wird eine auf ZimaOS laufende Nextcloud über eine normale öffentliche HTTPS-Domain erreichbar gemacht, ohne am privaten Internetanschluss eingehende Ports freizugeben.

Öffentliche Benutzer verbinden sich ausschließlich mit dem VPS. Dort nimmt Nginx die HTTPS-Verbindung entgegen. FRP transportiert die Anfrage zum Heimnetz, wobei die Verbindung zwischen frpc und frps durch das private Tailscale-Netz geführt wird. Vom FRP-Client auf ZimaOS geht die Anfrage anschließend an die lokale Nextcloud-Instanz.

Vorwort

Ich nutze die Kernarchitektur hinter dieser Anleitung bereits seit einiger Zeit in meinem eigenen Homelab: Nginx und FRP laufen auf einem öffentlichen VPS, während die FRP-Verbindung über Tailscale in meine private Proxmox-Umgebung geführt wird.

Für dieses Tutorial habe ich dasselbe Konzept auf ZimaOS übertragen. Da sich bei neueren Versionen von Tailscale, FRP und den verwendeten Container-Images einige Konfigurationsdetails geändert haben, habe ich den Aufbau aktualisiert und den vollständigen Verbindungsweg vor dem Schreiben der Anleitung erneut simuliert.

Dieses Tutorial verbindet damit eine bewährte Architektur mit einer aktualisierten Umsetzung für ZimaOS. IP-Adressen, Ports, Containernamen und die Proxy-Einstellungen von Nextcloud müssen an die jeweilige Umgebung angepasst werden.

Hinweis

Die Konfigurationsdateien wurden auf gültige YAML-, TOML- und Nginx-Syntax geprüft. Zusätzlich wurde der vollständige HTTP-Verbindungsweg über Nginx, einen lokalen FRP-Port, einen SOCKS5-Transport und einen internen Webdienst simuliert.

Einige Werte hängen jedoch zwangsläufig von der konkreten ZimaOS- und Nextcloud-Installation ab. Das betrifft insbesondere den veröffentlichten Nextcloud-Port, den Containernamen und die Adresse, die Nextcloud später als direkten Proxy sieht.


Was dieser Aufbau löst

Eine lokal betriebene Nextcloud benötigt für den öffentlichen Zugriff üblicherweise mindestens eine der folgenden Voraussetzungen:

  • eine eingehende Portfreigabe am Router,

  • eine öffentlich erreichbare IPv4-Adresse,

  • eine DynDNS-Lösung,

  • oder einen externen Relay-Dienst.

Bei diesem Aufbau wird am privaten Anschluss nichts eingehend veröffentlicht. Die gesamte Verbindung aus dem Heimnetz wird ausgehend aufgebaut.

Auch die Benutzer der Nextcloud benötigen kein Tailscale. Sie öffnen die Instanz ganz normal über ihre öffentliche Domain, beispielsweise:

cloud.example.com

Der VPS ist der einzige öffentlich erreichbare Einstiegspunkt.


Architektur

Internet
   │
   ▼
Öffentliche Domain / DNS
cloud.example.com
   │
   ▼
VPS
   ├─ Nginx
   │    └─ HTTPS, Zertifikat und Reverse Proxy
   │
   ├─ FRP-Server (frps)
   │    └─ lokaler Proxy-Port 127.0.0.1:18080
   │
   └─ Tailscale
          │
          │ private Tailscale-Verbindung
          ▼
ZimaOS
   ├─ Tailscale-Container
   ├─ FRP-Client (frpc)
   │
   ▼
Lokale Nextcloud-App

Der tatsächliche Datenweg lautet:

Internet
→ öffentliche Domain
→ Nginx auf dem VPS
→ lokaler FRP-Port auf dem VPS
→ frps
→ Tailscale
→ frpc auf ZimaOS
→ lokale Nextcloud

Am privaten Router existiert dabei keine eingehende NAT- oder Firewall-Regel für Nextcloud.


Aufgaben der einzelnen Komponenten

Nginx auf dem VPS

Nginx bildet den öffentlichen Einstiegspunkt und übernimmt:

  • die öffentliche Domain,

  • das TLS-Zertifikat,

  • die Weiterleitung von HTTP auf HTTPS,

  • die erforderlichen Proxy-Header,

  • Upload-Limits und Timeouts,

  • sowie bei Bedarf zusätzliche Schutzmechanismen oder Rate-Limits.

FRP

FRP übernimmt die eigentliche Veröffentlichung des internen Dienstes:

  • frpc baut die Verbindung aus dem Heimnetz aktiv auf,

  • frps stellt auf dem VPS einen lokalen Port bereit,

  • Nginx leitet öffentliche Anfragen an diesen lokalen Port,

  • FRP transportiert die Verbindung zurück zur internen Nextcloud.

Tailscale

Tailscale stellt die private Netzwerkverbindung zwischen dem VPS und dem FRP-Client auf ZimaOS bereit.

Tailscale ersetzt FRP in diesem Aufbau nicht. Beide Komponenten erfüllen unterschiedliche Aufgaben:

  • Tailscale: private Erreichbarkeit zwischen VPS und ZimaOS

  • FRP: Veröffentlichung und Zuordnung des internen Dienstes

Tailscale im Userspace-Modus

Die Hauptvariante dieser Anleitung verwendet Tailscale innerhalb eines Containers im Userspace-Modus mit einem SOCKS5-Proxy.

Dadurch werden auf ZimaOS keine folgenden Berechtigungen benötigt:

  • /dev/net/tun

  • NET_ADMIN

  • SYS_MODULE

  • Änderungen am Netzwerk des ZimaOS-Hosts

Der Nachteil kann ein geringerer Datendurchsatz gegenüber dem Kernel-Modus sein. Eine alternative Kernel-Konfiguration ist am Ende der Anleitung beschrieben.


Beispielwerte

Alle folgenden Werte sind Beispiele und müssen ersetzt werden.

Einstellung Beispiel
Öffentliche Nextcloud-Domain cloud.example.com
Öffentliche IPv4-Adresse des VPS 203.0.113.10
Tailscale-IP des VPS 100.64.0.10
Lokale IP-Adresse von ZimaOS 192.168.1.50
Veröffentlichter Nextcloud-Port 8080
FRP-Steuerport 7000
Lokaler FRP-Upstream-Port auf dem VPS 18080
Tailscale-Gerätename zimaos-frpc

Für ZimaOS sollte im Router eine DHCP-Reservierung oder eine feste IP-Adresse eingerichtet werden.

Für die folgenden Befehle wird die Beispieldomain als Variable verwendet:

NEXTCLOUD_DOMAIN="cloud.example.com"

Teil 1: Nextcloud auf ZimaOS vorbereiten

1. Nextcloud installieren

In der ZimaOS-Weboberfläche den App Center öffnen und Nextcloud installieren.

Nach der Installation muss Nextcloud zunächst im lokalen Netzwerk funktionieren, beispielsweise unter:

http://192.168.1.50:8080

Der tatsächliche Port hängt von der verwendeten App-Center-Vorlage und den gewählten Einstellungen ab.

Erst fortfahren, wenn Nextcloud von einem anderen Gerät im lokalen Netzwerk erreichbar ist.

2. Lokalen Endpunkt notieren

Für die spätere FRP-Konfiguration werden folgende Werte benötigt:

ZIMAOS_LAN_IP=192.168.1.50
NEXTCLOUD_PORT=8080

Zusätzlich sollte in den Einstellungen der Nextcloud-App geprüft werden, welcher Host-Pfad für die Konfiguration und die persistenten Daten verwendet wird.

ZimaOS speichert persistente App-Daten üblicherweise unter:

/DATA/AppData/...

Der genaue Unterordner hängt von der installierten Nextcloud-Vorlage ab.

3. Terminalzugriff aktivieren

In ZimaOS den Terminal- oder SSH-Zugriff aktivieren:

Einstellungen
→ Entwicklermodus
→ SSH-Zugriff

Die Shell-Befehle in dieser Anleitung werden über SSH oder das Webterminal auf ZimaOS ausgeführt.


Teil 2: VPS vorbereiten

Die folgenden Befehle gehen von einem aktuellen Debian- oder Ubuntu-System auf dem VPS aus.

1. Nginx, Docker, Compose und Certbot installieren

sudo apt update
sudo apt install -y nginx docker.io docker-compose-plugin certbot python3-certbot-nginx
sudo systemctl enable --now nginx docker

Prüfen, ob Docker Compose verfügbar ist:

docker compose version

2. Tailscale auf dem VPS installieren

Tailscale entsprechend der oben verlinkten offiziellen Dokumentation auf dem VPS installieren und anschließend ausführen:

sudo tailscale up

Den im Terminal angezeigten Authentifizierungslink öffnen und den VPS mit dem eigenen Tailnet verbinden.

Anschließend die Tailscale-IP des VPS ausgeben:

tailscale ip -4

Beispiel:

VPS_TAILSCALE_IP=100.64.0.10

Diese Adresse wird später in der frpc.toml auf ZimaOS eingetragen.

3. FRP-Token erzeugen

sudo mkdir -p /opt/frps
openssl rand -hex 32 | sudo tee /opt/frps/token >/dev/null
sudo chmod 600 /opt/frps/token
sudo cat /opt/frps/token

Das Token sicher kopieren. Auf ZimaOS muss später exakt dasselbe Token gespeichert werden.

Das echte Token darf niemals in einem öffentlichen Beitrag, Screenshot oder Anhang erscheinen.

4. frps.toml erstellen

Datei anlegen:

/opt/frps/frps.toml

Inhalt:

bindAddr = "100.64.0.10"
bindPort = 7000

# Die durch FRP erzeugten Proxy-Ports sind nur lokal auf dem VPS erreichbar.
proxyBindAddr = "127.0.0.1"

auth.method = "token"
auth.tokenSource.type = "file"
auth.tokenSource.file.path = "/etc/frp/token"

# Nur FRP-Clients mit aktiviertem TLS akzeptieren.
transport.tls.force = true

# frpc darf ausschließlich den für Nextcloud vorgesehenen Port anlegen.
allowPorts = [
  { single = 18080 }
]

log.to = "console"
log.level = "info"

Die Adresse 100.64.0.10 durch die tatsächliche Tailscale-IP des VPS ersetzen.

Diese Konfiguration stellt sicher:

  • Port 7000 ist nur über die Tailscale-IP des VPS erreichbar.

  • Der durch FRP erzeugte Port 18080 bindet ausschließlich an 127.0.0.1.

  • Der FRP-Client muss sich mit einem Token authentifizieren.

  • Der FRP-Client darf nur den freigegebenen Port 18080 verwenden.

  • Die FRP-Verbindung muss TLS verwenden.

5. frps als Container starten

Datei anlegen:

/opt/frps/docker-compose.yml

Inhalt:

services:
  frps:
    image: fatedier/frps:v0.70.1
    container_name: frps
    network_mode: host
    restart: unless-stopped
    command:
      - -c
      - /etc/frp/frps.toml
    volumes:
      - /opt/frps/frps.toml:/etc/frp/frps.toml:ro
      - /opt/frps/token:/etc/frp/token:ro

Container starten:

cd /opt/frps
sudo docker compose up -d
sudo docker logs frps

Prüfen, ob der FRP-Steuerport korrekt gebunden wurde:

sudo ss -ltnp | grep ':7000'

Der Port muss auf der Tailscale-IP lauschen.

Er darf nicht an folgenden Adressen gebunden sein:

0.0.0.0:7000
öffentliche-vps-ip:7000

Teil 3: Tailscale- und FRP-App auf ZimaOS erstellen

1. Persistente Verzeichnisse anlegen

Auf ZimaOS:

sudo mkdir -p /DATA/AppData/zima-frp-tunnel/tailscale
sudo mkdir -p /DATA/AppData/zima-frp-tunnel/frpc
sudo chmod 700 /DATA/AppData/zima-frp-tunnel/frpc

2. FRP-Token speichern

Folgende Datei anlegen:

/DATA/AppData/zima-frp-tunnel/frpc/token

Das zuvor auf dem VPS erzeugte Token einfügen und anschließend die Berechtigungen setzen:

sudo chmod 600 /DATA/AppData/zima-frp-tunnel/frpc/token

3. frpc.toml erstellen

Datei anlegen:

/DATA/AppData/zima-frp-tunnel/frpc/frpc.toml

Inhalt:

serverAddr = "100.64.0.10"
serverPort = 7000

# Bei einem vorübergehenden Ausfall von VPS oder Tailscale weiter versuchen.
loginFailExit = false

auth.method = "token"
auth.tokenSource.type = "file"
auth.tokenSource.file.path = "/etc/frp/token"

transport.protocol = "tcp"
transport.tls.enable = true

# FRP-Verbindung durch den Tailscale-Userspace-SOCKS5-Proxy führen.
transport.proxyURL = "socks5://127.0.0.1:1055"

log.to = "console"
log.level = "info"

[[proxies]]
name = "zimaos-nextcloud"
type = "tcp"

# Lokaler Nextcloud-Endpunkt auf ZimaOS.
localIP = "192.168.1.50"
localPort = 8080

# frps legt diesen Port auf 127.0.0.1 des VPS an.
remotePort = 18080

healthCheck.type = "tcp"
healthCheck.timeoutSeconds = 3
healthCheck.maxFailed = 3
healthCheck.intervalSeconds = 10

Folgende Werte ersetzen:

  • serverAddr: Tailscale-IP des VPS

  • localIP: lokale IP-Adresse von ZimaOS

  • localPort: veröffentlichter Port der Nextcloud-App

4. Tailscale-Authentifizierungsschlüssel erstellen

In der Tailscale-Administrationsoberfläche einen Auth-Key für den ZimaOS-Tunnel-Container erstellen.

Empfohlene Eigenschaften:

  • vorab autorisiert,

  • nicht flüchtig,

  • nur dann wiederverwendbar, wenn dies für die eigene Wiederherstellungsstrategie erforderlich ist,

  • optional über ein Tag und die Tailscale-Zugriffsregeln eingeschränkt.

Der Auth-Key ist ein Geheimnis und darf nicht veröffentlicht werden.

5. Benutzerdefinierte App in ZimaOS importieren

In ZimaOS öffnen:

App Center
→ Install a Customized App
→ Import

Folgende Compose-Konfiguration einfügen:

services:
  tailscale:
    image: tailscale/tailscale:stable
    container_name: zima-tunnel-tailscale
    hostname: zimaos-frpc
    restart: unless-stopped
    environment:
      TS_AUTHKEY: "HIER_DEN_TAILSCALE_AUTH_KEY_EINTRAGEN"
      TS_HOSTNAME: "zimaos-frpc"
      TS_STATE_DIR: "/var/lib/tailscale"
      TS_AUTH_ONCE: "true"
      TS_USERSPACE: "true"
      TS_SOCKS5_SERVER: ":1055"
      TS_ACCEPT_DNS: "false"
    volumes:
      - /DATA/AppData/zima-frp-tunnel/tailscale:/var/lib/tailscale

  frpc:
    image: fatedier/frpc:v0.70.1
    container_name: zima-tunnel-frpc
    network_mode: "service:tailscale"
    depends_on:
      - tailscale
    restart: unless-stopped
    command:
      - -c
      - /etc/frp/frpc.toml
    volumes:
      - /DATA/AppData/zima-frp-tunnel/frpc:/etc/frp:ro

Die App anschließend installieren.

Dieser Stack veröffentlicht auf ZimaOS bewusst keinen eingehenden Port. Beide Container bauen ausschließlich ausgehende Verbindungen auf.

6. Tailscale-Registrierung prüfen

In der Tailscale-Administrationsoberfläche sollte ein neues Gerät erscheinen:

zimaos-frpc

Auf ZimaOS die Logs des Tailscale-Containers prüfen:

docker logs zima-tunnel-tailscale

Danach die Logs des FRP-Clients prüfen:

docker logs zima-tunnel-frpc

Bei erfolgreicher Verbindung sollte der Proxy zimaos-nextcloud ohne Authentifizierungs- oder Verbindungsfehler gestartet werden.

7. Tailscale-Auth-Key nach der Registrierung entfernen

Der Zustand und die Geräteidentität von Tailscale werden persistent gespeichert unter:

/DATA/AppData/zima-frp-tunnel/tailscale

Nach der erfolgreichen ersten Registrierung den Eintrag TS_AUTHKEY aus der Compose-Konfiguration entfernen und die App neu starten.

Mit:

TS_AUTH_ONCE=true

und dem persistenten Statusverzeichnis sollte der Container seine Geräteidentität behalten.

Ein wiederverwendbarer Auth-Key darf nicht dauerhaft in der App-Konfiguration, in exportierten Dateien oder auf Screenshots verbleiben.


Teil 4: Privaten Tunnel prüfen

1. FRP-Port auf dem VPS kontrollieren

Nachdem frpc erfolgreich verbunden ist:

sudo ss -ltnp | grep ':18080'

Erwartete Bindung:

127.0.0.1:18080

Der Port darf nicht öffentlich gebunden sein:

0.0.0.0:18080

2. Upstream lokal auf dem VPS testen

curl -I http://127.0.0.1:18080

Eine Antwort mit beispielsweise 200, 301 oder 302 bestätigt, dass der private Verbindungsweg grundsätzlich funktioniert:

VPS
→ frps
→ Tailscale
→ frpc
→ Nextcloud

Auch eine Nextcloud-Meldung zu einer nicht vertrauenswürdigen Domain zeigt bereits, dass der Tunnel funktioniert. Die Domain-Konfiguration wird später korrigiert.


Teil 5: Öffentlichen DNS-Eintrag anlegen

Beim DNS-Anbieter einen A-Record erstellen:

cloud.example.com → 203.0.113.10

Als Ziel wird die öffentliche IPv4-Adresse des VPS verwendet.

Ein AAAA-Record sollte nur angelegt werden, wenn:

  • der VPS korrekt über IPv6 erreichbar ist,

  • Nginx über IPv6 lauscht,

  • und die Firewall entsprechend eingerichtet wurde.

DNS-Auflösung prüfen:

dig +short cloud.example.com

Teil 6: Nginx auf dem VPS konfigurieren

1. Nginx-Site anlegen

Datei erstellen:

/etc/nginx/sites-available/cloud.example.com

Zunächst folgende HTTP-Konfiguration verwenden:

server {
    listen 80;
    listen [::]:80;

    server_name cloud.example.com;

    client_max_body_size 10G;

    location / {
        proxy_pass http://127.0.0.1:18080;
        proxy_http_version 1.1;

        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $remote_addr;
        proxy_set_header X-Forwarded-Host  $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Port  $server_port;

        proxy_request_buffering off;
        proxy_buffering off;

        proxy_connect_timeout 60s;
        proxy_send_timeout 3600s;
        proxy_read_timeout 3600s;
    }
}

cloud.example.com durch die eigene Domain ersetzen.

Site aktivieren:

sudo ln -s /etc/nginx/sites-available/cloud.example.com \
  /etc/nginx/sites-enabled/cloud.example.com

Konfiguration prüfen und Nginx neu laden:

sudo nginx -t
sudo systemctl reload nginx

2. TLS-Zertifikat abrufen

sudo certbot --nginx -d cloud.example.com

Bei der Abfrage die automatische Weiterleitung von HTTP auf HTTPS auswählen.

Certbot ergänzt anschließend:

  • den HTTPS-Listener,

  • die Zertifikatspfade,

  • und die Weiterleitung auf HTTPS.

Automatische Zertifikatserneuerung testen:

sudo certbot renew --dry-run

3. CalDAV- und CardDAV-Weiterleitungen ergänzen

Im HTTPS-server-Block vor location / ergänzen:

location = /.well-known/carddav {
    return 301 https://$host/remote.php/dav/;
}

location = /.well-known/caldav {
    return 301 https://$host/remote.php/dav/;
}

Im HTTPS-Block müssen die externen Werte für Protokoll und Port weitergegeben werden:

proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;

Danach erneut prüfen:

sudo nginx -t
sudo systemctl reload nginx

Teil 7: Nextcloud für die öffentliche Domain konfigurieren

1. Containernamen der Nextcloud ermitteln

Auf ZimaOS:

docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Ports}}' | grep -i nextcloud

Anschließend eine Variable setzen:

NC_CONTAINER="HIER_DEN_NEXTCLOUD_CONTAINERNAMEN_EINTRAGEN"

Je nach ZimaOS-App-Vorlage kann der Container anders heißen.

2. Öffentliche Domain zu trusted_domains hinzufügen

Zunächst vorhandene Einträge anzeigen:

docker exec -u www-data "$NC_CONTAINER" \
  php occ config:system:get trusted_domains

Die öffentliche Domain am nächsten freien numerischen Index ergänzen:

docker exec -u www-data "$NC_CONTAINER" \
  php occ config:system:set trusted_domains 1 \
  --value="cloud.example.com"

Falls Index 1 bereits verwendet wird, den nächsten freien Index wählen.

Bei einigen Nextcloud-Templates muss der occ-Befehl mit einem anderen Benutzer oder aus einem anderen Pfad ausgeführt werden. In diesem Fall die Angaben der verwendeten App-Vorlage prüfen.

3. Externe HTTPS-Adresse konfigurieren

Externes Protokoll setzen:

docker exec -u www-data "$NC_CONTAINER" \
  php occ config:system:set overwriteprotocol \
  --value="https"

Öffentliche Basisadresse für Kommandozeilen- und Hintergrundprozesse setzen:

docker exec -u www-data "$NC_CONTAINER" \
  php occ config:system:set overwrite.cli.url \
  --value="https://${NEXTCLOUD_DOMAIN}"

Da Nginx den ursprünglichen Host-Header weitergibt, ist overwritehost normalerweise nicht notwendig.

Nur wenn Nextcloud weiterhin einen falschen Hostnamen erzeugt:

docker exec -u www-data "$NC_CONTAINER" \
  php occ config:system:set overwritehost \
  --value="cloud.example.com"

Soll dieselbe Nextcloud zusätzlich weiterhin lokal über HTTP erreichbar bleiben, sollte statt globaler Überschreibungen gegebenenfalls overwritecondaddr verwendet werden.

4. Vertrauenswürdigen Proxy festlegen

Nginx übergibt die ursprüngliche Client-Adresse über:

X-Forwarded-For

Nextcloud verwendet diesen Header jedoch nur dann, wenn die unmittelbar verbindende Quelle in trusted_proxies eingetragen ist.

Da FRP auf der ZimaOS-Seite eine neue lokale Verbindung erzeugt, sieht Nextcloud normalerweise nicht die Tailscale-IP des VPS als direkten Kommunikationspartner. Häufig erscheint stattdessen eine Docker-Bridge- oder Gateway-Adresse.

Die tatsächlich von Nextcloud gesehene Quelladresse muss nach einem öffentlichen Testaufruf im Access-Log des Nextcloud-Webservers ermittelt werden.

Danach ausschließlich diese Adresse oder das kleinstmögliche erforderliche Subnetz eintragen:

docker exec -u www-data "$NC_CONTAINER" \
  php occ config:system:set trusted_proxies 0 \
  --value="HIER_DIE_TATSAECHLICHE_PROXY_IP_ODER_DAS_CIDR_EINTRAGEN"

Mögliche Beispiele:

172.18.0.1
172.18.0.0/24

Nicht verwenden:

0.0.0.0/0

Die genaue Adresse hängt von der installierten Nextcloud-Vorlage und der Docker-Netzwerkstruktur ab. Sie kann deshalb in einer allgemeinen Anleitung nicht fest vorgegeben werden.

5. Nextcloud-Konfiguration prüfen

docker exec -u www-data "$NC_CONTAINER" \
  php occ config:list system

Die relevanten Werte sollten enthalten:

trusted_domains
trusted_proxies
overwriteprotocol = https
overwritehost = cloud.example.com   # nur falls erforderlich
overwrite.cli.url = https://${NEXTCLOUD_DOMAIN}

Teil 8: Abschließender Funktionstest

Test direkt vom VPS

curl -I http://127.0.0.1:18080

Test aus einem externen Netzwerk

Nicht aus dem eigenen WLAN testen, sondern beispielsweise über Mobilfunk:

curl -I https://${NEXTCLOUD_DOMAIN}

Alternativ im Browser öffnen:

cloud.example.com

Folgende Punkte prüfen:

  • Das TLS-Zertifikat ist gültig.

  • Der Browser bleibt dauerhaft auf HTTPS.

  • Die Anmeldung funktioniert.

  • Dateien lassen sich hochladen.

  • Dateien lassen sich herunterladen.

  • WebDAV funktioniert.

  • Am privaten Router existiert keine Portfreigabe.

  • Nextcloud ist nicht direkt über den VPS-Port erreichbar.

  • Der FRP-Steuerport lauscht nicht auf der öffentlichen VPS-Adresse.

  • Der FRP-Upstream-Port ist ausschließlich an 127.0.0.1 gebunden.


Für diese Anleitung durchgeführte Prüfung

Vor der Veröffentlichung wurden folgende Punkte geprüft:

  • Beide Docker-Compose-Dateien lassen sich als gültiges YAML einlesen.

  • Beide FRP-Konfigurationen lassen sich als gültiges TOML einlesen.

  • Die Nginx-Konfiguration besteht nginx -t.

  • Der vollständige HTTP-Pfad wurde lokal simuliert:

Nginx
→ ausschließlich lokal gebundener FRP-Port
→ ausgehende Reverse-Verbindung über einen SOCKS5-Endpunkt
→ interner HTTP-Dienst

Die Simulation lieferte eine erfolgreiche HTTP-Antwort und bestätigte die Weitergabe folgender Header:

Host
X-Real-IP
X-Forwarded-For
X-Forwarded-Host
X-Forwarded-Proto
X-Forwarded-Port

Umgebungsspezifisch und daher vor Ort zu prüfen sind:

  • das Importverhalten der benutzerdefinierten App in der verwendeten ZimaOS-Version,

  • die Tailscale-Registrierung und die eigenen Zugriffsregeln,

  • der tatsächliche Host-Port der verwendeten Nextcloud-App,

  • der genaue Nextcloud-Containername,

  • die konkrete Docker-Quelladresse für trusted_proxies,

  • sowie der reale Datendurchsatz bei großen und lang laufenden Übertragungen.


Sicherheitscheckliste

  • Am privaten Router existiert keine eingehende Portfreigabe.

  • Der VPS veröffentlicht nur die vorgesehenen Dienste, normalerweise 80/tcp und 443/tcp.

  • frps.bindAddr enthält die Tailscale-IP des VPS.

  • frps.proxyBindAddr ist auf 127.0.0.1 gesetzt.

  • allowPorts erlaubt ausschließlich den benötigten FRP-Port.

  • FRP-Token-Authentifizierung ist aktiviert.

  • FRP-TLS ist erzwungen.

  • Das echte FRP-Token erscheint in keinem öffentlichen Beitrag oder Anhang.

  • Der Tailscale-Auth-Key wurde nach der ersten Registrierung entfernt.

  • Das Tailscale-Statusverzeichnis wird persistent gespeichert.

  • Nextcloud vertraut nur der tatsächlich benötigten Proxy-IP oder dem kleinsten erforderlichen Subnetz.

  • Für Nextcloud-Administratoren ist Zwei-Faktor-Authentifizierung aktiviert.

  • ZimaOS, Nextcloud, Nginx, Tailscale, FRP und das Betriebssystem des VPS werden regelmäßig aktualisiert.

  • Backups sind vorhanden.

  • Die Wiederherstellung wurde dokumentiert oder getestet.


Fehlerbehebung

Nginx meldet 502 Bad Gateway

Prüfen, ob der FRP-Port auf dem VPS vorhanden ist:

sudo ss -ltnp | grep ':18080'

Logs von frps prüfen:

sudo docker logs frps

Logs auf ZimaOS prüfen:

docker logs zima-tunnel-frpc
docker logs zima-tunnel-tailscale

Häufige Ursachen:

  • frpc ist nicht verbunden.

  • Das FRP-Token stimmt nicht überein.

  • Die Tailscale-IP des VPS ist falsch.

  • Eine Tailscale-Zugriffsregel blockiert TCP-Port 7000.

  • localIP oder localPort in der frpc.toml ist falsch.

  • Die Nextcloud-App läuft nicht.

  • Der veröffentlichte Nextcloud-Port wurde geändert.

frpc kann frps nicht erreichen

Folgende Werte prüfen:

transport.protocol = "tcp"
transport.proxyURL = "socks5://127.0.0.1:1055"

Die FRP-Proxy-Einstellung funktioniert in diesem Aufbau nur mit einer TCP-Verbindung zu frps.

Außerdem prüfen, ob Tailscale den SOCKS5-Listener gestartet hat:

docker logs zima-tunnel-tailscale

Nextcloud meldet eine nicht vertrauenswürdige Domain

Die öffentliche Domain zu trusted_domains hinzufügen.

Beispiel:

cloud.example.com

Weiterleitungsschleife oder erzeugte HTTP-Links

Folgende Werte prüfen:

overwriteprotocol = https
overwrite.cli.url = https://${NEXTCLOUD_DOMAIN}
overwritehost = cloud.example.com   # nur falls erforderlich

Nginx muss außerdem folgende Werte weitergeben:

X-Forwarded-Proto: https
X-Forwarded-Port: 443
Host: cloud.example.com

Uploads brechen ab

Nginx-Limits und Timeouts prüfen:

client_max_body_size 10G;
proxy_request_buffering off;
proxy_send_timeout 3600s;
proxy_read_timeout 3600s;

Zusätzlich die Upload-Limits von PHP und Nextcloud innerhalb der installierten App prüfen.

Nextcloud protokolliert nur die Proxy-Adresse

Die direkte Quelladresse ermitteln, die Nextcloud tatsächlich sieht, und ausschließlich diese Adresse oder das kleinste notwendige Subnetz in trusted_proxies eintragen.

Nicht pauschal alle privaten Netze oder alle IP-Adressen freigeben.

Der direkte Kommunikationspartner von Nextcloud befindet sich auf dem FRP- beziehungsweise Docker-Pfad. Es ist nicht automatisch die öffentliche oder die Tailscale-IP des VPS.

Tailscale erscheint nach jedem Neustart als neues Gerät

Prüfen, ob das persistente Verzeichnis korrekt eingebunden ist:

/DATA/AppData/zima-frp-tunnel/tailscale:/var/lib/tailscale

Außerdem müssen folgende Werte gesetzt sein:

TS_STATE_DIR=/var/lib/tailscale
TS_AUTH_ONCE=true

Optional: Tailscale im Kernel-Modus

Der Userspace-Modus ist einfacher zu importieren und benötigt keine erweiterten Netzwerkberechtigungen. Der Kernel-Modus kann jedoch einen höheren Durchsatz liefern.

Für die Umstellung sind folgende Änderungen erforderlich.

1. SOCKS5-Server entfernen

Aus der Compose-Konfiguration entfernen:

TS_SOCKS5_SERVER: ":1055"

2. Userspace-Modus deaktivieren

TS_USERSPACE: "false"

3. TUN-Gerät und Berechtigungen ergänzen

Beim Tailscale-Service ergänzen:

devices:
  - /dev/net/tun:/dev/net/tun

cap_add:
  - NET_ADMIN
  - SYS_MODULE

4. SOCKS5-Proxy aus der FRP-Konfiguration entfernen

Aus frpc.toml löschen:

transport.proxyURL = "socks5://127.0.0.1:1055"

Da frpc weiterhin den Netzwerk-Namespace des Tailscale-Containers verwendet, kann der FRP-Client anschließend direkt zur Tailscale-IP des VPS verbinden.

Der Kernel-Modus sollte nur verwendet werden, wenn:

  • /dev/net/tun auf dem ZimaOS-Host verfügbar ist,

  • die importierte App die erforderlichen Berechtigungen erhält,

  • und die Auswirkungen auf das Host-Netzwerk verstanden werden.


Weitere ZimaOS-Dienste veröffentlichen

Mit derselben Architektur lassen sich weitere interne Webdienste über den VPS veröffentlichen.

Für jeden zusätzlichen Dienst:

  1. einen neuen, nur lokal gebundenen FRP-Port auf dem VPS auswählen,

  2. diesen Port in frps.allowPorts ergänzen,

  3. einen weiteren [[proxies]]-Block in frpc.toml anlegen,

  4. eine zusätzliche Nginx-Site für die neue Domain erstellen,

  5. die Anwendung für die externe Domain und die Proxy-Header konfigurieren.

Beispiel:

[[proxies]]
name = "zimaos-service-two"
type = "tcp"
localIP = "192.168.1.50"
localPort = 3000
remotePort = 18081

Nginx würde die zweite Domain anschließend an folgenden lokalen Upstream weiterleiten:

http://127.0.0.1:18081

Auch dieser Port sollte ausschließlich auf 127.0.0.1 des VPS lauschen.


Unterschied zum normalen ZimaOS-Fernzugriff

ZimaOS bietet bereits eigene Funktionen für Fernzugriff, Verwaltung und privaten Zugriff.

Diese Architektur löst jedoch einen anderen Anwendungsfall:

  • Zugriff über eine normale öffentliche HTTPS-Domain

  • keine zusätzliche Clientsoftware für die Nextcloud-Benutzer

  • zentrale TLS-Terminierung auf einem VPS

  • mehrere öffentliche Dienste über denselben VPS

  • keine eingehende Portfreigabe am privaten Anschluss

  • keine direkte öffentliche Freigabe der privaten ZimaOS-Adresse

Der Aufbau ist deshalb vor allem dann sinnvoll, wenn eine Anwendung wie Nextcloud regulär über eine öffentliche Domain erreichbar sein soll, ohne das Heimnetz direkt für eingehende Verbindungen zu öffnen.


Weiterführende Dokumentation

Für die Umsetzung sind vor allem diese beiden offiziellen Dokumentationen relevant:

  1. ZimaOS – Docker-Anwendungen anpassen:
    https://www.zimaspace.com/docs/zimaos/docker-app-publishing

  2. Nextcloud – Reverse-Proxy-Konfiguration:
    https://docs.nextcloud.com/server/stable/admin_manual/configuration_server/reverse_proxy_configuration.html