mirror of
https://gitverse.ru/kpa39l/icq.git
synced 2026-09-29 09:55:11 +00:00
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:
+165
-3
@@ -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).
|
||||
|
||||
Reference in New Issue
Block a user