Remote WordPress sicher hosten: Zero-Exposure via Tailscale & Traefik Reverse Proxy

Wer Webdienste wie WordPress an entfernten Standorten (z. B. auf einem Home-Server, Proxmox VE oder DietPi beim Kollegen) betreiben möchte, steht meist vor den üblichen Hürden: IPv6-only/DS-Lite-Anschlüsse, dynamische IP-Adressen und das Sicherheitsrisiko offener Router-Ports.

In diesem Setup lösen wir das Problem elegant: Ein öffentlich erreichbarer vServer (1blu) terminiert SSL über Traefik und leitet Anfragen über ein verschlüsseltes Tailscale-Mesh-Netzwerk direkt an die WordPress-Instanz am Remote-Standort weiter.

Vorteile:

  • Keine offenen Ports oder Portweiterleitungen am Remote-Standort nötig.
  • Automatisches Let’s Encrypt SSL-Management auf dem VPS.
  • Vollständig verschlüsselter Transportweg via WireGuard.

1. Die Architektur

Plaintext

[ Browser / Internet ]
         │ (HTTPS / 443)
         ▼
[ 1blu vServer ] 
  ├── Traefik v3 (SSL-Terminierung & Dynamic Routing)
  └── Tailscale Container (network_mode: host)
         │ 
         ▼ (Tailscale Mesh / WireGuard UDP via DERP/P2P)
[ Remote Proxmox / DietPi ]
  ├── Tailscale Client (100.104.85.99)
  └── Webserver / WordPress (Port 80)

2. Server-Setup (1blu VPS)

Schritt 2.1: IP-Forwarding aktivieren

Damit Docker-Container Pakete in das Tailscale-Netzwerk weiterleiten dürfen:

Bash

echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conf

Schritt 2.2: Tailscale Client als Host-Service im Docker

Der entscheidende Punkt: Tailscale muss mit network_mode: host laufen, damit das tailscale0-Interface direkt auf dem Host-Kernel existiert.

Datei: ~/tailscale/compose.yml

YAML

services:
  tailscale:
    image: tailscale/tailscale:latest
    container_name: tailscale
    hostname: v31480-relay
    network_mode: host
    environment:
      - TS_STATE_DIR=/var/lib/tailscale
      - TS_USERSPACE=false
      - TS_ACCEPT_DNS=true
      - TS_EXTRA_ARGS=--reset
    volumes:
      - ./tailscale-state:/var/lib/tailscale
      - /dev/net/tun:/dev/net/tun
      - /var/run/tailscale:/var/run/tailscale
    cap_add:
      - NET_ADMIN
      - NET_RAW
      - SYS_MODULE
    restart: unless-stopped

Starten und Verifizieren:

Bash

cd ~/tailscale
docker compose up -d
docker exec tailscale tailscale ping 100.104.85.99

Schritt 2.3: Traefik Docker-Compose erweitern

Traefik muss ein Verzeichnis für dynamische Datei-Konfigurationen (dynamic/) überwachen, um externe IP-Backends ansteuern zu können.

Auszug aus ~/traefik/docker-compose.yml:

YAML

services:
  traefik:
    image: traefik:v3.0
    container_name: traefik
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./acme.json:/letsencrypt/acme.json
      - ./dynamic:/dynamic:ro # Dynamische Konfigurationsdateien
    command:
      - --api.dashboard=true
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --providers.file.directory=/dynamic # File-Provider aktivieren
      - --providers.file.watch=true
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --entrypoints.web.http.redirections.entrypoint.to=websecure
      - --entrypoints.web.http.redirections.entrypoint.scheme=https
      - --certificatesresolvers.letsencrypt.acme.tlschallenge=true
      - --certificatesresolvers.letsencrypt.acme.email=webmaster@traunrocks.de
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
    networks:
      - traefik-net

networks:
  traefik-net:
    driver: bridge

Schritt 2.4: Traefik Dynamic Route für WordPress anlegen

Verzeichnis anlegen und Konfigurationsdatei mit vi erstellen:

Bash

mkdir -p ~/traefik/dynamic
vi ~/traefik/dynamic/wordpress.yml

Inhalt von ~/traefik/dynamic/wordpress.yml:

YAML

http:
  routers:
    wordpress:
      rule: "Host(`wp.minirocks.dynipv6.de`)"
      entryPoints:
        - websecure
      service: wordpress-service
      tls:
        certResolver: letsencrypt

  services:
    wordpress-service:
      loadBalancer:
        servers:
          - url: "http://100.104.85.99:80" # Tailscale-IP der DietPi-Instanz
        passHostHeader: true

Danach Traefik neu starten:

Bash

cd ~/traefik
docker compose up -d

3. Remote-Setup: Was auf der WordPress-Instanz noch fehlt

Da Traefik die SSL-Verschlüsselung übernimmt (SSL-Offloading) und die Anfrage intern per HTTP über Tailscale weiterleitet, weiß WordPress standardmäßig nicht, dass der Besucher HTTPS verwendet. Ohne Anpassung führt dies zu Redirect-Loops (ERR_TOO_MANY_REDIRECTS) oder Mixed-Content-Fehlern.

Der Reverse-Proxy Snippet-Block für wp-config.php

Öffne auf dem Remote-Server (DietPi) die Konfigurationsdatei:

Bash

vi /var/www/wordpress/wp-config.php

Füge diesen Block ganz oben, direkt nach dem öffnenden <?php, ein:

PHP

/**
 * Reverse Proxy & SSL Offloading Konfiguration für Traefik über Tailscale
 */
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
    $_SERVER['HTTPS'] = 'on';
}

// Host-Header explizit weiterreichen (verhindert interne IP-Verlinkungen)
if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
    $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST'];
}

4. Wichtigste Troubleshooting-Takeaways

  • Host vs. Bridge bei Tailscale-Containern: Läuft Tailscale in einem Standard-Docker-Bridge-Netzwerk, sieht der Host das tailscale0-Interface nicht. Pings und Routen vom Host auf 100.x.y.z scheitern dann. Lösung: Immer network_mode: host für den Tailscale-Container nutzen.
  • Crash-Loop changing settings requires mentioning all non-default flags: Tritt auf, wenn im State-Ordner alte Flags gespeichert sind. Abhilfe schafft die Umgebungsvariable TS_EXTRA_ARGS=--reset in der compose.yml.
  • URLs in WordPress Allgemein: Beide Einträge (WordPress-Adresse und Website-Adresse) müssen zwingend auf die öffentliche Domain mit https:// zeigen (z. B. [https://wp.minirocks.dynipv6.de](https://wp.minirocks.dynipv6.de)).

Comments

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert