Шифрування в MySQL: сховище ключів

Напередодні старту нового набору на курс "Бази даних" підготували вам переклад корисної статті.

Шифрування в MySQL: сховище ключів

Прозоре шифрування даних (Transparent Data Encryption, TDE) з'явилося в Percona Server for MySQL та MySQL досить давно. Але чи замислювалися ви колись про те, як воно працює під капотом і який вплив TDE може чинити на ваш сервер? У цій серії статей ми розглянемо, як TDE працює усередині. Почнемо зі зберігання ключів, оскільки воно потрібне для роботи будь-якого шифрування. Потім докладно розглянемо як працює шифрування Percona Server for MySQL/MySQL і які додаткові можливості є в Percona Server for MySQL.

MySQL Keyring

Keyring — це плагіни, які дозволяють серверу вимагати, створювати та видаляти ключі в локальному файлі (keyring_file) або на віддаленому сервері (наприклад, у HashiCorp Vault). Ключі завжди кешуються локально, щоб прискорити їхнє одержання.

Плагіни можна розділити на дві категорії:

  • Локальне сховище. Наприклад, локальний файл (ми називаємо це файловим сховищем ключів, file-based keyring).
  • Віддалене сховище. Наприклад Vault Server (ми називаємо це серверним сховищем ключів, server-based keyring).

Цей поділ є важливим, тому що різні типи сховищ поводяться трохи по-різному не тільки при зберіганні та отриманні ключів, але і при запуску.

При використанні файлового сховища при запуску в кеш завантажується весь вміст сховища: key id, key user, key type та сам ключ.

У разі серверного сховища (наприклад, сервер Vault) під час запуску завантажується лише key id та key user, тому отримання всіх ключів не уповільнює запуск. Ключі завантажуються ліниво. Тобто, сам ключ завантажується з Vault тільки тоді, коли він фактично знадобиться. Після завантаження ключ кешується в пам'яті, щоб у майбутньому не було необхідності звертатися за ним через з'єднання TLS до Vault Server. Далі розглянемо, яка інформація є у сховищі ключів.

Інформація про ключ містить наступне:

  • key id - Ідентифікатор ключа, наприклад:
    INNODBKey-764d382a-7324-11e9-ad8f-9cb6d0d5dc99-1
  • тип ключа — тип ключа, заснований на алгоритмі шифрування, що використовується, можливі значення: «AES», «RSA» або «DSA».
  • довжина ключа - Довжина ключа в байтах, AES: 16, 24 або 32, RSA 128, 256, 512 і DSA 128, 256 або 384.
  • користувач - Власник ключа. Якщо ключ є системним, наприклад, Master Key, це поле порожньо. Якщо ключ створюється за допомогою keyring_udf, це поле позначає власника ключа.
  • сам ключ

Ключ однозначно ідентифікується парою: key_id, user.

Також є відмінності у зберіганні та видаленні ключів.

Файлове сховище працює швидше. Можна припустити, що сховище ключів - це простий одноразовий запис ключа у файл, але ні - тут відбувається більше операцій. За будь-якої модифікації файлового сховища спочатку створюється резервна копія всього вмісту. Допустимо файл називається my_biggest_secrets, тоді резервна копія буде my_biggest_secrets.backup. Далі змінюється кеш (додаються або видаляються ключі) і, якщо все виконано успішно, кеш скидається у файл. В окремих випадках, таких як збій сервера, ви можете побачити цей файл резервної копії. Файл резервної копії видаляється під час наступного завантаження ключів (зазвичай після перезапуску сервера).

При збереженні або видаленні ключа в серверному сховищі, сховище має підключитися до сервера MySQL з командами "надіслати ключ" / "запитати видалення ключа" ("send the key" / "request key deletion").

Повернімося до швидкості запуску сервера. Крім того, що на швидкість запуску впливає саме сховище, є питання про те, скільки ключів зі сховища необхідно отримати при запуску. Звичайно, це особливо важливо для серверних сховищ. При запуску сервер перевіряє, який ключ необхідний для зашифрованих таблиць/табличних просторів та запитує ключ зі сховища. На "чистому" сервері з Master Key - шифруванням має бути один Master Key, який необхідно витягти зі сховища. Однак може знадобитися і більша кількість ключів, наприклад, коли на резервному сервері відновлюється резервна копія з основного сервера. У разі слід передбачити ротацію Master Key. Докладніше це буде розглянуто в майбутніх статтях, хоча тут я хотів би зазначити, що сервер, який використовує декілька Master Key, може запускатися трохи довше, особливо при використанні серверного сховища ключів.

Тепер поговоримо ще трохи про keyring_file. Коли я розробляв keyring_file, мене також турбувало, як перевіряти зміну keyring_file під час роботи сервера. У 5.7 перевірка виконувалася на основі статистики файлу, що не було ідеальним рішенням, і в 8.0 замінено на контрольну суму SHA256.

При першому запуску keyring_file обчислюється статистика файлу та контрольна сума, які запам'ятовуються сервером, та зміни застосовуються лише в тому випадку, якщо вони збігаються. При зміні файлу контрольна сума оновлюється.

Ми вже розглянули багато питань про сховища ключів. Однак є ще одна важлива тема, про яку часто забувають або розуміють неправильно - поділ ключів на сервери.

