chahinebrini 29bbf23405 feat(protection): iOS NEURLFilter-Spike + PIR-Server-Ops
NEURLFilter-Stack (iOS 26): Extension RebreakURLFilter -> URLFilterExtension
umbenannt, url-filter-provider-Entitlement, Bloom-Prefilter-Extension,
PIR-Client-Config (pirServerURL/pirAuthToken via Build-Env).
PIR-Server-Ops unter ops/pir-server/ (Dockerfile, build-and-deploy, Patches,
DTS-Report). backend/scripts/generate-pir-input.ts erzeugt die PIR-Datenbank.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 18:09:42 +02:00

8.1 KiB
Raw Blame History

PIR-Server — iOS NEURLFilter Backend

Status: LIVE (Staging)https://pir.staging.rebreak.org · Stand 2026-05-21

Dieses Dokument beschreibt den PIR-Server vollständig, damit eine andere Session ihn verstehen, betreiben und weiterentwickeln kann.


1. Was & Warum

Rebreaks iOS-Gambling-Blocker nutzt Apples NEURLFilter (iOS 26). NEURLFilter filtert URLs systemweit (Safari + alle WebKit-Browser + URLSession) und verlangt dafür zwingend einen vom Vendor gehosteten PIR-Server (Private Information Retrieval) — NEURLFilterManager.setConfiguration() akzeptiert keine Konfiguration ohne pirServerURL. Dieser Server ist dieser PIR-Server.

Ablauf zur Laufzeit:

  1. Das Gerät prüft die URL gegen einen lokalen Bloom-Filter (Prefilter, in der iOS-App-Extension NEURLFilterControlProvider).
  2. Bei einem Bloom-Treffer → PIR-Lookup gegen diesen Server, geroutet über Apples Oblivious-HTTP-Relay.
  3. Per Homomorphic Encryption sieht der Server die geprüfte URL nie im Klartext.

DSGVO/DiGA-Vorteil: Kein Beteiligter sieht URL + Identität zusammen — Rebreak erfährt das Surfverhalten der User nie. Datenminimierung per Krypto-Konstruktion.


2. Architektur-Bausteine

Basiert auf Apples Open-Source-Vorlagen:

  • apple/pir-service-examplePIRService (Swift/Hummingbird-HTTP-Server, bedient PIR-Queries + eingebauten PrivacyPass-Issuer) + ConstructDatabase.
  • apple/swift-homomorphic-encryptionPIRProcessDatabase (wandelt input.txtpb in PIR-optimierte Shards).

3. Deployment-Stand (Hetzner rebreak-server, 49.13.55.22)

Komponente Ort / Wert
Docker-Container pir-service-staging, restart=unless-stopped, 127.0.0.1:8090->8090
Docker-Image pir-service-staging:latest (Multi-Stage Swift-Build, ~5 GB)
Build-Repos /srv/pir-build/{pir-service-example,swift-homomorphic-encryption} + Dockerfile
Deploy-Verzeichnis /srv/pir-server/ (build-and-deploy.sh, gen-test-input.sh, config/, data/)
PIR-DB-Artifacts /srv/pir-server/data/input.txtpb + url-0..3.bin + url-0..3.params.txtpb (4 Shards)
Service-Config /srv/pir-server/config/service-config.json (Token inline — NICHT im Repo)
nginx /etc/nginx/sites-{available,enabled}/pir-staging.rebreak.org, server_name pir.staging.rebreak.org
TLS Let's Encrypt für pir.staging.rebreak.org, läuft 2026-08-18 ab, Auto-Renew aktiv
Public-URL https://pir.staging.rebreak.org

4. Konfiguration

service-config.json (auf dem Server; Token nicht im Repo → siehe service-config.template.json):

  • usecase-Name: org.rebreak.app.url.filtering — muss EXAKT mit der iOS-setConfiguration matchen, sonst HTTP 404.
  • shardCount: 4 (215k Domains / 50k pro Shard).
  • users[].tokens: enthält den PIR_AUTH_TOKEN.

PIR_AUTH_TOKEN — liegt in Infisical, Env staging, Key PIR_AUTH_TOKEN, Project-ID 14b11b35-ef59-4b8a-a16b-398f0cc3ad93. App-weit halten (NICHT pro-User — ein per-User-Token würde die PIR-Nicht-Verkettbarkeit schwächen).

process-config.json — Parameter für PIRProcessDatabase: entryCountPerShard 50000, rlweParameters n_4096_logq_27_28_28_logt_5, databaseType keyword.

PIR_ISSUER_REQUEST_URI — Env-Var am Container (docker run -e ...), gesetzt von build-and-deploy.sh auf https://pir.staging.rebreak.org/issue. Zwingend absolut (RFC 9578 §6): das Apple-pir-service-example advertised von Haus aus nur ein relatives /issue im Issuer-Directory (PrivacyPassController.swift, hardcodiert) — der NEURLFilter-Client kann damit keinen Privacy-Pass-Token holen und scheitert mit serverSetupIncomplete. Behoben durch patches/0001-absolute-issuer-request-uri.patch, das die URL aus dieser Env-Var liest.


