В навечерието на старта на новия набор курс подготвихме за вас превод на полезна статия.

Прозрачното шифриране на данни (Transparent Data Encryption, TDE) съществува в и 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. Нека разгледаме каква информация присъства в хранилището на ключове.
Информацията за ключа съдържа следното:
- key id — идентификатор на ключа, например:
INNODBKey-764d382a-7324-11e9-ad8f-9cb6d0d5dc99-1 - key type — тип на ключа, основан на използвания алгоритъм за шифриране, възможни стойности: “AES”, “RSA” или “DSA”.
- key length — дължина на ключа в байтове, AES: 16, 24 или 32, RSA 128, 256, 512 и DSA 128, 256 или 384.
- user — собственик на ключа. Ако ключът е системен, например Master Key, това поле е празно. Ако ключът се създава с помощта на keyring_udf, това поле обозначава собственика на ключа.
- самият ключ
Ключът се идентифицира недвусмислено чрез двойка: key_id, user.
Има също така разлики в съхранението и изтриването на ключовете.
Файловото хранилище работи по-бързо. Може да се предположи, че хранилището на ключовете е просто еднократно записване на ключ в файл, но не е така — тук се извършват повече операции. При всяка модификация на файловото хранилище първо се създава резервно копие на цялото съдържание. Да предположим, че файлът се нарича my_biggest_secrets, резервното копие ще бъде my_biggest_secrets.backup. След това се променя кеша (добавят се или изтриват ключове) и, ако всичко е успешно, кеша се записва в файла. В редки случаи, като провал на сървъра, можете да видите това резервно копие. Резервното копие се изтрива при следващото зареждане на ключовете (обикновено след перезареждане на сървъра).
При запаметяване или изтриване на ключ в сървърното хранилище, хранилището трябва да се свърже с MySQL сървър с командите 'изпрати ключ' / 'изискай изтриване на ключ'.
Нека се върнем на скоростта на зареждане на сървъра. Освен че на скоростта на зареждане влияе самото хранилище, има също и въпросът колко ключа от хранилището е необходимо да се получат при стартиране. Разбира се, това е особено важно за сървърните хранилища. При стартиране сървърът проверява кой ключ е необходим за криптирани таблици / таблични пространства и изисква ключ от хранилището. На 'чист' сървър с 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, за да се направи същото. Но така е по-лесно да се автоматизира създаването на точка на монтиране. Надявам се тази информация да бъде полезна за вас и ще се видим в следващите статии от тази серия.
Прочетете още:
Източник: habr.com