Що я маю на увазі? Кожен сервер (наприклад, Percona Server) у кластері повинен мати окреме місце на сервері Vault, де Percona Server повинен зберігати свої ключі. У кожному Master Key, збереженому в сховищі, міститься GUID сервера Percona Server всередині свого ідентифікатора. Чому це важливо? Уявіть, що у вас є лише один Vault Server і всі Percona Server у кластері використовують цей єдиний Vault Server. Проблема видається очевидною. Якби всі Percona Server використовували Master Key без унікальних ідентифікаторів, наприклад, id = 1, id = 2 і т. д., то всі сервери в кластері використовували один і той же Master Key. Що забезпечує GUID — розмежування між серверами. Навіщо говорити про поділ ключів між серверами, якщо вже існує унікальний GUID? Є ще один плагін – keyring_udf. За допомогою цього плагіна користувач сервера може зберігати свої ключі на сервері Vault. Проблема виникає, коли користувач створює ключ, наприклад, на сервері server1, а потім намагається створити ключ з таким самим ідентифікатором на server2, наприклад:

--server1:
select keyring_key_store('ROB_1','AES',"123456789012345");
1
--1 значит успешное завершение
--server2:
select keyring_key_store('ROB_1','AES',"543210987654321");
1

Зачекайте. Обидва сервери використовують той самий Vault Server, чи не повинна функція keyring_key_store завершитися з помилкою на сервері server2? Цікаво, що якщо ви спробуєте зробити те саме на одному сервері, то отримаєте помилку:

--server1:
select keyring_key_store('ROB_1','AES',"123456789012345");
1
select keyring_key_store('ROB_1','AES',"543210987654321");
0

Вірно, ROB_1 вже існує.

Давайте спочатку обговоримо другий приклад. Як ми вже говорили раніше, keyring_vault або будь-яка інша плагін сховищ (keyring) кешує всі ідентифікатори ключів у пам'яті. Таким чином, після створення нового ключа, ROB_1 додається на server1, і, крім відправки цього ключа Vault, ключ також додається в кеш. Тепер, коли ми намагаємося додати такий самий ключ вдруге, keyring_vault перевіряє, чи існує цей ключ у кеші і видає помилку.

У першому випадку ситуація інша. На серверах server1 та server2 є окремі кеші. Після додавання ROB_1 у кеш ключів на сервері server1 та сервер Vault, кеш ключів на server2 не синхронізовано. У кеші на server2 немає ключа ROB_1. Таким чином, ключ ROB_1 записується в keyring_key_store і сервер Vault, який фактично переписує (!) попереднє значення. Тепер ключ ROB_1 на сервері Vault дорівнює 543210987654321. Цікаво, що сервер Vault не блокує такі дії і просто переписує старе значення.

Тепер ми бачимо, чому поділ серверів на Vault може бути важливим - коли ви використовуєте keyring_udf і хочете зберігати ключі в Vault. Як забезпечити такий поділ на сервері Vault?

Є два способи поділу на Vault. Можна створити різні точки монтування для кожного сервера або використовувати різні шляхи всередині однієї точки монтування. Найкраще це показати на прикладах. Отже, погляньмо спочатку на окремі точки монтування:

--server1:
vault_url = http://127.0.0.1:8200
secret_mount_point = server1_mount
token = (...)
vault_ca = (...)

--server2:
vault_url = http://127.0.0.1:8200
secret_mount_point = sever2_mount
token = (...)
vault_ca = (...)

Тут видно, що server1 та server2 використовують різні точки монтування. При розділенні шляхів конфігурація буде виглядати так:

--server1:
vault_url = http://127.0.0.1:8200
secret_mount_point = mount_point/server1
token = (...)
vault_ca = (...)
--server2:
vault_url = http://127.0.0.1:8200
secret_mount_point = mount_point/sever2
token = (...)
vault_ca = (...)

В даному випадку обидва сервери використовують ту саму точку монтування «mount_point», але різні шляхи. При створенні першого секрету на сервері server1 цим шляхом сервер Vault автоматично створює директорію «server1». Для server2 все аналогічно. Коли ви видаляєте останній секрет у mount_point/server1 або mount_point/server2, сервер Vault також видаляє ці директорії. У випадку, якщо ви використовуєте розділення шляхів, ви повинні створити лише одну точку монтування та змінити конфігураційні файли, щоб сервери використовували окремі шляхи. Точку монтування можна створити за допомогою запиту HTTP. За допомогою CURL це можна зробити так:

curl -L -H "X-Vault-Token: TOKEN" –cacert VAULT_CA
--data '{"type":"generic"}' --request POST VAULT_URL/v1/sys/mounts/SECRET_MOUNT_POINT

Усі поля (TOKEN, VAULT_CA, VAULT_URL, SECRET_MOUNT_POINT) відповідають параметрам конфігураційного файлу. Звичайно, можна використовувати утиліти Vault, щоб зробити те саме. Але так простіше автоматизувати створення точки монтування. Сподіваюся, ця інформація виявиться вам корисною, і ми побачимося в наступних статтях цієї серії.

Шифрування в MySQL: сховище ключів

Читати ще:

Джерело: habr.com

Додати коментар або відгук