montana/Node/External-Audit/TRANSPARENCY.md

75 lines
5.4 KiB
Markdown
Raw Normal View History

2026-05-21 03:44:38 +03:00
# Прозрачность счётчика пользователей
Версия: **2026-05-19**
## Что мы доказываем
Число «Уникальных пользователей» на `montana.quest/vpn/`**не выдумано**.
## Что мы НЕ публикуем
IP-адреса пользователей. Это PII (персональные данные) — публиковать их незаконно (GDPR / 152-ФЗ) и аморально.
## Как доказательство работает
1. На узле Helsinki в `/var/lib/montana-vpn-stats/unique-ips.txt` накапливается список IP, когда-либо подключавшихся к VPN (из xray access.log). Файл root:root 600, не доступен публично.
2. Каждую минуту скрипт `/opt/montana-vpn-stats/transparency-sign.py`:
- читает список IP
- сортирует
- вычисляет `sha256(joined-with-LF)`**merkle_root**
- подписывает snapshot `{ts_utc, unique_users, merkle_root, algorithm}` ключом ed25519
- публикует:
- `https://montana.quest/vpn/transparency.json` — последний snapshot
- `https://montana.quest/vpn/transparency-log.txt` — append-only лог всех snapshot'ов
3. Публичный ключ подписи: `d9a8bf07871d35c8e85f7de4a9b62896c330ba0987732468515c7bda8bb4adde` (ed25519). Лежит здесь и в репозитории. Приватный ключ — только на узле, 600 root.
## Что может проверить аудитор без доступа к серверу
| Свойство | Как проверить |
|---|---|
| **Подпись валидна** | `verify-transparency.sh` проверяет ed25519 над snapshot |
| **Публичный ключ не сменили** | сравнить с публикуемым в этом репозитории |
| **Число не подкручивается вниз** | `transparency-log.txt` — append-only, аудитор скачивает последние N снимков, проверяет монотонность |
| **Snapshot timestamp реальный** | сравнить `ts_utc` с моментом скачивания (расхождение ≤ 60s) |
| **Один и тот же merkle root = одни и те же IPы** | повторный merkle root между snapshots = новых IP нет |
## Что аудитор НЕ может узнать
- Конкретные IP пользователей (они зашифрованы в merkle root).
- Страну/время подключения отдельного пользователя.
Чтобы доказать что конкретный IP в множестве — нужен сам IP (известен только владельцу) + полный список (raw `unique-ips.txt` доступен только под NDA на аудит, не публично).
## Запуск проверки
```bash
bash scripts/verify-transparency.sh
```
Скрипт:
1. Скачивает `https://montana.quest/vpn/transparency.json`.
2. Проверяет ed25519-подпись с известного `EXPECTED_PUBKEY`.
3. Скачивает `https://montana.quest/vpn/transparency-log.txt`.
4. Проверяет монотонность роста за последние 50 снимков.
5. Выводит зелёные галки и текущее число.
## Ограничения текущей реализации
1. **Anti-tamper только в пределах подписи.** Атакующий с доступом к privateKey может выдавать любое число. Защита: ключ 600 root + сервер изолирован.
2. **Нет on-chain anchor.** При желании можно anchored daily snapshot в Bitcoin через OpenTimestamps — это даст proof что snapshot существовал не позднее timestamp'а в чейне. Roadmap.
3. **IP = пользователь — приближение.** Один человек с двух IP считается как 2; несколько человек за одним NAT-IP считаются как 1. Это **верхняя оценка уникальных** для NAT и **нижняя** для multi-device. Точнее без trackable user-ID (что = приватность ↓).
4. **Уязвимость к ротации файла.** Если `unique-ips.txt` корраптится — счётчик откатывается. Защита: append-only лог сохраняет историю.
## Полный список секретов
- `/etc/montana/transparency-privkey` (Moscow, 600 root) — ed25519 приватный ключ подписи.
- Только этот хост может подписывать. При компрометации — генерация нового keypair, обновление публичного ключа в репо + публикация incident notice.
## Roadmap
- **P1:** Bitcoin OpenTimestamps anchoring каждый день в 00:00 UTC.
- **P2:** Migration на cryptographic accumulator (RSA / Merkle tree) для proof-of-membership без раскрытия списка.
- **P3:** Multi-party signing (2 of 3 узлов подписывают) — устраняет single point of trust на Moscow privateKey.