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

168 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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-example``PIRService` (Swift/Hummingbird-HTTP-Server, bedient
PIR-Queries + eingebauten PrivacyPass-Issuer) + `ConstructDatabase`.
- `apple/swift-homomorphic-encryption``PIRProcessDatabase` (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:**
```bash
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:**
```bash
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`.