- ChatBubble: useActionSheet replaces custom Modal (native iOS popup, Android bottom sheet) - DM mode (isDM prop): hides like-count, shows Insta-style heart badge under bubble when liked - Group chat unchanged - Cleanup: remove unused Modal/Platform imports, sheet styles, actionsOpen state - deploy.sh: auto-detect ANDROID_HOME + auto-create local.properties for local Gradle - NEXT_RELEASE.md: DM reactions release note - Includes other staged work across binder-mac, marketing, ops/mdm, ios/
191 lines
9.4 KiB
Markdown
191 lines
9.4 KiB
Markdown
# Unsupervised Sideload Profile — Test-Checklist
|
|
|
|
**Datei:** `rebreak-iphone-unsupervised-sideload.mobileconfig`
|
|
**Generator:** `generate-unsupervised-profile.py`
|
|
**Distribution:** Safari-Download via `UNSUPERVISED-NGINX.conf`
|
|
|
|
## Architektur-Kontext
|
|
|
|
ReBreak hat **zwei separate Schutz-Stacks** je nach Device-Mode:
|
|
|
|
| Device-Mode | Layer 1 | Layer 2 (FC) | Layer 3 (Anti-Cooldown-Manipulation) |
|
|
|--------------|----------------------------|---------------------------|--------------------------------------|
|
|
| **supervised** + MDM | NEFilter (MDM-pushed) | Family Controls (App-Auth) | MDM-pushed `com.apple.vpn.managed` |
|
|
| **unsupervised** | NEPacketTunnel (App-managed)| Family Controls (App-Auth) | **DIESES PROFILE** (Sideload) |
|
|
|
|
Layer 3 auf unsupervised existierte vorher nicht — User konnte während Cooldown
|
|
einfach App löschen oder VPN-Toggle aus, dann war Schutz weg. Dieses Profile
|
|
schließt diese Lücke (so gut Apple's unsupervised-Constraints es erlauben).
|
|
|
|
## Pre-Flight
|
|
|
|
- [ ] iPhone unsupervised (Settings → Allgemein → Info → KEINE "Dieses iPhone wird
|
|
von ... überwacht"-Banner)
|
|
- [ ] iOS 16.0 oder neuer (`Settings → Info → Software-Version`)
|
|
- [ ] ReBreak-App via TestFlight oder Ad-Hoc installiert
|
|
- [ ] PacketTunnelExtension-Bundle vorhanden (verifizierbar: nach App-Start einmal
|
|
Schutz manuell aktivieren → Settings → VPN zeigt "ReBreak Schutz"-Eintrag)
|
|
- [ ] Pre-existing ReBreak-Profile entfernt:
|
|
Settings → Allgemein → VPN & Geräteverwaltung → falls vorhanden "ReBreak Schutz"-Profile → Entfernen
|
|
- [ ] Test-Removal-PIN bekannt (z.B. `482915`)
|
|
|
|
## Install-Test
|
|
|
|
```bash
|
|
# Auf Mac:
|
|
python3 generate-unsupervised-profile.py \
|
|
--removal-password 482915 \
|
|
--org "ReBreak Test" \
|
|
--output ~/Desktop/rebreak-schutz-test.mobileconfig
|
|
|
|
# AirDrop oder via Safari-Download an iPhone übertragen.
|
|
# Safari-Path (production-realistisch): Profile auf nginx hosten, iPhone öffnet URL.
|
|
```
|
|
|
|
- [ ] iPhone öffnet Settings-Profile-Install-Sheet automatisch
|
|
- [ ] **iOS warnt "Nicht überprüft" (rot)** — erwartet, weil unsigned. User muss "Trotzdem installieren" tippen.
|
|
- [ ] ConsentText wird vor Install gezeigt (Casino-Schutz-Erklärung)
|
|
- [ ] Device-Passcode-Prompt erscheint
|
|
- [ ] Install-Success-Screen
|
|
|
|
## Verifikation: was greift?
|
|
|
|
### ✅ DNS-Lock (sollte funktionieren)
|
|
|
|
- [ ] `Settings → Allgemein → VPN, DNS & Geräteverwaltung → DNS` zeigt "ReBreak DNS-Filter"
|
|
- [ ] **Toggle ist grau/disabled** (ProhibitDisablement greift)
|
|
- [ ] Browser-Test: `https://lotto.de` → blocked (DNS-resolved-IP wird vom ReBreak-DNS-RPZ rejected)
|
|
- [ ] Browser-Test: `https://google.com` → loads (whitelist-pass)
|
|
- [ ] Captive-Portal-Test: WiFi-Login-Page (`captive.apple.com`) → muss laden (sonst Network broken)
|
|
|
|
### ✅ VPN-Auto-Connect (sollte funktionieren)
|
|
|
|
- [ ] `Settings → VPN` zeigt "ReBreak Schutz"-Eintrag
|
|
- [ ] VPN-Connection-Status: connected (oder verbindet sich binnen 5s nach Network-Connect)
|
|
- [ ] Toggle "Bedarf verbinden" ist auf **ON**
|
|
|
|
### ❌ OnDemand-Toggle-Lock (Apple blockt das auf Sideload — DOKUMENTIERT)
|
|
|
|
- [ ] User kann "Bedarf verbinden"-Toggle in Settings → VPN → ReBreak Schutz → "i" → **ausschalten**
|
|
- [ ] Nach OFF: VPN bleibt aus bis User manuell connected
|
|
- [ ] **Dies ist erwartetes Apple-Verhalten** auf unsupervised+sideload. Memory-ref:
|
|
`project_sideload_mdm_alternative_hypothesis.md`
|
|
- [ ] App-Side-Behaviour: NICHT überwacht (User-Decision 2026-05-26: kein Accountability-Webhook)
|
|
|
|
### ⚠️ PayloadRemovalDisallowed (Verhalten auf unsupervised verifizieren)
|
|
|
|
- [ ] `Settings → Allgemein → VPN & Geräteverwaltung → ReBreak Schutz` öffnen
|
|
- [ ] Prüfen: ist "Profil entfernen"-Button **vorhanden** oder **disabled/ausgeblendet**?
|
|
- [ ] **Falls vorhanden + tappable**: PayloadRemovalDisallowed greift auf unsupervised NICHT
|
|
(memory-doku korrigieren, Profile-Lock kommt allein von RemovalPassword)
|
|
- [ ] **Falls disabled**: PayloadRemovalDisallowed greift auf unsupervised auch (gut!)
|
|
|
|
### ✅ RemovalPassword (sollte funktionieren)
|
|
|
|
- [ ] Versuch: "Profil entfernen" tippen
|
|
- [ ] iOS promptet **PIN** (`Removal Passcode`)
|
|
- [ ] Falsche PIN: Profile bleibt
|
|
- [ ] Korrekte PIN (`482915`): Profile wird entfernt
|
|
- [ ] Edge-Case: User-Memory-Test — kann User die PIN erraten? 6 Ziffern = 1M Versuche.
|
|
iOS hat keine Rate-Limit auf RemovalPassword-Tries (Apple-Doku schweigt).
|
|
Empfehlung: längere PINs (8+) für Production.
|
|
|
|
### ❌ App-Removal-Lock (greift NICHT auf unsupervised — DOKUMENTIERT)
|
|
|
|
- [ ] Long-Press ReBreak-App auf Home-Screen
|
|
- [ ] "App löschen" ist **verfügbar** (allowAppRemoval=false ignored)
|
|
- [ ] **Dies ist Apple's supervised-only-Restriction-Regel.** Auf unsupervised
|
|
kein Lock erreichbar via .mobileconfig.
|
|
|
|
## Edge-Cases
|
|
|
|
### Airplane Mode
|
|
- [ ] Airplane Mode AN → VPN disconnects (erwartet)
|
|
- [ ] Airplane Mode AUS → OnDemand reconnects VPN automatisch
|
|
- [ ] WiFi+Cellular re-enabled simultaneously → OnDemand wählt korrektes Interface
|
|
|
|
### Network-Switch (WiFi ↔ Cellular)
|
|
- [ ] Verbunden mit WiFi → VPN connected
|
|
- [ ] WiFi aus → Wechsel zu Cellular → VPN disconnects + reconnects über Cellular binnen ~3s
|
|
- [ ] DNS bleibt gelocked auch während Reconnect-Lücke (DoH-Payload ist VPN-independent)
|
|
|
|
### Captive Portal
|
|
- [ ] Hotel-WiFi mit Login-Page
|
|
- [ ] iOS öffnet Captive-Portal automatisch (bypass VPN für login-domain)
|
|
- [ ] Nach Login: VPN connectet wieder
|
|
- [ ] Test mit echtem Café/Hotel-WiFi nötig — Simulation schwer
|
|
|
|
### Profile-Survival nach App-Uninstall
|
|
- [ ] App via Long-Press → "App löschen"
|
|
- [ ] **Profile bleibt installiert** (Settings → VPN & Geräteverwaltung zeigt's)
|
|
- [ ] **DNS-Lock bleibt aktiv** (Browser-Test `lotto.de` → still blocked)
|
|
- [ ] **VPN-Eintrag bleibt**, aber Connection-Status: failure (ProviderBundle nicht mehr resolvable)
|
|
- [ ] App-Reinstall: VPN-Connection wieder OK ohne Re-Install des Profils
|
|
- [ ] **DIESER PUNKT IST DER KERN-PURPOSE**: Anti-Cooldown-Manipulation. Auch wenn
|
|
User die App löscht, bleibt der DNS-Lock und damit ein Mindest-Schutz aktiv.
|
|
|
|
### Reboot
|
|
- [ ] iPhone neustarten
|
|
- [ ] VPN reconnects automatisch nach Boot
|
|
- [ ] DNS-Lock auch nach Reboot aktiv
|
|
|
|
## Companion-Path: MDM-Enrollment für Full-Lock (optional)
|
|
|
|
Wenn der User zusätzlich Toggle-Lock will (= "Bedarf verbinden" NICHT abschaltbar):
|
|
|
|
- [ ] User browsed zu `https://mdm.rebreak.org/enroll` in Safari
|
|
- [ ] Installiert NanoMDM-Enrollment-Profile (removable, mit `MDM_TYPE=Device`)
|
|
- [ ] **Gerät bleibt unsupervised** — Device-Enrollment ist nicht Supervision
|
|
- [ ] Backend pushed `com.apple.vpn.managed`-Payload via NanoMDM `InstallProfile`-Command
|
|
- [ ] **Jetzt empirisch verifizieren**: ist `OnDemandUserOverrideDisabled` auf
|
|
unsupervised+device-enrolled wirksam? Bisher unverifiziert — Memory hat nur
|
|
supervised+MDM-Empirie. Hypothese: ja, weil MDM-Channel ist der Differenzierer.
|
|
- [ ] Falls ja: User-Toggle für "Bedarf verbinden" ist disabled (grau).
|
|
- [ ] Bypass-Surface: User kann MDM-Enrollment via Settings entfernen → VPN-Toggle
|
|
wieder togglebar. Sideload-Profile bleibt aber stehen (separates Profile).
|
|
|
|
## Bekannte Limitations (Apple-Walls — non-fixable ohne Supervision)
|
|
|
|
| Limitation | Kompensiert durch |
|
|
|-------------------------------------------|------------------------------------------------|
|
|
| allowAppRemoval unsupervised ignored | App-Side-Cooldown-Logik (User kann App löschen, aber DNS bleibt) |
|
|
| OnDemandUserOverrideDisabled unsupervised | Companion-MDM-Enrollment (optional, User-Decision) |
|
|
| PayloadRemovalDisallowed unsupervised ? | RemovalPassword (PIN-Friction) |
|
|
|
|
## Update-Pfad
|
|
|
|
Wenn das Profile-Layout ändert (z.B. neue DNS-Server, geänderte VPN-Config):
|
|
|
|
1. Bump `PayloadIdentifier`-Suffix-Datum: `org.rebreak.protection.iphone.unsupervised.YYYYMMDD`
|
|
2. Frische UUIDs überall (Generator macht das automatisch)
|
|
3. User muss altes Profile mit RemovalPassword entfernen + neues installieren
|
|
4. Bei vielen Users: Web-Notification "Bitte Schutz erneuern" + Direkt-Link auf
|
|
neuen Download
|
|
|
|
Apple's iOS akzeptiert **kein** Profile-Update-in-place ohne RemovalPassword-Re-Auth
|
|
(Anti-Hijack-Design). Bei zu häufigen Updates User-Friction hoch → Profile-Schema
|
|
stabil halten.
|
|
|
|
## UI-Banner (geplant, falls Setup klappt)
|
|
|
|
`apps/rebreak-native/app/(app)/blocker.tsx` — wenn `mdmManaged=false` UND
|
|
`isUnsupervised=true` UND `hasSideloadProfile=false`:
|
|
|
|
```
|
|
┌────────────────────────────────────────────┐
|
|
│ 🛡️ Erweiterter Schutz verfügbar │
|
|
│ │
|
|
│ Du nutzt aktuell App-internen VPN + Family │
|
|
│ Controls. Für besseren Cooldown-Schutz │
|
|
│ installiere das Schutz-Profil — überlebt │
|
|
│ auch wenn du die App löschst. │
|
|
│ │
|
|
│ [Profil herunterladen] │
|
|
└────────────────────────────────────────────┘
|
|
```
|
|
|
|
Detection: App liest `NETunnelProviderManager.loadAllFromPreferences()` →
|
|
filter auf `isOnDemandEnabled && ProviderBundle == eigen` UND zusätzlich
|
|
einen zweiten Manager (das wäre das gepushte Profile). Oder: `dns_settings`-
|
|
Check via `nw_path_monitor` + DoH-Endpoint-Verify.
|