diff --git a/docs/services/vps-vpn/backups.md b/docs/services/vps-vpn/backups.md new file mode 100644 index 0000000..03d33ca --- /dev/null +++ b/docs/services/vps-vpn/backups.md @@ -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//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-токена. \ No newline at end of file