MAATRIX / Блог / Почему бэкап, доступный с сервера, — это не бэкап

Почему бэкап, доступный с сервера, — это не бэкап

MAATRIX

Смонтированная сетевая папка, к которой продакшн пишет каждую ночь, или локальная копия на соседнем разделе — выглядит как рабочая схема резервного копирования, пока не наступает день, когда нужно восстановиться именно от того, от чего бэкап и должен был защищать. Если сервер, который бэкап призван спасти, сам может дотянуться до этой копии на запись или удаление — это не резервная копия в инженерном смысле слова, а просто второй файл, зависящий от той же точки отказа. Разберём, почему так происходит, какую модель доступа нужно строить вместо этого и как это реализовать без отдельного штата инженеров по безопасности.

Тест на "это вообще бэкап?"

Прежде чем разбирать сценарии поломки, стоит зафиксировать критерий, по которому вообще стоит судить, является ли копия бэкапом. Формулируется он одним вопросом: может ли процесс, работающий на продакшн-сервере с его текущими правами, прочитать, изменить или удалить эту копию? Если да — это не бэкап, а зеркало, которое разделяет с оригиналом судьбу при любом инциденте на исходном сервере.

Три конфигурации проходят "визуальную" проверку (файлы есть, cron отрабатывает, размер растёт), но проваливают этот тест:

  • Смонтированная сетевая папка с правом записи. NFS- или SMB-экспорт, примонтированный на продакшене как rw, даёт не только класть туда новые файлы, но и стирать старые — отдельного режима "только дописывать" в стандартном монтировании нет.
  • Локальная копия на том же сервере. Второй раздел, другой диск в корпусе, RAID — всё это разделяет с оригиналом root-доступ. Скомпрометированному процессу с правом записи в /backup всё равно, локальный это путь или нет.
  • Удалённое хранилище с общими учётными данными. Копия физически в другом месте, но если для доступа используется тот же SSH- или S3-ключ, что даёт продакшену право на DELETE, изоляция существует только на бумаге.

Реальная защита определяется не физическим расстоянием между серверами, а правами прода по отношению к хранилищу копий. Расстояние без ограничения прав — это просто более длинный путь до того же результата.

Четыре сценария, где доступность бэкапа с прода убивает и оригинал, и копию

Шифровальщик. Программа-вымогатель, попавшая на сервер через уязвимое приложение или скомпрометированные учётные данные, не разбирает, что перед ней продакшн, а что — резервная копия. Она обходит все директории, доступные для записи процессу, от имени которого запущена, и шифрует всё подряд. Если хранилище бэкапов входит в этот список, шифруются и оригинал, и вся глубина истории копий за одну сессию.

Ошибочная команда очистки. Не нужен злой умысел — забытый find /mnt/backups -mtime +7 -delete в чужом cron-скрипте или опечатка в пути при ручной чистке места на диске сотрёт нужные файлы, если у выполняющего процесса есть право на запись в хранилище копий. Разбор похожего случая, когда старый cron-джоб чистил не ту директорию и удалял свежие дампы вместо временных файлов, — в статье хранилище бэкапов было доступно проду на запись.

Злонамеренный инсайдер. Сотрудник с обидой на компанию, подрядчик, к которому доступ не отозвали вовремя, — если у них есть те же права на продакшн-сервер, что дают доступ и к бэкапам, ничто не мешает удалить и то, и другое одним заходом.

Скомпрометированный root. Самый частый в реальности вариант: атакующий получает root не через экзотическую уязвимость, а банально — через утёкший SSH-ключ, слабый пароль на RDP или эскалацию привилегий через уязвимый пакет. С правами root процесс видит любые смонтированные тома, сохранённые в конфигах учётные данные, любые ключи в ~/.ssh. Если хоть один из этих путей ведёт к бэкап-хранилищу с правом записи, root на проде — это одновременно root и над бэкапами.

Общий механизм один: граница защиты бэкапа — это граница прав доступа продакшена, а не физическое или сетевое расстояние до хранилища.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Модель write-only: прод пишет, но не читает и не удаляет

Первый шаг к правильной модели — не полная изоляция (о ней дальше), а более простое и часто достаточное ограничение: продакшн может добавлять новые копии, но не может читать существующие и не может удалять ничего. Это резко снижает ущерб от компрометации прода: даже если атакующий получил полный контроль над сервером и его учётными данными для записи в бэкап, максимум, что он может сделать, — залить туда мусор или ещё одну копию текущих (уже скомпрометированных) данных. Историю прошлых, ещё не тронутых версий он стереть не может.

Практические реализации этого принципа:

BorgBackup в режиме append-only. На стороне сервера-приёмника репозиторий обслуживается через borg serve с флагом, запрещающим удаление и сжатие истории:

