Добавить docs/services/vps-vpn/backups.md

This commit is contained in:
zailon 2026-07-07 16:29:34 +05:00
parent 9c196e2b0f
commit 4f3a4dabe4

View File

@ -0,0 +1,848 @@
# Документация: Система бэкапов и мониторинга VPS
# Оглавление
1. [Общая архитектура](#1-общая-архитектура)
2. [Настройка SSH](#2-настройка-ssh)
- 2.1 [Изменение порта SSH](#изменение-порта-ssh)
- 2.2 [Отключение парольной аутентификации](#отключение-парольной-аутентификации)
- 2.3 [Генерация SSH-ключа](#генерация-ssh-ключа)
3. [CrowdSec](#3-crowdsec)
- 3.1 [Установка и настройка](#установка-и-настройка)
- 3.2 [Проверка работы](#проверка-работы)
4. [Настройка Nextcloud](#4-настройка-nextcloud)
- 4.1 [Создание пользователя hermes](#создание-пользователя-hermes)
- 4.2 [Создание App Password](#создание-app-password)
- 4.3 [Создание структуры папок](#создание-структуры-папок)
5. [Настройка Яндекс Диска](#5-настройка-яндекс-диска)
- 5.1 [Регистрация приложения](#регистрация-приложения)
- 5.2 [Авторизация rclone](#авторизация-rclone)
- 5.3 [Создание remote в rclone](#создание-remote-в-rclone)
6. [Systemd Credentials](#6-systemd-credentials)
7. [Скрипты бэкапов](#7-скрипты-бэкапов)
- 7.1 [logs-backup.sh (инкрементные бэкапы)](#logs-backupsh-инкрементные-бэкапы)
- 7.2 [logs-full-backup.sh (полный бэкап)](#logs-full-backupsh-полный-бэкап)
- 7.3 [backup.sh (бэкап 3x-ui)](#backupsh-бэкап-3x-ui)
8. [Systemd Units и Timers](#8-systemd-units-и-timers)
- 8.1 [logs-backup.service и timer](#logs-backupservice-и-timer)
- 8.2 [logs-full-backup.service и timer](#logs-full-backupservice-и-timer)
- 8.3 [xui-backup.service и timer](#xui-backupservice-и-timer)
- 8.4 [Активация timers](#активация-timers)
9. [Автоматизация на Nextcloud](#9-автоматизация-на-nextcloud)
- 9.1 [nextcloud-transit-processor.sh](#nextcloud-transit-processorsh)
- 9.2 [Добавление в cron](#добавление-в-cron)
10. [Расписание и ротация](#10-расписание-и-ротация)
11. [Безопасность](#11-безопасность)
- 11.1 [Защита от компрометации VPS](#защита-от-компрометации-vps)
- 11.2 [Изоляция через transit](#изоляция-через-transit)
- 11.3 [Шифрование credentials](#шифрование-credentials)
12. [Мониторинг и проверка](#12-мониторинг-и-проверка)
- 12.1 [Проверка работы timers](#проверка-работы-timers)
- 12.2 [Проверка логов](#проверка-логов)
- 12.3 [Проверка файлов на Яндекс Диске](#проверка-файлов-на-яндекс-диске)
- 12.4 [Проверка transit на Nextcloud](#проверка-transit-на-nextcloud)
- 12.5 [Проверка финальных папок](#проверка-финальных-папок)
13. [Добавление нового VPS](#13-добавление-нового-vps)
14. [Очистка истории и запуск мониторинга](#14-очистка-истории-и-запуск-мониторинга)
- 14.1 [Сброс offset](#сброс-offset)
- 14.2 [Очистка bash history](#очистка-bash-history)
- 14.3 [Ротация auth.log](#ротация-authlog)
- 14.4 [Очистка старых бэкапов](#очистка-старых-бэкапов)
- 14.5 [Тестовый запуск](#тестовый-запуск)
15. [Итоговый статус системы](#15-итоговый-статус-системы)
16. [Дальнейшее развитие](#16-дальнейшее-развитие)
- 16.1 [Алерты об успешных входах](#алерты-об-успешных-входах)
- 16.2 [Google Drive как третье хранилище](#google-drive-как-третье-хранилище)
- 16.3 [Шифрование бэкапов через rclone crypt](#шифрование-бэкапов-через-rclone-crypt)
## Общая архитектура
Система автоматического бэкапирования состоит из трёх компонентов:
1. **VPS (Charon-vpn)** — сервер, с которого собираются бэкапы
2. **Nextcloud (nc.zailon.ru)** — основное хранилище с автоматической обработкой через transit-папку
3. **Яндекс Диск** — резервное хранилище
Поток данных:
```mermaid
graph TD
A[VPS Charon-vpn] -->|logs-backup.sh| B[Nextcloud: transit/Charon-vpn/Logs]
A -->|xui-backup.sh| C[Nextcloud: transit/Charon-vpn/OlimpVPN]
A -->|rclone| D[Яндекс Диск: Olimp/Logs/Charon-vpn]
A -->|rclone| E[Яндекс Диск: Olimp/VPN/Charon-vpn]
B -->|cron: nextcloud-transit-processor.sh| F[Nextcloud: Admin/Olimp/OlimpBackup/Charon-vpn/Logs]
C -->|cron: nextcloud-transit-processor.sh| G[Nextcloud: Admin/Olimp/OlimpBackup/Charon-vpn/OlimpVPN]
```
## 1. Настройка SSH
### Изменение порта SSH
Создаём override для ssh.service:
```bash
mkdir -p /etc/systemd/system/ssh.service.d
cat > /etc/systemd/system/ssh.service.d/override.conf <<'EOF'
[Service]
ExecStart=
ExecStart=/usr/sbin/sshd -D -p 45132
EOF
systemctl daemon-reload
systemctl restart ssh
```
**Теория:** Override-файл позволяет изменить параметры запуска systemd-сервиса без редактирования основного unit-файла. Первая строка `ExecStart=` очищает оригинальную команду, вторая задаёт новую с портом 45132.
### Отключение парольной аутентификации
```bash
# В файле /etc/ssh/sshd_config установить:
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
systemctl restart ssh
```
**Теория:** После отключения `PasswordAuthentication` вход по паролю невозможен — только SSH-ключи. Это защищает от брутфорса.
### Генерация SSH-ключа на клиенте
```bash
ssh-keygen -t ed25519 -C "charon-vpn"
ssh-copy-id -p 45132 root@server-ip
```
**Теория:** Ed25519 — современный алгоритм, быстрее и безопаснее RSA. Ключ копируется на сервер через `ssh-copy-id`.
## 2. CrowdSec
### Установка и настройка
```bash
curl -s https://packagecloud.io/install/script/gpg/script.deb.sh | sudo bash
apt-get install crowdsec
```
**Теория:** CrowdSec анализирует логи в реальном времени. При обнаружении атаки (брутфорс SSH) автоматически добавляет IP в бан через iptables/nftables.
### Проверка работы
```bash
cscli bouncers list
cscli decisions list
```
## 3. Настройка Nextcloud
### Создание пользователя hermes
1. Зайти в Nextcloud как администратор
2. Настройки → Пользователи → Создать пользователя
3. Имя: `hermes`, пароль: любой (запомнить)
4. Не давать никаких прав, только базовый доступ
### Создание App Password
1. Зайти в Nextcloud как `hermes`
2. Настройки → Безопасность → Пароли приложений
3. Создать пароль с именем "Charon-vpn-backup"
4. Скопировать сгенерированный пароль
**Теория:** App Password — токен для доступа к Nextcloud без использования основного пароля. Можно отозвать в любой момент без изменения основного пароля.
### Создание структуры папок на сервере Nextcloud
```bash
mkdir -p /mnt/cloud/data/hermes/files/transit/Charon-vpn/Logs
mkdir -p /mnt/cloud/data/hermes/files/transit/Charon-vpn/OlimpVPN
chown -R www-data:www-data /mnt/cloud/data/hermes/files/transit/
sudo -u www-data php /var/www/nextcloud/occ files:scan hermes
```
**Теория:** Папка `transit` — временная зона, куда VPS загружает файлы. Скрипт на Nextcloud автоматически перемещает их в финальные папки. Это защищает от удаления файлов, если VPS скомпрометирован (hermes не имеет прав на удаление в финальных папках).
## 4. Настройка Яндекс Диска
### Регистрация приложения
1. Зайти на https://oauth.yandex.ru/client/new
2. Название: "Charon-vpn backup"
3. Платформы: Веб-сервисы
4. Callback URL: `https://localhost`
5. Права: `disk:write` (только запись)
### Авторизация rclone
```bash
rclone authorize "yandex" "CLIENT_ID" "CLIENT_SECRET"
```
Скопировать токен из вывода.
### Создание remote в rclone
```bash
rclone config
```
Выбрать:
- n) New remote
- name> yandex
- Storage> yandex (32)
- client_id> [вставить CLIENT_ID]
- client_secret> [вставить CLIENT_SECRET]
- token> [вставить токен из authorize]
**Теория:** OAuth-токен даёт доступ к Яндекс.Диску. Хранится в `~/.config/rclone/rclone.conf`. Права на файл должны быть 600.
## 5. Systemd Credentials (шифрование секретов)
### Создание файла с секретами
```bash
cat > /tmp/backup.creds <<'EOF'
nc.url=https://nc.zailon.ru
nc.user=hermes
nc.pass=EfyyJ-9E4L2-KH53k-8GsGe-dqysA
nc.path.logs=transit/Charon-vpn/Logs
nc.path.vpn=transit/Charon-vpn/OlimpVPN
EOF
```
### Шифрование через systemd-creds
```bash
systemd-creds --name=backup.creds encrypt /tmp/backup.creds /etc/backup/backup.creds
chmod 600 /etc/backup/backup.creds
shred -u /tmp/backup.creds
```
**Теория:** `systemd-creds` шифрует файл с секретами. Расшифровка происходит автоматически при запуске сервиса через `LoadCredentialEncrypted`. Файл доступен только во время работы сервиса в `/run/credentials/<service-name>/backup.creds`.
## 6. Скрипты бэкапов
### logs-backup.sh (инкрементные бэкапы)
```bash
cat > /opt/backup/logs-backup.sh <<'SCRIPT'
#!/bin/bash
set -euo pipefail
CREDS="${CREDENTIALS_DIRECTORY}/backup.creds"
NC_URL="$(grep '^nc.url=' "$CREDS" | cut -d= -f2-)"
NC_USER="$(grep '^nc.user=' "$CREDS" | cut -d= -f2-)"
NC_PASS="$(grep '^nc.pass=' "$CREDS" | cut -d= -f2-)"
NC_PATH_LOGS="$(grep '^nc.path.logs=' "$CREDS" | cut -d= -f2-)"
BACKUP_DIR="/opt/backup"
DATE_SHORT=$(date +%Y%m%d)
HOSTNAME="Charon-vpn"
LOG_FILE="/var/log/logs-backup.log"
AUTH_LOG="/var/log/auth.log"
OFFSET_FILE="${BACKUP_DIR}/.auth_offset"
INODE_FILE="${BACKUP_DIR}/.auth_inode"
YANDEX_REMOTE="yandex"
YANDEX_PATH="Olimp/Logs/${HOSTNAME}"
LOGS_BACKUP="${HOSTNAME}_ssh_inc_${DATE_SHORT}.tar.gz"
AUDIT_BACKUP="${HOSTNAME}_audit_${DATE_SHORT}.tar.gz"
CURRENT_INODE=$(stat -c%i "$AUTH_LOG" 2>/dev/null || echo 0)
LAST_INODE=$([ -f "$INODE_FILE" ] && cat "$INODE_FILE" || echo 0)
if [ "$CURRENT_INODE" != "$LAST_INODE" ]; then
echo "0" > "$OFFSET_FILE"
echo "$CURRENT_INODE" > "$INODE_FILE"
fi
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}"; }
mkdir -p "${BACKUP_DIR}"
log "=== Начало бэкапа логов ==="
log "Инкрементальный бэкап SSH-логов..."
TMP_LOG=$(mktemp -d)
LAST_OFFSET=$([ -f "$OFFSET_FILE" ] && cat "$OFFSET_FILE" || echo 0)
CURRENT_SIZE=$(stat -c%s "$AUTH_LOG" 2>/dev/null || echo 0)
if [ "$CURRENT_SIZE" -gt "$LAST_OFFSET" ]; then
tail -c +$((LAST_OFFSET + 1)) "$AUTH_LOG" > "${TMP_LOG}/ssh_inc_${DATE_SHORT}.log"
NEW_SIZE=$(stat -c%s "${TMP_LOG}/ssh_inc_${DATE_SHORT}.log")
log "Новых записей: ${NEW_SIZE} байт (offset: ${LAST_OFFSET} → ${CURRENT_SIZE})"
else
echo "No new SSH events" > "${TMP_LOG}/ssh_inc_${DATE_SHORT}.log"
log "Нет новых записей"
fi
echo "$CURRENT_SIZE" > "$OFFSET_FILE"
tar -czf "${BACKUP_DIR}/${LOGS_BACKUP}" -C "${TMP_LOG}" .
rm -rf "${TMP_LOG}"
log "Архив логов: $(du -h ${BACKUP_DIR}/${LOGS_BACKUP} | cut -f1)"
log "Сбор аудита системы..."
AUDIT_TMP=$(mktemp -d)
cp ~/.bash_history "${AUDIT_TMP}/history.txt" 2>/dev/null || true
dpkg -l > "${AUDIT_TMP}/packages.txt" 2>/dev/null || true
ps auxf > "${AUDIT_TMP}/processes.txt"
ss -tlnp > "${AUDIT_TMP}/network_listen.txt"
ss -anp > "${AUDIT_TMP}/network_all.txt"
crontab -l > "${AUDIT_TMP}/cron.txt" 2>/dev/null || true
cat /etc/passwd > "${AUDIT_TMP}/users.txt"
awk -F: '$3==0' /etc/passwd > "${AUDIT_TMP}/uid0_users.txt"
cat ~/.ssh/authorized_keys > "${AUDIT_TMP}/ssh_keys.txt" 2>/dev/null || true
systemctl list-units --type=service --state=running > "${AUDIT_TMP}/services.txt" 2>/dev/null || true
tar -czf "${BACKUP_DIR}/${AUDIT_BACKUP}" -C "${AUDIT_TMP}" .
rm -rf "${AUDIT_TMP}"
log "Архив аудита: $(du -h ${BACKUP_DIR}/${AUDIT_BACKUP} | cut -f1)"
log "Загрузка на Nextcloud (transit/Charon-vpn/Logs)..."
for file in "${LOGS_BACKUP}" "${AUDIT_BACKUP}"; do
curl -T "${BACKUP_DIR}/${file}" -u "${NC_USER}:${NC_PASS}" \
"${NC_URL}/remote.php/dav/files/${NC_USER}/${NC_PATH_LOGS}/${file}" \
--silent --show-error --fail 2>&1 | tee -a "${LOG_FILE}" || log "WARN: Nextcloud ${file}"
done
log "✓ Nextcloud загружен (transit/Charon-vpn/Logs)"
log "Загрузка на Яндекс Диск (${YANDEX_PATH})..."
rclone mkdir "${YANDEX_REMOTE}:${YANDEX_PATH}" 2>/dev/null || true
for file in "${LOGS_BACKUP}" "${AUDIT_BACKUP}"; do
rclone copy "${BACKUP_DIR}/${file}" "${YANDEX_REMOTE}:${YANDEX_PATH}/" 2>&1 | tee -a "${LOG_FILE}" || log "WARN: Yandex ${file}"
done
log "✓ Яндекс Диск загружен"
log "Ротация локальных файлов (>30 дней)..."
find "${BACKUP_DIR}" -name "${HOSTNAME}_ssh_inc_*.tar.gz" -mtime +30 -delete
find "${BACKUP_DIR}" -name "${HOSTNAME}_audit_*.tar.gz" -mtime +30 -delete
log "Ротация на Яндекс Диске (>30 дней)..."
rclone delete "${YANDEX_REMOTE}:${YANDEX_PATH}" --min-age 30d 2>&1 | tee -a "${LOG_FILE}" || log "WARN: Yandex rotation"
log "=== Бэкап логов завершён ==="
SCRIPT
chmod +x /opt/backup/logs-backup.sh
```
**Теория:** Скрипт использует инкрементальный бэкап — сохраняет только новые записи auth.log с момента последнего запуска. Отслеживает inode файла для обнаружения ротации logrotate. Собирает аудит системы (установленные пакеты, процессы, сетевые соединения, cron, пользователи).
### logs-full-backup.sh (полный бэкап раз в неделю)
```bash
cat > /opt/backup/logs-full-backup.sh <<'SCRIPT'
#!/bin/bash
set -euo pipefail
CREDS="${CREDENTIALS_DIRECTORY}/backup.creds"
NC_URL="$(grep '^nc.url=' "$CREDS" | cut -d= -f2-)"
NC_USER="$(grep '^nc.user=' "$CREDS" | cut -d= -f2-)"
NC_PASS="$(grep '^nc.pass=' "$CREDS" | cut -d= -f2-)"
NC_PATH_LOGS="$(grep '^nc.path.logs=' "$CREDS" | cut -d= -f2-)"
BACKUP_DIR="/opt/backup"
DATE_SHORT=$(date +%Y%m%d)
HOSTNAME="Charon-vpn"
LOG_FILE="/var/log/logs-backup.log"
YANDEX_REMOTE="yandex"
YANDEX_PATH="Olimp/Logs/${HOSTNAME}"
FULL_BACKUP="${HOSTNAME}_logs_full_${DATE_SHORT}.tar.gz"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}"; }
mkdir -p "${BACKUP_DIR}"
log "=== Начало полного бэкапа логов ==="
log "Создание полного архива логов..."
TMP_LOG=$(mktemp -d)
cp /var/log/auth.log "${TMP_LOG}/" 2>/dev/null || true
cp /var/log/auth.log.* "${TMP_LOG}/" 2>/dev/null || true
tar -czf "${BACKUP_DIR}/${FULL_BACKUP}" -C "${TMP_LOG}" .
rm -rf "${TMP_LOG}"
log "Полный архив логов: $(du -h ${BACKUP_DIR}/${FULL_BACKUP} | cut -f1)"
log "Загрузка на Nextcloud (transit/Charon-vpn/Logs)..."
curl -T "${BACKUP_DIR}/${FULL_BACKUP}" -u "${NC_USER}:${NC_PASS}" \
"${NC_URL}/remote.php/dav/files/${NC_USER}/${NC_PATH_LOGS}/${FULL_BACKUP}" \
--silent --show-error --fail 2>&1 | tee -a "${LOG_FILE}" || log "WARN: Nextcloud ${FULL_BACKUP}"
log "✓ Nextcloud загружен (transit/Charon-vpn/Logs)"
log "Загрузка на Яндекс Диск..."
rclone copy "${BACKUP_DIR}/${FULL_BACKUP}" "${YANDEX_REMOTE}:${YANDEX_PATH}/" 2>&1 | tee -a "${LOG_FILE}" || log "WARN: Yandex ${FULL_BACKUP}"
log "✓ Яндекс Диск загружен"
log "Ротация старых файлов (>30 дней)..."
find "${BACKUP_DIR}" -name "${HOSTNAME}_logs_full_*.tar.gz" -mtime +30 -delete
log "=== Полный бэкап логов завершён ==="
SCRIPT
chmod +x /opt/backup/logs-full-backup.sh
```
**Теория:** Создаёт полный архив всех логов auth.log и ротированных файлов. Запускается раз в неделю в воскресенье.
### backup.sh (бэкап 3x-ui)
```bash
cat > /opt/3x-ui/backup.sh <<'SCRIPT'
#!/bin/bash
set -euo pipefail
CREDS="${CREDENTIALS_DIRECTORY}/backup.creds"
NC_URL="$(grep '^nc.url=' "$CREDS" | cut -d= -f2-)"
NC_USER="$(grep '^nc.user=' "$CREDS" | cut -d= -f2-)"
NC_PASS="$(grep '^nc.pass=' "$CREDS" | cut -d= -f2-)"
NC_PATH_VPN="$(grep '^nc.path.vpn=' "$CREDS" | cut -d= -f2-)"
BACKUP_DIR="/opt/3x-ui/backups"
DATE=$(date +%Y%m%d_%H%M%S)
HOSTNAME="Charon-vpn"
LOG_FILE="/var/log/3x-ui-backup.log"
YANDEX_REMOTE="yandex"
YANDEX_PATH="Olimp/VPN/${HOSTNAME}"
VPN_BACKUP="${HOSTNAME}_3xui_${DATE}.tar.gz"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}"; }
mkdir -p "${BACKUP_DIR}"
log "=== Начало бэкапа 3x-ui ==="
log "Создание архива 3x-ui..."
tar -czf "${BACKUP_DIR}/${VPN_BACKUP}" -C /opt/3x-ui db cert docker-compose.yml 2>/dev/null || {
log "ERROR: не удалось создать архив 3x-ui"
exit 1
}
log "Архив 3x-ui: $(du -h ${BACKUP_DIR}/${VPN_BACKUP} | cut -f1)"
log "Загрузка 3x-ui на Nextcloud (transit/Charon-vpn/OlimpVPN)..."
curl -T "${BACKUP_DIR}/${VPN_BACKUP}" -u "${NC_USER}:${NC_PASS}" \
"${NC_URL}/remote.php/dav/files/${NC_USER}/${NC_PATH_VPN}/${VPN_BACKUP}" \
--silent --show-error --fail 2>&1 | tee -a "${LOG_FILE}" || {
log "ERROR: загрузка 3x-ui на Nextcloud"
exit 1
}
log "✓ Nextcloud загружен (transit/Charon-vpn/OlimpVPN)"
log "Загрузка на Яндекс Диск (${YANDEX_PATH})..."
rclone mkdir "${YANDEX_REMOTE}:${YANDEX_PATH}" 2>/dev/null || true
rclone copy "${BACKUP_DIR}/${VPN_BACKUP}" "${YANDEX_REMOTE}:${YANDEX_PATH}/" 2>&1 | tee -a "${LOG_FILE}" || log "WARN: Yandex ${VPN_BACKUP}"
log "✓ Яндекс Диск загружен"
log "Ротация локальных файлов (>30 дней)..."
find "${BACKUP_DIR}" -name "${HOSTNAME}_3xui_*.tar.gz" -mtime +30 -delete
log "Ротация на Яндекс Диске (>30 дней)..."
rclone delete "${YANDEX_REMOTE}:${YANDEX_PATH}" --min-age 30d 2>&1 | tee -a "${LOG_FILE}" || log "WARN: Yandex rotation"
log "=== Бэкап 3x-ui завершён ==="
SCRIPT
chmod +x /opt/3x-ui/backup.sh
```
**Теория:** Бэкапит конфигурацию 3x-ui (база данных, сертификаты, docker-compose.yml). Запускается раз в 3 дня.
## 7. Systemd Units и Timers
### logs-backup.service
```ini
[Unit]
Description=Daily logs and audit backup to Nextcloud and Yandex
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
LoadCredentialEncrypted=backup.creds:/etc/backup/backup.creds
ExecStart=/opt/backup/logs-backup.sh
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
```
### logs-backup.timer
```ini
[Unit]
Description=Run incremental logs backup Mon-Sat at 03:00
[Timer]
OnCalendar=Mon..Sat *-*-* 03:00:00
Persistent=true
RandomizedDelaySec=1h
[Install]
WantedBy=timers.target
```
### logs-full-backup.service
```ini
[Unit]
Description=Weekly full logs backup to Nextcloud and Yandex
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
LoadCredentialEncrypted=backup.creds:/etc/backup/backup.creds
ExecStart=/opt/backup/logs-full-backup.sh
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
```
### logs-full-backup.timer
```ini
[Unit]
Description=Run full logs backup weekly on Sunday at 04:00
[Timer]
OnCalendar=Sun *-*-* 04:00:00
Persistent=true
[Install]
WantedBy=timers.target
```
### xui-backup.service
```ini
[Unit]
Description=3x-ui backup to Nextcloud and Yandex
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
LoadCredentialEncrypted=backup.creds:/etc/backup/backup.creds
ExecStart=/opt/3x-ui/backup.sh
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
```
### xui-backup.timer
```ini
[Unit]
Description=Run 3x-ui backup every 3 days at 04:00
[Timer]
OnCalendar=*-*-* 04:00:00
Persistent=true
RandomizedDelaySec=2h
[Install]
WantedBy=timers.target
```
**Теория:** `LoadCredentialEncrypted` автоматически расшифровывает credentials при запуске сервиса. `Persistent=true` гарантирует, что пропущенные запуски будут выполнены при следующей возможности. `RandomizedDelaySec` добавляет случайную задержку, чтобы избежать одновременного запуска на множестве серверов.
### Активация timers
```bash
systemctl daemon-reload
systemctl enable --now logs-backup.timer
systemctl enable --now logs-full-backup.timer
systemctl enable --now xui-backup.timer
```
## 8. Автоматизация на Nextcloud
### nextcloud-transit-processor.sh
```bash
cat > /usr/local/bin/nextcloud-transit-processor.sh <<'EOF'
#!/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NEXTCLOUD_PATH="/var/www/nextcloud"
DATA_PATH="/mnt/cloud/data"
TRANSIT_PATH="$DATA_PATH/hermes/files/transit"
FINAL_PATH="$DATA_PATH/Zailon/files/Admin/Olimp/OlimpBackup"
LOG_FILE="/var/log/nextcloud-transit.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}
if [ ! -d "$TRANSIT_PATH" ]; then
log "ERROR: Папка transit не найдена: $TRANSIT_PATH"
exit 1
fi
for host_dir in "$TRANSIT_PATH"/*/; do
[ -d "$host_dir" ] || continue
hostname=$(basename "$host_dir")
log "Обнаружен хост: $hostname"
for category_dir in "$host_dir"*/; do
[ -d "$category_dir" ] || continue
category=$(basename "$category_dir")
target_dir="$FINAL_PATH/$hostname/$category"
if [ ! -d "$target_dir" ]; then
mkdir -p "$target_dir"
chown -R www-data:www-data "$target_dir"
log "Создана папка: $target_dir"
fi
file_count=0
for file in "$category_dir"*; do
if [ -f "$file" ]; then
filename=$(basename "$file")
mv "$file" "$target_dir/"
log "Перемещён: $hostname/$category/$filename"
file_count=$((file_count + 1))
fi
done
if [ "$file_count" -gt 0 ]; then
log "Перемещено файлов: $file_count из $hostname/$category"
fi
done
done
log "Обновление индекса Nextcloud..."
/usr/bin/php "$NEXTCLOUD_PATH/occ" files:scan --path="hermes/files/transit" >> "$LOG_FILE" 2>&1
/usr/bin/php "$NEXTCLOUD_PATH/occ" files:scan --path="Zailon/files/Admin/Olimp/OlimpBackup/" >> "$LOG_FILE" 2>&1
log "Индекс обновлён"
log "=== Обработка завершена ==="
EOF
chmod +x /usr/local/bin/nextcloud-transit-processor.sh
chown www-data:www-data /usr/local/bin/nextcloud-transit-processor.sh
```
### Добавление в cron
```bash
touch /var/log/nextcloud-transit.log
chown www-data:www-data /var/log/nextcloud-transit.log
chmod 644 /var/log/nextcloud-transit.log
(crontab -u www-data -l 2>/dev/null; echo "* * * * * /usr/local/bin/nextcloud-transit-processor.sh") | crontab -u www-data -
```
**Теория:** Скрипт запускается каждую минуту, автоматически обнаруживает новые хосты и категории в transit, перемещает файлы в финальные папки. Универсальный — не требует изменений при добавлении новых VPS или сервисов.
## 9. Расписание и ротация
| Сервис | Расписание | Nextcloud | Яндекс Диск |
|--------|------------|-----------|-------------|
| logs-backup (инкремент) | Пн-Сб 03:37 | transit/Charon-vpn/Logs | Olimp/Logs/Charon-vpn |
| logs-full-backup (полный) | Вс 04:00 | transit/Charon-vpn/Logs | Olimp/Logs/Charon-vpn |
| xui-backup (3x-ui) | Раз в 3 дня 05:31 | transit/Charon-vpn/OlimpVPN | Olimp/VPN/Charon-vpn |
**Ротация:** 30 дней для всех файлов (локально и на Яндекс Диске).
## 10. Безопасность
### Защита от компрометации VPS
Если злоумышленник получит root на VPS:
- Увидит credentials hermes (App Password)
- Сможет загружать файлы в transit
- **Не сможет** удалить файлы (hermes не имеет прав на удаление в финальных папках)
- **Не сможет** прочитать файлы других пользователей
- **Не знает** про структуру финального хранилища
### Изоляция через transit
Поток данных:
1. VPS загружает файлы в `transit/{hostname}/{category}/`
2. Cron на Nextcloud перемещает файлы в `Admin/Olimp/OlimpBackup/{hostname}/{category}/`
3. Файлы в transit удаляются автоматически
Даже если VPS скомпрометирован, злоумышленник видит только пустую папку transit.
### Шифрование credentials
Все секреты хранятся в зашифрованном виде через `systemd-creds`. Расшифровка происходит только во время работы сервиса.
## 11. Мониторинг и проверка
### Проверка работы timers
```bash
systemctl list-timers --all | grep -E 'logs-backup|logs-full|xui-backup'
```
### Проверка логов
```bash
journalctl -u logs-backup -n 30
journalctl -u logs-full-backup -n 30
journalctl -u xui-backup -n 30
```
### Проверка файлов на Яндекс Диске
```bash
rclone ls yandex:Olimp/Logs/Charon-vpn/
rclone ls yandex:Olimp/VPN/Charon-vpn/
```
### Проверка transit на Nextcloud
```bash
find /mnt/cloud/data/hermes/files/transit/ -type f
```
Должно быть пусто (файлы автоматически перемещаются).
### Проверка финальных папок
```bash
ls -la /mnt/cloud/data/Zailon/files/Admin/Olimp/OlimpBackup/Charon-vpn/Logs/
ls -la /mnt/cloud/data/Zailon/files/Admin/Olimp/OlimpBackup/Charon-vpn/OlimpVPN/
```
## 12. Добавление нового VPS
### На новом сервере
1. Создать credentials:
```bash
cat > /tmp/backup.creds <<'EOF'
nc.url=https://nc.zailon.ru
nc.user=hermes
nc.pass=EfyyJ-9E4L2-KH53k-8GsGe-dqysA
nc.path.logs=transit/NewVPS/Logs
nc.path.vpn=transit/NewVPS/OlimpVPN
EOF
systemd-creds --name=backup.creds encrypt /tmp/backup.creds /etc/backup/backup.creds
chmod 600 /etc/backup/backup.creds
shred -u /tmp/backup.creds
```
2. Скопировать скрипты:
```bash
scp /opt/backup/logs-backup.sh root@new-vps:/opt/backup/
scp /opt/backup/logs-full-backup.sh root@new-vps:/opt/backup/
scp /opt/3x-ui/backup.sh root@new-vps:/opt/3x-ui/
```
3. Обновить HOSTNAME в скриптах:
```bash
sed -i 's/HOSTNAME="Charon-vpn"/HOSTNAME="NewVPS"/' /opt/backup/logs-backup.sh
sed -i 's/HOSTNAME="Charon-vpn"/HOSTNAME="NewVPS"/' /opt/backup/logs-full-backup.sh
sed -i 's/HOSTNAME="Charon-vpn"/HOSTNAME="NewVPS"/' /opt/3x-ui/backup.sh
```
4. Настроить systemd units и timers (аналогично Charon-vpn)
5. На сервере Nextcloud создать папки:
```bash
mkdir -p /mnt/cloud/data/hermes/files/transit/NewVPS/Logs
mkdir -p /mnt/cloud/data/hermes/files/transit/NewVPS/OlimpVPN
chown -R www-data:www-data /mnt/cloud/data/hermes/files/transit/NewVPS/
sudo -u www-data php /var/www/nextcloud/occ files:scan hermes
```
**Теория:** Скрипт nextcloud-transit-processor.sh автоматически обнаружит новый хост и начнёт перемещать файлы. Никаких изменений в скрипте не требуется.
## 13. Очистка истории и запуск мониторинга
### Сброс offset для инкрементных бэкапов
```bash
echo "0" > /opt/backup/.auth_offset
> /opt/backup/.auth_inode
```
### Очистка bash history
```bash
history -c
> ~/.bash_history
history -w
unset HISTFILE
```
### Ротация auth.log
```bash
logrotate -f /etc/logrotate.conf
```
### Очистка старых локальных бэкапов
```bash
rm -f /opt/backup/Charon-vpn_*.tar.gz
rm -f /opt/3x-ui/backups/Charon-vpn_*.tar.gz
```
### Очистка логов бэкапов
```bash
> /var/log/logs-backup.log
> /var/log/3x-ui-backup.log
```
### Тестовый запуск
```bash
systemctl start logs-backup.service
systemctl start logs-full-backup.service
systemctl start xui-backup.service
```
### Проверка
```bash
journalctl -u logs-backup -n 20
journalctl -u logs-full-backup -n 20
journalctl -u xui-backup -n 20
```
## 14. Итоговый статус системы
| Компонент | Статус |
|-----------|--------|
| SSH порт 45132 | ✅ Работает |
| PasswordAuthentication | ✅ Отключён |
| CrowdSec | ✅ Работает |
| Бэкап 3x-ui | ✅ Раз в 3 дня |
| Инкрементные бэкапы логов | ✅ Пн-Сб 03:37 |
| Полные бэкапы логов | ✅ Вс 04:00 |
| Nextcloud (transit) | ✅ Автоматическое перемещение |
| Яндекс Диск | ✅ Прямая загрузка |
| Аудит системы | ✅ Ежедневный снэпшот |
| Ротация 30 дней | ✅ Автоматически |
| Отслеживание ротации auth.log | ✅ Через inode |
| Credentials зашифрованы | ✅ systemd-creds |
| Rclone конфиг | ✅ Права 600 |
| Универсальность | ✅ Новые хосты без изменений |
## 15. Дальнейшее развитие
### Алерты об успешных входах не из белого списка
Можно настроить через cron, который каждую минуту проверяет auth.log на новые успешные входы и отправляет алерты на Nextcloud/Telegram.
### Google Drive как третье хранилище
Добавить через `rclone authorize "drive"` для дополнительной отказоустойчивости.
### Шифрование бэкапов через rclone crypt
Создать encrypted remote поверх yandex: для защиты содержимого файлов даже при компрометации OAuth-токена.