rebreak-monorepo/ops/mdm/profiles/UNSUPERVISED-TEST-PLAN.md
chahinebrini b31066a04c feat(chat): native action sheet + Insta-style heart for DM messages
- 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/
2026-05-30 09:14:32 +02:00

9.4 KiB

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

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