Пост

Собираем Samba NAS в Proxmox LXC. Часть 6: Immutable backup через ZFS snapshots

Настраиваем immutable backup для Samba NAS на Proxmox: ZFS snapshots, zfs hold, защита от удаления и health-check скрипт.

Собираем Samba NAS в Proxmox LXC. Часть 6: Immutable backup через ZFS snapshots

Задача

В предыдущих частях мы собрали Samba/NAS:

1
2
3
4
/srv/samba
├── public
├── share
└── secure

Теперь нужно решить главный вопрос:

1
как защитить данные от удаления, ошибки администратора и ransomware?

Обычный backup — это хорошо. Но если backup можно удалить так же легко, как обычный файл, то это не стратегия, а надежда.

В этой части сделаем immutable backup на уровне Proxmox/ZFS.

Backup без проверки и защиты от удаления — это не backup, а предположение, что всё будет хорошо.


Что такое immutable backup

Immutable backup — это резервная копия, которую нельзя изменить или удалить до снятия защиты.

Цель:

  • пережить ошибочный rm -rf;
  • пережить ransomware;
  • защититься от случайного удаления;
  • иметь точку восстановления;
  • сделать backup независимым от LXC-контейнера.

Почему не внутри LXC

Если Samba работает в LXC, может возникнуть желание делать защиту внутри контейнера:

1
chattr +i backup.tar.zst

Это лучше, чем ничего, но это слабый уровень защиты.

Проблемы:

  • root внутри контейнера может быть скомпрометирован;
  • chattr +i можно снять;
  • ransomware внутри контейнера видит файловую систему контейнера;
  • контейнер не должен управлять своей единственной защитой.

Правильнее выносить immutable-слой на уровень Proxmox host.


Архитектура

Пример:

1
2
3
4
5
6
7
8
Proxmox host
└── ZFS pool: NAS
    └── subvol-102-disk-0
        └── LXC container: samba
            └── /srv/samba
                ├── public
                ├── share
                └── secure

Ключевая мысль:

1
2
LXC отдаёт файлы по SMB.
Proxmox/ZFS защищает snapshots.

Если snapshot создан на хосте и на него поставлен zfs hold, контейнер не сможет его удалить.


Варианты immutable-подхода

ВариантГде работаетПлюсыМинусы
chattr +iext4/xfsпростоroot может снять флаг
Btrfs read-only snapshotsbtrfsбыстро, удобноroot может удалить snapshot
ZFS snapshots + holdZFSлучший вариант для Proxmoxтребует ZFS и дисциплины

Для Proxmox с ZFS самый интересный вариант:

1
2
zfs snapshot pool/dataset@backup-YYYY-MM-DD
zfs hold immutable pool/dataset@backup-YYYY-MM-DD

Шаг 1. Найти dataset контейнера

На Proxmox host:

1
pct config 102

Пример:

1
rootfs: NAS:subvol-102-disk-0,size=30G

Значит dataset:

1
NAS/subvol-102-disk-0

Проверим:

1
zfs list | grep subvol-102-disk-0

Шаг 2. Создать snapshot

1
zfs snapshot NAS/subvol-102-disk-0@backup-$(date +%F)

Проверить:

1
zfs list -t snapshot | grep subvol-102-disk-0

Шаг 3. Поставить hold

1
zfs hold immutable NAS/subvol-102-disk-0@backup-$(date +%F)

Проверить:

1
zfs holds NAS/subvol-102-disk-0@backup-$(date +%F)

Ожидаемый смысл:

1
пока есть hold с тегом immutable, snapshot нельзя удалить

Шаг 4. Проверить защиту

Пробуем удалить snapshot:

1
zfs destroy NAS/subvol-102-disk-0@backup-$(date +%F)

Если всё правильно, ZFS не даст удалить snapshot и сообщит, что он удерживается hold.

Проверка удаления нужна не для того, чтобы сломать backup, а чтобы убедиться, что защита реально работает.


Скрипт создания immutable snapshot

Создадим скрипт на Proxmox host:

1
nano create-immutable-zfs-snapshot.sh

Содержимое:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#!/usr/bin/env bash
set -Eeuo pipefail

# ============================================================
# SMB на bash. Часть 6. Immutable Backups
# Создание ZFS snapshot + hold для LXC dataset
# Запускать на Proxmox host, не внутри контейнера.
# ============================================================

DATASET="${DATASET:-NAS/subvol-102-disk-0}"
TAG="${TAG:-immutable}"
PREFIX="${PREFIX:-backup}"
DATE="$(date +%F)"
SNAP="${DATASET}@${PREFIX}-${DATE}"

require_root() {
  if [[ "${EUID}" -ne 0 ]]; then
    echo "Ошибка: запусти скрипт от root на Proxmox host"
    exit 1
  fi
}

check_dataset() {
  if ! zfs list -H -o name "${DATASET}" >/dev/null 2>&1; then
    echo "Ошибка: dataset не найден: ${DATASET}"
    echo "Проверь: zfs list"
    exit 1
  fi
}

create_snapshot() {
  if zfs list -H -t snapshot -o name | grep -qx "${SNAP}"; then
    echo "Snapshot уже существует: ${SNAP}"
  else
    echo "==> Создаём snapshot ${SNAP}"
    zfs snapshot "${SNAP}"
  fi
}

apply_hold() {
  if zfs holds "${SNAP}" | grep -q "${TAG}"; then
    echo "Hold уже установлен: ${TAG}"
  else
    echo "==> Ставим hold ${TAG}"
    zfs hold "${TAG}" "${SNAP}"
  fi
}

