← К списку документов
Главная / Документация / Диагностика типовых проблем

Диагностика типовых проблем

Enrollment, политики, kiosk, lost-mode, Remote Assist, MQTT storm, HTTP errors. Пошаговая диагностика от симптома к решению.

Документ для IT-администратора. Пошаговая диагностика от симптома к решению. Каждый раздел — независимый сценарий, искать через TOC.

Содержание

  1. Устройство не enrolled
  2. Политика не применяется на устройство
  3. Kiosk-режим ломается после перезагрузки
  4. Lost mode не работает
  5. Remote Assist не подключается
  6. MQTT disconnect storm
  7. Dashboard 500 / tile 403 / CSP report overflow
  8. Где смотреть логи

1. Устройство не enrolled

1.1. Сценарии

QR-сканирование не переходит в установку DPC. - Устройство запускает встроенный браузер вместо DPC-процесса. - На экране appears Ошибка конфигурации DPC.

DPC устанавливается, но в /devices не появляется. - DPC активен, видно в шторке уведомлений, но в admin UI устройство не видно.

Устройство было enrolled, но пропало из списка онлайн.

1.2. Диагностика

Шаг 1 — определить, какой тип provisioning используется

Тип Как запускается Признак
QR Factory reset → 6 тапов по welcome com.android.managedprovisioning перехватывает QR
DPC Identifier afw#komendant в первом логине Google Требует Play Services
Zero-touch (ZT) Устройство пред-зарегистрировано у вендора Автоматически при первом старте
NFC bump Прикоснуться NFC-тегом Редко, для крупных развёртываний

Если клиент не использует Google Play (ТСД на AOSP), только QR работает.

Шаг 2 — проверить содержимое QR

Расшифруйте QR-код любым сканером. Должен содержать JSON:

{
  "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME":
    "ru.komendant.dpc/.receivers.AdminReceiver",
  "android.app.extra.PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM":
    "<base64-sha256>",
  "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION":
    "https://mdm.example.ru/downloads/dpc.apk",
  "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE": {
    "server_url": "https://api.mdm.example.ru",
    "enroll_token": "<jwt>"
  }
}

Частые проблемы QR:

Проблема Проверка Решение
checksum не совпадает с APK apksigner verify --print-certs dpc.apk и base64(sha256) первого cert Перегенерировать QR в /enroll/qr после rebuild APK
URL http:// вместо https:// смотрите QR-JSON enrollment требует TLS, исправить в /settings/enroll
enroll_token истёк JWT exp < now QR валиден только 24 часа, перегенерировать
Чужой tenant enroll_token.tenant_id очистить кэш, генерировать из корректного tenant

Шаг 3 — DPC установился, но не подключается к MQTT

На устройстве включить USB-debug, получить логи:

adb logcat -s KomendantDPC MQTT

Ищем строки:

Лог Диагноз
MqttException: Unable to find valid certification path MQTT-broker отдаёт non-trusted cert. DPC pinned на конкретный CA. Проверить, что mqtt.mdm.example.ru использует letsencrypt или указанный в сборке DPC CA
Connection refused на 8883 firewall блокирует исходящий 8883 со стороны клиента (корп-WiFi?). Проверить nc -vz mqtt.mdm.example.ru 8883
BadUserNameOrPassword DPC username-token истёк или подпись отвалилась. Перегенерировать enroll-token
ConnectionLost after CONNACK ACL EMQX отвергает подписку. Проверить /ops/emqx → ACL logs, обычно причина — mismatch client_id vs device_id в JWT

Шаг 4 — устройство подключено к MQTT, но нет в /devices

# на сервере
journalctl -u komendant-api --since "10 min ago" | grep -i enroll

Ищем строки enroll.register. Частые ошибки:

  • ErrTenantQuotaExceeded — превышен max_devices лицензии → увеличить лицензию
  • ErrFingerprintMismatch — устройство заявляет другой hardware-fingerprint чем при первом enroll → re-enroll через factory reset
  • ErrTenantMismatch — токен для tenant A, но broker routes в tenant B → проверить /settings/enroll/codes на корректность

1.3. Edge cases

  • Xiaomi/Realme/OPPO — у этих OEM агрессивное killing фоновых процессов. В onboarding-инструкции для пользователя: Настройки → Батарея → Без ограничений для DPC.
  • Huawei без GMS — DPC работает, но FCM-пуши не доступны; используется pure MQTT. Убедитесь, что MQTT-keepalive ≤ 60 сек.
  • Samsung Knox — для полного контроля нужен Knox-E-FOTA. В пилоте Триола — стандартный DPC через Android Enterprise.

