MDM-VPN-Pivot (Phase F.2 done): - ops/mdm/profiles/rebreak-iphone-protection.mobileconfig auf v5 mit com.apple.vpn.managed Payload + OnDemandUserOverrideDisabled. iPhone-User kann ReBreak-VPN-Profile nicht entfernen und "Bedarf verbinden"-Toggle ist disabled. allowEnablingRestrictions empirisch widerlegt für FC-Toggle- Lock — out. - DEV-removable Variante als Test-Profile dazu. - Bootstrap-Tool (rebreak-supervise.sh) + Supervision-Identity-Setup-Doc. - PHASES.md updated mit empirischen Befunden. App-side MDM-Detect (Pfad-a Banner-Logic): - modules/rebreak-protection: getDeviceState() returnt mdmManaged via Heuristik NETunnelProviderManager.count > 1 (App selbst kann nur einen eigenen erstellen, MDM-Push fügt einen zweiten hinzu). - DeviceLayers.mdmManaged?: boolean Type. - blocker.tsx: lockedIn-Bedingung erweitert um mdmManaged. Bei MDM-managed iPhones wird der App-Lock-Card (FC-Authorization-Toggle UI) ausgeblendet weil der per-App FC-Toggle nicht lockbar ist und durch den MDM-VPN-Layer redundant. Layer-2-Country-Curated-Pivot: - backend: vip-swap.post.ts raus, suggest.post.ts rein. Curated-domains durch admin (separate Tabelle/Pfad), getrennt von User-Custom-Domains. - Admin-APIs für curated-domain Pflege (index.get + [id].patch). - seed-country-blocklists Script für initiale Curated-Domain-Liste. - protection/webcontent-domains.get refactored für Country-Curated-Pfad. - Migration drop_vip_swap_fields.sql + schema.prisma adjusted. - docs/concepts/layer2-country-pivot.md mit Architektur + Decision-Trail. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
408 lines
16 KiB
Bash
Executable File
408 lines
16 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# rebreak-supervise.sh
|
|
# --------------------
|
|
# Backup-Sandwich-Bootstrap für iPhone-Supervision ohne sichtbaren Daten-Verlust.
|
|
#
|
|
# 1. idevicebackup2 encrypted Backup -> ~/.rebreak-supervise/backups/<UDID>/
|
|
# 2. cfgutil prepare --supervised -> wiped iPhone, Supervised-Flag setzen
|
|
# 3. idevicebackup2 restore -> Daten zurück, Supervised-Flag persistiert
|
|
# 4. cfgutil install-profile -> ReBreak-Schutz-Profil installieren
|
|
#
|
|
# Voraussetzungen (siehe README.md):
|
|
# - macOS
|
|
# - Apple Configurator 2 (App Store) + cfgutil im PATH
|
|
# - libimobiledevice (brew install libimobiledevice)
|
|
# - Supervision-Identity einmalig generiert (siehe SUPERVISION-IDENTITY-SETUP.md)
|
|
# - iPhone via USB-C verbunden, Find-My DEAKTIVIERT, Code entsperrt
|
|
# - Vertrauenshandshake (Trust-Dialog auf iPhone) bestätigt
|
|
#
|
|
# CLI:
|
|
# rebreak-supervise.sh [--dry-run] [--state-dir DIR] [--profile PATH] [--resume]
|
|
#
|
|
# Status: PROTOTYPE. Einige Steps (cfgutil-Aufruf, Verify-Pfad) sind erst auf
|
|
# physischem Test-iPhone final verifiziert. Markierungen "VERIFY ON DEVICE" im
|
|
# Code zeigen wo Hardware-in-Loop noch nachgehärtet werden muss.
|
|
|
|
set -euo pipefail
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Konfiguration + Defaults
|
|
# ------------------------------------------------------------------------------
|
|
|
|
STATE_DIR="${REBREAK_STATE_DIR:-$HOME/.rebreak-supervise}"
|
|
PROFILE_PATH_DEFAULT="$(cd "$(dirname "$0")/.." && pwd)/profiles/rebreak-iphone-protection.mobileconfig"
|
|
PROFILE_PATH=""
|
|
DRY_RUN=0
|
|
RESUME=0
|
|
|
|
CFGUTIL="/Applications/Apple Configurator.app/Contents/MacOS/cfgutil"
|
|
SUPERVISION_IDENTITY_P12="${SUPERVISION_IDENTITY_P12:-$STATE_DIR/supervision-identity.p12}"
|
|
SUPERVISION_IDENTITY_PASS_FILE="${SUPERVISION_IDENTITY_PASS_FILE:-$STATE_DIR/supervision-identity.pass}"
|
|
|
|
# Farben (nur wenn TTY)
|
|
if [[ -t 1 ]]; then
|
|
C_RESET="\033[0m"; C_RED="\033[1;31m"; C_GREEN="\033[1;32m"
|
|
C_YELLOW="\033[1;33m"; C_BLUE="\033[1;34m"; C_DIM="\033[2m"
|
|
else
|
|
C_RESET=""; C_RED=""; C_GREEN=""; C_YELLOW=""; C_BLUE=""; C_DIM=""
|
|
fi
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Helpers: Logging
|
|
# ------------------------------------------------------------------------------
|
|
|
|
LOG_FILE=""
|
|
|
|
log() { printf "%b\n" "$1" | tee -a "${LOG_FILE:-/dev/null}"; }
|
|
ok() { log "${C_GREEN}✓${C_RESET} $1"; }
|
|
warn() { log "${C_YELLOW}⚠${C_RESET} $1"; }
|
|
err() { log "${C_RED}✗${C_RESET} $1" >&2; }
|
|
info() { log "${C_BLUE}→${C_RESET} $1"; }
|
|
dim() { log "${C_DIM} $1${C_RESET}"; }
|
|
|
|
die() { err "$1"; exit "${2:-1}"; }
|
|
|
|
confirm() {
|
|
local prompt="$1"
|
|
[[ "$DRY_RUN" == 1 ]] && { dim "[dry-run] auto-yes: $prompt"; return 0; }
|
|
read -r -p "$(printf "%b" "${C_YELLOW}?${C_RESET} $prompt [y/N] ")" reply
|
|
[[ "$reply" =~ ^[yY]$ ]]
|
|
}
|
|
|
|
run() {
|
|
# Wraps a command: in dry-run, just echo. Otherwise execute.
|
|
if [[ "$DRY_RUN" == 1 ]]; then
|
|
dim "[dry-run] $*"
|
|
return 0
|
|
fi
|
|
"$@"
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# State-Management: simples JSON-ähnliches Format pro UDID
|
|
#
|
|
# Datei: $STATE_DIR/state-<UDID>.env (key=value, source-bar)
|
|
# Keys: STEP_PREFLIGHT_AT, STEP_BACKUP_AT, BACKUP_PATH,
|
|
# STEP_SUPERVISE_AT, STEP_RESTORE_AT, STEP_PROFILE_AT
|
|
# ------------------------------------------------------------------------------
|
|
|
|
state_file_for() { printf "%s/state-%s.env" "$STATE_DIR" "$1"; }
|
|
|
|
state_load() {
|
|
local f; f="$(state_file_for "$1")"
|
|
if [[ -f "$f" ]]; then
|
|
# shellcheck disable=SC1090
|
|
source "$f"
|
|
fi
|
|
}
|
|
|
|
state_set() {
|
|
# state_set UDID KEY VALUE
|
|
local f; f="$(state_file_for "$1")"
|
|
local key="$2"; local val="$3"
|
|
# In-Memory-Update
|
|
eval "$key=\"\$val\""
|
|
# Persist (re-write each time, ist klein)
|
|
local tmpf="${f}.tmp"
|
|
{
|
|
for k in STEP_PREFLIGHT_AT STEP_BACKUP_AT BACKUP_PATH STEP_SUPERVISE_AT STEP_RESTORE_AT STEP_PROFILE_AT BACKUP_PASSWORD_FILE; do
|
|
local v="${!k:-}"
|
|
[[ -n "$v" ]] && printf "%s=%q\n" "$k" "$v"
|
|
done
|
|
} > "$tmpf"
|
|
mv "$tmpf" "$f"
|
|
chmod 600 "$f"
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Step 0: Argumente parsen
|
|
# ------------------------------------------------------------------------------
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
Usage: $(basename "$0") [options]
|
|
|
|
Options:
|
|
--dry-run Befehle nur loggen, nicht ausführen
|
|
--state-dir DIR State-Verzeichnis (default: \$HOME/.rebreak-supervise)
|
|
--profile PATH Pfad zur .mobileconfig (default: ../profiles/rebreak-iphone-protection.mobileconfig)
|
|
--resume Bei vorhandenem State: bereits-erledigte Steps überspringen
|
|
-h, --help Diese Hilfe
|
|
|
|
Environment:
|
|
REBREAK_STATE_DIR Wie --state-dir
|
|
SUPERVISION_IDENTITY_P12 Pfad zur Supervision-Identity (default: STATE_DIR/supervision-identity.p12)
|
|
EOF
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--dry-run) DRY_RUN=1; shift ;;
|
|
--state-dir) STATE_DIR="$2"; shift 2 ;;
|
|
--profile) PROFILE_PATH="$2"; shift 2 ;;
|
|
--resume) RESUME=1; shift ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) err "Unbekannte Option: $1"; usage; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
PROFILE_PATH="${PROFILE_PATH:-$PROFILE_PATH_DEFAULT}"
|
|
|
|
mkdir -p "$STATE_DIR"
|
|
chmod 700 "$STATE_DIR"
|
|
|
|
LOG_FILE="$STATE_DIR/log-$(date +%Y%m%d-%H%M%S).txt"
|
|
touch "$LOG_FILE"
|
|
|
|
log ""
|
|
log "${C_BLUE}═══════════════════════════════════════════════════${C_RESET}"
|
|
log "${C_BLUE} ReBreak iPhone Supervise Bootstrap${C_RESET}"
|
|
log "${C_BLUE}═══════════════════════════════════════════════════${C_RESET}"
|
|
log "State-Dir: $STATE_DIR"
|
|
log "Profile: $PROFILE_PATH"
|
|
log "Log: $LOG_FILE"
|
|
[[ "$DRY_RUN" == 1 ]] && warn "DRY-RUN-Modus aktiv — keine Aktion wird durchgeführt"
|
|
log ""
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Step 1: PREFLIGHT — Dependencies, Identity, Profil, Device-Detection
|
|
# ------------------------------------------------------------------------------
|
|
|
|
info "[1/5] Preflight"
|
|
|
|
# Tools
|
|
for bin in idevice_id ideviceinfo idevicebackup2; do
|
|
command -v "$bin" >/dev/null 2>&1 \
|
|
|| die "Fehlt: $bin → brew install libimobiledevice"
|
|
done
|
|
ok "libimobiledevice tools im PATH"
|
|
|
|
[[ -x "$CFGUTIL" ]] || die "cfgutil nicht gefunden: $CFGUTIL → Apple Configurator 2 aus App Store installieren"
|
|
ok "cfgutil gefunden"
|
|
|
|
# Supervision-Identity
|
|
if [[ ! -f "$SUPERVISION_IDENTITY_P12" ]]; then
|
|
die "Supervision-Identity fehlt: $SUPERVISION_IDENTITY_P12
|
|
→ siehe SUPERVISION-IDENTITY-SETUP.md (einmaliger Setup-Step)"
|
|
fi
|
|
ok "Supervision-Identity vorhanden"
|
|
|
|
# Profil
|
|
[[ -f "$PROFILE_PATH" ]] || die "Profil nicht gefunden: $PROFILE_PATH"
|
|
if ! plutil -lint "$PROFILE_PATH" >/dev/null 2>&1; then
|
|
die "Profil ist kein gültiges Plist: $PROFILE_PATH"
|
|
fi
|
|
ok "Profil ist gültig"
|
|
|
|
# Connected device(s)
|
|
UDIDS="$(idevice_id -l 2>/dev/null || true)"
|
|
if [[ -z "$UDIDS" ]]; then
|
|
die "Kein iPhone via USB erkannt. Stelle sicher:
|
|
- USB-C-Kabel ist eingesteckt
|
|
- iPhone ist entsperrt
|
|
- 'Diesem Computer vertrauen?' wurde auf dem iPhone bestätigt"
|
|
fi
|
|
|
|
# Bei mehreren: erste, ggf. später interaktiv erweitern
|
|
UDID="$(echo "$UDIDS" | head -n1)"
|
|
log ""
|
|
info "Gerät: $UDID"
|
|
|
|
DEVICE_NAME="$(ideviceinfo -u "$UDID" -k DeviceName 2>/dev/null || echo "?")"
|
|
IOS_VERSION="$(ideviceinfo -u "$UDID" -k ProductVersion 2>/dev/null || echo "?")"
|
|
ACTIVATION="$(ideviceinfo -u "$UDID" -k ActivationState 2>/dev/null || echo "?")"
|
|
log "Name: $DEVICE_NAME"
|
|
log "iOS: $IOS_VERSION"
|
|
log "Activation: $ACTIVATION"
|
|
|
|
# Activation-Check
|
|
if [[ "$ACTIVATION" != "Activated" ]]; then
|
|
warn "ActivationState ist '$ACTIVATION' — Backup könnte scheitern"
|
|
fi
|
|
|
|
# State laden falls --resume
|
|
state_load "$UDID"
|
|
if [[ "$RESUME" == 1 ]]; then
|
|
[[ -n "${STEP_BACKUP_AT:-}" ]] && ok "[resume] Backup bereits erledigt: $STEP_BACKUP_AT"
|
|
[[ -n "${STEP_SUPERVISE_AT:-}" ]] && ok "[resume] Supervise bereits erledigt: $STEP_SUPERVISE_AT"
|
|
[[ -n "${STEP_RESTORE_AT:-}" ]] && ok "[resume] Restore bereits erledigt: $STEP_RESTORE_AT"
|
|
[[ -n "${STEP_PROFILE_AT:-}" ]] && ok "[resume] Profil bereits installiert: $STEP_PROFILE_AT"
|
|
fi
|
|
|
|
state_set "$UDID" STEP_PREFLIGHT_AT "$(date -Iseconds)"
|
|
log ""
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Step 2: BACKUP (encrypted)
|
|
# ------------------------------------------------------------------------------
|
|
|
|
if [[ "$RESUME" == 1 && -n "${STEP_BACKUP_AT:-}" ]]; then
|
|
info "[2/5] Backup übersprungen (resume)"
|
|
else
|
|
info "[2/5] Backup (idevicebackup2, encrypted)"
|
|
|
|
BACKUP_ROOT="$STATE_DIR/backups/$UDID"
|
|
mkdir -p "$BACKUP_ROOT"
|
|
|
|
# Encryption-Passwort: generieren wenn nicht vorhanden, persistieren
|
|
BACKUP_PASSWORD_FILE="${BACKUP_PASSWORD_FILE:-$STATE_DIR/backup-pass-$UDID.txt}"
|
|
if [[ ! -f "$BACKUP_PASSWORD_FILE" ]]; then
|
|
if [[ "$DRY_RUN" != 1 ]]; then
|
|
openssl rand -base64 24 > "$BACKUP_PASSWORD_FILE"
|
|
chmod 600 "$BACKUP_PASSWORD_FILE"
|
|
ok "Backup-Passwort generiert: $BACKUP_PASSWORD_FILE"
|
|
warn "WICHTIG: dieses Passwort wird zum Restore gebraucht. Sicher aufheben."
|
|
else
|
|
dim "[dry-run] würde openssl rand -base64 24 > $BACKUP_PASSWORD_FILE"
|
|
fi
|
|
fi
|
|
BACKUP_PASSWORD="$([[ -f "$BACKUP_PASSWORD_FILE" ]] && cat "$BACKUP_PASSWORD_FILE" || echo "DRYRUN")"
|
|
|
|
warn "Backup kann je nach iPhone-Größe 15-60 Minuten dauern."
|
|
warn "Diese Session NICHT abbrechen, USB-Kabel NICHT abziehen."
|
|
confirm "Backup jetzt starten?" || die "Abgebrochen vom User" 0
|
|
|
|
# Encryption aktivieren falls noch nicht
|
|
# idevicebackup2 will die Backup-Encryption-Konfiguration auf dem Gerät selbst setzen
|
|
# VERIFY ON DEVICE: ob 'encryption on' idempotent ist oder vorher 'encryption off' nötig
|
|
run idevicebackup2 -u "$UDID" -i backup encryption on "$BACKUP_PASSWORD" "$BACKUP_ROOT" \
|
|
|| warn "Encryption-Setup hat returned non-zero — möglicherweise bereits aktiv, fahre fort"
|
|
|
|
# Backup
|
|
run idevicebackup2 -u "$UDID" -i backup "$BACKUP_ROOT"
|
|
|
|
state_set "$UDID" BACKUP_PATH "$BACKUP_ROOT"
|
|
state_set "$UDID" BACKUP_PASSWORD_FILE "$BACKUP_PASSWORD_FILE"
|
|
state_set "$UDID" STEP_BACKUP_AT "$(date -Iseconds)"
|
|
ok "Backup fertig: $BACKUP_ROOT"
|
|
fi
|
|
log ""
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Step 3: SUPERVISE — WIPED das Gerät, setzt Supervised-Flag
|
|
# ------------------------------------------------------------------------------
|
|
|
|
if [[ "$RESUME" == 1 && -n "${STEP_SUPERVISE_AT:-}" ]]; then
|
|
info "[3/5] Supervise übersprungen (resume)"
|
|
else
|
|
info "[3/5] Supervise (cfgutil prepare)"
|
|
|
|
warn "DIESER SCHRITT WIPED DAS GERÄT."
|
|
warn "Backup MUSS in Step 2 erfolgreich gewesen sein."
|
|
warn "Find-My ist deaktiviert? Apple-ID-Passwort eingegeben? Falls nein: JETZT abbrechen."
|
|
confirm "Wirklich fortfahren mit Wipe+Supervise?" || die "Abgebrochen vom User" 0
|
|
|
|
# cfgutil-Aufruf — VERIFY ON DEVICE: exakte Syntax + ECID vs UDID
|
|
# Apple-Doku-Stand: cfgutil unterstützt --ecid; UDID-Filter via --ecid in 0x-Hex
|
|
# Für unsupervised+activated devices ist der einfachste Weg: alle connected devices
|
|
# (es sollte nur eins angeschlossen sein zu diesem Zeitpunkt)
|
|
run "$CFGUTIL" \
|
|
--foreach \
|
|
prepare \
|
|
--supervised \
|
|
--organization-name "ReBreak" \
|
|
--supervisor-host-certs "$SUPERVISION_IDENTITY_P12"
|
|
|
|
state_set "$UDID" STEP_SUPERVISE_AT "$(date -Iseconds)"
|
|
ok "Supervise-Aufruf abgesetzt. Gerät reboots gerade — warte auf Wieder-Verbindung."
|
|
fi
|
|
log ""
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Step 4: WAIT FOR RECONNECT + RESTORE
|
|
# ------------------------------------------------------------------------------
|
|
|
|
if [[ "$RESUME" == 1 && -n "${STEP_RESTORE_AT:-}" ]]; then
|
|
info "[4/5] Restore übersprungen (resume)"
|
|
else
|
|
info "[4/5] Restore (warte auf Re-Verbindung, dann idevicebackup2 restore)"
|
|
|
|
if [[ "$DRY_RUN" != 1 ]]; then
|
|
log "Warte auf iPhone-Reconnect (max 5 min)..."
|
|
for i in $(seq 1 60); do
|
|
if idevice_id -l 2>/dev/null | grep -q "$UDID"; then
|
|
ok "iPhone ist wieder verbunden"
|
|
break
|
|
fi
|
|
sleep 5
|
|
[[ $i -eq 60 ]] && die "Timeout: iPhone nicht innerhalb 5 min wieder verbunden"
|
|
done
|
|
|
|
# iOS-Setup-Assistant muss der User auf dem iPhone bis zum Punkt "Vom Backup wiederherstellen?"
|
|
# bringen — oder wir restoren direkt via libimobiledevice (was wir hier tun)
|
|
# VERIFY ON DEVICE: ob restore direkt nach Wipe geht (vor Setup-Assistant) oder
|
|
# erst nach Setup-Assistant + initial-activation
|
|
warn "iPhone zeigt jetzt 'Hallo'/Setup-Assistant."
|
|
warn "Folge der Anleitung in README.md → 'Post-Supervise iPhone-Setup'."
|
|
warn "Sobald iPhone die 'Mit Computer verbinden'-Stage erreicht: hier weiter."
|
|
confirm "iPhone ist im Recovery-Setup-Stadium und bereit für Restore?" \
|
|
|| die "Abgebrochen vom User" 0
|
|
fi
|
|
|
|
BACKUP_PASSWORD="$(cat "$BACKUP_PASSWORD_FILE")"
|
|
|
|
# VERIFY ON DEVICE: '-i restore' nimmt $BACKUP_ROOT als positional arg
|
|
# Encryption-Passwort wird via stdin oder Env erwartet — idevicebackup2 v1.3+ unterstützt --password
|
|
run idevicebackup2 -u "$UDID" -i restore \
|
|
--system --reboot \
|
|
--password "$BACKUP_PASSWORD" \
|
|
"$BACKUP_PATH"
|
|
|
|
state_set "$UDID" STEP_RESTORE_AT "$(date -Iseconds)"
|
|
ok "Restore abgesetzt. iPhone reboots."
|
|
fi
|
|
log ""
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Step 5: INSTALL PROFIL + VERIFY
|
|
# ------------------------------------------------------------------------------
|
|
|
|
info "[5/5] Profil installieren + Verify"
|
|
|
|
if [[ "$DRY_RUN" != 1 ]]; then
|
|
log "Warte auf iPhone-Reconnect post-restore (max 10 min)..."
|
|
for i in $(seq 1 120); do
|
|
if idevice_id -l 2>/dev/null | grep -q "$UDID"; then
|
|
ok "iPhone ist wieder verbunden"
|
|
break
|
|
fi
|
|
sleep 5
|
|
[[ $i -eq 120 ]] && die "Timeout: iPhone nicht innerhalb 10 min wieder verbunden"
|
|
done
|
|
|
|
warn "User muss iOS jetzt entsperren + Setup-Assistant abschließen falls noch nicht."
|
|
confirm "iPhone ist entsperrt und im Home-Screen?" || die "Abgebrochen vom User" 0
|
|
fi
|
|
|
|
# Profil installieren via cfgutil
|
|
# VERIFY ON DEVICE: 'cfgutil install-profile <path>' Syntax
|
|
run "$CFGUTIL" --foreach install-profile "$PROFILE_PATH"
|
|
|
|
state_set "$UDID" STEP_PROFILE_AT "$(date -Iseconds)"
|
|
|
|
# Verify supervised
|
|
if [[ "$DRY_RUN" != 1 ]]; then
|
|
IS_SUPERVISED="$("$CFGUTIL" --foreach get isSupervised 2>/dev/null || echo "?")"
|
|
log "Supervised-State (cfgutil get isSupervised): $IS_SUPERVISED"
|
|
fi
|
|
|
|
log ""
|
|
log "${C_GREEN}═══════════════════════════════════════════════════${C_RESET}"
|
|
log "${C_GREEN} Bootstrap fertig${C_RESET}"
|
|
log "${C_GREEN}═══════════════════════════════════════════════════${C_RESET}"
|
|
log ""
|
|
log "Manueller Verify auf dem iPhone:"
|
|
log " 1. Settings → Allgemein → Info → 'Dieses iPhone wird beaufsichtigt' sichtbar?"
|
|
log " 2. Long-Press auf Rebreak-Icon → kein 'App löschen' mehr?"
|
|
log " 3. Settings → VPN → Rebreak → Toggle disabled?"
|
|
log " 4. Settings → Allgemein → VPN, DNS und Gerätemanagement → Profil 'Nicht entfernbar'?"
|
|
log ""
|
|
log "Backend-Enrollment (separater Step):"
|
|
log " → in der Rebreak-App: Profil-Tab → 'Schutz aktivieren' → QR scannen"
|
|
log ""
|
|
log "Logs: $LOG_FILE"
|
|
log "State: $(state_file_for "$UDID")"
|
|
log "Backup: $BACKUP_PATH"
|
|
log "Backup-Pass: $BACKUP_PASSWORD_FILE ${C_YELLOW}(sicher aufheben!)${C_RESET}"
|