Защитата на базата данни е важна за всяка настройка на MySQL. Потребителите са основата на всяка система. Що се отнася до системите за бази данни, аз обикновено ги мисля в две отделни групи:
- Потребители на приложение, услуга или програма - основно клиенти или клиенти, използващи услуга.
- Разработчици на бази данни, администратори, анализатори и др… - Тези, които поддържат, работят или наблюдават инфраструктурата на базата данни.
Въпреки че всеки потребител трябва да има достъп до базата данни на някакво ниво, тези разрешения не са създадени равни.
Например, клиентите и клиентите се нуждаят от достъп до данните за техните „свързани потребителски акаунти“, но дори и това трябва да се наблюдава с известно ниво на контрол. Някои таблици и данни обаче трябва да са строго забранени (напр. системни таблици).
Въпреки това:
- Аналитикът се нуждае от „достъп за четене “, за да съберете информация и прозрения чрез запитване на таблици...
- Разработчиците изискват множество разрешения и привилегии, за да извършват работата си...
- DBA се нуждае от 'root' или подобен тип привилегии, за да стартира шоуто...
- Купувачите на услуга трябва да видят своята история на поръчките и плащанията...
Можете да си представите (знам, че го правя) колко трудна е задачата да управлявате множество потребители или групи потребители в рамките на екосистемата на база данни.
В по-старите версии на MySQL среда за множество потребители е установена по малко монотонен и повтарящ се начин.
И все пак, версия 8 внедрява изключителна и мощна стандартна SQL функция - Роли - , която облекчава една от по-излишните области на целия процес:присвояване на привилегии на потребител.
И така, какво е роля в MySQL?
Със сигурност можете да посетите MySQL през 2018 г.:Какво има в 8.0 и други наблюдения, написах за блога Severalnines тук, където споменавам роли за преглед на високо ниво. Въпреки това, където аз само ги обобщих там, тази публикация изглежда да отиде по-дълбоко и да се съсредоточи единствено върху ролите.
Ето как онлайн документацията на MySQL дефинира роля:„Ролята на MySQL е именувана колекция от привилегии“.
Само това определение не изглежда ли полезно?
Но как?
Ще видим в примерите, които следват.
За отбелязване на предоставените примери
Примерите, включени в тази публикация, са в лична работна станция/среда за разработка и обучение за един потребител, така че бъдете сигурни и прилагайте онези най-добри практики, които са ви от полза за вашите конкретни нужди или изисквания. Показаните потребителски имена и пароли са чисто произволни и слаби.
Потребители и привилегии в предишни версии
В MySQL 5.7 роли не съществуват. Предоставянето на привилегии на потребителите се извършва индивидуално. За да разберем по-добре какво предоставят ролите, нека не ги използваме. Това изобщо няма смисъл, знам. Но с напредването на публикацията ще стане.
По-долу създаваме някои потребители:
CREATE USER 'reader_1'@'localhost' IDENTIFIED BY 'some_password';
CREATE USER 'reader_writer'@'localhost' IDENTIFIED BY 'another_password';
CREATE USER 'changer_1'@'localhost' IDENTIFIED BY 'a_password';
След това на тези потребители се предоставят някои привилегии:
GRANT SELECT ON some_db.specific_table TO 'reader_1'@'localhost';
GRANT SELECT, INSERT ON some_db.specific_table TO 'reader_writer'@'localhost';
GRANT UPDATE, DELETE ON some_db.specific_table TO 'changer_1'@'localhost';
Уви, радвам се, че свърши. Сега обратно към...
И точно така, имате заявка за внедряване на още двама потребители „само за четене“…
Обратно към чертожната дъска:
CREATE USER 'reader_2'@'localhost' IDENTIFIED BY 'password_2';
CREATE USER 'reader_3'@'localhost' IDENTIFIED BY 'password_3';
Предоставяйки им и привилегии:
GRANT SELECT ON some_db.specific_table TO 'reader_2'@'localhost';
GRANT ALL ON some_db.specific_table TO 'reader_3'@'localhost';
Можете ли да видите как това е по-малко от продуктивно, пълно с повторения и податливо на грешки? Но по-важното е, че хванахте ли грешката?
Браво за теб!
Докато предоставям привилегии за тези двама допълнителни потребители, случайно даде ВСИЧКИ привилегии на нов потребител reader_3.
Ами сега.
Грешка, която всеки може да направи.
Въведете MySQL роли
С ролите, голяма част от горните систематични присвояването и делегирането на привилегии могат да бъдат донякъде опростени .
Създаването на потребител основно остава същото, но присвоява привилегии чрез роли, които се различават:
mysql> CREATE USER 'reader_1'@'localhost' IDENTIFIED BY 'some_password';
Query OK, 0 rows affected (0.19 sec)
mysql> CREATE USER 'reader_writer'@'localhost' IDENTIFIED BY 'another_password';
Query OK, 0 rows affected (0.22 sec)
mysql> CREATE USER 'changer_1'@'localhost' IDENTIFIED BY 'a_password';
Query OK, 0 rows affected (0.08 sec)
mysql> CREATE USER 'reader_2'@'localhost' IDENTIFIED BY 'password_2';
Query OK, 0 rows affected (0.28 sec)
mysql> CREATE USER 'reader_3'@'localhost' IDENTIFIED BY 'password_3';
Query OK, 0 rows affected (0.12 sec)
Запитвайки системната таблица mysql.user, можете да видите, че тези новосъздадени потребители съществуват:
(Забележка:Имам няколко потребителски акаунта в тази среда за обучение/развитие и съм потиснал голяма част от изхода за по-добра яснота на екрана.)
mysql> SELECT User FROM mysql.user;
+------------------+
| User |
+------------------+
| changer_1 |
| mysql.infoschema |
| mysql.session |
| mysql.sys |
| reader_1 |
| reader_2 |
| reader_3 |
| reader_writer |
| root |
| | --multiple rows remaining here...
+------------------+
23 rows in set (0.00 sec)
Имам тази произволна таблица и примерни данни:
mysql> SELECT * FROM name;
+--------+------------+
| f_name | l_name |
+--------+------------+
| Jim | Dandy |
| Johhny | Applesauce |
| Ashley | Zerro |
| Ashton | Zerra |
| Ashmon | Zerro |
+--------+------------+
5 rows in set (0.00 sec)
Нека сега да използваме роли за установяване и присвояване на привилегии за новите потребители да използват таблицата с имена.
Първо създайте ролите:
mysql> CREATE ROLE main_read_only;
Query OK, 0 rows affected (0.11 sec)
mysql> CREATE ROLE main_read_write;
Query OK, 0 rows affected (0.11 sec)
mysql> CREATE ROLE main_changer;
Query OK, 0 rows affected (0.14 sec)
Забележете отново таблицата mysql.user:
mysql> SELECT User FROM mysql.user;
+------------------+
| User |
+------------------+
| main_changer |
| main_read_only |
| main_read_write |
| changer_1 |
| mysql.infoschema |
| mysql.session |
| mysql.sys |
| reader_1 |
| reader_2 |
| reader_3 |
| reader_writer |
| root |
| |
+------------------+
26 rows in set (0.00 sec)
Въз основа на този изход можем да предположим; че по същество ролите са всъщност самите потребители.
След това присвояване на привилегии:
mysql> GRANT SELECT ON practice.name TO 'main_read_only';
Query OK, 0 rows affected (0.14 sec)
mysql> GRANT SELECT, INSERT ON practice.name TO 'main_read_write';
Query OK, 0 rows affected (0.07 sec)
mysql> GRANT UPDATE, DELETE ON practice.name TO 'main_changer';
Query OK, 0 rows affected (0.16 sec)
Кратка интерлюдия
Чакай малко. Мога ли просто да вляза и да изпълнявам някакви задачи със самите ролеви акаунти? В крайна сметка те са потребители и имат необходимите привилегии.
Нека се опитаме да влезем в практиката база данни с ролята main_changer:
:~$ mysql -u main_changer -p practice
Enter password:
ERROR 1045 (28000): Access denied for user 'main_changer'@'localhost' (using password: YES
Простият факт, че ни се представя подкана за парола, е добра индикация, че не можем (поне в този момент). Както си спомняте, не зададох парола за нито една от ролите по време на създаването им.
Какво трябва да каже колоната authentication_string на системните таблици mysql.user?
mysql> SELECT User, authentication_string, password_expired
-> FROM mysql.user
-> WHERE User IN ('main_read_only', 'root', 'main_read_write', 'main_changer')\G
*************************** 1. row ***************************
User: main_changer
authentication_string:
password_expired: Y
*************************** 2. row ***************************
User: main_read_only
authentication_string:
password_expired: Y
*************************** 3. row ***************************
User: main_read_write
authentication_string:
password_expired: Y
*************************** 4. row ***************************
User: root
authentication_string: ***various_jumbled_mess_here*&&*&*&*##
password_expired: N
4 rows in set (0.00 sec)
Включих root потребителя сред имената на ролите за проверката на предиката IN(), за да демонстрирам просто, че има низ за автентикация, където ролите не го правят.
Този пасаж в документацията CREATE ROLE го изяснява добре:„Когато е създадена роля, е заключена, няма парола и й е присвоен плъгин за удостоверяване по подразбиране. (Тези атрибути на ролята могат да бъдат променени по-късно с оператора ALTER USER от потребители, които имат глобална привилегия CREATE USER.)"
Обратно към текущата задача, вече можем да възлагаме ролите на потребителите въз основа на необходимото им ниво на привилегии.
Забележете, че в командата не присъства клауза ON:
mysql> GRANT 'main_read_only' TO 'reader_1'@'localhost', 'reader_2'@'localhost', 'reader_3'@'localhost';
Query OK, 0 rows affected (0.13 sec)
mysql> GRANT 'main_read_write' TO 'reader_writer'@'localhost';
Query OK, 0 rows affected (0.16 sec)
mysql> GRANT 'main_changer', 'main_read_only' TO 'changer_1'@'localhost';
Query OK, 0 rows affected (0.13 sec)
Може да е по-малко объркващо, ако използвате някакъв вид „конвенция за именуване ' при установяване на имена на роли, (не знам дали MySQL предоставя такова в момента... Общност?), ако не по друга причина, освен за визуално разграничаване между тях и обикновените потребители без роля.
Остава още работа
Това беше супер лесно, нали?
По-малко излишен от стария начин на присвояване на привилегии.
Нека накараме тези потребители да работят сега.
Можем да видим предоставените привилегии за потребител със синтаксис SHOW GRANTS. Ето какво в момента е присвоено на потребителския акаунт на reader_1:
mysql> SHOW GRANTS FOR 'reader_1'@'localhost';
+------------------------------------------------------+
| Grants for [email protected] |
+------------------------------------------------------+
| GRANT USAGE ON *.* TO `reader_1`@`localhost` |
| GRANT `main_read_only`@`%` TO `reader_1`@`localhost` |
+------------------------------------------------------+
2 rows in set (0.02 sec)
Въпреки че това осигурява информативен изход, можете да „настройвате ' изявлението за още по-подробна информация за всички точни привилегии, предоставени от дадена роля, като включи клауза USING в оператора SHOW GRANTS и назова името на присвоените роли:
mysql> SHOW GRANTS FOR 'reader_1'@'localhost' USING 'main_read_only';
+-------------------------------------------------------------+
| Grants for [email protected] |
+-------------------------------------------------------------+
| GRANT USAGE ON *.* TO `reader_1`@`localhost` |
| GRANT SELECT ON `practice`.`name` TO `reader_1`@`localhost` |
| GRANT `main_read_only`@`%` TO `reader_1`@`localhost` |
+-------------------------------------------------------------+
3 rows in set (0.00 sec)
След като влезете с reader_1:
mysql> SELECT * FROM practice.name;
ERROR 1142 (42000): SELECT command denied to user 'reader_1'@'localhost' for table 'name'
Какво, за Бога? На този потребител бяха предоставени привилегии SELECT чрез ролята main_read_only.
За да проучим, нека да посетим 2 нови таблици във версия 8, специално за роли.
Таблицата mysql.role_edges показва какви роли са били предоставени на всички потребители:
mysql> SELECT * FROM mysql.role_edges;
+-----------+-----------------+-----------+---------------+-------------------+
| FROM_HOST | FROM_USER | TO_HOST | TO_USER | WITH_ADMIN_OPTION |
+-----------+-----------------+-----------+---------------+-------------------+
| % | main_changer | localhost | changer_1 | N |
| % | main_read_only | localhost | changer_1 | N |
| % | main_read_only | localhost | reader_1 | N |
| % | main_read_only | localhost | reader_2 | N |
| % | main_read_only | localhost | reader_3 | N |
| % | main_read_write | localhost | reader_writer | N |
+-----------+-----------------+-----------+---------------+-------------------+
6 rows in set (0.00 sec)
Но смятам, че другата допълнителна таблица, mysql.default_roles, ще ни помогне по-добре да решим проблемите SELECT за потребителя reader_1:
mysql> DESC mysql.default_roles;
+-------------------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+----------+------+-----+---------+-------+
| HOST | char(60) | NO | PRI | | |
| USER | char(32) | NO | PRI | | |
| DEFAULT_ROLE_HOST | char(60) | NO | PRI | % | |
| DEFAULT_ROLE_USER | char(32) | NO | PRI | | |
+-------------------+----------+------+-----+---------+-------+
4 rows in set (0.00 sec)
mysql> SELECT * FROM mysql.default_roles;
Empty set (0.00 sec)
Празен набор от резултати.
Оказва се, че за да може даден потребител да използва роля - и в крайна сметка привилегиите – на потребителя трябва да бъде назначена роля по подразбиране.
mysql> SET DEFAULT ROLE main_read_only TO 'reader_1'@'localhost', 'reader_2'@'localhost', 'reader_3'@'localhost';
Query OK, 0 rows affected (0.11 sec)
(Ролята по подразбиране може да бъде присвоена на множество потребители с една команда, както е по-горе...)
mysql> SET DEFAULT ROLE main_read_only, main_changer TO 'changer_1'@'localhost';
Query OK, 0 rows affected (0.10 sec)
(Потребителят може да има няколко определени роли по подразбиране, както в случая за потребителя changer_1…)
Потребителят reader_1 вече е влязъл...
mysql> SELECT CURRENT_USER();
+--------------------+
| CURRENT_USER() |
+--------------------+
| [email protected] |
+--------------------+
1 row in set (0.00 sec)
mysql> SELECT CURRENT_ROLE();
+----------------------+
| CURRENT_ROLE() |
+----------------------+
| `main_read_only`@`%` |
+----------------------+
1 row in set (0.03 sec)
Можем да видим текущо активната роля и също така, че reader_1 може да издава SELECT команди сега:
mysql> SELECT * FROM practice.name;
+--------+------------+
| f_name | l_name |
+--------+------------+
| Jim | Dandy |
| Johhny | Applesauce |
| Ashley | Zerro |
| Ashton | Zerra |
| Ashmon | Zerro |
+--------+------------+
5 rows in set (0.00 sec)
Други скрити нюанси
Има още една важна част от пъзела трябва да разберем.
Има потенциално 3 различни „нива“ или „варианти“ на присвояване на роли:
SET ROLE …;
SET DEFAULT ROLE …;
SET ROLE DEFAULT …;
Ще ПРЕДОСТАВЯ допълнителна роля на потребител reader_1 и след това ще вляза с този потребител (не е показано):
mysql> GRANT 'main_read_write' TO 'reader_1'@'localhost';
Query OK, 0 rows affected (0.17 sec)
Тъй като ролята main_read_write има привилегията INSERT, потребителят reader_1 вече може да изпълни тази команда, нали?
mysql> INSERT INTO name(f_name, l_name)
-> VALUES('Josh', 'Otwell');
ERROR 1142 (42000): INSERT command denied to user 'reader_1'@'localhost' for table 'name'
Какво става тук?
Това може да помогне...
mysql> SELECT CURRENT_ROLE();
+----------------------+
| CURRENT_ROLE() |
+----------------------+
| `main_read_only`@`%` |
+----------------------+
1 row in set (0.00 sec)
Припомняме, че първоначално зададохме на потребителя reader_1 роля по подразбиране на main_read_only. Тук трябва да използваме едно от онези отделни „нива“ на това, което свободно наричам 'настройка на роля':
mysql> SET ROLE main_read_write;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT CURRENT_ROLE();
+-----------------------+
| CURRENT_ROLE() |
+-----------------------+
| `main_read_write`@`%` |
+-----------------------+
1 row in set (0.00 sec)
Сега опитайте да INSERT отново:
mysql> INSERT INTO name(f_name, l_name)
-> VALUES('Josh', 'Otwell');
Query OK, 1 row affected (0.12 sec)
Въпреки това, след като потребителят reader_1 излезе отново, ролята main_read_write вече няма да бъде активна, когато reader_1 влезе отново. Въпреки че потребителят reader_1 има предоставена му роля main_read_write, тя не е по подразбиране.
Нека сега да се запознаем с 3-то „ниво“ на „настройка на ролята“, ЗАДАВАНЕ НА РОЛЯТА ПО ПОДРАЗБИРАНЕ.
Да предположим, че потребителят reader_1 все още няма назначени роли:
mysql> SHOW GRANTS FOR 'reader_1'@'localhost';
+----------------------------------------------+
| Grants for [email protected] |
+----------------------------------------------+
| GRANT USAGE ON *.* TO `reader_1`@`localhost` |
+----------------------------------------------+
1 row in set (0.00 sec)
Нека ПРЕДОСТАВИМ на този потребител 2 роли:
mysql> GRANT 'main_changer', 'main_read_write' TO 'reader_1'@'localhost';
Query OK, 0 rows affected (0.07 sec)
Задайте роля по подразбиране:
mysql> SET DEFAULT ROLE ‘main_changer’ TO 'reader_1'@'localhost';
Query OK, 0 rows affected (0.17 sec)
След това с влязъл потребител reader_1, тази роля по подразбиране е активна:
mysql> SELECT CURRENT_ROLE();
+--------------------+
| CURRENT_ROLE() |
+--------------------+
| `main_changer`@`%` |
+--------------------+
1 row in set (0.00 sec)
Сега превключете на ролята main_read_write:
mysql> SET ROLE 'main_read_write';
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT CURRENT_ROLE();
+-----------------------+
| CURRENT_ROLE() |
+-----------------------+
| `main_read_write`@`%` |
+-----------------------+
1 row in set (0.00 sec)
Но за да се върнете обратно към присвоената роля по подразбиране, използвайте SET ROLE DEFAULT, както е показано по-долу:
mysql> SET ROLE DEFAULT;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT CURRENT_ROLE();
+--------------------+
| CURRENT_ROLE() |
+--------------------+
| `main_changer`@`%` |
+--------------------+
1 row in set (0.00 sec)
Роли не са предоставени
Въпреки че user changer_1 има 2 налични роли по време на сесия:
mysql> SELECT CURRENT_ROLE();
+-----------------------------------------+
| CURRENT_ROLE() |
+-----------------------------------------+
| `main_changer`@`%`,`main_read_only`@`%` |
+-----------------------------------------+
1 row in set (0.00 sec)
Какво се случва, ако се опитате да зададете на потребител роля, която не му е предоставена?
mysql> SET ROLE main_read_write;
ERROR 3530 (HY000): `main_read_write`@`%` is not granted to `changer_1`@`localhost`
Отказано.
Взема
Нито една система за управление на потребителите не би била завършена без възможността за ограничаване или дори премахване на достъпа до определени операции, ако възникне необходимост.
Имаме командата SQL REVOKE на наше разположение, за да премахнем привилегиите от потребители и роли.
Припомнете си, че ролята main_changer има този набор от привилегии, по същество всички тези потребители, на които е предоставена тази роля, също имат:
mysql> SHOW GRANTS FOR main_changer;
+-----------------------------------------------------------------+
| Grants for [email protected]% |
+-----------------------------------------------------------------+
| GRANT USAGE ON *.* TO `main_changer`@`%` |
| GRANT UPDATE, DELETE ON `practice`.`name` TO `main_changer`@`%` |
+-----------------------------------------------------------------+
2 rows in set (0.00 sec)
mysql> REVOKE DELETE ON practice.name FROM 'main_changer';
Query OK, 0 rows affected (0.11 sec)
mysql> SHOW GRANTS FOR main_changer;
+---------------------------------------------------------+
| Grants for [email protected]% |
+---------------------------------------------------------+
| GRANT USAGE ON *.* TO `main_changer`@`%` |
| GRANT UPDATE ON `practice`.`name` TO `main_changer`@`%` |
+---------------------------------------------------------+
2 rows in set (0.00 sec)
За да разберем кои потребители е засегнала тази промяна, можем да посетим отново таблицата mysql.role_edges:
mysql> SELECT * FROM mysql.role_edges WHERE FROM_USER = 'main_changer';
+-----------+--------------+-----------+-----------+-------------------+
| FROM_HOST | FROM_USER | TO_HOST | TO_USER | WITH_ADMIN_OPTION |
+-----------+--------------+-----------+-----------+-------------------+
| % | main_changer | localhost | changer_1 | N |
+-----------+--------------+-----------+-----------+-------------------+
1 row in set (0.00 sec)
И можем да видим, че потребителят changer_1 вече няма привилегията DELETE:
mysql> SHOW GRANTS FOR 'changer_1'@'localhost' USING 'main_changer';
+--------------------------------------------------------------------------+
| Grants for [email protected] |
+--------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `changer_1`@`localhost` |
| GRANT UPDATE ON `practice`.`name` TO `changer_1`@`localhost` |
| GRANT `main_changer`@`%`,`main_read_only`@`%` TO `changer_1`@`localhost` |
+--------------------------------------------------------------------------+
3 rows in set (0.00 sec)
И накрая, ако трябва напълно да се отървем от роля, имаме командата DROP ROLE за това:
mysql> DROP ROLE main_read_only;
Query OK, 0 rows affected (0.17 sec)
И при запитване на таблицата mysql.role_edges, ролята main_read_only е премахната:
mysql> SELECT * FROM mysql.role_edges;
+-----------+-----------------+-----------+---------------+-------------------+
| FROM_HOST | FROM_USER | TO_HOST | TO_USER | WITH_ADMIN_OPTION |
+-----------+-----------------+-----------+---------------+-------------------+
| % | main_changer | localhost | changer_1 | N |
| % | main_read_write | localhost | reader_1 | N |
| % | main_read_write | localhost | reader_writer | N |
+-----------+-----------------+-----------+---------------+-------------------+
3 rows in set (0.00 sec)
(Бонус:Този фантастичен видеоклип в YouTube беше страхотен учебен ресурс за мен за Роли.)
Този пример за създаване на потребител, присвояване на роли и настройка е в най-добрия случай елементарен. И все пак ролите имат собствен набор от правила, които ги правят далеч от тривиални. Надявам се чрез тази публикация в блога да хвърля светлина върху онези области, които са по-малко интуитивни от другите, което позволява на читателите да разберат по-добре потенциалните употреби на роли в техните системи.
Благодаря ви, че прочетохте.