2. Политика не применяется на устройство

2.1. Симптом

В admin UI /devices/<id> → Policy Applied показывает desired_version=5, applied_version=3 и висит так > 10 минут.

2.2. Диагностика

Шаг 1 — проверить, получило ли устройство policy push

/devices/<id>/timeline

Событие policy.push.sent должно быть не старше 2 минут. Если нет — MQTT disconnected, перейти в раздел 6.

Шаг 2 — проверить audit events

/audit?device_id=<id>&event=policy.apply.*
Event Значение
policy.apply.started устройство приняло команду
policy.apply.completed успех
policy.apply.failed ошибка, см. reason
отсутствует устройство не получило → MQTT

Шаг 3 — типовые причины policy.apply.failed

reason Диагноз Решение
DPM_SET_APPLICATION_RESTRICTIONS_failed приложение не установлено или заблокировано Play Protect в policy убрать ссылку на несуществующий package
permission_denied_kiosk DPC не device-owner, а только profile-owner factory reset + re-enroll как device-owner
ca_cert_install_failed сертификат в policy повреждён или уже установлен пересобрать сертификат через /settings/certificates
password_complexity_not_supported требования paroля выше чем поддерживает OS снизить password_quality или обновить OS
system_app_missing policy ссылается на package, которого нет на устройстве в target_devices отфильтровать по OS version

Шаг 4 — RLS / tenant mismatch на бэке

Если /audit пуст вообще — возможно, вы смотрите из-под super-admin без tenant_id:

-- внутри /admin/sql (super-admin only)
SELECT COUNT(*) FROM audit_events WHERE device_id = '<id>' AND tenant_id = '<tenant>';

Если 0 — device не принадлежит вашему tenant, это проблема enrollment; см. раздел 1.

Шаг 5 — DPM API fallback

Некоторые прошивки (MIUI 13, ColorOS 12) периодически сбрасывают policy после OTA. DPC при каждом старте делает DevicePolicyManager.isProfileOwnerApp() → если false, посылает событие dpc.degraded серверу, и сервер показывает устройство в статусе degraded.

Решение: инструктаж пользователя → factory reset и re-enroll.


3. Kiosk-режим ломается после перезагрузки

3.1. Симптом

Устройство в kiosk-policy с lock_task_enabled=true. После reboot — возвращается launcher OS, kiosk теряется до ручного открытия приложения.

3.2. Диагностика

Шаг 1 — проверить BootReceiver

DPC регистрирует BOOT_COMPLETED receiver. Проверить в logcat после перезагрузки:

adb logcat -s KomendantDPC | grep BootReceiver

Должно быть: BootReceiver.onReceive: restoring kiosk mode.

Если нет — receiver не вызван. Причины:

  • OEM battery optimization бъёт BOOT_COMPLETED до того, как app'у дают CPU. Решение: в policy добавить request_battery_optimization_exempt=true.
  • App hibernation (Android 12+) — ОС «усыпила» app за неделю без запуска. Решение: policy app_hibernation_exempt через DevicePolicyManager.setApplicationExemptions (требует Android 13+).

Шаг 2 — Foreground Service permission

DPC держит FGS (notification "Управление устройством активно"). Если FGS убита — kiosk тоже. Проверка:

adb shell dumpsys activity services | grep komendant

Должен быть active service. Если AnrScreen: fgs_timeout — приложение превысило лимит FGS без interaction.

Решение: в AndroidManifest.xml DPC используется foregroundServiceType="systemExempted" + REQUEST_COMPANION_RUN_IN_BACKGROUND permission, подписывается через policy fgs_always_on.

Шаг 3 — Persistent notification

/devices/<id>/policy → kiosk → show_persistent_notification=true. Без этого некоторые Android (13+) могут убить foreground service через 6 часов.


4. Lost mode не работает

4.1. Симптом

Нажимаем /devices/<id> → Lost Mode. Экран устройства не блокируется, координаты не приходят.

4.2. Диагностика

Шаг 1 — проверить delivery

/audit?device_id=<id>&event=lostmode.*

Ищем: - lostmode.command.sent — сервер послал команду - lostmode.overlay.shown — устройство приняло и наложило overlay - lostmode.location.report — первая локация

