Перейти к содержимому

2026-03-24 — Аудит безопасности и укрепление IPC

24 марта было днём безопасности и надёжности. Четыре стабильных релиза в быстрой последовательности закрыли комплексный аудит безопасности, лимиты IPC-буферов, SSE event storms при воспроизведении и давнюю гонку маршрутизации PulseAudio sink. Runtime также получил крупнейшее разделение модулей со времён извлечения orchestrator.

Аудит безопасности — восемь исправлений в v2.47.0

Заголовок раздела «Аудит безопасности — восемь исправлений в v2.47.0»

Систематический аудит безопасности дал восемь исправлений, выпущенных одним пакетом:

  • Фиксация сессий — сессия теперь очищается перед установкой состояния аутентификации, предотвращая атаку с предустановкой session cookie и наследованием логина оператора.
  • Валидация MAC-адресовBluetoothManager.__init__ теперь проверяет формат MAC, блокируя инъекцию команд bluetoothctl через подделанные адреса устройств.
  • Защита от SSRF — proxy-запросы artwork отклоняются для URL, не принадлежащих MA, а параметры ha_url в вызовах HA Core API защищены от SSRF.
  • Предотвращение OOM — чтение artwork proxy ограничено 10 МБ для предотвращения крашей out-of-memory от вредоносных upstream-ответов.
  • Белый список параметров действий — endpoint-ы pause/play валидируют параметр action перед отправкой в IPC.
  • Валидация тегов обновлений — белый список символов для update tag refs предотвращает path traversal и инъекции через подделанные строки версий.
  • Флаг auth в диагностике — endpoint-ы диагностики теперь включают флаг auth_enabled и предупреждение при отключённой аутентификации, чтобы операторы и автоматические сканеры могли обнаружить незащищённые экземпляры.

Релиз безопасности также обновил основную аудио-зависимость с sendspin 5.3.2 до 5.7.1, принеся upstream-фиксы для сброса громкости при переподключении, pitch shift при смене формата и порядка server/hello. Новый PulseVolumeController реализует протокол sendspin VolumeController для прямого управления PulseAudio/PipeWire sink через pulsectl, а BridgeDaemon пропускает ручную синхронизацию sink, когда upstream-библиотека управляет громкостью нативно.

Три самых больших модуля проекта были разделены для снижения сложности:

  • bluetooth_manager.py (1 226 → 669 строк) — извлечены bt_audio.py (обнаружение sink), bt_monitor.py (polling и D-Bus циклы), bt_dbus.py (D-Bus хелперы)
  • routes/api_ma.py (2 343 → 150 строк) — извлечены routes/ma_auth.py, routes/ma_playback.py, routes/ma_groups.py
  • config.py (999 → 449 строк) — извлечены config_auth.py, config_migration.py, config_network.py

Все публичные API и ре-экспорты сохранены для обратной совместимости. Разделения сделали каждый модуль достаточно маленьким для самостоятельного осмысления.

BluetoothManager был отвязан от SendspinClient через новый Protocol bt_types.BluetoothManagerHost. Это разорвало циклическую зависимость, которая делала тестирование и извлечение модулей всё более болезненным.

Обнаружился краш, когда бинарные фреймы artwork превышали дефолтный 64 КБ readline-буфер asyncio: Separator is found, but chunk is longer than limit. Исправление увеличило буфер stdout подпроцесса до 1 МБ и ограничило artwork-фреймы до 48 КБ raw, чтобы уложиться в бюджет IPC-строки.

Во время воспроизведения с активными ролями visualizer и artwork SSE status-нотификации срабатывали многократно в секунду — достаточно быстро, чтобы закрывать модальные окна и popup-ы в веб-интерфейсе. Фреймы visualizer больше не вызывают status-нотификации вообще, а фреймы artwork уведомляют только при фактическом изменении содержимого изображения.

Когда подключается новое Bluetooth-устройство, module-rescue-streams PulseAudio может тихо перенести существующий поток на вновь появившийся sink. Это означало, что аудио колонки A могло внезапно начать играть через колонку B после переподключения колонки B.

Bridge теперь обнаруживает это в течение 3 секунд и возвращает потоки на правильные sink-и. Новый метод get_subprocess_pid() в протоколе BluetoothManagerHost обеспечивает безопасную идентификацию потоков по PID для коррекции.

Волна релизов также закрыла значительный список проблем конкурентности: гонка в отслеживании громкости (атомарный read-compare-update под единым lock), TOCTOU в build_device_snapshot (новый атомарный метод snapshot()), неатомарная замена списка в state.py (slice assignment вместо clear() + extend()), утечки задач при CancelledError в MA monitor и замена использования asyncio.run() в WSGI-потоках на ThreadPoolExecutor.

Шестьдесят девять новых тестов вышли в рамках исправлений безопасности и надёжности: тесты потокобезопасности для конкурентных операций status/config/notification, регрессионные тесты принудительной аутентификации, интеграционные тесты IPC-протокола, тесты путей ошибок для некорректного IPC и невалидных входных данных, а также два новых тестовых файла для PulseAudio volume controller и функций bridge daemon.

Аудит безопасности был давно назревшим. Bridge работает в домашних сетях с разной степенью изоляции, и несколько исправленных проблем — фиксация сессий, SSRF, инъекция MAC — могли быть эксплуатированы в неправильно настроенных сетях. Выпуск всех восьми исправлений в одном стабильном релизе сделал путь обновления чистым.

Исправления IPC и SSE закрыли две самые разрушительные runtime-проблемы, о которых сообщали операторы: краши при воспроизведении с обильным artwork и неработоспособность UI из-за постоянно закрывающихся модальных окон. Коррекция PulseAudio sink исправила проблему, которая вызывала путаницу в маршрутизации аудио с момента, когда проект впервые поддержал несколько одновременных колонок.

С укреплёнными уровнями безопасности и IPC проект перешёл к нативным транспортным командам Sendspin и Phase 2 системы null-sink standby, которая требовала надёжного фундамента IPC и маршрутизации sink.