Sheets via neuer KeyboardAwareSheet-Composable (in Modal pattern, auto-grow mit Tastatur, paddingBottom-Lift): EditMail, AddDomain, CreateRoom, ConnectMail. GameOverScreen behält Spring-Slide-In, nutzt RN Keyboard.addListener für Lift. - KeyboardAwareSheet.tsx — universal modal with sheet-grow + keyboard-padding - react-native-keyboard-controller installiert + KeyboardProvider in Root - Snake: time + ScoreProgressBar + useSnakeSounds (haptic, audio TODO) - Tetris: title weg, Buttons zentriert, kein Pressable mit style-fn - DPad-Buttons 60→48, more bg, no scale - useMe: pub-sub listener pattern für app-weite avatar/nickname-Updates - dm.tsx: resolveAvatar wrap (iron.png-Warning) - Mail-error-humanizer + locales Recovery-Doc-Update in docs/internal/RECOVERY_LOG_2026-05-10.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
260 lines
13 KiB
Markdown
260 lines
13 KiB
Markdown
# MDM Setup — Phasen
|
|
|
|
## Phase A ✅ Server-Bootstrap
|
|
|
|
Erledigt vor 2026-05-10.
|
|
|
|
- apt-update + apt-upgrade
|
|
- Pakete installiert: nginx, postgresql, docker.io, certbot, python3-certbot-nginx, ufw, fail2ban
|
|
- UFW konfiguriert: 22/tcp, 80/tcp, 443/tcp erlaubt, default-deny
|
|
- fail2ban aktiv (SSH-Brute-Force-Schutz)
|
|
- DNS: IONOS A-Record `mdm.rebreak.org` → 178.105.101.137
|
|
|
|
## Phase B ✅ TLS-Zertifikat
|
|
|
|
Erledigt vor 2026-05-10.
|
|
|
|
- `certbot --nginx -d mdm.rebreak.org` ausgeführt
|
|
- Cert liegt in `/etc/letsencrypt/live/mdm.rebreak.org/`
|
|
- certbot.timer (systemd) erneuert automatisch
|
|
|
|
## Phase C ✅ NanoMDM Container + nginx-Vhost
|
|
|
|
Erledigt 2026-05-10.
|
|
|
|
**Was gemacht wurde:**
|
|
|
|
1. PostgreSQL-Datenbank `nanomdm` mit User `nanomdm` und Passwort aus `/root/.nanomdm_db_pass` angelegt
|
|
2. `ALTER USER nanomdm WITH PASSWORD '...'` explizit gesetzt (scram-sha-256 braucht explizites Passwort)
|
|
3. `pg_hba.conf` ergänzt für Docker-Netze (172.17.0.0/16, 172.18.0.0/16)
|
|
4. `listen_addresses` in `postgresql.conf` auf `localhost,172.17.0.1,172.18.0.1` erweitert
|
|
5. MDM CA generiert: `ca.key` + `ca.crt` in `/opt/nanomdm/certs/`
|
|
6. `/opt/nanomdm/.env` mit `NANOMDM_DB_PASS` geschrieben (chmod 600)
|
|
7. `/opt/nanomdm/docker-compose.yml` mit `network_mode: host` (kritisch, sonst postgres nicht erreichbar wegen NAT-Masquerade)
|
|
8. `docker compose up -d` — Container läuft, `starting server listen=127.0.0.1:9000` bestätigt
|
|
9. nginx-Vhost `/etc/nginx/sites-available/mdm.rebreak.org` geschrieben + in sites-enabled symlinkt
|
|
10. `nginx -t && systemctl reload nginx`
|
|
11. Externer Verify: `curl -sI https://mdm.rebreak.org/` → `HTTP/2 404` von nanomdm (korrekt, kein 502)
|
|
|
|
**Bekannte Tücken aus diesem Setup:**
|
|
|
|
- `micromdm/nanomdm` auf Docker Hub existiert nicht. Korrektes Image: `ghcr.io/micromdm/nanomdm:latest`
|
|
- nanomdm v0.9 kennt `-storage postgres` nicht. Korrekt: `-storage pgsql` (bzw. `NANOMDM_STORAGE=pgsql`)
|
|
- Docker-Compose-Netzwerk (172.18.x) geht via NAT durch Host — Postgres sieht externe IP als Source. Lösung: `network_mode: host` im Compose, dann verbindet nanomdm direkt zu `127.0.0.1:5432`
|
|
- nginx 1.24 kennt `http2 on;` nicht (das ist nginx 1.25+). Korrekt: `listen 443 ssl http2;`
|
|
|
|
## Phase D ✅ Apple Push CSR generiert
|
|
|
|
Erledigt 2026-05-10.
|
|
|
|
```
|
|
openssl req -newkey rsa:2048 -nodes \
|
|
-keyout /opt/nanomdm/certs/push.key \
|
|
-out /opt/nanomdm/certs/push.csr \
|
|
-subj '/CN=ReBreak MDM Push/O=Raynis/C=DE'
|
|
chmod 600 /opt/nanomdm/certs/push.key
|
|
```
|
|
|
|
CSR-Content liegt in `/opt/nanomdm/certs/push.csr`. Der private Key `push.key` verlässt den Server nie.
|
|
|
|
## Phase D.0.5 ✅ mdmcert.download Signing-Request
|
|
|
|
Erledigt 2026-05-10.
|
|
|
|
**Warum dieser Schritt notwendig ist:**
|
|
|
|
Apple Push Notification Service (APNS) für MDM akzeptiert keine rohen CSRs von Self-Hostern direkt im Apple Push Portal. Apple verlangt, dass die CSR von einem akkreditierten MDM-Vendor signiert wird. Self-Hoster ohne Apple-MDM-Vendor-Status nutzen `mdmcert.download` — ein Service des MicroMDM-Teams, der die CSR mit einem akzeptierten Vendor-Key gegen-signiert und encrypted per Email zurückschickt.
|
|
|
|
**Was passiert:**
|
|
1. Wir schicken unseren CSR base64-encoded + eine Encryption-Cert an `https://mdmcert.download/api/v1/signrequest`
|
|
2. mdmcert.download signiert ihn mit ihrem Apple-akkreditierten Vendor-Key
|
|
3. Sie verschlüsseln das Ergebnis mit unserer Encryption-Cert (PKCS7) und senden es per Email an `hello@chahine-brini.com`
|
|
4. Das entschlüsselte Ergebnis (nicht der raw CSR, nicht das `.b64.p7`) wird im Apple Push Portal hochgeladen
|
|
|
|
**Was gemacht wurde:**
|
|
|
|
1. Encryption-Keypair auf dem MDM-Server generiert:
|
|
- Cert: `/opt/nanomdm/certs/mdmcert-encryption.crt` (public, wird an mdmcert.download geschickt)
|
|
- Key: `/opt/nanomdm/certs/mdmcert-encryption.key` (chmod 600, verlässt Server nie)
|
|
|
|
```bash
|
|
openssl req -new -newkey rsa:2048 -nodes \
|
|
-keyout /opt/nanomdm/certs/mdmcert-encryption.key \
|
|
-x509 -days 365 \
|
|
-out /opt/nanomdm/certs/mdmcert-encryption.crt \
|
|
-subj '/CN=ReBreak mdmcert encryption'
|
|
chmod 600 /opt/nanomdm/certs/mdmcert-encryption.key
|
|
```
|
|
|
|
2. Signing-Request an mdmcert.download abgeschickt (shared public API-Key aus micromdm-Source, öffentlich dokumentiert):
|
|
|
|
```bash
|
|
PUSH_CSR_B64=$(base64 -w0 /opt/nanomdm/certs/push.csr)
|
|
ENC_CRT_B64=$(base64 -w0 /opt/nanomdm/certs/mdmcert-encryption.crt)
|
|
|
|
curl -X POST https://mdmcert.download/api/v1/signrequest \
|
|
-H "Content-Type: application/json" \
|
|
-H "User-Agent: micromdm/certhelper" \
|
|
-d "{\"csr\":\"$PUSH_CSR_B64\",\"email\":\"hello@chahine-brini.com\",\"key\":\"<shared-api-key>\",\"encrypt\":\"$ENC_CRT_B64\"}"
|
|
```
|
|
|
|
Antwort: `{"result":"success"}`
|
|
|
|
**Naechster Schritt:** Email von mdmcert.download bei `hello@chahine-brini.com` prüfen. Anhang-Name hat Format `mdm_signed_request.YYYYMMDD_HHMMSS_NNN.plist.b64.p7`. Dann weiter mit Phase D.0.7.
|
|
|
|
**Technische Details (wichtig fuer Decrypt):**
|
|
- Der Dateiname endet auf `.b64.p7` — irreführend. Der tatsächliche Inhalt ist **hex-encoded PKCS7**, nicht base64. (Quelle: micromdm/micromdm cmd/mdmctl/mdmcert.download.go, Decrypt-Pfad)
|
|
- Der Decrypt-Befehl (`openssl cms` oder PKCS7-Tooling) muss zuerst hex→binary decodieren, dann PKCS7 mit dem mdmcert-encryption.key entschlüsseln
|
|
|
|
## Phase D.0.7 ⏳ Signed CSR entschlüsseln
|
|
|
|
**Voraussetzung:** Email von mdmcert.download mit Anhang empfangen (Phase D.0.5 abgeschlossen)
|
|
|
|
**Wer:** Chahine schickt den Anhang per `scp` auf den MDM-Server. Oder Backyard entschlüsselt wenn Anhang auf den Server kopiert wurde.
|
|
|
|
**Schritte:**
|
|
|
|
1. Anhang von Email speichern (z.B. `mdm_signed_request.20260510_XXXXXX.plist.b64.p7`)
|
|
|
|
2. Datei auf Server kopieren:
|
|
```bash
|
|
scp ~/Downloads/mdm_signed_request.*.plist.b64.p7 rebreak-mdm:/opt/nanomdm/certs/signed_request.p7
|
|
```
|
|
|
|
3. Hex→Binary dekodieren + PKCS7 entschlüsseln (micromdm-Tooling macht beides intern):
|
|
```bash
|
|
# Hex-String aus der Datei zu Binary konvertieren
|
|
xxd -r -p /opt/nanomdm/certs/signed_request.p7 > /opt/nanomdm/certs/signed_request.der
|
|
|
|
# PKCS7 mit unserem Encryption-Key entschlüsseln
|
|
openssl cms -decrypt \
|
|
-in /opt/nanomdm/certs/signed_request.der \
|
|
-inform DER \
|
|
-inkey /opt/nanomdm/certs/mdmcert-encryption.key \
|
|
-recip /opt/nanomdm/certs/mdmcert-encryption.crt \
|
|
-out /opt/nanomdm/certs/push_request.plist
|
|
```
|
|
|
|
4. Ergebnis `/opt/nanomdm/certs/push_request.plist` prüfen — sollte eine Apple Plist-Datei sein.
|
|
```bash
|
|
head -5 /opt/nanomdm/certs/push_request.plist
|
|
# Erwartete Ausgabe: <?xml version="1.0" ... oder PEM-ähnliches Format
|
|
```
|
|
|
|
5. DIESE Datei (`push_request.plist`) wird bei https://identity.apple.com hochgeladen (Phase D.1).
|
|
|
|
**Status:** Wartet auf Email-Empfang bei `hello@chahine-brini.com`
|
|
|
|
## Phase D.1 ⏳ Apple Push Cert — Benutzeraktion
|
|
|
|
**Voraussetzung:** Phase D.0.7 abgeschlossen (entschlüsseltes Plist `push_request.plist` auf Server)
|
|
|
|
**Wer:** Chahine (muss mit Apple-ID eingeloggt sein, die als MDM-Zertifikats-Owner gelten soll)
|
|
|
|
**WICHTIG: NICHT den raw push.csr oder die `.b64.p7`-Datei hochladen.**
|
|
Hochgeladen wird die entschlüsselte `push_request.plist` aus Phase D.0.7.
|
|
|
|
**Schritte:**
|
|
|
|
1. Phase D.0.7 abschliessen — `push_request.plist` auf Server entschlüsselt
|
|
2. Datei lokal runterladen: `scp rebreak-mdm:/opt/nanomdm/certs/push_request.plist ~/Downloads/`
|
|
3. Oeffne https://identity.apple.com/pushcert/ im Browser (einloggen mit Apple-ID)
|
|
4. Klicke "Create a Certificate"
|
|
5. Lade `push_request.plist` hoch (NICHT push.csr, NICHT die `.b64.p7`-Datei)
|
|
6. Download das ausgestellte Zertifikat (`.pem` oder `.cer`)
|
|
7. Kopiere es auf den Server: `scp ~/Downloads/MDMCertificate.pem rebreak-mdm:/opt/nanomdm/certs/push.pem`
|
|
7. Setze Permissions: `ssh rebreak-mdm "chmod 600 /opt/nanomdm/certs/push.pem"`
|
|
8. Informiere Backyard-Agent fuer Phase E
|
|
|
|
**Wichtig:**
|
|
- Das Zertifikat ist an die Apple-ID geknuepft, mit der es erstellt wurde
|
|
- Gueltigkeitsdauer: 1 Jahr
|
|
- Bei Renewal: GLEICHEN `push.key` verwenden (kein neues keypair generieren)
|
|
- Wenn push.key verloren geht: alle Geraete muessen re-enrollen
|
|
|
|
## Phase D.2 ✅ NanoMDM mit Push-Cert konfiguriert
|
|
|
|
Erledigt 2026-05-10.
|
|
|
|
**Was gemacht wurde:**
|
|
|
|
1. NanoMDM API-Key generiert (32-char-hex), in `/opt/nanomdm/.env` (`NANOMDM_API=`) + `/root/.nanomdm_admin_pass` (chmod 600)
|
|
2. Container force-recreated mit neuem env-file
|
|
3. Postgres-Schema von https://raw.githubusercontent.com/micromdm/nanomdm/main/storage/pgsql/schema.sql geladen + applied (8 tables: devices, push_certs, commands, etc.) — fehlte aus initial-setup
|
|
4. Push-Cert via `PUT /v1/pushcert` (basic-auth) hochgeladen → in `push_certs` table
|
|
5. Verify: Topic `com.apple.mgmt.External.816a2d4a-4ce1-4b44-9264-2831b891206a`, valid bis 2027-05-10
|
|
6. External smoke-test: `curl -u nanomdm:<key> https://mdm.rebreak.org/version` → `{"version":"v0.9.0"}` ✅
|
|
|
|
**Bekannte Tücke:** Initial-setup hat das postgres-schema nicht angewendet. NanoMDM-Container hat keine eingebaute migrate-step. Schema muss manuell via `psql -f schema.sql` geladen werden bevor erster API-call funktioniert.
|
|
|
|
## Phase E ⏸ Email-Distribution an Ina — geparkt (User-Decision 2026-05-10)
|
|
|
|
**Status: PARKED — alles server-side ready, Versand verschoben.**
|
|
|
|
User-Entscheidung: PIN-Versand an Ina jetzt nicht — wird später nachgeholt. iPhone-Enrollment kann ohne laufen (MASTER-PIN ist Recovery-Backup, nicht Voraussetzung für enrollment).
|
|
|
|
Server-Status:
|
|
- ✅ MASTER-Recovery-PIN auf Server: `/root/.nanomdm_master_pin` (chmod 600)
|
|
- ✅ Ina-Email-Draft auf Server: `/root/INA_EMAIL_DRAFT.md` (chmod 600)
|
|
- ✅ Resend-API-Key auf Server: `/root/.resend_api_key` (chmod 600)
|
|
- ⏸ Resend-Domain-Verify ungetan — Versand würde fehlschlagen ohne `chahine-brini.com` oder `rebreak.org` verified
|
|
|
|
Reaktivierung: User sagt „Phase E GO", wir verifizieren Domain in Resend, senden, fertig. Files bleiben bis dahin auf Server.
|
|
|
|
## Phase F ⏳ Device-Enrollment
|
|
|
|
Wartet auf Phase E.
|
|
|
|
Was passiert:
|
|
1. iPhone auf Werkseinstellungen zurücksetzen (Backup vorher!)
|
|
2. Während Setup: iPhone via USB-C mit Mac verbinden, Apple Configurator 2 öffnen
|
|
3. In Apple Configurator 2: Gerät preparieren (Supervised Mode aktivieren)
|
|
4. MDM-Enrollment-Profil von `https://mdm.rebreak.org/enroll` auf Gerät installieren
|
|
5. Verifyieren dass Profil als "nicht entfernbar" markiert ist
|
|
6. Apps installieren (ReBreak, etc.)
|
|
|
|
**Hinweis zum Supervised Mode:** Ohne Supervision kann das MDM-Profil vom User entfernt werden. Supervision braucht einmalig USB + Apple Configurator. Danach ist OTA-MDM-Update möglich.
|
|
|
|
**Scope-Constraint (User-bestätigt 2026-05-10):** Profil enthält NUR `allowAppRemoval=false` für Bundle-ID `org.rebreak.app` + `allowMDMProfileRemoval=false`. KEIN App-Store-Block, keine weiteren Restrictions. iOS-App-Store hat keine Echtgeld-Casino-Apps (Apple-Policy), Browser-Casinos werden von ReBreak's NEFilter geblockt.
|
|
|
|
## Phase G ⏳ iPad-Enrollment (optional, später)
|
|
|
|
Identisch zu Phase F, gleicher flow:
|
|
1. iPad via USB-C mit Mac verbinden
|
|
2. Apple Configurator 2 → Supervised-Mode → factory-reset
|
|
3. MDM-enrollment-profile von `https://mdm.rebreak.org/enroll`
|
|
4. ReBreak-iOS app installieren (läuft nativ auf iPad)
|
|
5. Verifyieren: ReBreak nicht entfernbar, MDM-profile nicht entfernbar
|
|
|
|
**Aufwand:** ~30min nach Phase F. Apple Push Cert deckt iPad mit ab (kein zusätzlicher cert nötig).
|
|
|
|
**Voraussetzung:** Phase F erfolgreich getestet auf iPhone.
|
|
|
|
## Phase H ⏳ MacBook-Enrollment (optional, später)
|
|
|
|
Anders als iPhone/iPad weil:
|
|
- **Kein ReBreak-Mac-app** existiert → MDM-profile muss eigene Blocking-Mechanik mitbringen
|
|
- Lösung: **Web-Content-Filter-Payload** im profile (DNS/URL-blocklist auf OS-Ebene)
|
|
- Mac-Supervised-Mode: factory-reset des MacBook nötig (analog iPad), via Apple Configurator 2 + USB-C
|
|
|
|
**Schritte:**
|
|
|
|
1. ReBreak-Blocklist (~208k domains) als Web-Content-Filter-Payload formattieren
|
|
- Payload-type: `com.apple.webcontent-filter`
|
|
- oder `com.apple.dnsSettings.managed` für DNS-level-block
|
|
2. MDM-profile assemblen mit:
|
|
- `allowMDMProfileRemoval=false` (braucht supervised-mode)
|
|
- Web-Content-Filter mit Casino-Blocklist
|
|
- Optional: `allowSafariAutoFill=false` (verhindert auto-login auf bekannten casino-sites)
|
|
3. MacBook factory-reset → Apple Configurator 2 → supervised-mode → MDM-enrollment
|
|
4. Verify: Casino-domain im Browser → blocked
|
|
|
|
**Aufwand:** ~1 Tag (blocklist-conversion + profile-assembly + test). Plus factory-reset-zeit.
|
|
|
|
**Voraussetzung:**
|
|
- Phase F+G erfolgreich
|
|
- User explizites GO (factory-reset MacBook = großer Schritt)
|
|
- Backup von wichtigen MacBook-Daten
|
|
|
|
**Tradeoff:** Kein ReBreak-Mac-app = nur URL-blocking, keine SOS-features, kein Lyra, keine Community auf Mac. Wer ReBreak-features auf Mac will, braucht später entweder native Mac-app (s. `ops/mac-version-research.md`) oder Browser-Extension.
|