Если command.sent есть, а overlay.shown нет — устройство offline либо DPC degraded.

Шаг 2 — Lock Screen Overlay permission

Overlay использует TYPE_APPLICATION_OVERLAY. На Android 11+ требует runtime-permission SYSTEM_ALERT_WINDOW. У device-owner даётся автоматически, но на некоторых OEM (Xiaomi) нужно отдельно включить в Настройки → Приложения → DPC → Отображение поверх других.

Проверить:

adb shell appops get ru.komendant.dpc SYSTEM_ALERT_WINDOW

Должно быть allow. Если default — DPC policy auto_grant_overlay=true.

Шаг 3 — Location permission

Lost mode шлёт координаты раз в 60 сек. Для этого нужен ACCESS_BACKGROUND_LOCATION + FGS_TYPE_LOCATION (Android 14+).

adb shell dumpsys package ru.komendant.dpc | grep permission | grep granted=true

Должны быть: - android.permission.ACCESS_FINE_LOCATION: granted=true - android.permission.ACCESS_BACKGROUND_LOCATION: granted=true - android.permission.FOREGROUND_SERVICE_LOCATION: granted=true

Если нет — обновить DPC до версии с foregroundServiceType="location" в manifest.

Шаг 4 — Google Play Services

На AOSP без GMS location работает через FusedLocationProvider fallback на LocationManager (GPS+NETWORK). На Huawei без HMS — только GPS, точность ниже. Это ожидаемое поведение.


5. Remote Assist не подключается

5.1. Симптом

В /remote-assist → New Session → Device admin и устройство показывают Connecting..., сессия не устанавливается.

5.2. Диагностика

Шаг 1 — TURN-сервер

Remote Assist использует WebRTC с принудительным iceTransportPolicy: "relay" (всё через TURN). Проверить:

curl -X POST https://api.mdm.example.ru/api/v1/remote-assist/turn-credentials \
  -H "Authorization: Bearer <admin-token>"

Должен вернуть JSON с urls, username, credential. Далее проверка самого TURN:

# С клиентской машины
turnutils_uclient -v -u <username> -w <credential> turn.mdm.example.ru -p 3478

Должны быть allocation и refresh успешными.

Частые причины отказа:

Ошибка Решение
401 Unauthorized на TURN ephemeral-credentials истекли; сервер выдаёт с lifetime 60 мин, генерировать новые через API
Connection timeout на UDP 3478 корп-firewall режет UDP; включить TCP 3478 в coturn (listening-ip=...,relay-ip=... в turnserver.conf + no-udp-relay=false)
443 relay over TLS недоступен если сеть вообще без UDP, нужен TURN-TLS на 5349 (по умолчанию выключен), включить в /settings/remote-assist → TURN over TLS

Шаг 2 — RELAY-only ICE

Если на сети клиента жёсткий NAT и нет UDP — даже TURN не поможет без 443/TCP. Fallback:

/settings/remote-assist
  ice_servers:
    - turns:turn.mdm.example.ru:5349?transport=tcp

Шаг 3 — сигналинг не достиг

WebRTC offer/answer идут через WebSocket к API. Проверка:

/devices/<id>/timeline → ищем ra.session.invite.received

Если событие есть на сервере, но DPC не показал UI — значит WebSocket разорван. Проверить в DPC logs adb logcat -s KomendantDPC-WS:

WS connection lost, code=1006, reason=

1006 — сеть прервалась. DPC переподключается автоматически через exp-backoff (1с, 2, 4, 8, 16, 32, 60).

Шаг 4 — Permission SYSTEM_CAPTURE_VIDEO_OUTPUT

Для share-screen через MediaProjection устройство покажет диалог Разрешить Комендант DPC записывать экран?. Это одноразовое согласие пользователя, skipped быть не может (требование Android security). Пользователь должен нажать «Разрешить».


6. MQTT disconnect storm

6.1. Симптом

В /ops/grafana → Komendant Overview → MQTT видно пик emqx_mqtt_disconnects_total > 50/минута. Устройства флапают online ⇄ offline.

6.2. Диагностика

Шаг 1 — проверить EMQX logs

docker logs emqx --tail 500 | grep -E 'disconn|acl|auth'

Ищем паттерны:

