Zum Inhalt

Reverse-Proxy-Anforderung

Bifolk muss immer hinter einem Reverse-Proxy (z. B. Traefik, Nginx oder Caddy) betrieben werden. Der direkte Zugang zum Internet ohne Proxy ist keine unterstützte Konfiguration.

Zielgruppe: Systemadministratoren, DevOps-Ingenieure


Warum ein Reverse-Proxy erforderlich ist

Bifolk delegiert mehrere Sicherheitsaufgaben bewusst an die Reverse-Proxy-Schicht, anstatt diese in Django zu implementieren:

  • TLS-Terminierung — HTTPS wird vom Proxy verwaltet, nicht von der Django-Anwendung.
  • HTTP-Sicherheitsheader — Header wie HSTS, CSP und X-Content-Type-Options werden vom Proxy gesetzt und können nicht garantiert werden, wenn die Anwendung direkt aufgerufen wird.
  • Endpunkt-Zugangskontrolle — Interne Endpunkte (z. B. /health/) müssen auf Proxy-Ebene für den öffentlichen Zugang gesperrt werden.

Danger

Der direkte Zugriff auf Bifolk ohne Reverse-Proxy entfernt all diese Schutzmaßnahmen. Die Anwendung selbst setzt keine Sicherheitsheader durch.


Erforderliche Sicherheitsheader

Ihr Reverse-Proxy muss die folgenden Header bei allen Antworten setzen:

Header Empfohlener Wert
Strict-Transport-Security max-age=31536000; includeSubDomains
X-Content-Type-Options nosniff
X-Frame-Options DENY
Referrer-Policy strict-origin-when-cross-origin
Content-Security-Policy Siehe Beispiel unten
Permissions-Policy geolocation=(), microphone=(), camera=()

Content-Security-Policy

Bifolk verwendet Chart.js (vom jsDelivr-CDN), Leaflet (von unpkg und jsDelivr-CDN), Bootstrap und liefert eigene statische Dateien aus. Eine Basis-CSP, die diese Quellen abdeckt:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://cdn.jsdelivr.net https://unpkg.com;
  style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://unpkg.com;
  img-src 'self' data: https://*.tile.openstreetmap.org;
  font-src 'self';
  connect-src 'self';
  frame-ancestors 'none';

Note

Testen Sie die CSP zunächst im Report-Only-Modus (Content-Security-Policy-Report-Only), um Verstöße zu erkennen, bevor Sie diese durchsetzen.


Nur intern erreichbare Endpunkte

Der folgende Endpunkt darf nicht aus dem öffentlichen Internet erreichbar sein:

Endpunkt Zweck Einschränkung
/health/ Container-Gesundheitsprüfung (gibt Datenbankverbindungsstatus preis) Nur intern / Container-Netzwerk

Sperren Sie diesen Endpunkt auf Traefik-Router-Ebene oder entsprechend.


Traefik-Konfigurationsbeispiel

Sicherheitsheader-Middleware

# traefik/dynamic/middlewares.yml
http:
  middlewares:
    bifolk-security-headers:
      headers:
        stsSeconds: 31536000
        stsIncludeSubdomains: true
        stsPreload: true
        forceSTSHeader: true
        contentTypeNosniff: true
        frameDeny: true
        referrerPolicy: "strict-origin-when-cross-origin"
        permissionsPolicy: "geolocation=(), microphone=(), camera=()"
        customResponseHeaders:
          Content-Security-Policy: >-
            default-src 'self';
            script-src 'self' https://cdn.jsdelivr.net https://unpkg.com;
            style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://unpkg.com;
            img-src 'self' data: https://*.tile.openstreetmap.org;
            font-src 'self';
            connect-src 'self';
            frame-ancestors 'none';

Sperrung des Health-Endpunkts

# traefik/dynamic/routers.yml
http:
  routers:
    bifolk:
      rule: "Host(`ihredomain.de`) && !PathPrefix(`/health/`)"
      middlewares:
        - bifolk-security-headers
      service: bifolk
      tls:
        certResolver: letsencrypt

Vertrauenswürdige Proxys

Konfigurieren Sie TRUSTED_PROXIES in Ihrer .env-Datei mit der IP des Traefik-Containers, damit X-Forwarded-For-Header für Zugriffsprotokollierung und Rate-Limiting vertrauenswürdig sind:

TRUSTED_PROXIES=172.18.0.2

