simbiot-harness/docs/audit.md

4.9 KiB
Raw Blame History

Криптографический аудит харнеса (t-260930083716)

Запрос Alex (m-516173240): у харнеса свой приватный ключ; каждое событие стора и каждая команда подписаны. Цели: полная аудируемость, доказуемое авторство («сделал харнес»), защита стора и командной шины от инъекций.

Схема

  • Алгоритм: ed25519 (библиотека cryptography).
  • Ключи: прив. /root/simbiot-harness/.audit/ed25519.key (0600, создан атомарно tmp+rename), публ. рядом ed25519.pub (0644, отдавай внешним аудиторам). key_id = первые 16 hex sha256 от публ. ключа.
  • Активные ключи (созданы 2026-10-02):
    • pubkey (raw b64): P8puVpwg9UtKtEnYUKCCGz/NA4zXU1ubgkdurp2V+CY=
    • key_id: faf8d19d9aed71fe

Что подписывается

  1. События стора — es.append_event подписывает каждое событие при записи. Плайлоад: aggregate|aggregate_id|event_type|canonical_json(data)| timestamp|version|prev_sig|key_id (\x1f-сшит). В колонки события пишутся sig, key_id, prev_sig.
  2. Тампер-цепочка: prev_sig каждого события = подпись предыдущего — переписать середину стора, не сломав голову цепочки, нельзя.
  3. Команды CLI — cli.py audit-command '{"op":…}' → конверт {envelope, payload_b64, signature, key_id}; вход в командную шину верифицируется cli.py verify-command (выход 0/2).

Верификация

  • cli.py verify — полный аудит цепочки (выход 0/2): криптопроверка каждой подписанной строки + link-check prev_sig. Может выполнять любая внешняя сторона с публичным ключом.
  • Проекции (es.run_projections) проверяют хвост-дельту: событие с битой подписью или разрывом цепочки ⇒ RuntimeError — подделка не попадает в read model (проверено тестом).
  • cli.py audit-key — публичный ключ/отпечаток/пути.

Граница доверия: genesis и legacy

  • Все события до внедрения подписи (2026-10-02, 465 255 шт.) помечены ретро-маркером sig='genesis:<id>' (водяной знак projection_meta. sig_genesis_upto — раз и навсегда). Это честная граница: до неё авторство не доказывалось криптоключом.
  • Подписанная строка после границы, чей prev_sig указывает на дого- генезисное состояние, не бракуется по link-check (граница сбрасывает проверку сцепки), но её payload-подпись строго проверяется — контент послегенезисной строки тамперить нельзя.
  • Неподписанные послегенезисные строки (старый код демонов до их рестарта) = legacy: аудит их считает, ошибкой является только тампер подписанной строки. После рестарта сервисов (просьба к Alex в дайджесте дня) всё новое гарантированно подписано.

Инвариант защиты

Атака Чем ловится
Правка data/timestamp события подпись payload (verify)
Правка middle-строки всего стора подпись + цепочка (full audit)
Вставка фейкового события нет валидной подписи → legacy-статус, но не «валидный харнес»; в хвосте проекций — RuntimeError
Подмена команды конверт-подпись (verify-command)
Кража подписи плайлоад привязан к содержимому; ключ 0600

Файлы

  • harness/audit.py — ключи, каноника, sign/verify, командные конверты
  • harness/es.py — миграция (sig-колонки/genesis), подпись в append_event, хвост-верификация в run_projections
  • cli.py — verify, audit-command, verify-command, audit-key
  • Тест: tests/test_audit.py (17 проверок, hermetic: тампер/genesis/ legacy/конверты/CLI)