Версия:

Управление доступом

Управление доступом

Understanding security details is primarily an issue for administrators. However, ordinary users should at least skim this section to get an idea of how Tarantool makes it possible for administrators to prevent unauthorized access to the database and to certain functions.

Общая информация:

  • Существует метод, который с помощью паролей проверяет, что пользователи являются теми, за кого себя выдают (“аутентификация”).
  • Существует системный спейс _user, где хранятся имена пользователей и хеши паролей.
  • Существуют функции, чтобы дать определенным пользователям права совершать определенные действия (“привилегии”).
  • Существует системный спейс _priv, где хранятся привилегии. Когда пользователь пытается выполнить операцию, проводится проверка на наличие у него привилегии на выполнение такой операции (“управление доступом”).

Далее рассмотрим эти пункты более подробно.

Пользователи

Для любой локальной или удаленной программы, работающей с Tarantool’ом, есть текущий пользователь. Если удаленное соединение использует бинарный порт, то текущим пользователем, по умолчанию, будет „guest“ (гость). Если соединение использует порт для административной консоли, текущим пользователем будет „admin“ (администратор). При выполнении скрипта инициализации на Lua, текущим пользователем также будет ‘admin’.

Имя текущего пользователя можно узнать с помощью box.session.user().

Текущего пользователя можно изменить:

  • For a binary port connection – with the AUTH protocol command, supported by most clients;
  • Для соединения по порту для административной консоли и при выполнении скрипта инициализации на Lua — с помощью box.session.su;
  • For a binary-port connection invoking a stored function with the CALL command – if the SETUID property is enabled for the function, Tarantool temporarily replaces the current user with the function’s creator, with all the creator’s privileges, during function execution.

Пароли

У каждого пользователя (за исключением гостя „guest“) может быть пароль. Паролем является любая буквенно-цифровая строка.

Пароли Tarantool’а хранятся в системном спейсе _user с криптографической хеш-функцией, так что если паролем является ‘x’, хранится хеш-пароль в виде длинной строки, например ‘lL3OvhkIPOKh+Vn9Avlkx69M/Ck=‘. Когда клиент подключается к экземпляру Tarantool’а, экземпляр отправляет случайное значение соль, которое клиент должен сложить вместе с хеш-паролем перед отправкой на экземпляр. Таким образом, изначальное значение ‘x’ никогда не хранится нигде, кроме как в голове самого пользователя, а хешированное значение никогда не передается по сети, кроме как в смешанном с солью виде.

Примечание

For more details of the password hashing algorithm (e.g. for the purpose of writing a new client application), read the scramble.h header file.

Система не дает злоумышленнику определить пароли путем просмотра файлов журнала или слежения за активностью. Это та же система, несколько лет назад внедренная в MySQL, которой оказалось достаточно для объектов со средней степенью безопасности. Тем не менее, администраторы должны предупреждать пользователей, что никакая система не защищена полностью от постоянных длительных атак, поэтому пароли следует охранять и периодически изменять. Администраторы также должны рекомендовать пользователям выбирать длинные неочевидные пароли, но сами пользователи выбирают свои пароли и изменяют их.

Для управления паролями в Tarantool’е есть две функции: box.schema.user.password() для изменения пароля пользователя и box.schema.user.passwd() для получения хеш-пароля.

Владельцы и привилегии

Tarantool has one database. It may be called «box.schema» or «universe». The database contains database objects, including spaces, indexes, users, roles, sequences, and functions.

The owner of a database object is the user who created it. The owner of the database itself, and the owner of objects that are created initially – the system spaces and the default users – is „admin“.