# в authorized_keys на бэкап-сервере, для ключа продакшена
command="borg serve --append-only --restrict-to-path /backups/prod-db",restrict ssh-ed25519 AAAA...

С таким ограничением на SSH-ключ продакшн физически не может выполнить borg prune или borg delete через этот канал — сервер отклонит операцию, даже если её попробует выполнить скомпрометированный клиент. Компакция и удаление старых архивов делаются отдельно, локально на бэкап-сервере, от имени, у которого нет способа быть скомпрометированным через прод.

Ограниченный rsync через command= и rrsync. В комплекте rsync идёт скрипт rrsync, который ограничивает операции конкретной директорией и запрещает удаление на стороне приёмника:

# authorized_keys на бэкап-сервере
command="rrsync -wo /backups/prod-web/",no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAA...

Флаг -wo (write-only) — ключевой: клиенту разрешена только запись новых файлов в указанный путь, чтение и удаление существующих недоступны через этот ключ в принципе, вне зависимости от того, что подключающийся сервер попытается отправить.

S3-совместимое хранилище с раздельными правами по IAM/ключам. У продакшена — ключ доступа с политикой, разрешающей только s3:PutObject, без s3:DeleteObject, s3:GetObject и s3:ListBucket. Отдельный ключ, с правом на чтение и удаление, существует только для операций восстановления и ротации и никогда не оказывается в конфигах на продакшн-сервере:

# отправка бэкапа с прода — ключ умеет только класть объекты
aws s3 cp /backup/db_2026-08-30.sql.gz s3://prod-backups/db/ \
  --profile write-only-prod

Тот же принцип раздельных ключей применим к любому самостоятельно развёрнутому S3-совместимому хранилищу.

Важная оговорка: NFS и SMB в классическом виде такого разграничения не дают — режим rw в экспорте включает и запись новых файлов, и перезапись или удаление существующих. Если инфраструктура завязана на сетевую файловую систему, для канала бэкапов её стоит либо заменить на SSH/S3-подход выше, либо хотя бы выдавать проду отдельный экспорт ro, а запись делать не с прода, а в обратном направлении — см. следующий раздел.

Модель полной изоляции: отдельная система со своей авторизацией

Write-only снижает ущерб, но не убирает канал полностью: у продакшена всё ещё есть валидные учётные данные, дающие доступ к бэкап-хранилищу, пусть и урезанный. Более сильная модель убирает этот канал целиком — учётные данные для доступа к бэкапам вообще не существуют на продакшн-сервере.

Достигается это разворотом направления инициации соединения. Вместо того чтобы продакшн отправлял данные на бэкап-сервер (push), бэкап-сервер сам забирает их с продакшена (pull):

# выполняется на бэкап-сервере, не на проде
rsync -avz -e "ssh -i /root/.ssh/backup_pull_key" \
  backup-reader@prod-db:/var/lib/postgresql/backups/ \
  /backups/prod-db/

На стороне продакшена для этого заводится отдельный пользователь backup-reader без shell, с доступом только на чтение к конкретной директории (например, куда pg_dump заранее выгружает дамп) и без прав на запись куда бы то ни было за её пределами. Ключевой эффект: скомпрометировав продакшн полностью, включая root, атакующий не находит там ни адреса бэкап-сервера с рабочими правами записи, ни ключа, которым можно было бы туда попасть, — потому что направление доступа развёрнуто, и инициатором всегда выступает бэкап-сервер, а не прод.

У модели pull есть цена: бэкап-сервер должен иметь сетевой доступ ко всем источникам, которые он опрашивает, и хранить у себя ключи для входа на каждый из них — то есть компрометация самого бэкап-сервера становится критичным событием для всей инфраструктуры разом. Поэтому эта роль обычно выделяется в отдельную систему с минимальной поверхностью атаки: ничего, кроме SSH-клиента и планировщика задач, никаких веб-приложений, никакого выхода в интернет кроме как к источникам бэкапов и, при необходимости, к вторичному хранилищу.

Отдельный сервер под эту роль удобно арендовать именно как выделенную машину без прочих сервисов — требования по CPU минимальны, важны объём диска и надёжность канала; подробнее о выборе конфигурации — в статье VPS для бэкапов и архива: что выбрать и как настроить. Общий принцип разнесения копии и оригинала на разные физические серверы также разбирается в статье про антипаттерн, когда бэкап лежит на том же сервере — там речь больше о физическом соседстве, здесь — о правах доступа поверх него.

Air-gapped и immutable-бэкапы: следующий уровень защиты

Изолированный бэкап-сервер решает проблему "прод достаёт до копии", но сам становится новой единственной точкой отказа: если скомпрометирован он, атакующий с достаточным терпением может добраться и до истории копий, которые он хранит. Для инфраструктуры, где цена потери данных особенно высока, к изоляции добавляют ещё два слоя.