5. iOS-Integrationswerte (NEURLFilterManager.setConfiguration)

Parameter Wert
pirServerURL https://pir.staging.rebreak.org
pirPrivacyPassIssuerURL https://pir.staging.rebreak.org (PIRService bedient beides)
pirAuthenticationToken Infisical staging/PIR_AUTH_TOKEN
usecase-Name org.rebreak.app.url.filtering
controlProviderBundleIdentifier org.rebreak.app.URLFilterExtension (geplant — beim iOS-Scaffolding final festlegen)

6. Daten-Pipeline (Blocklist → PIR-DB)

  1. backend/scripts/generate-pir-input.ts erzeugt input.txtpb aus ~215k Gambling-Domains.
    • DB-Modus (Default): liest die BlocklistDomain-Tabelle — braucht infisical run für DATABASE_URL.
    • HaGeZi-Modus (--source hagezi): direkt von HaGeZis gambling.txt, kein DB/Infisical nötig. Aktuell verwendet, weil infisical nicht auf dem Dev-Mac liegt. Äquivalent — die BlocklistDomain-Tabelle wird ohnehin aus genau dieser HaGeZi-Liste gespeist.
    • Output-Format: pro Domain eine Zeile rows: { keyword: "<domain>" value: "1" }.
  2. input.txtpb → per scp nach /srv/pir-server/data/input.txtpb.
  3. PIRProcessDatabase /data/process-config.json (im Container) → url-N.bin + url-N.params.txtpb.

7. Betrieb / Runbook

Health-Check:

curl -s -o /dev/null -w '%{http_code}\n' \
  https://pir.staging.rebreak.org/.well-known/private-token-issuer-directory   # → 200
ssh rebreak-server 'docker ps | grep pir-service-staging'

Blocklist aktualisieren:

  1. Neue input.txtpb generieren (backend/scripts/generate-pir-input.ts) + nach /srv/pir-server/data/input.txtpb scp'en.
  2. Alte Shards löschen: ssh rebreak-server 'rm /srv/pir-server/data/url-*.bin /srv/pir-server/data/url-*.params.txtpb'.
  3. PIRProcessDatabase neu laufen lassen (siehe build-and-deploy.sh Step 6) — erzeugt neue Shards.
  4. shardCount in service-config.json prüfen + Container neu starten.

Komplett neu bauen/deployen:

ssh rebreak-server 'bash /srv/pir-server/build-and-deploy.sh'

Swift-Build ~2030 Min, RAM-intensiv (~23 GB → Swap nötig, off-peak bauen).


8. Bekannte Issues / TODOs

  • build-and-deploy.sh shardCount-Bug (BEHOBEN 2026-05-21): Step 3 leitet den shardCount jetzt aus den vorhandenen url-*.bin-Artifacts ab statt ihn fix auf 1 zu setzen — kein Regress mehr von 4 Shards auf 1 bei Re-Runs.
  • Relatives issuer-request-uri (BEHOBEN 2026-05-21): siehe §4 PIR_ISSUER_REQUEST_URI. War die Ursache für serverSetupIncomplete auf der iOS-Seite. Fix via patches/0001-absolute-issuer-request-uri.patch, von build-and-deploy.sh Step 3b automatisch auf den pir-service-example-Clone angewandt.
  • Deploy-Skripte liegen sowohl auf dem Server (/srv/pir-server/, /srv/pir-build/) als auch hier im Repo. Bei Änderungen synchron halten — oder das Repo als Single-Source-of-Truth etablieren (empfohlen).
  • App-Store-Distribution: braucht zusätzlich Apples OHTTP-Onboarding-Formular, ein OHTTP-Gateway und einen DNS-TXT-Record apple-url-filter=org.rebreak.app. Für TestFlight/Dev NICHT nötig. → Phase 2.
  • Entitlement com.apple.developer.networking.networkextension.url-filter-provider: Dev-Builds ausgenommen; vor TestFlight/Store über den „Capability Requests"-Tab im Apple-Developer-Portal (Certificates, Identifiers & Profiles → Identifiers) beantragen.
  • Apple zu pir-service-example: „example service, not for production" — Produktionshärtung (persistente PrivacyPass-Keys statt ephemerer, Monitoring, Ressourcen-Tuning) ist ein eigener späterer Schritt.

9. Dateien in diesem Verzeichnis

Datei Zweck
README.md dieses Dokument
Dockerfile Multi-Stage Swift-Build (Kopie von /srv/pir-build/Dockerfile)
build-and-deploy.sh Deploy-Script (Kopie von /srv/pir-server/)
gen-test-input.sh Test-DB-Generator (10 Domains, Fallback)
process-config.json PIRProcessDatabase-Parameter
service-config.template.json Service-Config-Vorlage (Token-Platzhalter)
nginx-pir.staging.rebreak.org.conf nginx-Reverse-Proxy-Config (Referenz)
patches/*.patch Quell-Patches für pir-service-example, von build-and-deploy.sh Step 3b angewandt

Der input.txtpb-Generator selbst liegt bei backend/scripts/generate-pir-input.ts.