пятница, 7 февраля 2020 г.

Proxmox VE. Шифрованное ZFS хранилище.

Иногда возникает необходимость в организации защиты критических данных на виртуальной машине: выходом является использование зашифрованного хранилища. Ранее для реализации подобного функционала приходилось прибегать к сторонним инструментам, но, начиная с Proxmox VE v6.0, появилась поддержка шифрования наборов данных ZFS из коробки.
В общем случае набор данных создается поверх существующего пула командой:
zfs create <pool>/<new_dataset>
Для включения шифрования набора данных необходимо при его создании указать ряд опций (указываются с ключом -o):

Из man:

Создание зашифрованного набора данных требует указания свойств шифрования и keyformat во время создания, а также дополнительного расположения ключей и pbkdf2iters.
После ввода ключа шифрования созданный набор данных станет корнем шифрования. Все наборы данных-потомки по умолчанию наследуют свой ключ шифрования от корня шифрования, что означает, что загрузка, выгрузка или изменение ключа для корня шифрования будет неявно делать то же самое для всех наследующих наборов данных.
Если это наследование нежелательно, просто введите keyformat при создании дочернего набора данных или используйте ZFS change-key для разрыва существующей связи, создавая новый корень шифрования на дочернем наборе данных.
Обратите внимание, что дочерний формат ключа может совпадать с родительским при создании нового корня шифрования, и что изменение свойства шифрования само по себе не создает новый корень шифрования; это просто будет использовать другой набор шифров с тем же ключом, что и его корень шифрования. Единственное исключение состоит в том, что клоны всегда будут использовать ключ шифрования своего источника. В результате этого исключения некоторые свойства, связанные с шифрованием (а именно keystatus, keyformat, keylocation и pbkdf2iters), не наследуются подобно другим свойствам ZFS и вместо этого используют значение, определенное их корневым ключом шифрования. Наследование корня шифрования можно отслеживать с помощью свойства encryptionroot, доступного только для чтения.
encryption=off|on|aes-128-ccm|aes-192-ccm|aes-256-ccm|aes-128-gcm|aes-192-gcm|aes-256-gcm
Управляет набором шифров шифрования (блочный шифр, длина ключа и режим), используемым для этого набора данных. Требует, чтобы функция шифрования была включена в пуле. Требует, чтобы во время создания набора данных был установлен формат ключа.
Выбор encryption=on при создании набора данных означает, что будет выбран набор шифрования по умолчанию, который в настоящее время является aes-256-ccm. Чтобы обеспечить согласованную защиту данных, шифрование должно быть задано во время создания набора данных и не может быть изменено впоследствии.
keyformat=raw|hex|passphrase
Определяет, в каком формате будет предоставляться ключ шифрования пользователя. Это свойство устанавливается только при шифровании набора данных.
Необработанные ключи и шестнадцатеричные ключи должны иметь длину 32 байта (независимо от выбранного набора шифрования) и генерироваться случайным образом. Необработанный ключ может быть создан с помощью следующей команды:
# dd if=/dev/urandom of=/path/to/output/key bs=32 count=1
Длина парольных фраз должна составлять от 8 до 512 байт, и они будут обработаны через PBKDF2 перед использованием (см. свойство pbkdf2iters). Несмотря на то, что набор шифрования не может быть изменен после создания набора данных, keyformat может быть изменен с помощью ZFS change-key.
keylocation=prompt|file://</absolute/file/path>
Определяет, откуда будет загружен ключ шифрования пользователя по умолчанию для таких команд, как zfs load-key и zfs mount-l. Это свойство задается только для зашифрованных наборов данных, являющихся корнями шифрования. Если не указано, то по умолчанию используется prompt.
Несмотря на то, что набор шифрования не может быть изменен после создания набора данных, keyformat может быть изменен с помощью ZFS change-key. Если выбран параметр prompt, ZFS запросит ключ в командной строке, когда потребуется получить доступ к зашифрованным данным. Этот параметр также позволяет передавать ключ через STDIN, но пользователи должны быть осторожны, чтобы не размещать ключи, которые должны храниться в секрете в командной строке. Если выбран URI файла, то ключ будет загружен из указанного абсолютного пути к файлу.
pbkdf2iters=iterations
Определяет количество итераций PBKDF2, через которые должен быть выполнен ключ шифрования парольной фразы при его обработке в ключ шифрования. Это свойство определяется только в том случае, если включено шифрование и выбран формат ключа парольной фразы. Цель PBKDF2-значительно увеличить вычислительную сложность, необходимую для перебора парольной фразы пользователя. Это достигается путем принудительного выполнения злоумышленником каждой парольной фразы через вычислительно-дорогостоящую функцию хэширования много раз, прежде чем они получат ключ. Пользователь, который действительно знает пароль, должен будет заплатить эту цену только один раз. Поскольку процессоры становятся мощнее, это число должно быть достаточно большим, чтобы гарантировать, что атака перебором все еще невозможна. Текущее значение по умолчанию-350000, а минимальное-100000. Это свойство может быть изменено с помощью zfs change-key.

На практике это выглядит следующим образом:

  1. Создаем пул на свободном диске (можно использовать существующий пул, так же это может быть любой тип ZFS рэйда)
    zpool create -f <PoolName> /dev/sd[x]

    Внимание!
    Следует помнить, что для обеспечения возможности репликации виртуальных машин, следует обеспечить наличие общего для нод кластера именования ресурсов (соглашение об именах).

  2. Поверх пула создается набор данных с включенным шифрованием
    zfs create -o encryption=on -o keyformat=passphrase <PoolName>/<DatasetName>
    Enter passphrase:
    Re-enter passphrase:
    Зашифрованный набор данных создается и сразу монтируется, для того, чтоб он был доступен в дальнейшем, его следует подключать командой zfs load-key:
    zfs load-key <PoolName>/<DatasetName>
    zfs mount <PoolName>/<DatasetName>
    а отключать zfs unload-key:
    umount /<PoolName>/<DatasetName> 
    zfs unload-key <PoolName>/<DatasetName>
  3. Добавляем в кластер наше защищенное хранилище (выполняется 1 раз для кластера):
    pvesm add zfspool <StorageName> -pool <PoolName>/<DatasetName> --sparse
Задача создания защищенного хранилища решена!
P.S.
В большинстве случаев нет необходимости шифровать всю виртуальную машину: на мой взгляд достаточно создать виртуалке дополнительный диск на зашифрованном хранилище и монтировать его средствами операционной системы в нужное место.

4 комментария:

  1. Александр, спасибо за новую статью, прошу поправить опечатки:
    написано "а отключать zfs load-key" вместо "а отключать zfs unload-key"
    написано "системы в нудное место" вместо "системы в нужное место"

    ОтветитьУдалить
  2. вдогонку:
    написано "он был поступен" вместо "он был доступен"

    ОтветитьУдалить
  3. Ну уж тогда стоит вспомнить правило правописание "о" и "ё" после шипящих. Всё-таки "ключом". А за статью спасибо.

    ОтветитьУдалить