Owners automatically have privileges for what they create. They can share these privileges with other users or with roles, using box.schema.user.grant requests. The following privileges can be granted:

  • „read“, e.g. allow select from a space
  • „write“, e.g. allow update on a space
  • „execute“, e.g. allow call of a function
  • „create“, e.g. allow box.schema.space.create (access to certain system spaces is also necessary)
  • „alter“, e.g. allow box.space.x.index.y:alter (access to certain system spaces is also necessary)
  • „drop“, e.g. allow box.sequence.x:drop (currently this can be granted but has no effect)
  • „usage“, e.g. whether any action is allowable regardless of other privileges (sometimes revoking „usage“ is a convenient way to block a user temporarily without dropping the user)
  • „session“, e.g. whether the user can „connect“.

To create objects, users need the „create“ privilege and at least „read“ and „write“ privileges on the system space with a similar name (for example, on the _space if the user needs to create spaces).

To access objects, users need an appropriate privilege on the object (for example, the „execute“ privilege on function F if the users need to execute function F). See below some examples for granting specific privileges that a grantor – that is, „admin“ or the object creator – can make.

To drop an object, users must be the object’s creator or be „admin“. As the owner of the entire database, „admin“ can drop any object including other users.

To grant privileges to a user, the object owner says grant(). To revoke privileges from a user, the object owner says revoke(). In either case, there are three or four parameters:

(user-name, privilege, object-type [, object-name])
  • user-name is the user (or role) that will receive or lose the privilege;
  • privilege is any of „read“, „write“, „execute“, „create“, „alter“, „drop“, „usage“, or „session“ (or a comma-separated list);
  • object-type is any of „space“, „index“, „sequence“, „function“, role-name, or „universe“;
  • object-name is what the privilege is for (omitted if object-type is „universe“).

Example for granting many privileges at once

In this example user „admin“ grants many privileges on many objects to user „U“, with a single request.

box.schema.user.grant('U','read,write,execute,create,drop','universe')

Examples for granting privileges for specific operations

In these examples the object’s creator grants precisely the minimal privileges necessary for particular operations, to user „U“.

-- So that 'U' can create spaces:
  box.schema.user.grant('U','create','universe')
  box.schema.user.grant('U','write', 'space', '_schema')
  box.schema.user.grant('U','write', 'space', '_space')
-- So that 'U' can  create indexes (assuming 'U' created the space)
  box.schema.user.grant('U','read', 'space', '_space')
  box.schema.user.grant('U','read,write', 'space', '_index')
-- So that 'U' can  create indexes on space T (assuming 'U' did not create space T)
  box.schema.user.grant('U','create','space','T')
  box.schema.user.grant('U','read', 'space', '_space')
  box.schema.user.grant('U','write', 'space', '_index')
-- So that 'U' can  alter indexes on space T (assuming 'U' did not create the index)
  box.schema.user.grant('U','alter','space','T')
  box.schema.user.grant('U','read','space','_space')
  box.schema.user.grant('U','read','space','_index')
  box.schema.user.grant('U','read','space','_space_sequence')
  box.schema.user.grant('U','write','space','_index')
-- So that 'U' can create users or roles:
  box.schema.user.grant('U','create','universe')
  box.schema.user.grant('U','read,write', 'space', '_user')
  box.schema.user.grant('U','write','space', '_priv')
-- So that 'U' can create sequences:
  box.schema.user.grant('U','create','universe')
  box.schema.user.grant('U','read,write','space','_sequence')
-- So that 'U' can create functions:
  box.schema.user.grant('U','create','universe')
  box.schema.user.grant('U','read,write','space','_func')
-- So that 'U' can grant access on objects that 'U' created
  box.schema.user.grant('U','read','space','_user')
-- So that 'U' can select or get from a space named 'T'
  box.schema.user.grant('U','read','space','T')
-- So that 'U' can update or insert or delete or truncate a space named 'T'
  box.schema.user.grant('U','write','space','T')
-- So that 'U' can execute a function named 'F'
  box.schema.user.grant('U','execute','function','F')
-- So that 'U' can use the "S:next()" function with a sequence named S
  box.schema.user.grant('U','read,write','sequence','S')
-- So that 'U' can use the "S:set()" or "S:reset() function with a sequence named S
  box.schema.user.grant('U','write','sequence','S')

Example for creating users and objects then granting privileges

