b9747ee66d
F (plugin-host, 17): - exec-путь плагина (/data/apps/<id>/, код exec-поддерево / данные noexec); IPC-передача Install→App-Host (LoadApp/UnloadApp); teardown с явным StopApp+отзыв грантов; атомарный install/update (staging+rename, a-base §3); ре-валидация манифеста на каждом activate + capability-pin; sideload-гигиена распаковки; бюджет ресурсов (cgroup+OOM); аудит операций; namespace manifest-id ru.shturman.* vs OWN-экспорт ru.shturman.plugin.<id>.*; WASM↔identity-конфликт; PluginManager в ipc; dev-tools/тест-харнесс (#13); фазы (SDK/dev v0, экосистема v3) J (cameras, 20): - Stage 0 ранний путь = DRM/KMS из initramfs (не Wayland) + DRM-master handoff smithay без чёрного кадра; реверс-сигнал owner=Power/GPIO (CAN-gear требует сигнал в E); camera_in capability + per-node видео-гейтинг (/dev/videoN only first-party); fail-safe frame-watchdog → no_signal (не stale DMABUF); dashcam privacy (152-ФЗ, fscrypt, retention, factory-reset, индикатор); lifecycle (не пишет в sleep, ShutdownImminent-флаш сегментами, grace-hold via B); surround/video ресурсы (VPU/DMABUF/a-base §8); dev-симулятор камер (#13); Camera в ipc Кросс-док: ipc (AppHost.LoadApp/UnloadApp, PluginManager, Camera), security-privacy (namespace-фикс ru.shturman.*, camera_in, audit-scope), a-base §3 (dashcam в at-rest fscrypt) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
199 lines
20 KiB
Markdown
199 lines
20 KiB
Markdown
# Контракт: безопасность и приватность
|
||
|
||
> Модель изоляции, разрешений и приватности (152-ФЗ). Здесь **решаются открытые
|
||
> вопросы**, направленные из [ipc.md](ipc.md): гейтинг data-plane, экспорт объектов
|
||
> из песочницы, приватное хранилище, целостность манифеста.
|
||
|
||
Статус: **v2 (на ревью).** v2 — после ретро-ревью (10 находок).
|
||
Связано с: [architecture.md](../architecture.md) (§5,§7) · [ipc.md](ipc.md) · [plugin-sdk.md](plugin-sdk.md) · [principles.md](../principles.md) (#1,#2,#7) · [a-base-system](../domains/a-base-system.md) · домены C, F
|
||
|
||
---
|
||
|
||
## 1. Слои защиты (defense-in-depth)
|
||
|
||
| Слой | Что гарантирует |
|
||
|------|-----------------|
|
||
| **Архитектурный** | write-пути к CAN нет в системе → красные линии держатся даже при полном компромиссе (принципы #1, #2) |
|
||
| **Песочница** | bubblewrap (апы/плагины) + systemd-hardening (ядро) — ограничивает syscalls/FS/сеть |
|
||
| **Брокер/портал** | доступ к ресурсам только через capability-гейтинг (D-Bus-прокси + сокеты) |
|
||
| **Грант пользователя** | сторонние capabilities подтверждает пользователь |
|
||
|
||
> Ключевой инвариант: **сбежавший из песочницы плагин всё равно не запишет в CAN** —
|
||
> писать некуда. Песочница защищает данные и приватность, не красные линии.
|
||
|
||
## 1a. Модель угроз (кого и чем закрываем)
|
||
|
||
| Адверсарий | Чем адресуем | Предел |
|
||
|------------|--------------|--------|
|
||
| Злонамеренный сторонний плагин | песочница + capability-гейтинг + рисковые комбинации (§7) | данные вне его storage не гарантированы без at-rest-шифрования |
|
||
| Сбежавший/скомпрометированный плагин | архитектурный слой (нет write-пути к CAN на нативном silent-CAN) | приватность чужих данных НЕ защищена |
|
||
| Багнутый/скомпрометированный first-party (авто-грант) | **не ограничивается** capability-контролем; доверие — подписанный образ (v4) | до v4 — trust-by-build |
|
||
| Сетевой MITM к онлайн-LLM | TLS + RU-провайдеры/152-ФЗ | что уходит в промпт — §7 |
|
||
| Supply-chain / подмена образа·манифеста | secure boot + подписанный OTA (v4) + install-id-check (§5) | задел; до v4 частично |
|
||
| Изъятый носитель (eMMC/SD) | at-rest fscrypt + OTP ([a-base](../domains/a-base-system.md) §3, v4) | до v4 — открыто |
|
||
|
||
**Вне scope:** украденное целиком самораз-блокирующееся устройство (headless-unlock из eFuse); баги в самих ядро-сервисах (Vehicle-Data/брокер); недоверенное железо/BSP/DBC; атаки на стороне RU-LLM-провайдера.
|
||
|
||
## 2. Песочница
|
||
|
||
- **Апы/плагины — bubblewrap:** mount/pid/net/user namespaces, **seccomp**-фильтр
|
||
syscalls, минимальный FS-вид, `no-new-privileges`. Плагин видит только: свой код,
|
||
разрешённые сокеты, своё приватное хранилище.
|
||
- **Ядро — systemd-hardening:** `ProtectSystem`, `RestrictAddressFamilies`,
|
||
`SystemCallFilter`, и т.п. на доверенных юнитах.
|
||
- WASM-тир для лёгких логических плагинов — задел (см. architecture §7).
|
||
|
||
## 3. Модель capabilities
|
||
|
||
Манифест декларирует, что апу/плагину нужно. Каждая capability открывает ровно один
|
||
ресурс и гейтится на своём канале:
|
||
|
||
| capability | что открывает | канал / механизм гейтинга |
|
||
|------------|---------------|----------------------------|
|
||
| `vehicle_read:[signals]` | чтение указанных сигналов | D-Bus-прокси: `VehicleData`, фильтр по списку |
|
||
| `assistant_intents:[…]` | регистрация интентов | D-Bus: `Assistant.RegisterIntents` + OWN `IntentHandler` |
|
||
| `ui_tiles` / `ui_screens` | вклад в UI | D-Bus: `Shell.Register*` (+ Wayland-сокет, если поверхность) |
|
||
| `audio_out` | воспроизведение | **PipeWire Security Context** + WirePlumber-политика: видны только playback-ноды (не capture, не monitor чужих выходов) |
|
||
| `audio_in` (микрофон) | запись с микрофона — **высокочувствительно** | **per-node** capture-разрешение, выдаётся в момент **runtime-гранта** (не голый сокет!); **видимый индикатор**; сторонним — ограниченно. Нужен PipeWire с Security Context (~0.3.65+) |
|
||
| `network` | доступ в сеть | net-namespace + **runtime-грант**. ⚠️ Строго — только бинарно on/off; **host-allowlist** требует forced-proxy/egress-firewall по SNI/Host (DNS-allowlist обходится прямым IP), иначе best-effort (§7) |
|
||
| `location` | GPS/положение | D-Bus `ru.shturman.Location` (ipc §3) + **runtime-грант** |
|
||
| `camera_in:[sources]` (камера) | захват видео с источников — **высокочувствительно** | **per-node** PipeWire Security Context + WirePlumber (video-ноды, default-deny), **runtime-грант + видимый индикатор**; `/dev/videoN` — только first-party; стороннему — узкий per-source грант |
|
||
| `gpu` (render) | GPU-ускорение (UI/ML) | bubblewrap пропускает `/dev/dri` |
|
||
| `storage` | приватное хранилище | bubblewrap **mount** `/data/apps/<id>/` |
|
||
|
||
> Чего нет в манифесте — физически недоступно: нет сокета, нет имени на шине, нет mount.
|
||
|
||
## 4. Гейтинг по каналам (закрывает routed-вопросы из ipc)
|
||
|
||
- **D-Bus** — фильтрующий per-app прокси из манифеста (детали — [ipc.md](ipc.md) §5).
|
||
- **PipeWire (аудио)** — **проброс сокета сам по себе НЕ гейтинг** (голый сокет открывает
|
||
все ноды сессии: любой capture-источник и monitor любого выхода). Гейтинг — **per-node**:
|
||
PipeWire Security Context (sandbox-id на соединении) + WirePlumber-политика (по умолчанию
|
||
только playback; capture/микрофон — по явному per-node гранту в момент `audio_in`).
|
||
Индикатор и runtime-грант (§7) enforce-ятся на слое PipeWire/WirePlumber, не на сокете.
|
||
→ *закрывает «гейтинг data-plane».*
|
||
- **Wayland (графика/UI)** — wayland-сокет пропускается только при `ui_*` с поверхностью.
|
||
→ *закрывает «гейтинг data-plane».*
|
||
- **Экспорт объектов из песочницы** — прокси разрешает плагину **OWN** имена строго
|
||
под префиксом `ru.shturman.plugin.<id>.*`, чтобы Assistant/Shell могли звать его
|
||
`IntentHandler`/UI-объекты. → *закрывает «экспорт объектов».*
|
||
- **Приватное хранилище** — `storage` монтирует в песочницу только `/data/apps/<id>/`;
|
||
чужие данные и системные пути не видны. → *закрывает «приватное хранилище».*
|
||
|
||
## 5. Жизненный цикл разрешения
|
||
|
||
```
|
||
declare (манифест) → install: пользователь видит запрошенные capabilities и
|
||
подтверждает → enforce (прокси + sandbox конфигурятся под манифест) →
|
||
runtime-грант для чувствительных (network / audio_in / location) — отдельный
|
||
промпт в момент использования
|
||
```
|
||
|
||
**Кто что enforce-ит (две роли):**
|
||
- **Статически (в пути вызова):** per-app D-Bus-прокси + bubblewrap-сокеты/mounts,
|
||
**настраивает App-Host** из манифеста — физически блокирует незаявленное (ipc §5).
|
||
- **Рантайм-гранты** (чувствительные caps): **Perm-Broker** (`RequestRuntimeGrant`, ipc §3) —
|
||
владеет политикой и согласием, не статическим путём данных. *(В architecture §7
|
||
«единственная дверь» = единый грантодатель/портал, не «брокер в каждом вызове».)*
|
||
|
||
**Жизненный цикл гранта:** привязан к активной сессии; **авто-снимается на
|
||
StopApp/AppCrashed** (Perm-Broker — источник истины; App-Host закрывает ресурс —
|
||
PipeWire-capture/location). Для `audio_in`/`location` — **while-in-use**: отзыв/ре-промпт
|
||
при уходе в фон. Персист («всегда») — только по явному выбору; иначе TTL на простое.
|
||
|
||
- **First-party** апы — авто-грант. *Якорь:* они **часть read-only base-образа**.
|
||
⚠️ **До v4 криптоподписи образа НЕТ** (secure boot + подписанный OTA — v4, [a-base](../domains/a-base-system.md) §4):
|
||
в v0–v3 якорь = trust-by-build / физический контроль (dev-ключи, eFuse не жжём).
|
||
- **Сторонние** — в `/data`, **всегда** в песочнице; install-ревью + runtime-промпты.
|
||
**Целостность id при установке:** App-Host/Perm-Broker **отвергает** плагин, чей `id`
|
||
совпадает с уже установленным или встроенным/first-party; manifest-id namespace **`ru.shturman.*` (весь tree)** зарезервирован за first-party/образом —
|
||
сторонний id = свой reverse-DNS (`dev.example.*`). *(D-Bus OWN-экспорт плагина — отдельный
|
||
платформенный префикс `ru.shturman.plugin.<id>.*` (§4), это не manifest-id, не путать.)* ⚠️ Без подписи (§6) это ловит только случайные коллизии, НЕ
|
||
намеренную подмену ещё-не-установленного легитимного id — аргумент за подпись **раньше** «маховика».
|
||
|
||
## 6. Доверие и дистрибуция (целостность манифеста)
|
||
|
||
🟡 **Выбор на подтверждение:**
|
||
- **(рек., сейчас)** install-time approve + песочница: пользователь видит
|
||
capabilities и соглашается; защита — sandbox. Просто, достаточно для раннего этапа.
|
||
- **(задел)** **подпись манифеста** + keyring издателей — против подмены capabilities
|
||
при сторонней дистрибуции. → домен F (экосистема/«магазин»).
|
||
- **(опц.)** курируемый стор с ревью.
|
||
|
||
Рекомендую: сейчас — approve+sandbox; подпись/стор — когда включаем community-маховик.
|
||
→ *закрывает «целостность манифеста» (с явным заделом).*
|
||
|
||
## 7. Приватность / 152-ФЗ
|
||
|
||
**Принцип:** local-first; наружу — минимум и осознанно.
|
||
|
||
| Данные | Где обрабатываются | Уходит в облако? |
|
||
|--------|--------------------|------------------|
|
||
| Голос (аудио) | локально (STT/TTS) | **никогда** |
|
||
| Текст запроса | локально → онлайн-LLM при необходимости | только текст, только в онлайн-режиме |
|
||
| Данные машины (OBD/DTC) | локально | **в промпт онлайн-LLM** — только онлайн, по запросу **И при явном (отзываемом) согласии** на весь исходящий промпт; офлайн — не уходит |
|
||
| Память о водителе (`.md`) | локально | нет (без явного согласия) |
|
||
| Телеметрия | локально | **opt-in**, по умолчанию выключена |
|
||
|
||
- Онлайн-LLM — только **RU-провайдеры** (GigaChat/YandexGPT), данные в РФ (152-ФЗ).
|
||
- **Прозрачность:** в онлайн-режиме контекст машины уходит провайдеру в составе
|
||
промпта — это явно сообщается; офлайн-фолбэк держит всё на устройстве.
|
||
- **Согласие, не только уведомление:** при первом онлайн-использовании — явный
|
||
отзываемый consent-гейт на ВЕСЬ исходящий промпт (данные машины + подмешанное: VIN,
|
||
локация, память водителя). По умолчанию **VIN и точную локацию из промпта исключаем**,
|
||
включаем только по отдельному гранту.
|
||
- **152-ФЗ — роли и основание:** в BYO-схеме (креды пользователя) определить оператора/
|
||
обработчика ПДн и правовое основание передачи (→ legal-трек). **Локализация ≠ покрытие
|
||
152-ФЗ:** «данные в РФ» (где хранятся) ≠ основание на обработку/передачу (согласие).
|
||
|
||
**Микрофон:** wake-word слушает **в состояниях `running`/`accessory`** (гейт Power — **не**
|
||
безусловно always-on; в `sleep`/`off` выключен, риск разряда АКБ, принцип #5; домен B §7 / D §8),
|
||
всё **локально**; в обработку идёт только после слова-активатора; включённый микрофон — **видимый индикатор**.
|
||
|
||
**Охват гарантий и рисковые комбинации.** Таблица выше описывает поведение
|
||
first-party (ассистент). Сторонний плагин с `network` **+** чувствительными данными
|
||
(`vehicle_read`/`location`) теоретически может слить их куда угодно — это **рисковая
|
||
комбинация**: требует заметного предупреждения при установке. **Host-allowlist —
|
||
best-effort** (от случайной утечки, не от злонамеренного: обходится прямым IP без
|
||
forced-proxy, §3); строгая гарантия — только бинарный `network` on/off. Системная
|
||
приватность держится на **capability-контроле**, а не только на политике ассистента.
|
||
|
||
---
|
||
|
||
## Открытые вопросы (найдено на self-review → роутинг)
|
||
|
||
- ◻️ **Отзыв и управление грантами.** Пользователь должен видеть и **отзывать**
|
||
выданные capabilities по каждому апу. → Settings/Shell + домен C.
|
||
- 🟡 **Цепочка доверия к base-образу.** Secure boot (якорь) — в [hardware.md](hardware.md) §3;
|
||
осталась вторая половина — **подписанные OTA**. → домен A.
|
||
- 🟡 **Host-scoping сети.** Host-allowlist — **best-effort** (DNS-allowlist обходится по IP);
|
||
строго — только forced-proxy/egress-firewall по SNI/Host, иначе бинарный on/off. → §3/§7 + plugin-sdk §3.
|
||
- ✅ **Локальный audit-log — нормативный** (не опция): пишет домен A рядом с логированием
|
||
([a-base](../domains/a-base-system.md) §9), в fscrypt-поддерево `/data` (§3 там), durable
|
||
через fsync. Факты доступа к чувствительному (audio_in/сеть/location) + cloud-egress
|
||
(провайдер, время, capability — без payload). Просмотр/отзыв — **UI домена C** (не сервис `ru.shturman.Settings`).
|
||
- 🟡 **Шифрование данных at-rest.** Изъятая eMMC/SD = открытая утечка ПДн водителя
|
||
(память `.md`, токены, app-storage); sandbox это не закрывает (модель §1–§6 — про
|
||
работающую систему, не про физическое изъятие). → решено направлением в
|
||
[a-base-system.md](../domains/a-base-system.md) §3 (fscrypt чувствительных поддеревьев,
|
||
привязка к OTP/eFuse, v4 вместе с secure boot).
|
||
|
||
---
|
||
|
||
## Журнал решений (security-privacy)
|
||
|
||
| Решение | Выбор | Дата |
|
||
|---------|-------|------|
|
||
| Изоляция | bubblewrap (апы) + systemd-hardening (ядро); WASM — задел | 2026-06-16 |
|
||
| Гейтинг ресурсов | per-capability на своём канале (D-Bus / PipeWire / Wayland / FS) | 2026-06-16 |
|
||
| Экспорт из песочницы | OWN под префиксом `ru.shturman.plugin.<id>.*` | 2026-06-16 |
|
||
| Хранилище | `storage` → mount `/data/apps/<id>/` | 2026-06-16 |
|
||
| Доверие к манифесту | install-approve + sandbox сейчас; подпись/стор — задел (🟡) | 2026-06-16 |
|
||
| Приватность | local-first; в облако только текст; OBD в промпт лишь онлайн + по запросу **+ явное согласие**; телеметрия opt-in | 2026-06-16 |
|
||
| Enforce-split | статически — прокси+sandbox (App-Host); рантайм-гранты — Perm-Broker; грант while-in-use, авто-снятие | 2026-06-16 |
|
||
| Аудио | per-node гейтинг (PipeWire Security Context + WirePlumber), не голый сокет; capture по гранту | 2026-06-16 |
|
||
| Сеть | строго — бинарно on/off; host-allowlist best-effort без forced-proxy | 2026-06-16 |
|
||
| Целостность id | install-time отказ при коллизии id; manifest-id `ru.shturman.*` зарезервирован за first-party (OWN-экспорт `ru.shturman.plugin.<id>.*` — отдельный платформенный префикс) | 2026-06-16 |
|
||
| `camera_in` | per-node гейтинг видео (PipeWire Security Context, как audio_in); `/dev/videoN` — only first-party | 2026-06-16 |
|
||
| Audit-log | нормативный, владелец — домен A (§9, fscrypt-поддерево); просмотр — UI домена C | 2026-06-16 |
|