Многим организациям, особенно финансовым, приходится иметь дело с различными стандартами безопасности, такими как PCI DSS. Такие сертификаты требуют шифрования данных.
Прозрачное шифрование данных на диске реализовано во многих промышленных СУБД.
Apache Ignite используется в банках, поэтому было решено внедрить TDE в нем.
Я расскажу вам, как мы разрабатывали TDE через сообщество, публично, через процессы Apache. Ниже представлена текстовая версия отчета: Я постараюсь рассказать об архитектуре, сложности разработки, о том, как это на самом деле выглядит в open source.
Что сделано и что предстоит?
Apache Ignite TDE в настоящее время реализован.Фаза 1. Он включает в себя базовые возможности по работе с зашифрованными кэшами:
- Ключевой менеджмент
- Создание зашифрованных кешей
- Сохранение всех данных кэша на диск в зашифрованном виде
На этапе 3 появилась возможность ротации ключей кэша.
Терминология
- Transparent Data Encryption – прозрачное (для пользователя) шифрование данных при сохранении на диск.
В случае с Ignite — шифрование кэша, потому что Ignite — это кэши.
- Кэш Ignite — это кеш «ключ-значение» в Apache Ignite. Данные кэша могут быть сохранены на диск
- Страницы — страницы данных.
В Ignite все данные разделены на страницы.
Страницы записываются на диск и должны быть зашифрованы.
- WAL - предварительная запись журнала.
Там сохраняются все изменения данных в Ignite, все действия, которые мы совершали для всех кэшей.
- Keystore — это стандартное хранилище ключей Java, создаваемое Java keytool. Он работает и сертифицирован везде, где мы его использовали.
- Мастер-ключ – мастер-ключ.
Он используется для шифрования ключей таблиц и кэширования ключей шифрования.
Хранится в хранилище ключей Java.
- Ключи кэша — это ключи, с помощью которых данные фактически шифруются.
Вместе с отмычкой получается двухуровневая структура.
Мастер-ключ хранится отдельно от ключей кэша и основных данных — в целях безопасности, разделения прав доступа и т. д.
Архитектура
Все было реализовано по следующей схеме:- Все данные кэша шифруются с помощью нового Encryption SPI.
- По умолчанию используется AES, промышленный алгоритм шифрования.
- Мастер-ключ хранится в файле JKS — стандартном Java-файле ключей.
Понятно, что мы предусмотрели возможность подсунуть собственный Encryption SPI — ту реализацию шифрования, которая нужна конкретному пользователю.
Схема работы
Итак, у нас есть ОЗУ — оперативная память со страницами, содержащими чистые данные.
Использование оперативной памяти означает, что мы не защищаемся от хакера, который получил root-доступ и сбросил всю память.
Защищаемся от администратора, который берет жесткий диск и продает его на Тушинском рынке (или там, где сейчас продаются подобные данные).
Помимо страниц кэша, данные также сохраняются в журнале упреждающей записи, который записывает на диск дельту записей, измененных в ходе транзакции.
В хранилище метаданных хранятся ключи шифрования кэша.
И в отдельном файле есть мастер-ключ.
Каждый раз, когда создается ключ кэша, прежде чем он будет записан или отправлен в сеть, мы шифруем этот ключ с помощью главного ключа.
Чтобы никто не мог получить ключ кэша путем получения данных Ignite. Только украв мастер-ключ и данные, вы сможете получить к ним доступ.
Это маловероятно, поскольку для доступа к этим файлам требуются разные разрешения.
Алгоритм действий следующий:
- При запуске ноды читаем мастер-ключ из jks.
- При запуске узла мы читаем метахранилище и расшифровываем ключи кэша.
- При объединении узлов в кластер:
— проверяем хеши мастер-ключа.
— проверяем ключи для общих кешей.
— сохраняем ключи для новых тайников.
- При динамическом создании кеша мы генерируем ключ и сохраняем его в метахранилище.
- При чтении/записи страницы мы ее расшифровываем/шифруем.
- Мы также шифруем каждую запись WAL для зашифрованного кеша.
У нас уже есть главный ключ, поэтому мы можем расшифровать ключи и получить доступ к данным кэша.
Отдельно есть очень интересный процесс — как нам объединить новую ноду в кластер.
У нас уже есть распределенная система, состоящая из нескольких узлов.
Как убедиться, что новый узел настроен правильно и что это не злоумышленник? Выполняем следующие действия:
- Когда прибывает новый узел, он отправляет хэш главного ключа.
Проверяем, что оно соответствует существующему
- Затем проверяем ключи для общих кешей.
Идентификатор кэша и зашифрованный ключ кэша поступают от узла.
Мы проверяем их, чтобы убедиться, что все данные на всех узлах зашифрованы одним и тем же ключом.
Если это не так, то мы просто не имеем права пускать ноду в кластер — иначе будет разброс ключей и данных.
- Если у нового узла есть какие-то новые ключи и кэши, мы сохраняем их для дальнейшего использования.
- При динамическом создании кэша предусмотрена функция генерации ключей.
Генерируем его, сохраняем в метахранилище и дальше можем проводить описанные операции.
Страницы записываются в файл раздела.
Наше дополнение проверяет, из каких страниц кэша взяты, соответствующим образом шифрует их и сохраняет. То же самое касается WAL. Существует сериализатор, который сериализует объекты записей WAL. А если запись для зашифрованных кэшей, то мы должны ее зашифровать и только потом сохранять на диск.
Трудности в разработке
Трудности свойственны всем более или менее сложным проектам с открытым исходным кодом:- Для начала нужно в общих чертах разобраться с устройством Ignite. Зачем, что и как там делалось, как и в какие места прикреплять свои процессоры.
- Нам необходимо обеспечить обратную совместимость.
Это может быть довольно сложно и не очевидно.
Когда вы разрабатываете продукт, который используют другие, нужно учитывать, что пользователи хотят без проблем обновляться.
Обратная совместимость – это правильно и хорошо.
Когда вы делаете такую большую модификацию, как TDE, вы меняете правила сохранения на диск и что-то шифруете.
И нам предстоит поработать над обеспечением обратной совместимости.
- Еще один неочевидный момент связан с распространением нашей системы.
Когда разные клиенты пытаются создать один и тот же кэш, необходимо согласовать ключ шифрования, поскольку по умолчанию будут сгенерированы два разных ключа.
Мы решили эту проблему.
Подробно вдаваться не буду — решение заслуживает отдельного поста.
Теперь мы гарантированно используем один ключ.
- Следующее важное событие привело к большому количеству доработок, когда казалось, что все уже готово (звучит знакомо?) :).
Шифрование имеет накладные расходы.
У нас есть вектор инициализации — нулевые случайные данные, которые используются в алгоритме AES. Они хранятся в открытом виде, и с их помощью мы увеличиваем энтропию: одни и те же данные в разных сеансах шифрования будут зашифрованы по-разному.
Грубо говоря, даже если у нас есть два Ивана Петрова с одинаковой фамилией, то каждый раз при шифровании мы будем получать разные зашифрованные данные.
Это снижает вероятность взлома.
Шифрование происходит блоками по 16 байт, и если данные не выровнены по 16 байт, то мы добавляем информацию о дополнении — сколько данных мы на самом деле зашифровали.
Вам необходимо записать на диск страницу размером кратным 2 КБ.
Таковы требования к производительности: мы должны использовать дисковый буфер.
Если мы запишем не 2 КБ (не 4 и не 8, в зависимости от буфера диска), то сразу получим большое падение производительности.
Как мы решили проблему? Пришлось зайти в PageIO, в оперативную память, и отрезать от каждой страницы по 16 байт, которые бы шифровались при записи на диск.
Записываем вектор инициализации в эти 16 байт.
- Еще одна сложность – ничего не сломать.
Это обычное дело, когда вы приходите и вносите какие-то изменения.
На самом деле все не так просто, как кажется.
- MVP получился 6 тысяч строк.
Пересматривать сложно, да и мало кто хочет этим заниматься, особенно специалисты, у которых и так нет времени.
У нас есть разные части — общедоступный API, основная часть, менеджеры SPI, постоянное хранилище страниц, менеджеры WAL. Изменения в разных подсистемах требуют, чтобы их анализировали разные люди.
И это тоже накладывает дополнительные трудности.
Особенно, когда вы работаете в сообществе, где каждый занят своими делами.
Тем не менее, нам это удалось.
Что произойдет в TDE.Фазы 2 и 3
Фаза 1 уже реализована.Вы, как разработчик, можете помочь на втором этапе.
Предстоящие задачи будут интересными.
PCI DSS, как и другие стандарты, требует дополнительных свойств системы шифрования.
Наша система должна иметь возможность менять мастер-ключ.
Например, если он был скомпрометирован или просто пришло время в соответствии с политикой безопасности.
Ignite не может сделать это прямо сейчас.
Но в следующих выпусках мы научим TDE менять мастер-ключ.
То же самое и с возможностью изменить ключ кэша без остановки кластера и работы с данными.
Если кеш долгоживущий и при этом хранит какие-то данные — финансовые, медицинские — Ignite должна иметь возможность менять ключ шифрования кеша и перешифровать все на лету.
Эту проблему мы решим на третьем этапе.
Итого: Как реализовать большую функцию в проекте с открытым исходным кодом?
Подведем итоги.Они будут актуальны для любого открытого кода.
Я участвовал в Kafka и других проектах — история везде одна и та же.
- Начните с небольших задач.
Никогда не беритесь за очень большую проблему сразу.
Нам нужно разобраться, что происходит, как это происходит, как это реализуется.
Кто вам поможет? И вообще – с какой стороны подойти к этому проекту.
- Понять проект. Обычно все разработчики — по крайней мере я — приходят и говорят: надо всё переписать.
До меня все было плохо, но сейчас перепишу и все будет хорошо.
С такими высказываниями желательно остановиться, разобраться, что именно плохо и нужно ли это менять.
- Обсудите, нужны ли улучшения.
У меня были случаи, когда я приходил со своими работами в различные сообщества — например, в Spark. Я ему рассказал, но сообщество почему-то не заинтересовалось.
Все может случиться.
Вам нужно это улучшение, но сообщество говорит: нет, нам не интересно, мы не будем сливаться и помогать.
- Сделайте дизайн.
Есть проекты с открытым исходным кодом, в которых это является обязательным.
Вы не можете начать кодирование без проекта, согласованного коммиттерами и опытными людьми.
В Ignite формально этого нет, но в целом это важная часть разработки.
Вам необходимо составить описание на грамотном английском или русском языке, в зависимости от проекта.
Чтобы текст был читабелен и из него было понятно, что именно вы собираетесь делать.
- Обсудите общедоступный API. Главный аргумент: если есть красивый и понятный публичный API, которым легко пользоваться, значит, дизайн правильный.
Эти вещи обычно соседствуют друг с другом.
- Реализуйте функцию, ничего не нарушив.
Делайте тесты.
- Спрашивайте и ждите (это может быть самое сложное) отзывов от правильных ребят, от правильных участников сообщества.
- Сделайте бенчмарки, выясните, есть ли у вас падение производительности.
Это особенно важно при доработке некоторых критически важных подсистем.
- Дождитесь мерджа, сделайте примеры и документацию.
-
Кубунту 9.10 - После Долгого Перерыва
19 Oct, 24 -
Системы Контроля И Управления Доступом
19 Oct, 24 -
О Выпуске Small Basic 0.2
19 Oct, 24 -
Дизайн И Текст Сайта
19 Oct, 24