Управление доступом
В Tarantool предусмотрено гибкое управление доступом к ресурсам базы данных. Основные понятия системы управления доступом Tarantool:
- Пользователь - это человек или программа, взаимодействующая с экземпляром Tarantool.
- Объект - это сущность, к которой можно предоставить доступ, например, спейс, индекс или функция.
- Привилегия – это право на выполнение определенных операций над конкретными объектами, например чтение, обновление данных или создание спейсов.
- Роль – это именованный набор привилегий, который можно предоставить пользователю.
Пользователь - это человек или программа, взаимодействующая с экземпляром Tarantool. Возможны разные типы пользователей, например:
- Администратор базы данных, отвечающий за общее управление базой данных. Администратор может создавать других пользователей и предоставлять им привилегии.
- Пользователь с ограниченным доступом к данным и хранимым функциям. Такие пользователи могут получать привилегии от администратора базы данных.
- Пользователи, используемые для взаимодействия между экземплярами Tarantool. Например, такие пользователи могут быть созданы для обеспечения репликации и шардирования в кластере Tarantool.
В Tarantool есть два встроенных пользователя:
-
admin– пользователь со всеми доступными административными привилегиями. Если подключение использует порт административной консоли, текущим пользователем являетсяadmin. Например,adminиспользуется при локальном подключении к экземпляру с помощью команды tt connect по имени экземпляра:$ tt connect app:instance001Чтобы разрешить удаленные подключения через бинарный порт от имени пользователя
admin, задайте пароль. -
guest– пользователь с минимальными привилегиями, используемый по умолчанию для удаленных подключений через бинарный порт. Например,guestиспользуется при подключении к экземпляру командой tt connect по IP-адресу и порту без указания имени пользователя:$ tt connect 192.168.10.10:3301
Также см. пример.
У любого пользователя (кроме guest) должен быть пароль. Если пароль не задан, пользователь не сможет подключаться к экземплярам
Tarantool.
Хэши паролей Tarantool
хранятся в системном спейсе _user. По умолчанию
Tarantool использует протокол CHAP для аутентификации пользователей и применяет хэширование SHA-1 к паролям. Так, если
пароль - 123456, его сохраненный хэш выглядит как строка a7SDfrdDKRBe5FaN2n3GftLKKtk=. В Tarantool Enterprise Edition можно
включить PAP-
аутентификацию с
алгоритмом хэширования SHA256.
В Tarantool Enterprise Edition предусмотрены средства повышения безопасности базы данных: обязательное использование надежных паролей, ограничение срока действия пароля и т. д. Подробнее см. в разделе Аутентификация.
Объект - это защищаемая сущность, к которой можно предоставить доступ. В Tarantool предусмотрен ряд объектных типов, для гибкого управления доступом к данным, хранимым функциям, определенным действиям и т. д.
Ниже приведено несколько примеров объектных типов:
universeпредставляет собой базу данных (box.schema) которая содержит объекты базы данных, включая спейсы, индексы, пользователей, роли, последовательности и функции. Предоставление привилегий на типuniverseдает пользователю доступ к любому объекту в базе данных.spaceпозволяет предоставлять привилегии для пользовательских или системных спейсов.functionпозволяет предоставлять привилегии для функций.
Привилегии, предоставленные пользователю, определяют, какие операции он может выполнять, например:
- Привилегии
readиwrite, предоставленные на объектspace, позволяют пользователю читать или изменять данные в указанном спейсе. - Привилегия
create, предоставленная на объектspace, позволяет пользователю создавать новые спейсы. - Привилегия
execute, предоставленная на объектfunction, позволяет пользователю выполнять указанную функцию. - Привилегия
session, предоставленная на объектuniverse, позволяет пользователю подключаться к экземпляру по IPROTO. - Привилегия
usage, предоставленная на объектuniverse, позволяет пользователю использовать свои привилегии на объектах базы данных (например, читать, записывать и изменять спейсы). - Привилегия
alter, предоставленная пользователю, позволяет ему изменять его собственные настройки, например, пароль. - Привилегия
drop, предоставленная пользователю, позволяет ему удалять пользователей.
Обратите внимание, что некоторые привилегии могут требовать доступа на чтение и запись к определенным системным спейсам. Например,
привилегия create, предоставленная на объект space, требует привилегий read и write для системного спейса
_space. Аналогично, предоставление возможности
создавать функции требует привилегий read и write на спейс
_func.
Роль - это совокупность привилегий, которая может быть назначена пользователям. Роли также могут назначаться другим ролям, образуя иерархию ролей.
В Tarantool предусмотрены следующие встроенные роли:
-
super– включает в себя все административные привилегии. -
public- включает в себя привилегии на чтение. Эта роль автоматически назначается новым пользователям при их создании. -
replication- может быть назначена пользователю, применяемому для обеспечения репликации в кластере. -
sharding– может быть назначена пользователю, применяемому для обеспечения шардирования в кластере.
Ниже приведены диаграммы, демонстрирующие, как привилегии могут быть предоставлены пользователю без ролей и с ними.
-
В этом примере пользователь получает привилегии без использования ролей:
user1 ── privilege1├─── privilege2└─── privilege3 -
В этом примере пользователь получает все привилегии, входящие в роль
role1, а также отдельные привилегии, назначенные напрямую:user1 ── role1 ── privilege1│ └─── privilege2├─── privilege3└─── privilege4 -
В этом примере роль
role2назначается ролиrole1. Это означает, что пользователь с рольюrole1получит все привилегии обеих ролей -role1иrole2.user1 ── role1 ── privilege1│ ├─── privilege2│ └─── role2│ ├─── privilege3│ └─── privilege4├─── privilege5└─── privilege6
Также см. пример.
Владелец объекта базы данных – это пользователь, который его создал. Владельцем
базы данных и объектов, созданных изначально (системных спейсов и пользователей по умолчанию), является
пользователь. Владельцы автоматически получают
привилегии на создаваемые ими объекты. Они могут
назначить эти привилегии другим пользователям или ролям с
помощью функций box.schema.user.grant() и box.schema.role.grant().
Сессия - это состояние подключения к Tarantool. Сессия содержит:
- Целочисленный идентификатор подключения.
- Текущего пользователя, связанного с подключением.
- Текстовое описание подключенного узла.
- Локальное состояние, например Lua-переменные и функции.
В Tarantool одна сессия может выполнять несколько транзакций параллельно. Каждая транзакция идентифицируется уникальным целочисленным идентификатором, который можно получить в начале транзакции с помощью функции box.session.sync().
В этом разделе приведён список минимально необходимых разрешений для следующих типовых сценариев работы с Tarantool.
Дополнительные ограничения и рекомендации, связанные с обновлением модуля crud,
см. в разделе Обновление модуля crud.
CRUD передаёт на хранилище (storage) имя пользователя, от имени которого операция была инициирована на роутере. Внутренний вызов выполняется через служебного пользователя vshard, после чего CRUD переключается на переданного пользователя и выполняет операцию с его правами.
Начиная с версии crud 1.6.0 (crud-ee 1.7.3) операции с данными выполняются с правами пользователя, от имени которого инициирован запрос на роутере.
Не рекомендуется выдавать прикладному пользователю право execute на universe, так как оно позволяет выполнять произвольный
Lua-код и существенно расширяет полномочия пользователя. Вместо этого следует выдавать execute точечно — только на необходимые
методы crud.*, которые вызываются через lua_call:
credentials:users:db_user:password: 'secret'privileges:- permissions: [execute]lua_call:- crud.select- crud.get- crud.insert- crud.replace- crud.update- crud.upsert- crud.delete
Минимальный набор прав для экземпляра хранилища зависит от используемой функциональности и версии CRUD.
-
Для чтения и записи данных пользователю требуются права на чтение и запись в пользовательские спейсы, например в спейс
bands:box.schema.user.grant('db_user', 'read,write', 'space', 'bands') -
Доступ к системным спейсам для маршрутизации:
readна_bucket— начиная с CRUD 1.7.0 требуется storage-операциямbucket_ref/unrefдля проверки состояния и принадлежности бакета.- При использовании DDL-метаданных требуется
readна_ddl_sharding_keyи_ddl_sharding_func. - Настройка прав на экземпляр хранилища через YAML:
credentials:users:db_user:password: 'secret'privileges:- permissions: [read]spaces: [_bucket]- permissions: [read]spaces: [_ddl_sharding_key, _ddl_sharding_func]- permissions: [read, write]spaces: [bands]
-
Внутренние вызовы vshard/CRUD на экземпляр хранилища. В некоторых конфигурациях (в частности, на Tarantool 2.x или при ручной настройке прав) для выполнения внутренних вызовов на экземпляр хранилища может потребоваться право
executeна набор служебных функцийvshard.storage.*. Не рекомендуется выдавать правоexecuteдля областиuniverse.
Чтобы создать нового пользователя, вызовите функцию box.schema.user.create(). В примере ниже пользователь создается без пароля:
/code_snippets/test/access_control/grant_user_privileges_test.lua
В этом примере пароль передается в параметре options:
/code_snippets/test/access_control/grant_user_privileges_test.lua
Чтобы задать или изменить пароль пользователя, используйте функцию box.schema.user.passwd(). В примере ниже пароль устанавливается для текущего авторизованного пользователя:
/code_snippets/test/access_control/grant_user_privileges_test.lua
Чтобы задать пароль для указанного пользователя, передайте имя пользователя и пароль, как показано ниже:
/code_snippets/test/access_control/grant_user_privileges_test.lua
Чтобы предоставить привилегии пользователю, используйте функцию
box.schema.user.grant().
В примере ниже пользователю testuser предоставляются привилегии на чтение спейса writers и на чтение и запись спейса books:
/code_snippets/test/access_control/grant_user_privileges_test.lua
Подробнее о предоставлении привилегий для различных типов объектов см. в разделе Предоставление привилегий.
Чтобы проверить, существует ли пользователь, вызовите функцию box.schema.user.exists():
box.schema.user.exists('testuser')--[[- true--]]
Чтобы получить информацию о привилегиях, предоставленных пользователю, вызовите функцию box.schema.user.info():
box.schema.user.info('testuser')--[[- - - execute- role- public- - read- space- writers- - read,write- space- books- - session,usage- universe-- - alter- user- testuser-–]]
В примере выше у пользователя testuser есть следующие привилегии:
- Привилегия
executeна рольpublicозначает, что эта роль назначена пользователю. - Привилегия
readна спейсwritersозначает, что пользователь может читать данные из этого спейса. - Привилегии
read,writeна спейсbooksозначают, что пользователь может читать и изменять данные в этом спейсе. - Привилегии
session,usageнаuniverseозначают следующее:session: пользователь может аутентифицироваться через IPROTO-соединение.usage: позволяет пользователю применять свои привилегии к объектам базы данных (например, читать и изменять данные в спейсе).
- Привилегия
alterпозволяет пользователюtestuserизменять собственные настройки, например, пароль.
Чтобы отозвать привилегии, используйте функцию
box.schema.user.revoke().
В примере ниже отзывается привилегия на запись в спейс books:
/code_snippets/test/access_control/grant_user_privileges_test.lua
Отзыв привилегии session на объект universe приводит к запрету подключения пользователя к экземпляру Tarantool:
/code_snippets/test/access_control/grant_user_privileges_test.lua
Имя текущего пользователя можно узнать с помощью функции box.session.user().
box.session.user()--[[- admin--]]
Текущего пользователя можно изменить:
-
Для подключения через административную консоль: с помощью функции box.session.su():
box.session.su('testuser')box.session.user()--[[- testuser–]] -
Для подключения через бинарный порт используйте команду AUTH протокола, поддерживаемую большинством клиентов.
-
При вызове хранимой функции через бинарный порт с помощью команды
CALL: если для функции включено свойство SETUID, Tarantool временно заменяет текущего пользователя на создателя функции, предоставляя все его привилегии на время выполнения функции.
Чтобы удалить пользователя, вызовите функцию box.schema.user.drop():
/code_snippets/test/access_control/grant_user_privileges_test.lua
Чтобы создать новую роль, вызовите функцию box.schema.role.create(). В этом примере создаются две роли:
/code_snippets/test/access_control/grant_roles_test.lua
Чтобы назначить роли привилегии, используйте функцию
box.schema.role.grant().
В примере ниже роли books_space_manager предоставляются привилегии на чтение и запись в спейс books:
/code_snippets/test/access_control/grant_roles_test.lua
В примере ниже роли writers_space_reader предоставляются привилегии на чтение спейса writers:
/code_snippets/test/access_control/grant_roles_test.lua
Подробнее о предоставлении привилегий на различные типы объектов см. в разделе Предоставление привилегий.
Ролям можно назначать другие роли. В примере ниже вновь созданной роли all_spaces_manager назначаются все привилегии,
назначенные ролям books_space_manager и writers_space_reader:
/code_snippets/test/access_control/grant_roles_test.lua
Чтобы назначить роль пользователю, используйте функцию box.schema.user.grant().
В примере ниже пользователю testuser назначаются привилегии, назначенные ролям books_space_manager и writers_space_reader:
/code_snippets/test/access_control/grant_roles_test.lua
Чтобы проверить существование роли, вызовите функцию box.schema.role.exists():
box.schema.role.exists('books_space_manager')--[[- true--]]
Чтобы получить информацию о привилегиях, назначенных роли, вызовите функцию box.schema.role.info():
box.schema.role.info('books_space_manager')--[[- - - read,write- space- books--]]
Если у роли есть привилегия execute на другие роли, это означает, что эти роли
назначены данной родительской роли:
box.schema.role.info('all_spaces_manager')--[[- - - execute- role- books_space_manager- - execute- role- writers_space_reader--]]
Чтобы отозвать роль у пользователя, отзовите привилегию execute на эту роль с помощью функции
box.schema.user.revoke().
В примере ниже у пользователя testuser отзывается роль books_space_reader:
/code_snippets/test/access_control/grant_roles_test.lua
Чтобы отозвать привилегии у роли, используйте функцию box.schema.role.revoke().
Чтобы удалить роль из системы, вызовите функцию box.schema.role.drop():
/code_snippets/test/access_control/grant_roles_test.lua
Чтобы предоставить привилегии пользователю или роли, используйте функции
box.schema.user.grant() и
box.schema.role.grant(),
которые имеют похожие сигнатуры и принимают одинаковый набор аргументов. Например, сигнатура box.schema.user.grant() выглядит
следующим образом:
box.schema.user.grant(username, permissions, object-type, object-name[, {options}])
-
username: имя пользователя, которому предоставляются указанные привилегии. -
permissions: строковое значение, представляющее привилегии, предоставляемые пользователю. Если привилегий несколько, они перечисляются через запятую без пробелов. -
object-type: объектный тип, на который предоставляются привилегии. -
object-name: имя объекта, на который предоставляются привилегии. Если вместоobject-nameпередать пустую строку ("") илиnil, привилегии будут предоставлены на все объекты указанного типа.
В приведенном ниже примере пользователь testuser получает привилегии на создание объектов любого типа:
box.schema.user.grant('testuser','read,write,create','universe')
В этом примере пользователь testuser может предоставлять доступ к созданым им объектам:
box.schema.user.grant('testuser','write','space','_priv')
В примере ниже пользователю testuser предоставляются привилегии на создание
спейсов:
box.schema.user.grant('testuser','create','space')box.schema.user.grant('testuser','write', 'space', '_schema')box.schema.user.grant('testuser','write', 'space', '_space')
Для создания спейсов также требуется привилегия write на некоторые системные спейсы.
Чтобы разрешить пользователю testuser удалять спейс со связанными объектами, добавьте ему следующие привилегии:
box.schema.user.grant('testuser','create,drop','space')box.schema.user.grant('testuser','write','space','_schema')box.schema.user.grant('testuser','write','space','_space')box.schema.user.grant('testuser','write','space','_space_sequence')box.schema.user.grant('testuser','read','space','_trigger')box.schema.user.grant('testuser','read','space','_fk_constraint')box.schema.user.grant('testuser','read','space','_ck_constraint')box.schema.user.grant('testuser','read','space','_func_index')
В примере ниже пользователь testuser получает привилегии на
создание индексов в спейсе writers:
box.schema.user.grant('testuser','create,read','space','writers')box.schema.user.grant('testuser','read,write','space','_space_sequence')box.schema.user.grant('testuser','write', 'space', '_index')
Чтобы разрешить пользователю testuser изменять индексы в спейсе writers, выдайте ему привилегии из примера ниже. Этот пример
предполагает, что индексы в спейсе writers созданы не пользователем testuser.
box.schema.user.grant('testuser','alter','space','writers')box.schema.user.grant('testuser','read','space','_space')box.schema.user.grant('testuser','read','space','_index')box.schema.user.grant('testuser','read','space','_space_sequence')box.schema.user.grant('testuser','write','space','_index')
Если пользователь testuser создал индексы в спейсе writers, для их изменения достаточно выдать следующие привилегии:
box.schema.user.grant('testuser','read','space','_space_sequence')box.schema.user.grant('testuser','read,write','space','_index')
В этом примере пользователю testuser предоставляются привилегии на
выбор данных из спейса writers:
box.schema.user.grant('testuser','read','space','writers')
В этом примере пользователю testuser разрешается читать и изменять данные в спейсе books:
box.schema.user.grant('testuser','read,write','space','books')
В этом примере пользователь testuser получает привилегии на создание
последовательностей:
box.schema.user.grant('testuser','create','sequence')box.schema.user.grant('testuser', 'read,write', 'space', '_sequence')
Чтобы разрешить пользователю testuser удалять последовательность, предоставьте ему следующие привилегии:
box.schema.user.grant('testuser','drop','sequence')box.schema.user.grant('testuser','write','space','_sequence_data')box.schema.user.grant('testuser','write','space','_sequence')
В этом примере пользователю testuser разрешено использовать функцию id_seq:next() для последовательности id_seq:
box.schema.user.grant('testuser','read,write','sequence','id_seq')
В следующем примере пользователю testuser разрешено использовать функции id_seq:set() или id_seq:reset() для
последовательности id_seq:
box.schema.user.grant('testuser','write','sequence','id_seq')
В этом примере пользователь testuser получает привилегии на создание
функций:
box.schema.user.grant('testuser','create','function')box.schema.user.grant('testuser','read,write','space','_func')
Чтобы разрешить пользователю testuser удалять функции, выдайте ему следующие привилегии:
box.schema.user.grant('testuser','drop','function')box.schema.user.grant('testuser','write','space','_func')
Чтобы разрешить пользователю testuser выполнять функцию с именем sum, выдайте ему следующие привилегии:
box.schema.user.grant('testuser','execute','function','sum')
Предоставление привилегии execute на объект типа lua_call дает пользователю возможность вызывать любую глобальную (доступную
через Lua-таблицу _G) пользовательскую Lua-функцию с помощью запроса IPROTO_CALL. Чтобы предоставить привилегию на конкретную
непостоянную функцию, указажите ее имя при выдаче привилегии lua_call.
function my_func_1() endfunction my_func_2() endbox.cfg({listen = 3301})box.schema.user.create('alice', {password = 'secret'})conn = require('net.box').connect(box.cfg.listen, {user = 'alice', password = 'secret'})box.schema.user.grant('alice', 'execute', 'lua_call', 'my_func_1')conn:call('my_func_1') -- okconn:call('my_func_2') -- access deniedbox.schema.user.grant('alice', 'execute', 'lua_call', 'box.session.su')conn:call('box.session.su', {'admin'}) -- ok
В этом примере пользователь testuser получает привилегии на создание других пользователей:
box.schema.user.grant('testuser','create','user')box.schema.user.grant('testuser', 'read,write', 'space', '_user')box.schema.user.grant('testuser', 'write', 'space', '_priv')
Чтобы пользователь testuser мог создавать новые роли, предоставьте ему следующие привилегии:
box.schema.user.grant('testuser','create','role')box.schema.user.grant('testuser', 'read,write', 'space', '_user')box.schema.user.grant('testuser', 'write', 'space', '_priv')
Чтобы пользователь testuser мог выполнять Lua-код, предоставьте ему привилегию execute на объект lua_eval:
box.schema.user.grant('testuser','execute','lua_eval')
Аналогично, для выполнения произвольного SQL-выражения требуется привилегия execute на объект sql:
box.schema.user.grant('testuser','execute','sql')
В примере ниже созданная Lua-функция выполняется от имени ее создателя, даже если она вызывается другим пользователем.
Сначала создаются два спейса (space1 и space2), и пользователю без пароля (private_user) предоставляется полный доступ к
ним. Затем определяется функция read_and_modify, и пользователь private_user становится ее создателем. Наконец, другому
пользователю (public_user) предоставляется привилегия на выполнение Lua-функций, созданных пользователем private_user.
box.schema.space.create('space1')box.schema.space.create('space2')box.space.space1:create_index('pk')box.space.space2:create_index('pk')box.schema.user.create('private_user')box.schema.user.grant('private_user', 'read,write', 'space', 'space1')box.schema.user.grant('private_user', 'read,write', 'space', 'space2')box.schema.user.grant('private_user', 'create', 'universe')box.schema.user.grant('private_user', 'read,write', 'space', '_func')function read_and_modify(key)local space1 = box.space.space1local space2 = box.space.space2local fiber = require('fiber')local t = space1:get{key}if t ~= nil thenspace1:put{key, box.session.uid()}space2:put{key, fiber.time()}endendbox.session.su('private_user')box.schema.func.create('read_and_modify', {setuid= true})box.session.su('admin')box.schema.user.create('public_user', {password = 'secret'})box.schema.user.grant('public_user', 'execute', 'function', 'read_and_modify')
Каждый раз, когда пользователь public_user вызывает эту функцию, она выполняется от имени ее создателя, private_user.
Тип объекта | Описание |
|---|---|
| База данных (box.schema), содержащая объекты базы данных, включая спейсы, индексы, пользователей, роли, последовательности и функции. Предоставление привилегий на объект типа |
| |
| Роль. |
| |
| |
| |
| Выполнение произвольного Lua-кода. |
| Вызов любой глобальной пользовательской Lua-функции. |
| Выполнение произвольного SQL-выражения. |
Привилегия | Тип объекта | Назначается ролям | Описание |
|---|---|---|---|
| Все | Да | Разрешает чтение данных указанного объекта. Например, эту привилегию можно использовать, чтобы разрешить пользователю выбирать данные из указанного спейса. |
| Все | Да | Разрешает обновление данных указанного объекта. Например, эту привилегию можно использовать, чтобы разрешить пользователю изменять данные в указанном спейсе. |
| Все | Да | Разрешает создание объектов указанного типа. Например, эту привилегию можно использовать, чтобы разрешить пользователю создавать новые спейсы. Обратите внимание, что эта привилегия требует привилегий |
| Все | Да | Разрешает изменение объектов указанного типа. Обратите внимание, что эта привилегия требует привилегий |
| Все | Да | Разрешает удаление объектов указанного типа. Обратите внимание, что эта привилегия требует привилегий |
|
| Да | Для |
|
| Нет | Разрешает пользователю подключаться к экземпляру по IPROTO. |
|
| Нет | Разрешает пользователю использовать свои привилегии к объектам базы данных (например, читать, записывать и изменять спейсы). |
Тип объекта | Подробности |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|