# 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.