Wenn Traefik (oder ein anderer Proxy) TLS terminiert, sieht die Django-Anwendung intern nur einfaches HTTP. Ohne zusätzliche Konfiguration verwenden Einladungslinks und andere generierte URLs http:// statt https://.

Setzen Sie TRUST_X_FORWARDED_PROTO=True, damit Django den X-Forwarded-Proto-Header liest, den der Proxy weiterleitet:

SITE_DOMAIN=bee.example.org
TRUST_X_FORWARDED_PROTO=True

SITE_DOMAIN legt den öffentlichen Hostnamen fest, der in Einladungs-E-Mails, allauth-E-Mails (z. B. E-Mail-Verifizierung) und QR-Codes verwendet wird. Ohne diese Einstellung wird auf den ersten Eintrag in DJANGO_ALLOWED_HOSTS zurückgefallen, der in Docker-Setups oft localhost ist.

Warning

TRUST_X_FORWARDED_PROTO sollte nur aktiviert werden, wenn der Reverse-Proxy unter eigener Kontrolle steht und sichergestellt ist, dass er den X-Forwarded-Proto-Header aus nicht vertrauenswürdigen eingehenden Anfragen entfernt. Andernfalls könnten Clients das Schema fälschen.

Weitere Details zu diesen Einstellungen finden Sie unter Konfiguration.


Nginx-Konfigurationsbeispiel

Falls Sie Nginx als Reverse-Proxy verwenden:

server {
    listen 443 ssl;
    server_name ihredomain.de;

    # TLS
    ssl_certificate     /etc/ssl/certs/ihredomain.pem;
    ssl_certificate_key /etc/ssl/private/ihredomain.key;

    # Sicherheitsheader
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

    # Internen Health-Endpunkt sperren
    location /health/ {
        deny all;
        return 404;
    }

    location / {
        proxy_pass http://bifolk-app:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Mediendateien ausliefern

Bifolk liefert vom Benutzer hochgeladene Mediendateien (Profilbilder, Bienenstock-Fotos und Exportdateien) über Django mit Authentifizierungsprüfungen aus. Der Reverse-Proxy muss /media/-Anfragen durch die Django-Anwendung weiterleiten — sie dürfen nicht direkt aus dem Docker-Volume ausgeliefert werden.

Danger

Wenn Ihr Reverse-Proxy /media/ direkt aus dem Speicher-Volume ausliefert (z. B. über einen Nginx-alias oder Traefik-serveFile-Middleware), werden alle Authentifizierungskontrollen umgangen. Jeder, der eine Datei-URL kennt, kann sie ohne Anmeldung herunterladen.

Was Django für Mediendateien erzwingt

Dateityp Schutz
Profilbilder, Bienenstock-Fotos @login_required — nicht authentifizierte Anfragen werden zur Anmeldung weitergeleitet
Exportdateien (/media/exports/) @login_required + Überprüfung der Organisationsmitgliedschaft. Nicht-Mitglieder erhalten 404.
Alle Mediendateien Schutz vor Pfad-Traversal

Korrekte Nginx-Konfiguration

Leiten Sie /media/ an die Anwendung weiter, nicht an das Volume:

location /media/ {
    # Verwenden Sie hier KEIN alias oder root.
    # Weiterleitung durch Django zur Authentifizierungsprüfung.
    proxy_pass http://bifolk-app:8000;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Korrekte Traefik-Konfiguration

Fügen Sie keine serveFile- oder fileServer-Regel für /media/ hinzu. Die Standard-proxy_pass an den App-Container ist ausreichend und korrekt.


Header überprüfen

Überprüfen Sie nach der Bereitstellung, ob die Sicherheitsheader vorhanden sind. Verwenden Sie dazu die Entwicklertools des Browsers oder eine Befehlszeilenprüfung:

curl -I https://ihredomain.de

In der Antwort sollten Strict-Transport-Security, X-Content-Type-Options, X-Frame-Options und Referrer-Policy enthalten sein.

Online-Tools wie securityheaders.com können Ihre Domain scannen und fehlende oder falsch konfigurierte Header melden.


Nächste Schritte

  • Installation - Vollständige Installationsanleitung
  • Konfiguration - Umgebungsvariablen-Referenz einschließlich TRUSTED_PROXIES
  • Monitoring - Log-Dateispeicherorte und Zugriffslog-Format