32 KiB
Документация: Система бэкапов и мониторинга VPS
Оглавление
- Общая архитектура
- Настройка SSH
- CrowdSec
- 3.1 Установка и настройка
- 3.2 Проверка работы
- Настройка Nextcloud
- Настройка Яндекс Диска
- Systemd Credentials
- Скрипты бэкапов
- Systemd Units и Timers
- Автоматизация на Nextcloud
- Расписание и ротация
- Безопасность
- Мониторинг и проверка
- Добавление нового VPS
- Очистка истории и запуск мониторинга
- 14.1 Сброс offset
- 14.2 Очистка bash history
- 14.3 Ротация auth.log
- 14.4 Очистка старых бэкапов
- 14.5 Тестовый запуск
- Итоговый статус системы
- Дальнейшее развитие
Общая архитектура
Система автоматического бэкапирования состоит из трёх компонентов:
- VPS (Charon-vpn) — сервер, с которого собираются бэкапы
- Nextcloud (nc.zailon.ru) — основное хранилище с автоматической обработкой через transit-папку
- Яндекс Диск — резервное хранилище
Поток данных:
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
- Зайти в Nextcloud как администратор
- Настройки → Пользователи → Создать пользователя
- Имя:
hermes, пароль: любой (запомнить) - Не давать никаких прав, только базовый доступ
Создание App Password
- Зайти в Nextcloud как
hermes - Настройки → Безопасность → Пароли приложений
- Создать пароль с именем "Charon-vpn-backup"
- Скопировать сгенерированный пароль
Теория: 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. Настройка Яндекс Диска
Регистрация приложения
- Зайти на https://oauth.yandex.ru/client/new
- Название: "Charon-vpn backup"
- Платформы: Веб-сервисы
- Callback URL:
https://localhost - Права:
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
Поток данных:
- VPS загружает файлы в
transit/{hostname}/{category}/ - Cron на Nextcloud перемещает файлы в
Admin/Olimp/OlimpBackup/{hostname}/{category}/ - Файлы в 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
На новом сервере
- Создать 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
- Скопировать скрипты:
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/
- Обновить 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
-
Настроить systemd units и timers (аналогично Charon-vpn)
-
На сервере 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-токена.