Здесь создадим Lua-функциб, которая будет выполняться от ID пользователя, который является ее создателем, даже если она вызывается другим пользователем.

Для начала создадим два спейса („u“ и „i“) и дадим полный доступ к ним пользователю без пароля („internal“). Затем определим функцию („read_and_modify“), и пользователь без пароля становится создателем функции. Наконец, дадим другому пользователю („public_user“) доступ на выполнение Lua-функций, созданных пользователем без пароля.

box.schema.space.create('u')
box.schema.space.create('i')
box.space.u:create_index('pk')
box.space.i:create_index('pk')

box.schema.user.create('internal')

box.schema.user.grant('internal', 'read,write', 'space', 'u')
box.schema.user.grant('internal', 'read,write', 'space', 'i')
box.schema.user.grant('internal', 'create', 'universe')
box.schema.user.grant('internal', 'read,write', 'space', '_func')

function read_and_modify(key)
  local u = box.space.u
  local i = box.space.i
  local fiber = require('fiber')
  local t = u:get{key}
  if t ~= nil then
    u:put{key, box.session.uid()}
    i:put{key, fiber.time()}
  end
end

box.session.su('internal')
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')

Роли

Роль представляет собой контейнер для привилегий, которые можно предоставить обычным пользователям. Вместо того, чтобы предоставлять или отменять индивидуальные привилегии, можно поместить все привилегии в роль, а затем назначить или отменить роль.

Информация о роли хранится в спейсе _user, но третье поле кортежа — поле типа — это ‘роль’, а не ‘пользователь’.

В управлении доступом на основе ролей один из главных моментов — это то, что роли могут быть вложенными. Например, роли R1 можно предоставить привилегию «роль R2», то есть пользователи с ролью R1 тогда получат все привилегии роли R1 и роли R2. Другими словами, пользователь получает все привилегии, которые предоставляются ролям пользователя напрямую и опосредованно.

The „usage“ and „session“ privileges cannot be granted to roles.

Пример

-- Этот пример сработает для пользователя со множеством привилегий, например, 'admin'
        -- или для пользователя с заданной ролью 'super'
        -- Создать спейс T с первичным индексом
        box.schema.space.create('T')
        box.space.T:create_index('primary', {})
        -- Создать пользователя U1, чтобы затем можно было заменить текущего пользователя на U1
        box.schema.user.create('U1')
        -- Создать две роли, R1 и R2
        box.schema.role.create('R1')
        box.schema.role.create('R2')
        -- Предоставить роль R2 для роли R1, а роль R1 пользователю U1 (порядок не имеет значения)
        box.schema.role.grant('R1', 'execute', 'role', 'R2')
        box.schema.user.grant('U1', 'execute', 'role', 'R1')
        -- Предоставить привилегии чтение/запись на спейс T для роли R2
        -- (но не для роли R1 и не пользователю U1)
        box.schema.role.grant('R2', 'read,write', 'space', 'T')
        -- Изменить текущего пользователя на пользователя U1
        box.session.su('U1')
        -- Теперь вставка в спейс T сработает, потому что благодаря вложенным ролям
        -- у пользователя U1 есть привилегия записи в спейс T
        box.space.T:insert{1}

Для получения подробной информации о функциях Tarantool’а, связанных с управлением доступом на основе ролей, см. справочник по вложенному модулю box.schema.

Сессии и безопасность

Сессия — это состояние подключения к Tarantool’у. Она содержит:

  • идентификатор в виде целого числа, определяющий соединение,
  • текущий пользователь, использующий соединение,
  • текстовое описание подключенного узла и
  • локальное состояние сессии, например, переменные и функции на Lua.

В Tarantool’е отдельная сессия может выполнять несколько транзакций одновременно. Каждая транзакция определяется по уникальному идентификатору в виде целого числа, который можно запросить в начале транзакции с помощью box.session.sync().

Примечание

Чтобы отследить все подключения и отключения, можно использовать триггеры соединений и аутентификации.