Docs/docs/services/vps-vpn/backups.md

848 lines
32 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Документация: Система бэкапов и мониторинга VPS
# Оглавление
[Общая архитектура](#общая-архитектура)
1. [Настройка SSH](#1-настройка-ssh)
- 1.1 [Изменение порта SSH](#изменение-порта-ssh)
- 1.2 [Отключение парольной аутентификации](#отключение-парольной-аутентификации)
- 1.3 [Генерация SSH-ключа](#генерация-ssh-ключа)
2. [CrowdSec](#2-crowdsec)
- 2.1 [Установка и настройка](#установка-и-настройка)
- 2.2 [Проверка работы](#проверка-работы)
3. [Настройка Nextcloud](#3-настройка-nextcloud)
- 3.1 [Создание пользователя hermes](#создание-пользователя-hermes)
- 3.2 [Создание App Password](#создание-app-password)
- 3.3 [Создание структуры папок](#создание-структуры-папок)
4. [Настройка Яндекс Диска](#4-настройка-яндекс-диска)
- 4.1 [Регистрация приложения](#регистрация-приложения)
- 4.2 [Авторизация rclone](#авторизация-rclone)
- 4.3 [Создание remote в rclone](#создание-remote-в-rclone)
5. [Systemd Credentials](#5-systemd-credentials)
6. [Скрипты бэкапов](#6-скрипты-бэкапов)
- 6.1 [logs-backup.sh (инкрементные бэкапы)](#logs-backupsh-инкрементные-бэкапы)
- 6.2 [logs-full-backup.sh (полный бэкап)](#logs-full-backupsh-полный-бэкап)
- 6.3 [backup.sh (бэкап 3x-ui)](#backupsh-бэкап-3x-ui)
7. [Systemd Units и Timers](#7-systemd-units-и-timers)
- 7.1 [logs-backup.service и timer](#logs-backupservice-и-timer)
- 7.2 [logs-full-backup.service и timer](#logs-full-backupservice-и-timer)
- 7.3 [xui-backup.service и timer](#xui-backupservice-и-timer)
- 7.4 [Активация timers](#активация-timers)
8. [Автоматизация на Nextcloud](#8-автоматизация-на-nextcloud)
- 8.1 [nextcloud-transit-processor.sh](#nextcloud-transit-processorsh)
- 8.2 [Добавление в cron](#добавление-в-cron)
9. [Расписание и ротация](#9-расписание-и-ротация)
10. [Безопасность](#10-безопасность)
- 10.1 [Защита от компрометации VPS](#защита-от-компрометации-vps)
- 10.2 [Изоляция через transit](#изоляция-через-transit)
- 10.3 [Шифрование credentials](#шифрование-credentials)
11. [Мониторинг и проверка](#11-мониторинг-и-проверка)
- 11.1 [Проверка работы timers](#проверка-работы-timers)
- 11.2 [Проверка логов](#проверка-логов)
- 11.3 [Проверка файлов на Яндекс Диске](#проверка-файлов-на-яндекс-диске)
- 11.4 [Проверка transit на Nextcloud](#проверка-transit-на-nextcloud)
- 11.5 [Проверка финальных папок](#проверка-финальных-папок)
12. [Добавление нового VPS](#12-добавление-нового-vps)
13. [Очистка истории и запуск мониторинга](#13-очистка-истории-и-запуск-мониторинга)
- 13.1 [Сброс offset](#сброс-offset)
- 13.2 [Очистка bash history](#очистка-bash-history)
- 13.3 [Ротация auth.log](#ротация-authlog)
- 13.4 [Очистка старых бэкапов](#очистка-старых-бэкапов)
- 13.5 [Тестовый запуск](#тестовый-запуск)
14. [Итоговый статус системы](#14-итоговый-статус-системы)
15. [Дальнейшее развитие](#15-дальнейшее-развитие)
- 15.1 [Алерты об успешных входах](#алерты-об-успешных-входах)
- 15.2 [Google Drive как третье хранилище](#google-drive-как-третье-хранилище)
- 15.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-токена.