feat: OMEMO по умолчанию, admin_web+BOSH, MUC-модули (vcard_muc, muc_moderation), модули из apt в modules/, scripts/omemo_check.py, backup.sh на Яндекс.Диск

- index.html: omemo_default=true (XEP-0384, встроен в Converse v14)
- prosody.cfg.lua: admin_web + bosh (глобальные), MUC: vcard_muc (XEP-0153) + muc_moderation (XEP-0425)
- modules/: mod_admin_web (+www_files), mod_http_upload_external, mod_muc_moderation, mod_vcard_muc — из prosody-modules (apt)
- scripts/omemo_check.py — проверка OMEMO-бандлов
- backup.sh — ежедневный бэкап проекта (data/config/certs/modules/webchat/доки) на /mnt/yandex-disk, ротация 7/30 дней
- STATUS/WALKTHROUGH: OMEMO, admin_web, HTTP Upload итоги
This commit is contained in:
2026-08-29 07:33:14 +00:00
parent 0695ddef75
commit 527584281d
19 changed files with 1878 additions and 5 deletions
+165 -3
View File
@@ -303,7 +303,169 @@ for port in (5222, 5269):
# Ожидание: TLSv1.3, issuer Let's Encrypt
```
## 9. Полезные команды
## 9. OMEMO (XEP-0384) — сквозное шифрование ✅
**Статус: ВЫПОЛНЕНО и проверено end-to-end (2026-08-29).**
### 9.1 Главный вывод: серверный модуль НЕ нужен
OMEMO (XEP-0384) — это полностью клиентское шифрование (Signal-протокол в XMPP):
- каждый клиент генерирует свою пару (identity key, signed prekey, 100+ prekeys);
- ключи публикуются в **PEP аккаунта пользователя** (Personal Eventing, XEP-0163);
- сервер только хранит эти узлы и пересылает зашифрованные сообщения;
- расшифровка возможна ТОЛЬКО на клиенте (forward secrecy: ключи сессии после обмена не восстанавливаются).
Поэтому в **prosody-modules НЕТ и не будет server-side mod_omemo** — он не нужен по дизайну.
Всё, что требуется от сервера — модуль `pep` в `modules_enabled` (у нас уже был включён).
Ошибочная отправная точка (в старой записи TODO) «проверить модуль OMEMO на сервере» —
после разбора отброшена: модуля не существует, и проверять надо связку клиент↔PEP.
### 9.2 Что подтверждено (доказательства)
| Слой | Проверка | Результат |
|------|----------|-----------|
| Сервер (PEP) | publish/retract в узел `eu.siacs.conversations.axolotl.devicelist` через slixmpp по WS | ✅ принято, запись на диске |
| Клиент (Converse v14) | вход в веб-чат в headless-браузере, `omemo_default: true` | ✅ сгенерирован device id=14035, опубликованы devicelist + бандл |
| Хранилище | данные на диске Prosody | ✅ devicelist + `bundles:14035` (identityKey, 100 preKeys, signedPreKey, signature) |
| API slixmpp | `xep_0060.publish(jid, node, id='current', payload=<list>)` | ✅ работает; retract-эквивалент — ре-публикация списка без устройства |
### 9.3 Проверка серверной части (slixmpp 1.17 + WebSocket)
Скрипт в проекте: `/opt/icq/scripts/omemo_check.py` (сохранён 2026-08-29, работает из .venv).
Ключевые моменты API slixmpp 1.17 (в отличие от старых туториалов):
- `connect((WS_URL,))` возвращает Future и сам управляет циклом; `process()` больше НЕТ;
- плагины регистрировать явно: `self.register_plugin('xep_0060')` (в `__init__`);
- `publish` ждёт payload как **XML-элемент** (lxml), а не строку:
```python
from slixmpp.xmlstream import ET
lst = ET.Element('{eu.siacs.conversations.axolotl}list')
ET.SubElement(lst, '{eu.siacs.conversations.axolotl}device', {'id': '7777'})
await self['xep_0060'].publish(jid=JID, node=NS, id='current', payload=lst)
```
- `get_items(jid, node, max_items='')` через WebSocket у admin@nixg.ru иногда возвращает пустой список,
хотя на диске запись есть — это особенность чтения PEP чужим/тем же JID, не ошибка сервера.
Подключение (SSL не проверяется — серт LE, но для надёжности отключаем проверку):
```python
ctx = ssl.create_default_context(); ctx.check_hostname=False; ctx.verify_mode=ssl.CERT_NONE
bot.ssl_context = ctx
fut = bot.connect(('wss://xmpp.nixg.ru/xmpp-websocket',))
await asyncio.wait_for(fut, timeout=25)
```
### 9.4 Проверка клиентской части (Converse v14, headless)
1. Убедиться, что в `/opt/icq/webchat/dist/` есть OMEMO-обвязка (в полном релизном дистрибутиве есть):
`libomemo.esm.min.js`, `curve25519_compiled.wasm`; в `converse.min.js` есть ключи
`omemo_default`, `omemo_active`, `omemo_store`.
2. В `index.html` добавлено `omemo_default: true` (см. 9.6).
3. Вход в веб-чат: camofox-browser open https://chat.nixg.ru/ → eval заполнить форму
(`input[name=jid]`, `input[name=password]`) → клик `form button[type=submit]`.
4. Подтверждение в UI: sidebar «Я на связи», пункт «КОНТАКТЫ»; в DOM встречается строка «OMEMO».
5. Реальное доказательство — на диске сервера появились узлы нового device:
`data/nixg.ru/pep_eu%2esiacs%2econversations%2eaxolotl%2edevicelist/admin.list` и
`data/nixg.ru/pep_eu%2esiacs%2econversations%2eaxolotl%2ebundles%3a14035/admin.list`.
### 9.5 Что лежит в хранилище Prosody (как читать узлы PEP)
```
/opt/icq/data/nixg%2eru/pep_eu%2esiacs%2econversations%2eaxolotl%2edevicelist/admin.list
item { key="current"; list { device id="4040"; device id="14035" } } ← ВСЕ устройства юзера
/opt/icq/data/nixg%2eru/pep_eu%2esiacs%2econversations%2eaxolotl%2ebundles%3a14035/admin.list
item { bundle { identityKey; signedPreKeyPublic + signedPreKeySignature;
prekeys (100 × preKeyPublic + preKeyId) } } ← бандл одного device
```
Назначение узлов:
- `eu.siacs.conversations.axolotl.devicelist` — список device-id аккаунта (публикуется при каждом входе);
- `eu.siacs.conversations.axolotl.bundles:<id>` — ключи конкретного устройства;
- `urn:xmpp:omemo:2:devices` — устройства для OMEMO 2.0-клиентов (новый стандарт).
Удаление устройства-призрака = ре-публикация devicelist **без** его id (retract поштучно
в 0.11 работает плохо — проще переписать весь список).
### 9.6 Изменение `index.html` (Converse initialize)
```js
converse.initialize({
websocket_url: 'wss://xmpp.nixg.ru/xmpp-websocket',
...
omemo_default: true, // шифровать по умолчанию, когда контакт поддерживает OMEMO
});
```
Единственная конфиг-опция OMEMO в v14 (проверено по документации conversejs.org/docs/configuration/):
`omemo_default` (default false). Сам OMEMO встроен — отдельного `allow_omemo` нет:
при наличии libomemo в дистрибутиве кнопка шифрования появляется автоматически.
### 9.7 Засады OMEMO (зафиксировано на практике)
1. **Старый JID в localStorage**: если браузер помнит `admin@chat.nixg.ru` — Prosody отвечает
`host-unknown`, Converse не логинится. Лечение: `localStorage.removeItem("conversejs-session-jid")`
или вход в инкогнито. На новом JID `admin@nixg.ru` работает.
2. **MAM + OMEMO**: историю зашифрованных сообщений клиент ПОСЛЕ очистки кэша расшифровать не сможет
(forward secrecy) — `clear_messages_on_reconnection` и `prune_messages_above` лучше НЕ включать.
3. **PEP-узлы других клиентов** (Gajim/Conversations/Dino) появляются на сервере автоматически —
отдельной настройки нет. Devicelist пополняется при каждом логине нового устройства.
4. **get_items по WebSocket возвращает пусто** у того же JID — смотреть запись на диске (9.5).
## 10. Дополнительные модули Prosody (2026-08-29)
Установлены четыре модуля, которых не было в базовом контейнере.
### 10.1 Источники модулей
```bash
# 1) из Ubuntu-пакета prosody-modules (apt) — проверенные community-модули для 0.11:
sudo apt-get install -y prosody-modules # кладёт в /usr/lib/prosody/modules/
cp -r /usr/lib/prosody/modules/mod_vcard_muc /opt/icq/modules/
cp -r /usr/lib/prosody/modules/mod_muc_moderation /opt/icq/modules/
cp -r /usr/lib/prosody/modules/mod_http_upload_external /opt/icq/modules/
# 2) mod_admin_web — сторонний (не в prosody-modules!), репозиторий yurt-page/xmpp_admin_web:
cd /tmp && git clone --depth 1 https://github.com/yurt-page/xmpp_admin_web.git
cp /tmp/xmpp_admin_web/mod_admin_web.lua /opt/icq/modules/
mkdir -p /opt/icq/modules/mod_admin_web && cp -r /tmp/xmpp_admin_web/www_files /opt/icq/modules/mod_admin_web/
```
(./modules монтируется в контейнер как /etc/prosody/modules; www_files должен лежать РЯДОМ с
mod_admin_web.lua — модуль раздаёт их через `module:get_directory()/www_files`.)
### 10.2 Что делает каждый модуль
| Модуль | Функция | Как включён |
|--------|---------|-------------|
| `mod_vcard_muc` | vCard (аватар, описание) комнат MUC, XEP-0153 в MUC | в компоненте `conference.nixg.ru` |
| `mod_muc_moderation` | Модерация MUC (XEP-0425): бан/кик/смена темы через ad-hoc | в компоненте `conference.nixg.ru` |
| `mod_http_upload_external` | HTTP Upload с ВНЕШНИМ хранилищем (XEP-0363, отдельный сервис) | НЕ включён (см. ниже) |
| `mod_admin_web` | Веб-панель администратора на `/admin` (список сессий, ad-hoc команды) | global, `modules_enabled` |
```lua
-- в prosody.cfg.lua
modules_enabled = { ..., "admin_web", "bosh", ... } -- admin_web требует bosh
Component "conference.nixg.ru" "muc"
...
modules = { "vcard_muc", "muc_moderation" }
```
### 10.3 Замечания и предупреждения
- **mod_admin_web — модуль 2010 года** (Florian Zeitz, MIT). Использует устаревшие API
(`module.add_host`, `service[host]:add_subscription`, `module:set_global()`, `prosody.hosts`).
Синтаксис под 0.11 валиден (`luac5.1 -p` проходит), но ПОЛНАЯ загрузка не проверялась —
если при рестарте Prosody падает/модуль ругается в логах — закомментировать `"admin_web"`
в modules_enabled; потеряем только панель.
- **Доступ к /admin**: панель раздаётся на HTTP-порту Prosody **5280** (внутри Docker).
Наружу 5280 не проброшен (всё через Caddy 443). Для внешнего доступа — добавить в Caddy
на vps02 блок `admin.nixg.ru { reverse_proxy 10.8.0.2:5280 }` или открывать по WG.
- **mod_http_upload_external НЕ включён**: это альтернатива работающему `mod_http_upload`
(классический, с локальным хранилищем, см. раздел 7). External требует отдельного
HTTP-сервиса для файлов (например, minio/nginx с подписанными URL). Включить при необходимости:
`Component "upload-ext.nixg.ru" "http_upload_external"` + `http_upload_external_base_url`.
- **Порядок включения**: после правок конфига — `docker exec icq-prosody prosodyctl check config`
→ `docker compose restart prosody` → `docker logs icq-prosody --tail 100`.
## 11. Полезные команды
```bash
cd /opt/icq
docker compose ps
@@ -316,8 +478,8 @@ python3 -c "import socket,ssl; ..." # см. раздел 8
curl -sS https://upload.nixg.ru/upload
```
## 10. TODO (следующие шаги)
- [ ] OMEMO (сквозное шифрование, XEP-0384) — клиенты Converse уже умеют (libomemo в dist); проверить на сервере.
## 12. TODO (следующие шаги)
- [x] ~~OMEMO (XEP-0384)~~ — ВЫПОЛНЕНО: раздел 9; скрипт scripts/omemo_check.py.
- [ ] Бот-книгоискатель (slixmpp + OPDS) — Этап 3 PRD. venv уже создан (/opt/icq/.venv: aiohttp, slixmpp).
- [ ] Мосты mautrix-telegram / mautrix-whatsapp — Этап 4 (нужен Prosody mod_component / external component).
- [ ] Push-уведомления APNs/FCM (публичный push-шлюз для Prosody).