Immutable-хранение (WORM — write once, read many). Копия, однажды записанная, не может быть изменена или удалена в течение заданного срока, даже с валидными учётными данными администратора хранилища:

  • S3 Object Lock в режиме Compliance или Governance — объект нельзя удалить или перезаписать до истечения retention-периода, даже вызовом DeleteObject с полными правами;
  • Версионирование бакета без Object Lock — мягче: DELETE создаёт delete-маркер, а не стирает данные физически, и версию можно восстановить;
  • ZFS snapshot с zfs hold — снапшот с меткой holds не удаляется командой zfs destroy до снятия метки, даже от root на этой же машине;
  • Borg append-only — compact и prune запускаются только с отдельной консоли, сам append-only режим уже частично реализует эту идею.

Air-gap. Хранилище без постоянного сетевого пути к продакшену и даже к изолированному бэкап-серверу — соединение появляется только на время синхронизации и сразу разрывается. Классика — ротация лент или внешних дисков, которые подключают раз в сутки и убирают в сейф; в облаке имитируется через файрвол-правило, открывающее вход по расписанию только на окно синхронизации.

Честная оговорка: air-gap и immutable усложняют и удорожают инфраструктуру, требуют более сложного восстановления и оправданы не для любого проекта. Для небольшого сервиса с некритичным простоем достаточно модели полной изоляции с pull из предыдущего раздела — эти два слоя стоит добавлять, когда цена потери данных исчисляется днями работы бизнеса, а не часами.

Практические реализации для небольшой инфраструктуры

Для типичной инфраструктуры из одного-трёх продакшн-серверов и без выделенной команды безопасности разумный набор мер выглядит так, от простого к более защищённому:

УровеньЧто даётВо что обходится
Write-only push (append-only Borg, rrsync -wo, S3-ключ только на PutObject)Компрометация прода не может удалить историю копийПочти бесплатно, меняются только конфиги
Pull-изоляция (отдельный бэкап-сервер сам забирает данные)У прода вообще нет учётных данных к бэкап-хранилищуОтдельный сервер под роль приёмника
Immutable-хранение (Object Lock, ZFS hold)Даже компрометация самого бэкап-сервера не удаляет данные мгновенноЧуть сложнее ротация и восстановление
Air-gapУстойчивость к любому сетевому вектору атаки, включая на бэкап-серверРучной или полуавтоматический процесс, дороже по железу

Для старта разумный минимум — второй и третий уровень одновременно, без air-gap:

  1. Завести отдельный сервер только под роль бэкап-приёмника, у другого провайдера или хотя бы в другом дата-центре.
  2. На продакшене создать пользователя без shell с доступом на чтение только к директории с готовыми дампами перед отправкой.
  3. На бэкап-сервере настроить cron, который сам инициирует rsync или borg create через SSH к продакшену, а не наоборот.
  4. Для S3-совместимого хранилища завести два ключа: один только на запись для прода, второй — на чтение и удаление, используемый вручную при восстановлении и никогда не хранящийся в автоматизации.
  5. Настроить алерт не только на факт выполнения задачи бэкапа (exit code 0), но и на изменение объёма хранилища — падение размера каталога вне окна ротации сигнализирует о проблеме раньше, чем это обнаружится при попытке восстановиться.

Проверить, что схема реально работает, а не просто выглядит настроенной, — отдельная задача: наличие файла в хранилище не гарантирует, что из него можно восстановиться, а базовые правила про количество копий и их размещение разобраны в статье правило 3-2-1 для бэкапов.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Если у прода и бэкап-сервера один и тот же SSH-ключ, разве вынос копии на другую машину не защищает?

Нет — если этим ключом можно и записывать, и удалять на стороне бэкап-сервера, компрометация прода даёт тот же результат, что и локальная копия на одном диске.

Как быстро понять, что мой бэкап именно write-only, а не просто "лежит на другом сервере"?

Подключитесь под теми же учётными данными, что использует продакшн для записи, и попробуйте удалить или перезаписать уже сохранённый файл. Получилось — это push с полным доступом, а не write-only.

Стоит ли сразу использовать S3 с Object Lock вместо связки "отдельный сервер + rsync"?

Если инфраструктура и так тяготеет к объектным хранилищам — да. Если стек построен на файловых серверах и SSH, добавить pull-модель с append-only обычно быстрее.

Что делать, если бюджет не позволяет держать отдельный сервер только под бэкапы?

Минимум, который стоит сделать бесплатно, — перевести существующий канал записи в write-only режим (append-only Borg, rrsync -wo, S3-ключ без Delete/Get). Полной изоляции это не даёт, но закрывает самый дешёвый в атаке сценарий.

Защищает ли шифрование самого бэкапа от описанных сценариев?

Нет, это решает другую задачу — конфиденциальность при краже носителя, а не право на удаление. Зашифрованный архив стирается так же легко, как и незашифрованный, если доступ к нему не ограничен.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →