diff --git a/docs/11-backup.md b/docs/11-backup.md
index f514617..05991b5 100644
--- a/docs/11-backup.md
+++ b/docs/11-backup.md
@@ -28,7 +28,7 @@
| **Расписание** | ✅ | 2 раза в день (02:30 и 22:30) |
| **Retention** | ✅ | 14 последних, 7 ежедневных, 4 недельных, 4 месячных |
| **Режим** | ✅ | Snapshot (без остановки сервисов) |
-| **Уведомления** | ✅ | Telegram-бот через Python-реле (порт 8085) |
+| **Уведомления** | ✅ | Telegram-бот через прокси (XRay + Redsocks) |
### 📊 Архитектура
@@ -39,8 +39,7 @@ graph TD
A -->|Network| D["Proxmox Backup Server
Olimpbs 192.168.1.199"]
D --> E["Datastore: olimpbkp
1 TB LVM"]
E --> F["/var/lib/proxmox-backup/olimpbkp"]
- D -->|Python Relay 8085| G["Telegram API
api.telegram.org"]
- G -->|SOCKS5 1080| H["XRay Proxy
2.27.50.20:2054"]
+ D -->|HTTPS via proxy| G["Telegram API
api.telegram.org"]
```
---
@@ -279,19 +278,272 @@ proxmox-backup-client verify ct/201/2026-04-11T13:19:03Z \
---
-## 📦 Уведомления в Telegram (через Python-реле)
+## 📦 Уведомления в Telegram
### 🎯 Цель
-Получать красивые уведомления в Telegram о статусе бэкапов с форматированием времени и эмодзи.
+Получать уведомления в Telegram об успешных/неудачных бэкапах с Proxmox Backup Server, без изменений на гипервизоре.
+В системе настроено **два независимых канала** уведомлений в Telegram:
+
+| Тип | Источник | Сервер | Порт реле | Что уведомляет |
+|-----|----------|--------|-----------|----------------|
+| **PBS Backup Notifications** | Proxmox Backup Server | 192.168.1.199 | 8085 | ✅ Успешные/❌ неудачные бэкапы |
+| **Grafana Alerts** | VictoriaMetrics + Grafana | 192.168.1.208 | 8085 | ⚠️ Бэкапы старше 24 часов |
### 📋 Предварительные требования
-- Сервер с доступом в интернет через прокси (XRay SOCKS5)
+- PBS сервер (192.168.1.199) с доступом в интернет через прокси
- Telegram бот: токен и chat_id
-- Python 3 установлен
+- Прокси: VLESS+Reality на `2.27.50.20:2054`
+
+### 🔧 Настройка прокси (XRay + Redsocks + iptables)
+
+**1. Установка XRay клиента**
+```bash
+# Установка зависимостей
+apt update && apt install unzip curl iptables -y
+
+# Скачиваем XRay
+cd /tmp
+curl -L -o xray.zip https://github.com/XTLS/Xray-core/releases/latest/download/Xray-linux-64.zip
+unzip xray.zip
+chmod +x xray
+mv xray geoip.dat geosite.dat /usr/local/bin/
+
+# Проверка
+xray version
+```
+
+**2. Конфигурация XRay**
+```bash
+mkdir -p /usr/local/etc/xray
+
+cat > /usr/local/etc/xray/config.json << 'EOF'
+{
+ "log": {"loglevel": "warning"},
+ "inbounds": [{"port": 1080, "listen": "127.0.0.1", "protocol": "socks", "settings": {"auth": "noauth", "udp": true}}],
+ "outbounds": [
+ {
+ "protocol": "vless",
+ "settings": {
+ "vnext": [{
+ "address": "2.27.50.20",
+ "port": 2054,
+ "users": [{"id": "68f44a38-396d-48da-b832-79b5dc5716ab", "encryption": "none", "level": 8}]
+ }]
+ },
+ "streamSettings": {
+ "network": "xhttp",
+ "security": "reality",
+ "realitySettings": {
+ "serverName": "cloud.zailon.ru",
+ "publicKey": "TOyddQCTdSpycmO20uiLOqMABuKabpwVhw57tWmvJws",
+ "shortId": "174fc0",
+ "spiderX": "/"
+ },
+ "xhttpSettings": {
+ "host": "",
+ "path": "/remote.php/dav/upload",
+ "mode": "stream-one"
+ }
+ },
+ "tag": "proxy"
+ },
+ {"protocol": "freedom", "tag": "direct"}
+ ],
+ "routing": {
+ "rules": [
+ {"type": "field", "ip": ["geoip:private"], "outboundTag": "direct"},
+ {"type": "field", "domain": ["api.telegram.org"], "outboundTag": "proxy"}
+ ]
+ }
+}
+EOF
+```
+
+**3. Запуск XRay как сервис**
+```bash
+cat > /etc/systemd/system/xray-client.service << 'EOF'
+[Unit]
+Description=XRay Client
+After=network.target
+
+[Service]
+Type=simple
+ExecStart=/usr/local/bin/xray run -config /usr/local/etc/xray/config.json
+Restart=on-failure
+RestartSec=5
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+systemctl daemon-reload
+systemctl enable --now xray-client
+systemctl status xray-client
+```
+
+**4. Установка и настройка Redsocks**
+```bash
+# Установка
+apt install redsocks -y
+
+# Конфигурация
+cat > /etc/redsocks.conf << 'EOF'
+base {
+ log_debug = off;
+ log_info = off;
+ log = stderr;
+ daemon = on;
+ redirector = iptables;
+}
+
+redsocks {
+ bind = "0.0.0.0:12345";
+ relay = "127.0.0.1:1080";
+ type = socks5;
+ autoproxy = 0;
+ timeout = 10;
+}
+EOF
+
+# Запуск
+systemctl enable --now redsocks
+```
+
+**5. Настройка iptables для Telegram**
+```bash
+cat > /usr/local/bin/telegram-proxy.sh << 'EOF'
+#!/bin/bash
+iptables -t nat -F TELEGRAM_PROXY 2>/dev/null
+iptables -t nat -X TELEGRAM_PROXY 2>/dev/null
+iptables -t nat -N TELEGRAM_PROXY
+
+# Telegram IP ranges
+iptables -t nat -A TELEGRAM_PROXY -p tcp -d 149.154.160.0/20 -j REDIRECT --to-ports 12345
+iptables -t nat -A TELEGRAM_PROXY -p tcp -d 91.108.4.0/22 -j REDIRECT --to-ports 12345
+
+iptables -t nat -A OUTPUT -p tcp --dport 443 -j TELEGRAM_PROXY
+EOF
+
+chmod +x /usr/local/bin/telegram-proxy.sh
+/usr/local/bin/telegram-proxy.sh
+```
+
+**6. Проверка соединения**
+```bash
+curl -s "https://api.telegram.org/bot/getMe"
+```
+*Ожидаемый результат*: JSON с информацией о боте.
+
+### 🔧 Скрипт уведомлений
+
+**7. Создание скрипта**
+```bash
+cat > /usr/local/bin/pbs-backup-notify.sh << 'EOF'
+#!/bin/bash
+
+TOKEN="YOUR_BOT_TOKEN"
+CHAT="YOUR_CHAT_ID"
+ARCHIVE="/var/log/proxmox-backup/tasks/archive"
+STATE="/var/run/pbs-notify-lastline"
+
+# Получаем последнюю обработанную строку
+LAST_LINE=$(cat $STATE 2>/dev/null || echo 0)
+CURR_LINE=$(wc -l < $ARCHIVE)
+
+# Если файл уменьшился - начинаем сначала
+if [ "$CURR_LINE" -lt "$LAST_LINE" ]; then
+ LAST_LINE=0
+fi
+
+# Читаем новые строки
+if [ "$CURR_LINE" -gt "$LAST_LINE" ]; then
+ tail -n +$((LAST_LINE + 1)) $ARCHIVE | while IFS= read -r line; do
+ # Только backup задачи
+ if echo "$line" | grep -q ":backup:"; then
+ # Извлекаем имя бэкапа (конвертируем \x3a в :)
+ BACKUP=$(echo "$line" | grep -oP 'backup:\K[^:]+' | sed 's/\\x3a/:/g')
+ STATUS=$(echo "$line" | grep -oP 'OK|FAILED|ERROR')
+ TIME=$(date '+%Y-%m-%d %H:%M:%S')
+
+ if [ "$STATUS" = "OK" ]; then
+ curl -s -X POST "https://api.telegram.org/bot$TOKEN/sendMessage" \
+ -H "Content-Type: application/json" \
+ -d "{\"chat_id\":\"$CHAT\",\"text\":\"✅ *Backup Success*\\n📦 $BACKUP\\n⏰ $TIME\",\"parse_mode\":\"Markdown\"}" > /dev/null
+ else
+ curl -s -X POST "https://api.telegram.org/bot$TOKEN/sendMessage" \
+ -H "Content-Type: application/json" \
+ -d "{\"chat_id\":\"$CHAT\",\"text\":\"❌ *Backup Failed*\\n📦 $BACKUP\\n⏰ $TIME\\n📝 Status: $STATUS\",\"parse_mode\":\"Markdown\"}" > /dev/null
+ fi
+ fi
+ done
+fi
+
+# Сохраняем позицию
+echo $CURR_LINE > $STATE
+EOF
+
+chmod +x /usr/local/bin/pbs-backup-notify.sh
+```
+
+**8. Настройка Cron (каждые 5 минут)**
+```bash
+echo "*/5 * * * * root /usr/local/bin/pbs-backup-notify.sh" >> /etc/cron.d/pbs-notify
+```
+
+**9. Инициализация и тест**
+```bash
+# Сброс состояния для теста
+echo "0" > /var/run/pbs-notify-lastline
+
+# Запуск вручную
+/usr/local/bin/pbs-backup-notify.sh
+
+# Проверка состояния
+cat /var/run/pbs-notify-lastline
+wc -l < /var/log/proxmox-backup/tasks/archive
+```
+
+
+### 📝 Переменные для замены
+
+| Переменная | Описание | Пример |
+|------------|----------|--------|
+| `YOUR_BOT_TOKEN` | Токен Telegram бота | `7657027552:AAHQ6OGDRbm6wtqh_4GOQr7jd6C0BAQMyF4` |
+| `YOUR_CHAT_ID` | ID чата для уведомлений | `292909723` |
+| `address` | Адрес прокси-сервера | `2.27.50.20` |
+| `port` | Порт прокси | `2054` |
+| `id` | UUID пользователя VLESS | `68f44a38-396d-48da-b832-79b5dc5716ab` |
+| `publicKey` | Публичный ключ Reality | `TOyddQCTdSpycmO20uiLOqMABuKabpwVhw57tWmvJws` |
+| `serverName` | Домен для Reality | `cloud.zailon.ru` |
+
+### 📁 Файлы конфигурации
+
+| Файл | Назначение |
+|------|------------|
+| `/usr/local/etc/xray/config.json` | Конфиг XRay прокси |
+| `/etc/redsocks.conf` | Конфиг Redsocks |
+| `/usr/local/bin/telegram-proxy.sh` | Правила iptables для Telegram |
+| `/usr/local/bin/pbs-backup-notify.sh` | Скрипт уведомлений |
+| `/var/run/pbs-notify-lastline` | Позиция последнего прочитанного лога |
+| `/etc/cron.d/pbs-notify` | Cron задача |
+
+## 📦 Настройка уведомлений из Grafana в Telegram
+
+### 🎯 Цель
+Настроить отправку алертов из Grafana в Telegram с красивым форматированием, эмодзи и автоматическим преобразованием времени.
+
+### 📋 Архитектура
+
+```mermaid
+graph LR
+ A["Grafana
192.168.1.208"] -->|HTTP POST| B["Python Relay
localhost:8085"]
+ B -->|SOCKS5| C["XRay Client
127.0.0.1:1080"]
+ C -->|HTTPS| D["Telegram API
api.telegram.org"]
+```
---
-### 🔧 Настройка Python-реле (универсальный способ)
+### 🔧 Шаг 1: Установка Python-реле
**1. Создай скрипт реле:**
@@ -309,6 +561,7 @@ CHAT_ID = "292909723"
PROXY = "socks5://127.0.0.1:1080"
def format_duration(seconds):
+ """Преобразует секунды в читаемый формат"""
try:
sec = float(seconds)
if sec < 60:
@@ -325,9 +578,11 @@ def format_duration(seconds):
return str(seconds)
def get_icon(backup):
+ """Выбирает эмодзи по имени бэкапа"""
return "💻" if backup.startswith('vm-') else "📦" if backup.startswith('ct-') else "🗄️"
def send_telegram(text):
+ """Отправляет текст в Telegram через прокси"""
try:
cmd = [
'curl', '-sx', PROXY, '-s',
@@ -342,6 +597,7 @@ def send_telegram(text):
def handle_client(client_socket, addr):
try:
+ # Читаем запрос частями
data = b""
client_socket.settimeout(2)
while True:
@@ -356,6 +612,7 @@ def handle_client(client_socket, addr):
data = data.decode('utf-8', errors='ignore')
body = data.split('\r\n\r\n', 1)[1] if '\r\n\r\n' in data else data
+ # Парсим JSON от Grafana
backups = re.findall(r'"backup"\s*:\s*"([^"]+)"', body)
statuses = re.findall(r'"status"\s*:\s*"([^"]+)"', body)
values = re.findall(r'value=([\d.]+)', body)
@@ -375,6 +632,7 @@ def handle_client(client_socket, addr):
status = statuses[idx] if idx < len(statuses) else "unknown"
icon = get_icon(backup)
+ # Форматируем время из секунд
if len(values) >= (idx * 2 + 1):
seconds = float(values[idx * 2])
human_time = format_duration(seconds)
@@ -401,14 +659,16 @@ def handle_client(client_socket, addr):
except:
pass
+# Запуск сервера
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('0.0.0.0', PORT))
server.listen(10)
-print(f"▶ Relay on {PORT}", file=sys.stderr)
+print(f"▶ Telegram Relay on {PORT}", file=sys.stderr)
while True:
client, addr = server.accept()
+ print(f"← {addr}", file=sys.stderr)
handle_client(client, addr)
EOF
chmod +x /usr/local/bin/telegram-relay.py
@@ -440,10 +700,10 @@ systemctl daemon-reload
systemctl enable --now telegram-relay
```
-**3. Проверь что работает:**
+**3. Проверь работу:**
```bash
-# Статус сервиса
+# Проверка статуса
systemctl status telegram-relay
# Проверка порта
@@ -457,88 +717,107 @@ curl -X POST http://127.0.0.1:8085 -d "🎉 Test message"
---
-### 🔧 Настройка в Grafana (если используешь мониторинг)
+### 🔧 Шаг 2: Настройка Contact Point в Grafana
-**1. Contact Points → Add new:**
+**1. Открой Grafana:**
+- Перейди: **Alerting** → **Contact points**
+- Нажми **+ Add contact point**
+
+**2. Заполни поля:**
| Поле | Значение |
|------|----------|
| **Name** | `Telegram Relay` |
-| **Type** | `Webhook` |
-| **URL** | `http://192.168.1.208:8085` |
+| **Integration** | `Webhook` |
+| **URL** | `http://127.0.0.1:8085` |
| **HTTP Method** | `POST` |
| **Max Alerts** | `0` (без лимита) |
-**2. Сообщение (Message):**
-```bash
+**3. Сообщение (Message):**
+
+```text
{{ range .Alerts }}
📦 {{ .Labels.backup }}
⏱ {{ .Annotations.description }}
Status: {{ if eq .Status "firing" }}🔥{{ else }}✅{{ end }} {{ .Status }}
+---
{{ end }}
```
-**3. В Alert Rule → Annotations → Description:**
-```bash
+**4. Сохрани:**
+- Нажми **Save contact point**
+- Нажми **Test** — должно прийти сообщение
+
+---
+
+### 🔧 Шаг 3: Создание Alert Rule
+
+**1. Перейди:** **Alerting** → **Alert rules** → **+ New alert rule**
+
+**2. Query и условие:**
+
+| Поле | Значение |
+|------|----------|
+| **Query** | `time() - last_over_time(pbs_backup_timestamp[1h])/1000` |
+| **Alert condition** | `A is above 86400` |
+| **Evaluation group** | `Backup alerts` (создать новый) |
+| **Evaluation interval** | `1m` |
+| **Pending period** | `1m` |
+
+**3. Annotations → Description:**
+
+```text
{{ .Value }}
```
*(Python-скрипт сам превратит секунды в "11 ч 35 мин")*
-**4. Протестируй:**
-- Contact Point → **Test**
-- Должно прийти красивое уведомление
+**4. Notifications:**
+- Выбери Contact Point: `Telegram Relay`
+
+**5. Сохрани правило**
---
-### 🔧 Настройка в Proxmox Backup Server (прямые уведомления)
+### 🔧 Шаг 4: Проверка
-Если хочешь уведомления напрямую из PBS (без Grafana):
-
-**1. Создай endpoint (webhook):**
+**1. Проверка реле:**
```bash
-proxmox-backup-manager notification endpoint webhook create telegram \
- --url "http://192.168.1.208:8085" \
- --method post \
- --header "Content-Type: application/json"
+# Логи реле
+journalctl -u telegram-relay -f
+
+# Тестовый запрос
+curl -X POST http://127.0.0.1:8085 \
+ -H "Content-Type: application/json" \
+ -d '{"text":"✅ Grafana relay test"}'
```
-**2. Создай matcher для ошибок:**
+**2. Проверка XRay:**
```bash
-proxmox-backup-manager notification matcher create backup-error \
- --endpoint telegram \
- --cal-filter backup \
- --severity error
+# Статус XRay
+systemctl status xray-client
+
+# Тест соединения с Telegram
+curl -sx socks5://127.0.0.1:1080 \
+ https://api.telegram.org/bot7657027552:AAHQ6OGDRbm6wtqh_4GOQr7jd6C0BAQMyF4/getMe
```
-**3. Создай matcher для успехов (опционально):**
-
-```bash
-proxmox-backup-manager notification matcher create backup-success \
- --endpoint telegram \
- --cal-filter backup \
- --severity info
-```
-
-**4. Протестируй:**
-
-```bash
-proxmox-backup-manager notification target test telegram
-```
+**3. Тест алерта:**
+- В Grafana: Contact Point → **Test**
+- Должно прийти сообщение в Telegram
---
### 📝 Переменные для замены
-| Переменная | Описание | Пример |
-|------------|----------|--------|
+| Переменная | Описание | Значение |
+|------------|----------|----------|
| `BOT_TOKEN` | Токен Telegram бота | `7657027552:AAHQ6OGDRbm6wtqh_4GOQr7jd6C0BAQMyF4` |
| `CHAT_ID` | ID чата для уведомлений | `292909723` |
-| `PROXY` | SOCKS5 прокси (XRay) | `socks5://127.0.0.1:1080` |
-| `PORT` | Порт реле (локальный) | `8085` |
-| `GRAFANA_HOST` | IP сервера с Grafana | `192.168.1.208` |
+| `PROXY` | SOCKS5 прокси | `socks5://127.0.0.1:1080` |
+| `PORT` | Порт реле | `8085` |
---
@@ -548,32 +827,7 @@ proxmox-backup-manager notification target test telegram
|------|------------|
| `/usr/local/bin/telegram-relay.py` | Python-скрипт реле |
| `/etc/systemd/system/telegram-relay.service` | systemd-юнит |
-| `/var/log/syslog` | Логи (через journalctl) |
-
----
-
-### 🧪 Тестирование
-
-**1. Проверка соединения с Telegram:**
-
-```bash
-curl -sx socks5://127.0.0.1:1080 \
- https://api.telegram.org/bot$BOT_TOKEN/getMe
-```
-
-**2. Проверка реле:**
-
-```bash
-curl -X POST http://127.0.0.1:8085 \
- -H "Content-Type: application/json" \
- -d '{"text":"✅ Relay is working!"}'
-```
-
-**3. Просмотр логов:**
-
-```bash
-journalctl -u telegram-relay -f
-```
+| `/etc/grafana/provisioning/alerting/` | Конфиги алертов Grafana |
---
@@ -581,25 +835,28 @@ journalctl -u telegram-relay -f
| Проблема | Решение |
|----------|---------|
-| Не приходит сообщение | Проверь `journalctl -u telegram-relay -n 20` |
+| Не приходит сообщение | `journalctl -u telegram-relay -n 20` |
| Ошибка 400 от Telegram | Убери `parse_mode` или экранируй спецсимволы |
| Реле не слушает порт | `systemctl restart telegram-relay` |
-| XRay не подключается | Проверь `systemctl status xray-client` |
+| XRay не подключается | `systemctl status xray-client` |
| Сообщение обрезается | Скрипт разбивает на части по 5 бэкапов — это норма |
+| Grafana не шлёт | Проверь что Alert Rule в состоянии "Firing" |
---
-### 🎨 Формат уведомления
+### 🎨 Пример уведомления
-Пример того, что придёт в Telegram:
-```mermaid
+```text
🚨 Backup Alert
+
📦 ct-201
⏱ Последний бэкап: 11 ч 35 мин назад
Status: 🔥 firing
+
📦 ct-202
⏱ Последний бэкап: 11 ч 34 мин назад
Status: 🔥 firing
+
💻 vm-205
⏱ Последний бэкап: 11 ч 30 мин назад
Status: ✅ resolved
@@ -615,8 +872,6 @@ Status: ✅ resolved
### 🔄 Обновление скрипта
-Если нужно изменить формат или логику:
-
```bash
# Останови сервис
systemctl stop telegram-relay
@@ -630,7 +885,6 @@ systemctl start telegram-relay
# Проверь статус
systemctl status telegram-relay
```
-
---
## Безопасность
@@ -646,7 +900,7 @@ systemctl status telegram-relay
- **Доступ**: только из локальной сети (192.168.1.0/24)
### Рекомендации
-- ✅ Включить **2FA** для доступа к PBS
+- ✅ Включить **2FA** для `root@pam`
- ✅ Создать **отдельного пользователя** для бэкапов (не root)
- ✅ Настроить **firewall** на PBS сервере
- ✅ Регулярно **обновлять** систему (unattended-upgrades)
@@ -715,9 +969,9 @@ pvesm update olimpbkp --password <новый_токен>
| Проблема | Решение |
|----------|---------|
| Не приходят уведомления | Проверить `curl -s https://api.telegram.org/bot/getMe` |
-| Реле не работает | `journalctl -u telegram-relay -n 20 --no-pager` |
-| XRay не запускается | `systemctl status xray-client` |
-| Дубликат уведомлений | Проверить Max Alerts в Contact Point |
+| Ошибка iptables | Перезапустить `/usr/local/bin/telegram-proxy.sh` |
+| XRay не запускается | `journalctl -u xray-client -n 20 --no-pager` |
+| Дубликат уведомлений | Удалить `/var/run/pbs-notify-lastline` и пересоздать |
---