
Облачная папка полезна как отдельное место хранения копий, но не превращает синхронизацию в полноценное резервное копирование. Если удаление исходника автоматически удаляет облачный файл, заражение или ошибка оператора могут уничтожить обе версии. Если архив не восстанавливался, успешная загрузка доказывает только передачу байтов.
Ниже — сценарий для Linux: подготовленная согласованная копия, rclone с OAuth, клиентское шифрование, отдельная папка каждого запуска, проверка содержимого и тест возврата. Рабочие данные и команды адаптируют на тесте; Яндекс.Диск не следует считать хранилищем с гарантированной неизменяемостью.
1. Определите состав копии, RPO и RTO
Для сайта обычно нужны файлы приложения и загружаемые данные, база, конфигурация и информация о версиях. Экспорт базы и архив файлов должны соответствовать друг другу по времени и прикладному состоянию. Простое копирование каталога живой MySQL не даёт корректного логического дампа.
RPO задаёт допустимую потерю данных, RTO — требуемое время восстановления. Они влияют на расписание, скорость канала и необходимость локальной копии. Если восстановление из облака занимает часы, оно не обеспечит RTO в двадцать минут без дополнительной архитектуры.
Для дампа используйте проверенную методику из статьи о MySQL. Для VM — совместимый backup-инструмент и процедуру консистентности, а не архивирование работающего виртуального диска через tar. Далее предполагается, что каталог /srv/backup/staging/site-ready содержит уже завершённый набор согласованных файлов.
2. Подготовьте отдельную учётную запись и rclone
Установите rclone из доверенного источника, подходящего дистрибутиву, проверьте rclone version и происхождение пакета. Не запускайте случайный curl-скрипт из старого блога от root. Для нового отдельного задания создайте служебную запись, если такой записи ещё нет:
sudo useradd --system --create-home --home-dir /var/lib/backupjob --shell /usr/sbin/nologin backupjob
sudo install -d -o backupjob -g backupjob -m 0700 /var/lib/backupjob/rclone
sudo install -d -o backupjob -g backupjob -m 0700 /srv/backup/outgoing
Команды рассчитаны на систему с useradd и указанным shell; сначала проверьте его существование. Существующий пользователь не должен быть переопределён без проверки. Служебной записи выдайте чтение только завершённой staging-копии. Она не обязана иметь полный sudo или права записи в рабочий сайт.
Разделите этап создания консистентной копии и отправку. Тогда ошибка облачного токена не затронет подготовку локального архива, а права загрузчика будут уже, чем права процесса резервного копирования.
3. Настройте Яндекс.Диск через OAuth
Запустите настройку от имени той записи, которой будет выполняться загрузка:
sudo -u backupjob rclone config --config /var/lib/backupjob/rclone/rclone.conf
Создайте remote yandex, тип backend — yandex, выполните авторизацию для выделенной организационной учётной записи. Номера пунктов зависят от версии: выбирайте тип по имени. При headless-сервере используйте штатную процедуру remote setup на доверенной рабочей станции, а не откройте OAuth-порт сервера всем в интернете.
Документация Yandex backend объясняет параметры авторизации. Не переносите обещание старого скрипта «только папка приложения» на произвольный rclone remote: фактически выданные разрешения проверяют в OAuth и настройках backend.
Файл конфигурации содержит токен. Ограничьте его до 600 и убедитесь в правильном владельце. Не публикуйте вывод rclone config show, не вставляйте токен в команду и не храните конфигурацию в web-root сайта.
4. Добавьте клиентское шифрование crypt
В том же rclone config создайте remote yandex-crypt типа crypt, который указывает на yandex:company-backups. Выберите шифрование имён файлов и каталогов, задайте отдельный сильный секрет и соль предусмотренным диалогом. Затем проверьте доступ:
sudo -u backupjob rclone lsd yandex-crypt: --config /var/lib/backupjob/rclone/rclone.conf
Crypt backend шифрует содержимое до передачи в облако. Он не защищает от удаления файлов тем, кто имеет облачный доступ, и не скрывает всю метаинформацию, например размеры и время. Обфускация пароля в конфигурации не равна защите ключа от человека, который получил этот файл.
Ключ, соль и параметры remote сохраните отдельно в контролируемом хранилище организации и проверьте восстановление на второй машине. Единственная копия ключа на том же сервере, который вы резервируете, делает облачный архив бесполезным при потере сервера. Общий личный аккаунт сотрудника также создаёт проблему преемственности.

