Добавить docs/services/vps-vpn/backups.md
This commit is contained in:
parent
9c196e2b0f
commit
4f3a4dabe4
848
docs/services/vps-vpn/backups.md
Normal file
848
docs/services/vps-vpn/backups.md
Normal 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-токена.
|
||||
Loading…
Reference in New Issue
Block a user