Паттерн Диагноз
acl_deny: subscribe denied on topic устройства пытаются подписаться не на свой topic (bad JWT tenant_id)
keep_alive_timeout сетевые проблемы клиентов; проверить, не упал ли провайдер
connection_rejected: not_authorized истёк enroll-token; DPC должен сам запросить refresh через /api/v1/dpc/token/refresh
max_connections достигнут лимит EMQX max_conn=1000 на listener; увеличить в emqx.conf

Шаг 2 — ACL rules

Каждый device подписан только на devices/<device_id>/cmd/# и publish только в devices/<device_id>/ack/#. Проверить:

docker exec emqx emqx ctl acl list | grep <device_id>

Если нет записи → команда komendant-cli mqtt sync-acl --device <id>.

Шаг 3 — Token refresh loop

DPC раз в 6 часов делает refresh. Если API возвращает 401 — DPC повторяет → storm. Проверка:

journalctl -u komendant-api --since "1 hour ago" | grep -c 'token refresh failed'

Если > 50 → на сервере истёк JWT signing key rotation, нужно komendant-cli jwt rotate-key. После — все DPC переконнектятся автоматически.

Шаг 4 — Clean session

Если в policy случайно выставлен mqtt_clean_session=true — DPC при каждом reconnect теряет in-flight messages, что вызывает дубликаты команд и новые disconnects. Фикс: /settings/mqtt → clean_session=false (default).


7. Dashboard 500 / tile 403 / CSP report overflow

7.1. Dashboard 500

В admin UI /dashboard показывает 500 Internal Server Error.

Проверка:

journalctl -u komendant-api --since "5 min ago" | grep ERROR | tail -20

Частые причины:

ERROR Решение
pgx: connection refused PostgreSQL упал; systemctl status postgresql@17-main
redis: LOADING Redis is loading dataset in memory Redis после рестарта прогревает RDB; подождать 30-60 сек
context deadline exceeded: query running 30s медленный запрос; EXPLAIN ANALYZE и добавить индекс
panic: runtime error: invalid memory address баг; собрать goroutine-dump через curl http://127.0.0.1:6060/debug/pprof/goroutine?debug=2 и в поддержку

7.2. Tile 403

Отдельный дашборд-плиток возвращает 403. Причина — RLS: пользователь смотрит данные не из своего tenant.

Типовой случай: super-admin переключился в tenant A, но в URL остался tenant_id=B. Очистить cookie komendant_active_tenant и выбрать нужный в /admin/tenants.

7.3. CSP report overflow

В /ops/prometheus видно рост komendant_csp_reports_total > 1000/min.

Причина: браузер пользователей рапортует нарушения CSP (inline scripts, сторонние trackers). Это не критично, но забивает storage.

Решение:

  1. Проверить /admin/security/csp-reports — какие директивы флапают.
  2. Если это какой-то легитимный ресурс (например, Яндекс.Метрика в landing) — добавить в whitelist connect-src в nginx.
  3. Включить CSP rate-limit в nginx: limit_req zone=csp burst=5 nodelay на /csp-report.

8. Где смотреть логи

8.1. Серверные логи

Сервис Команда
API journalctl -u komendant-api -f
Admin UI journalctl -u komendant-admin -f
PostgreSQL journalctl -u postgresql@17-main -f
EMQX docker logs emqx -f
Redis journalctl -u redis-server -f
MinIO docker logs minio -f
nginx access tail -f /var/log/nginx/access.log
nginx error tail -f /var/log/nginx/error.log
Backup journalctl -u komendant-backup.service

8.2. Grafana + Loki

Если observability развернут: https://mdm.example.ru/ops/grafana (basic-auth из .env).

Dashboards: - Komendant Overview — устройства, policy-apply, MQTT, SLA - API SLO — p50/p95/p99 latency, error rate - Licensing — active devices vs limit - Backup — last success timestamp, S3 offsite

8.3. Audit log (для IT-админа tenant-а)

/admin/audit — фильтры по user, device, event type, время. Экспорт в CSV и JSON. Хранится 7 лет (152-ФЗ + наш SLA).

8.4. Export в SIEM

Если у клиента MaxPatrol/Kaspersky KUMA — настройте syslog-forwarder:

/settings/integrations/siem
  type: syslog
  host: siem.example.ru
  port: 514
  protocol: tcp+tls
  format: CEF | LEEF

Теги и поля — см. docs/security-log-tags.md.


Версия документа: 1.0 (2026-04-18). Для ошибок, не описанных здесь: support@komendant-mdm.ru, приложить journalctl --since "1 hour ago" -u komendant-api > api.log.