75 lines
5.4 KiB
Markdown
75 lines
5.4 KiB
Markdown
# Прозрачность счётчика пользователей
|
||
|
||
Версия: **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.
|