verify() {
  echo "==> Проверяем snapshot"
  zfs list "${SNAP}" >/dev/null

  echo "==> Проверяем hold"
  zfs holds "${SNAP}" | grep -q "${TAG}"

  echo
  echo "Immutable snapshot готов:"
  echo "  ${SNAP}"
}

main() {
  require_root
  check_dataset
  create_snapshot
  apply_hold
  verify
}

main "$@"

Запуск

1
2
chmod +x create-immutable-zfs-snapshot.sh
sudo DATASET="NAS/subvol-102-disk-0" ./create-immutable-zfs-snapshot.sh

Health-check immutable backup

Backup должен не только создаваться. Его нужно проверять.

Проверим:

  1. snapshot существует;
  2. snapshot имеет hold;
  3. ZFS pool без ошибок;
  4. snapshot не старше 24 часов.

Создай скрипт:

1
nano healthcheck-immutable-zfs-snapshot.sh

Содержимое:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#!/usr/bin/env bash
set -Eeuo pipefail

# ============================================================
# Health-check immutable ZFS snapshot
# Запускать на Proxmox host.
# ============================================================

DATASET="${DATASET:-NAS/subvol-102-disk-0}"
TAG="${TAG:-immutable}"
PREFIX="${PREFIX:-backup}"
DATE="$(date +%F)"
SNAP="${DATASET}@${PREFIX}-${DATE}"

EXIT_OK=0
EXIT_WARN=1
EXIT_CRIT=2

echo "=== ZFS Immutable Backup Health Check ==="
echo "Dataset : ${DATASET}"
echo "Snapshot: ${SNAP}"
echo

if ! zfs list -H -t snapshot -o name | grep -qx "${SNAP}"; then
  echo "CRITICAL: snapshot not found"
  exit "${EXIT_CRIT}"
fi

echo "OK: snapshot exists"

if ! zfs holds "${SNAP}" | grep -q "${TAG}"; then
  echo "CRITICAL: snapshot exists but hold '${TAG}' not found"
  exit "${EXIT_CRIT}"
fi

echo "OK: snapshot is immutable"

POOL="$(echo "${DATASET}" | cut -d/ -f1)"

if ! zpool status "${POOL}" >/tmp/zpool-status.$$; then
  echo "CRITICAL: cannot read zpool status for ${POOL}"
  rm -f /tmp/zpool-status.$$
  exit "${EXIT_CRIT}"
fi

if grep -q "errors: No known data errors" /tmp/zpool-status.$$; then
  echo "OK: ZFS pool has no known data errors"
else
  echo "WARNING: ZFS pool reports issues"
  cat /tmp/zpool-status.$$
  rm -f /tmp/zpool-status.$$
  exit "${EXIT_WARN}"
fi

rm -f /tmp/zpool-status.$$

CREATION="$(zfs get -H -o value creation "${SNAP}")"
CREATION_TS="$(date -d "${CREATION}" +%s)"
NOW_TS="$(date +%s)"
AGE_HOURS="$(( (NOW_TS - CREATION_TS) / 3600 ))"

if [[ "${AGE_HOURS}" -gt 24 ]]; then
  echo "WARNING: snapshot is older than 24 hours: ${AGE_HOURS}h"
  exit "${EXIT_WARN}"
fi

echo "OK: snapshot age ${AGE_HOURS}h"
echo
echo "BACKUP HEALTH: OK"
exit "${EXIT_OK}"

Запуск health-check

1
2
chmod +x healthcheck-immutable-zfs-snapshot.sh
sudo DATASET="NAS/subvol-102-disk-0" ./healthcheck-immutable-zfs-snapshot.sh

Ожидаемый результат:

1
2
3
4
5
6
OK: snapshot exists
OK: snapshot is immutable
OK: ZFS pool has no known data errors
OK: snapshot age 0h

BACKUP HEALTH: OK

Как удалить immutable snapshot

Удалять такой snapshot нужно осознанно.

Сначала снять hold:

1
zfs release immutable NAS/subvol-102-disk-0@backup-2026-05-10

Потом удалить:

1
zfs destroy NAS/subvol-102-disk-0@backup-2026-05-10

Проверить:

1
zfs list -t snapshot | grep subvol-102-disk-0

Снятие hold — административное действие. Его стоит логировать и не делать автоматически без политики retention.


Retention-политика

На старте можно использовать простую схему:

1
2
3
daily snapshots    → 7 дней
weekly snapshots   → 4 недели
monthly snapshots  → 6 месяцев

Но важно: если на snapshot стоит hold, его нельзя удалить обычной ротацией. Значит ротация должна:

  1. выбирать snapshot;
  2. проверять возраст;
  3. снимать hold только по политике;
  4. удалять snapshot;
  5. писать лог.

Это лучше вынести в отдельную статью, чтобы не смешивать создание backup и retention.


Что получилось в серии

Мы собрали Samba/NAS по частям:

ЧастьРезультат
1Архитектура public/share/secure/backup
2Рабочая шара [share]
3Защищённая шара [secure]
4Audit-логирование для [secure]
5Public read-only шара [public]
6Immutable backup на Proxmox/ZFS

Итог

Immutable backup лучше делать не внутри Samba-контейнера, а на уровне Proxmox/ZFS.

Итоговая схема:

1
2
3
4
5
6
7
Samba LXC
└── отдаёт файлы по SMB

Proxmox host
└── создаёт ZFS snapshots
    └── ставит zfs hold
        └── защищает backup от удаления

Такой подход заметно повышает устойчивость к ошибкам, ransomware и случайному удалению данных.

Авторский пост защищен лицензией CC BY 4.0 .

Популярные теги