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

32 KiB
Raw Blame History

Документация: Система бэкапов и мониторинга VPS

Оглавление

Общая архитектура

  1. Настройка SSH
  2. CrowdSec
  3. Настройка Nextcloud
  4. Настройка Яндекс Диска
  5. Systemd Credentials
  6. Скрипты бэкапов
  7. Systemd Units и Timers
  8. Автоматизация на Nextcloud
  9. Расписание и ротация
  10. Безопасность
  11. Мониторинг и проверка
  12. Добавление нового VPS
  13. Очистка истории и запуск мониторинга
  14. Итоговый статус системы
  15. Дальнейшее развитие

Общая архитектура

Система автоматического бэкапирования состоит из трёх компонентов:

  1. VPS (Charon-vpn) — сервер, с которого собираются бэкапы
  2. Nextcloud (nc.zailon.ru) — основное хранилище с автоматической обработкой через transit-папку
  3. Яндекс Диск — резервное хранилище

Поток данных:

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:

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.

Отключение парольной аутентификации

# В файле /etc/ssh/sshd_config установить:
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no

systemctl restart ssh

Теория: После отключения PasswordAuthentication вход по паролю невозможен — только SSH-ключи. Это защищает от брутфорса.

Генерация SSH-ключа на клиенте

ssh-keygen -t ed25519 -C "charon-vpn"
ssh-copy-id -p 45132 root@server-ip

Теория: Ed25519 — современный алгоритм, быстрее и безопаснее RSA. Ключ копируется на сервер через ssh-copy-id.

2. CrowdSec

Установка и настройка

curl -s https://packagecloud.io/install/script/gpg/script.deb.sh | sudo bash
apt-get install crowdsec

Теория: CrowdSec анализирует логи в реальном времени. При обнаружении атаки (брутфорс SSH) автоматически добавляет IP в бан через iptables/nftables.

Проверка работы

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

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

rclone authorize "yandex" "CLIENT_ID" "CLIENT_SECRET"

Скопировать токен из вывода.

Создание remote в rclone

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 (шифрование секретов)

Создание файла с секретами

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

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 (инкрементные бэкапы)

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 (полный бэкап раз в неделю)

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)

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

[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

[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

[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

[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

[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

[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

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

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

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

systemctl list-timers --all | grep -E 'logs-backup|logs-full|xui-backup'

Проверка логов

journalctl -u logs-backup -n 30
journalctl -u logs-full-backup -n 30
journalctl -u xui-backup -n 30

Проверка файлов на Яндекс Диске

rclone ls yandex:Olimp/Logs/Charon-vpn/
rclone ls yandex:Olimp/VPN/Charon-vpn/

Проверка transit на Nextcloud

find /mnt/cloud/data/hermes/files/transit/ -type f

Должно быть пусто (файлы автоматически перемещаются).

Проверка финальных папок

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:
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
  1. Скопировать скрипты:
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/
  1. Обновить HOSTNAME в скриптах:
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
  1. Настроить systemd units и timers (аналогично Charon-vpn)

  2. На сервере Nextcloud создать папки:

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 для инкрементных бэкапов

echo "0" > /opt/backup/.auth_offset
> /opt/backup/.auth_inode

Очистка bash history

history -c
> ~/.bash_history
history -w
unset HISTFILE

Ротация auth.log

logrotate -f /etc/logrotate.conf

Очистка старых локальных бэкапов

rm -f /opt/backup/Charon-vpn_*.tar.gz
rm -f /opt/3x-ui/backups/Charon-vpn_*.tar.gz

Очистка логов бэкапов

> /var/log/logs-backup.log
> /var/log/3x-ui-backup.log

Тестовый запуск

systemctl start logs-backup.service
systemctl start logs-full-backup.service
systemctl start xui-backup.service

Проверка

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-токена.