848 lines
32 KiB
Markdown
848 lines
32 KiB
Markdown
# Документация: Система бэкапов и мониторинга 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-токена. |