246 lines
7.0 KiB
Markdown
246 lines
7.0 KiB
Markdown
|
|
# 3-Mirror System
|
|||
|
|
|
|||
|
|
**Отказоустойчивая сеть Montana**
|
|||
|
|
**Montana Protocol v1.0**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Abstract
|
|||
|
|
|
|||
|
|
3-Mirror — распределённая система из 5 узлов с автоматическим failover. При падении любых 4 из 5 узлов сеть продолжает работать. Время восстановления < 10 секунд.
|
|||
|
|
|
|||
|
|
**Ключевая формула:**
|
|||
|
|
```
|
|||
|
|
4/5 узлов могут упасть = сеть жива
|
|||
|
|
Восстановление < 10 секунд
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. Архитектура
|
|||
|
|
|
|||
|
|
### 1.1 Цепочка узлов
|
|||
|
|
|
|||
|
|
**Исходный код:** [leader_election.py](../бот/leader_election.py)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
BOT_CHAIN = [
|
|||
|
|
("amsterdam", "72.56.102.240"), # PRIMARY
|
|||
|
|
("moscow", "176.124.208.93"), # MIRROR 1
|
|||
|
|
("almaty", "91.200.148.93"), # MIRROR 2
|
|||
|
|
("spb", "188.225.58.98"), # MIRROR 3
|
|||
|
|
("novosibirsk", "147.45.147.247"), # MIRROR 4
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 1.2 Роли узлов
|
|||
|
|
|
|||
|
|
| Роль | Узел | IP | Функция |
|
|||
|
|
|------|------|----|---------|
|
|||
|
|
| PRIMARY | Amsterdam | 72.56.102.240 | Активный бот |
|
|||
|
|
| MIRROR 1 | Moscow | 176.124.208.93 | Standby |
|
|||
|
|
| MIRROR 2 | Almaty | 91.200.148.93 | Standby |
|
|||
|
|
| MIRROR 3 | SPB | 188.225.58.98 | Standby |
|
|||
|
|
| MIRROR 4 | Novosibirsk | 147.45.147.247 | Standby |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. Leader Election
|
|||
|
|
|
|||
|
|
### 2.1 Детерминированный выбор лидера
|
|||
|
|
|
|||
|
|
**Исходный код:** [leader_election.py](../бот/leader_election.py)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def am_i_the_master(self) -> bool:
|
|||
|
|
"""
|
|||
|
|
Я мастер если ВСЕ узлы ДО меня в цепочке мертвы.
|
|||
|
|
"""
|
|||
|
|
for i, (name, ip) in enumerate(self.chain):
|
|||
|
|
if name == self.my_name:
|
|||
|
|
return True # Дошли до себя — я мастер
|
|||
|
|
if check_node_health(ip):
|
|||
|
|
return False # Кто-то выше жив
|
|||
|
|
return False
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2.2 Константы мониторинга
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
CHECK_INTERVAL = 5 # секунд между проверками
|
|||
|
|
PING_TIMEOUT = 2 # секунд таймаут пинга
|
|||
|
|
STARTUP_DELAY = 3 # секунд перед первой проверкой
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. Failover Protocol
|
|||
|
|
|
|||
|
|
### 3.1 Сценарий падения Amsterdam
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
1. Amsterdam падает
|
|||
|
|
2. Moscow проверяет каждые 5 сек: "Amsterdam жив?"
|
|||
|
|
3. Через 5 сек: Amsterdam мёртв → Moscow становится MASTER
|
|||
|
|
4. Almaty/SPB/Novosibirsk: Moscow жив → остаются STANDBY
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.2 Сценарий восстановления Amsterdam
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
1. Amsterdam поднимается
|
|||
|
|
2. Moscow проверяет: "Amsterdam жив?"
|
|||
|
|
3. Да → Moscow останавливает polling → STANDBY
|
|||
|
|
4. Amsterdam запускает polling → MASTER
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. Проверка здоровья узла
|
|||
|
|
|
|||
|
|
**Исходный код:** [leader_election.py](../бот/leader_election.py)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def check_node_health(ip: str) -> bool:
|
|||
|
|
"""Комплексная проверка здоровья узла"""
|
|||
|
|
# 1. ICMP ping
|
|||
|
|
if is_node_alive(ip):
|
|||
|
|
return True
|
|||
|
|
# 2. TCP port 22 (SSH)
|
|||
|
|
if is_node_alive_tcp(ip, 22):
|
|||
|
|
return True
|
|||
|
|
# 3. TCP port 443 (HTTPS)
|
|||
|
|
if is_node_alive_tcp(ip, 443):
|
|||
|
|
return True
|
|||
|
|
return False
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. Интеграция с ботом
|
|||
|
|
|
|||
|
|
**Исходный код:** [junomontanaagibot.py](../бот/junomontanaagibot.py)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
async def run_with_3mirror():
|
|||
|
|
leader = get_leader_election()
|
|||
|
|
|
|||
|
|
await leader.run_leader_loop(
|
|||
|
|
on_become_master=start_polling,
|
|||
|
|
on_become_standby=stop_polling
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. Деплой
|
|||
|
|
|
|||
|
|
### 6.1 Systemd service
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# /etc/systemd/system/junona.service
|
|||
|
|
Environment="MONTANA_NODE_NAME=amsterdam"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 6.2 Скрипт деплоя
|
|||
|
|
|
|||
|
|
**Исходный код:** [deploy_nodes.sh](../бот/deploy_nodes.sh)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
./deploy_nodes.sh
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. Breathing Sync — Git синхронизация
|
|||
|
|
|
|||
|
|
### 7.1 Механизм
|
|||
|
|
|
|||
|
|
**Исходный код:** [breathing_sync.py](../бот/breathing_sync.py)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Каждые 12 секунд узлы "дышат"
|
|||
|
|
SYNC_INTERVAL_SEC = 12
|
|||
|
|
|
|||
|
|
def breathe():
|
|||
|
|
inhale() # git pull — получаем изменения
|
|||
|
|
exhale() # git push — отправляем свои изменения
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 7.2 Метафора дыхания
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────────────────────┐
|
|||
|
|
│ BREATHING CYCLE │
|
|||
|
|
├─────────────────────────────────────────────────────────────┤
|
|||
|
|
│ 🫁 Inhale (вдох) │ git pull │ Получаем из сети │
|
|||
|
|
│ 💨 Exhale (выдох) │ git push │ Отдаём в сеть │
|
|||
|
|
│ 🔄 Цикл │ 12 сек │ ~5 "вдохов" в минуту │
|
|||
|
|
└─────────────────────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 7.3 Синхронизируемые файлы
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
SYNC_PATHS = [
|
|||
|
|
"data/users.json", # Пользователи
|
|||
|
|
"node_crypto/nodes.json" # Узлы
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Статус:** ✅ Реализовано
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. TLS Шифрование
|
|||
|
|
|
|||
|
|
### 8.1 Защищённая связь
|
|||
|
|
|
|||
|
|
**Исходный код:** [node_tls.py](../бот/node_tls.py)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# TLS 1.3 шифрование между узлами
|
|||
|
|
SECURE_PORT = 19333
|
|||
|
|
TLS_VERSION = TLSv1_3
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 8.2 Сертификаты
|
|||
|
|
|
|||
|
|
Каждый узел имеет self-signed сертификат с привязкой к криптографическому адресу:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
CN = amsterdam.efir.org
|
|||
|
|
UID = mta46b633d258059b90db46adffc6c5ca08f0e8d6c
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 8.3 Защита от атак
|
|||
|
|
|
|||
|
|
| Атака | Защита | Статус |
|
|||
|
|
|-------|--------|--------|
|
|||
|
|
| Man-in-the-middle | TLS 1.3 | ✅ |
|
|||
|
|
| Перехват трафика | Шифрование | ✅ |
|
|||
|
|
| Harvest now, decrypt later | TLS 1.3 + ML-DSA-65 | ✅ |
|
|||
|
|
|
|||
|
|
**Статус:** ✅ Реализовано
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9. Реализация
|
|||
|
|
|
|||
|
|
| Компонент | Файл | Статус |
|
|||
|
|
|-----------|------|--------|
|
|||
|
|
| Leader Election | [leader_election.py](../бот/leader_election.py) | ✅ Работает |
|
|||
|
|
| Проверка здоровья | [leader_election.py](../бот/leader_election.py) | ✅ Работает |
|
|||
|
|
| Breathing Sync | [breathing_sync.py](../бот/breathing_sync.py) | ✅ Работает |
|
|||
|
|
| TLS Шифрование | [node_tls.py](../бот/node_tls.py) | ✅ Работает |
|
|||
|
|
| Интеграция с ботом | [junomontanaagibot.py](../бот/junomontanaagibot.py) | ✅ Работает |
|
|||
|
|
| Деплой скрипт | [deploy_nodes.sh](../бот/deploy_nodes.sh) | ✅ Готов |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Alejandro Montana
|
|||
|
|
Montana Protocol v1.0
|
|||
|
|
Январь 2026
|
|||
|
|
```
|