5. Подготовьте архив завершённой копии
Пример выполняется от backupjob после того, как отдельная задача создала staging-набор и предоставила ему чтение. Staging нельзя менять во время упаковки; передачу между этапами координируйте состоянием задания или единым планировщиком.
set -euo pipefail
umask 077
source_dir='/srv/backup/staging/site-ready'
outgoing='/srv/backup/outgoing'
run_id=$(date -u +%Y%m%dT%H%M%SZ)
run_dir="$outgoing/$run_id"
test -d "$source_dir"
test -r "$source_dir/backup-ready.txt"
mkdir "$run_dir"
tar -C "$source_dir" -czf "$run_dir/site.tar.gz" .
gzip -t "$run_dir/site.tar.gz"
(cd "$run_dir" && sha256sum site.tar.gz > SHA256SUMS)
backup-ready.txt — согласованный сигнал завершения, создаваемый после успешного формирования копии; наличие случайного старого файла не доказывает свежесть. В реальном процессе в нём сохраняют время, идентификатор и версию приложения, а загрузчик проверяет их по контракту задания.
mkdir без -p для каталога запуска не позволит незаметно перезаписать уже существующий набор при совпавшем идентификаторе. Проверка gzip выявляет повреждение сжатого потока, но не подтверждает консистентность базы и полноту состава.
6. Загрузите отдельный набор и проверьте байты
Для переменных из предыдущего блока:
config='/var/lib/backupjob/rclone/rclone.conf'
remote_dir="yandex-crypt:site01/$run_id"
rclone copy "$run_dir" "$remote_dir" --config "$config" --exclude upload.log --dry-run
rclone copy "$run_dir" "$remote_dir" --config "$config" --exclude upload.log --log-level INFO --log-file "$run_dir/upload.log"
rclone check "$run_dir" "$remote_dir" --config "$config" --download --one-way --exclude upload.log
Copy не удаляет удалённые файлы, отсутствующие в источнике. Это важное отличие от sync. --download у check сравнивает содержимое с загрузкой, что полезно для crypt; проверка требует времени и входящего трафика. В зависимости от backend альтернативой может быть cryptcheck.
Лог исключён из проверки, потому что меняется при выполнении и не является частью доказательства полноты копии. Лог также исключён из передачи приведёнными командами. Для архива и манифеста размерная проверка не заменяет контроль содержимого.
Ненулевой exit code означает, что запуск нельзя считать успешным. Частично загруженный каталог не становится принятой копией только потому, что в нём появился один файл.
7. Как автоматизировать загрузку без пересечений заданий
Ниже полный upload-сценарий для описанной схемы. Сохраните его как /usr/local/sbin/site-backup-upload, владелец root, права 0755, без возможности изменения пользователем backupjob. Проверьте реальные пути rclone, tar, gzip, sha256sum и flock. Он не создаёт дамп живой базы: работает только с завершённым staging-набором.
#!/bin/bash
set -euo pipefail
umask 077
exec 9>/srv/backup/outgoing/.upload.lock
flock -n 9 || exit 75
source_dir='/srv/backup/staging/site-ready'
marker="$source_dir/backup-ready.txt"
config='/var/lib/backupjob/rclone/rclone.conf'
test -r "$marker"
test -r "$config"
# Учебный контракт: marker создан после успешной подготовки, не старше суток.
now=$(date +%s)
mtime=$(stat -c %Y "$marker")
(( now >= mtime && now - mtime <= 86400 )) || exit 76
run_id=$(date -u +%Y%m%dT%H%M%SZ)
run_dir="/srv/backup/outgoing/$run_id"
mkdir "$run_dir"
tar -C "$source_dir" -czf "$run_dir/site.tar.gz" .
gzip -t "$run_dir/site.tar.gz"
(cd "$run_dir" && sha256sum site.tar.gz > SHA256SUMS)
remote_dir="yandex-crypt:site01/$run_id"
rclone copy "$run_dir" "$remote_dir" --config "$config"
rclone check "$run_dir" "$remote_dir" --config "$config" --download --one-way
printf '%s\n' "$run_id" > /srv/backup/outgoing/last-success.tmp
mv /srv/backup/outgoing/last-success.tmp /srv/backup/outgoing/last-success
Подготовщик обязан удалить прежний marker до изменения staging и создать новый после успешного завершения. Планировщик или общий lock должен исключать изменение staging во время упаковки; блокировка upload защищает лишь от двух загрузчиков. Порог 86400 секунд — учебный допуск свежести, замените его своим RPO. Код 75 означает пересечение запусков, 76 — нарушение свежести; оба требуют разборки, а не зелёного статуса.
Для systemd сохраните /etc/systemd/system/site-backup-upload.service:
[Unit]
Description=Upload prepared site backup to encrypted remote
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
User=backupjob
Group=backupjob
Environment=PATH=/usr/local/bin:/usr/bin:/bin
ExecStart=/usr/local/sbin/site-backup-upload
UMask=0077
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/srv/backup/outgoing /var/lib/backupjob/rclone
И /etc/systemd/system/site-backup-upload.timer:
[Unit]
Description=Daily prepared backup upload
[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true
[Install]
WantedBy=timers.target
03:30 относится к часовому поясу сервера. Persistent может запустить пропущенную задачу после включения; проверка свежести при этом обязательна. Подготовка копии должна завершиться раньше, а при разных длительностях надёжнее связать этапы единым заданием. В ReadWritePaths разрешены только каталог результатов и каталог конфигурации: rclone должен сохранять обновлённый OAuth-токен. Конфигурационный каталог сохраняйте с 0700, файл — с 0600. Остальные области остаются read-only благодаря ProtectSystem=strict.
Параметры изоляции описаны в systemd.exec, а расписание — в systemd.timer. Сначала вручную протестируйте service и восстановление; затем включайте timer:
sudo systemctl daemon-reload
sudo systemctl start site-backup-upload.service
sudo systemctl status site-backup-upload.service --no-pager
sudo journalctl -u site-backup-upload.service -n 100 --no-pager
sudo systemctl enable --now site-backup-upload.timer
systemctl list-timers site-backup-upload.timer
Ненулевой результат задачи, исчерпание квоты, отзыв токена и устаревший marker должны вызывать уведомление через вашу систему мониторинга. Контролируйте свежесть last-success, возраст исходного marker, размер и восстановление. active (waiting) у timer означает только ожидание запуска. Для отмены расписания отключите именно этот timer и сохраните локальные/облачные наборы; остановка автоматизации не требует удаления копий.
8. Как хранить несколько версий и удалять старые
Папка каждого запуска позволяет сохранить историю. Выберите политику по RPO, сроку обнаружения ошибок, объёму и времени восстановления: несколько ежедневных копий и отдельные более редкие поколения — один из вариантов, а не универсальный норматив.
Очистка — самостоятельная операция. Сначала сформируйте список точных завершённых наборов, которые разрешено удалить, и проверьте его на dry run. Не применяйте rclone sync к общему корню, если требуется история. Не включайте --yandex-hard-delete без необходимости: он меняет поведение удаления.
Новая успешная копия не доказывает, что можно удалить все старые: повреждение приложения могло произойти раньше. При компрометации токена архивы в Яндекс.Диске могут быть удалены вместе с рабочими данными, поэтому дополнительное независимое или неизменяемое хранилище остаётся отдельным уровнем защиты.
9. Как восстановить копию на отдельной машине
На тестовом узле восстановите конфигурацию crypt из защищённого хранилища, предоставив ему только необходимый доступ. Скачайте выбранный набор в новый пустой каталог:
restore_dir="$HOME/restore-site01-demo"
mkdir -m 700 "$restore_dir"
rclone copy yandex-crypt:site01/CHOSEN_RUN "$restore_dir" --config "$HOME/.config/rclone/rclone.conf"
(cd "$restore_dir" && sha256sum -c SHA256SUMS)
mkdir -m 700 "$restore_dir/unpacked"
tar -xzf "$restore_dir/site.tar.gz" -C "$restore_dir/unpacked"
CHOSEN_RUN заменяется точным именем принятого набора; конфигурационный путь здесь принадлежит тестовому пользователю. Архив извлекают без root и в изолированную область. Проверьте состав, восстановите базу в отдельный экземпляр, отключите отправку писем, вебхуки и другие боевые интеграции тестового приложения.
Контрольная сумма выявляет изменение байтов, но не подтверждает доверенность архива, если злоумышленник способен заменить и файл, и манифест. Для более строгих требований манифест подписывают и хранят подтверждение отдельно.
10. Какие критерии означают, что backup действительно работает
Копия считается принятой, когда известен её состав и время, подготовка завершена без ошибок, загрузка и проверка содержимого успешны, ключ доступен организации, а восстановление проверено прикладными операциями. Отдельно измерьте время загрузки, скачивания и запуска приложения: именно оно показывает достижимость RTO.
Отрабатывайте потерю сервера и отсутствие исходной конфигурации rclone, а не только восстановление файла на том же узле. Проверьте, что аварийный администратор сможет найти инструкцию и получить ключ по контролируемой процедуре.
Яндекс.Диск в этой схеме — транспорт и место хранения зашифрованных поколений. Он полезен, когда дополняет согласованную стратегию копирования, контроль свежести и реальные тесты восстановления.
