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

32 KiB
Raw Permalink 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-токена.