Access control
Tarantool enables flexible management of access to various database resources. The main concepts of Tarantool access control system are as follows:
- A user is a person or program that interacts with a Tarantool instance.
- An object is an entity to which access can be granted, for example, a space, an index, or a function.
- A privilege allows a user to perform certain operations on specific objects, for example, creating spaces, reading or updating data.
- A role is a named collection of privileges that can be granted to a user.
A user identifies a person or program that interacts with a Tarantool instance. There might be different types of users, for example:
- A database administrator responsible for the overall management and administration of a database. An administrator can create other users and grant them specified privileges.
- A user with limited access to certain data and stored functions. Such users can get their privileges from the database administrator.
- Users used in communications between Tarantool instances. For example, such users can be created to maintain replication and sharding in a Tarantool cluster.
There are two built-in users in Tarantool:
admin
is a user with all available administrative privileges. If the connection uses an admin-console port, the current user isadmin
. For example,admin
is used when connecting to an instance using tt connect locally using the instance name:$ tt connect app:instance001
To allow remote binary port connections using the
admin
user, you need to set a password.guest
is a user with minimum privileges used by default for remote binary port connections. For example,guest
is used when connecting to an instance using tt connect using the IP address and port without specifying the name of a user:$ tt connect 192.168.10.10:3301
Warning
Given that the
guest
user allows unauthenticated access to Tarantool instances, it is not recommended to grant additional privileges to this user. For example, granting theexecute
access to universe allows remote code execution on instances.
Note
Information about users is stored in the _user space.
Any user (except guest
) may have a password.
If a password is not set, a user cannot connect to Tarantool instances.
Tarantool password hashes are stored in the _user system space.
By default, Tarantool uses the CHAP
protocol to authenticate users and applies SHA-1
hashing to
passwords.
So, if the password is ‘123456’, the stored hash is a string like ‘a7SDfrdDKRBe5FaN2n3GftLKKtk=’.
In the Enterprise Edition, you can enable PAP
authentication with the SHA256
hashing algorithm.
Tarantool Enterprise Edition allows you to improve database security by enforcing the use of strong passwords, setting up a maximum password age, and so on. Learn more from the Authentication topic.
An object is a securable entity to which access can be granted. Tarantool has a number of objects that enable flexible management of access to data, stored functions, specific actions, and so on.
Below are a few examples of objects:
universe
represents a database (box.schema) that contains database objects, including spaces, indexes, users, roles, sequences, and functions. Granting privileges touniverse
gives a user access to any object in a database.space
enables granting privileges to user-created or system spaces.function
enables granting privileges to functions.
Note
The full list of object types is available in the Object types section.
The privileges granted to a user determine which operations the user can perform, for example:
- The
read
andwrite
permissions granted to thespace
object allow a user to read or modify data in the specified space. - The
create
permission granted to thespace
object allows a user to create new spaces. - The
execute
permission granted to thefunction
object allows a user to execute the specified function. - The
session
permission granted to a user allows connecting to an instance over IPROTO.
Note that some privileges might require read and write access to certain system spaces.
For example, the create
permission granted to the space
object requires read
and write
permissions to the _space system space.
Similarly, granting the ability to create functions requires read
and write
access to the _func space.
Note
Information about privileges is stored in the _priv space.
A role is a container for privileges that can be granted to users. Roles can also be assigned to other roles, creating a role hierarchy.
There are the following built-in roles in Tarantool:
super
has all available administrative permissions.public
has certain read permissions. This role is automatically granted to new users when they are created.replication
can be granted to a user used to maintain replication in a cluster.sharding
can be granted to a user used to maintain sharding in a cluster.Note
The
sharding
role is created only if an instance is managed using YAML configuration.
Below are a few diagrams that demonstrate how privileges can be granted to a user without and with using roles.
In this example, a user gets privileges directly without using roles.
user1 ── privilege1 ├─── privilege2 └─── privilege3
In this example, a user gets all privileges provided by
role1
and specific privileges assigned directly.user1 ── role1 ── privilege1 │ └─── privilege2 ├─── privilege3 └─── privilege4
In this example,
role2
is granted torole1
. This means that a user withrole1
subsequently gets all privileges from both rolesrole1
androle2
.user1 ── role1 ── privilege1 │ ├─── privilege2 │ └─── role2 │ ├─── privilege3 │ └─── privilege4 ├─── privilege5 └─── privilege6
Note
Information about roles is stored in the _user space.
An owner of a database object is the user who created it.
The owner of the database and the owner of objects that are created initially (the system spaces and the default users) is the admin
user.
Owners automatically have privileges for objects they create.
They can share these privileges with other users or roles using box.schema.user.grant()
and box.schema.role.grant()
.
Note
Information about users who gave the specified privileges is stored in the _priv space.
A session is the state of a connection to Tarantool. The session contains:
- An integer ID identifying the connection.
- The current user associated with the connection.
- The text description of the connected peer.
- A session’s local state, such as Lua variables and functions.
In Tarantool, a single session can execute multiple concurrent transactions. Each transaction is identified by a unique integer ID, which can be queried at the start of the transaction using box.session.sync().
Note
To track all connects and disconnects, you can use connection and authentication triggers.
To create a new user, call box.schema.user.create(). In the example below, a user is created without a password:
box.schema.user.create('testuser')
In this example, the password is specified in the options
parameter:
box.schema.user.create('testuser', { password = 'foobar' })
To set or change a user’s password, use box.schema.user.passwd(). In the example below, a user password is set for a currently logged-in user:
box.schema.user.passwd('foobar')
To set the password for the specified user, pass a username and password as shown below:
box.schema.user.passwd('testuser', 'foobar')
Note
box.schema.user.password() returns a hash of the specified password.
To grant the specified privileges to a user, use the box.schema.user.grant() function.
In the example below, testuser
gets read permissions to the writers
space and read/write permissions to the books
space:
box.schema.user.grant('testuser', 'read', 'space', 'writers')
box.schema.user.grant('testuser', 'read,write', 'space', 'books')
Learn more about granting privileges to different types of objects from Granting privileges.
To check whether the specified user exists, call box.schema.user.exists():
box.schema.user.exists('testuser')
--[[
- true
--]]
To get information about privileges granted to a user, call 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
--]]
In the example above, testuser
has the following privileges:
- The
execute
permission to thepublic
role means that this role is assigned to the user. - The
read
permission to thewriters
space means that the user can read data from this space. - The
read,write
permissions to thebooks
space mean that the user can read and modify data in this space. - The
session,usage
permissions touniverse
mean the following:session
: the user can authenticate over an IPROTO connection.usage
: lets the user use their privileges on database objects (for example, read and modify data in a space).
- The
alter
permission letstestuser
modify its own settings, for example, a password.
To revoke the specified privileges, use the box.schema.user.revoke() function.
In the example below, write access to the books
space is revoked:
box.schema.user.revoke('testuser', 'write', 'space', 'books')
Revoking the session
permission to universe
can be used to disallow a user to connect to a Tarantool instance:
box.schema.user.revoke('testuser', 'session', 'universe')
The current user name can be found using box.session.user().
box.session.user()
--[[
- admin
--]]
The current user can be changed:
For an admin-console connection: using box.session.su():
box.session.su('testuser') box.session.user() --[[ - testuser --]]
For a binary port connection: using the AUTH protocol command, supported by most clients.
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.
To create a new role, call box.schema.role.create(). In the example below, two roles are created:
box.schema.role.create('books_space_manager')
box.schema.role.create('writers_space_reader')
To grant the specified privileges to a role, use the box.schema.role.grant() function.
In the example below, the books_space_manager
role gets read and write permissions to the books
space:
box.schema.role.grant('books_space_manager', 'read,write', 'space', 'books')
The writers_space_reader
role gets read permissions to the writers
space:
box.schema.role.grant('writers_space_reader', 'read', 'space', 'writers')
Learn more about granting privileges to different types of objects from Granting privileges.
Note
Not all privileges can be granted to roles. Learn more from Permissions.
Roles can be assigned to other roles.
In the example below, the newly created all_spaces_manager
role gets all privileges granted to books_space_manager
and writers_space_reader
:
box.schema.role.create('all_spaces_manager')
box.schema.role.grant('all_spaces_manager', 'books_space_manager')
box.schema.role.grant('all_spaces_manager', 'writers_space_reader')
To grant the specified role to a user, use the box.schema.user.grant()
function.
In the example below, testuser
gets privileges granted to the books_space_manager
and writers_space_reader
roles:
box.schema.user.grant('testuser', 'books_space_manager')
box.schema.user.grant('testuser', 'writers_space_reader')
To check whether the specified role exists, call box.schema.role.exists():
box.schema.role.exists('books_space_manager')
--[[
- true
--]]
To get information about privileges granted to a role, call box.schema.role.info():
box.schema.role.info('books_space_manager')
--[[
- - - read,write
- space
- books
--]]
If a role has the execute
permission to other roles, this means that these roles are granted to this parent role:
box.schema.role.info('all_spaces_manager')
--[[
- - - execute
- role
- books_space_manager
- - execute
- role
- writers_space_reader
--]]
To revoke the specified role from a user, revoke the execute
privilege for this role using the box.schema.user.revoke() function.
In the example below, the books_space_reader
role is revoked from testuser
:
box.schema.user.revoke('testuser', 'execute', 'role', 'writers_space_reader')
To revoke role’s privileges, use box.schema.role.revoke().
To drop the specified role, call box.schema.role.drop():
box.schema.role.drop('writers_space_reader')
To grant the specified privileges to a user or role, use the box.schema.user.grant() and box.schema.role.grant() functions,
which have similar signatures and accept the same set of arguments.
For example, the box.schema.user.grant()
signature looks as follows:
box.schema.user.grant(username, permissions, object-type, object-name[, {options}])
username
: the name of the user that gets the specified privileges.permissions
: a string value that represents permissions granted to the user. If there are several permissions, they should be separated by commas without a space.object-type
: a type of object to which permissions are granted.object-name
: the name of the object to which permissions are granted. An empty string (""
) ornil
provided instead ofobject-name
grants the specified permissions to all objects of the specified type.Note
object-name
is ignored for the following combinations of permissions and object types:- Any permission granted to
universe
. - The
create
anddrop
permissions for the following object types:user
,role
,space
,function
,sequence
. - The
execute
permission for the following object types:lua_eval
,lua_call
,sql
.
- Any permission granted to
In the example below, testuser
gets privileges allowing them to create any object of any type:
box.schema.user.grant('testuser','read,write,create','universe')
In this example, testuser
can grant access to objects that testuser
created:
box.schema.user.grant('testuser','write','space','_priv')
In the example below, testuser
gets privileges allowing them to create spaces:
box.schema.user.grant('testuser','create','space')
box.schema.user.grant('testuser','write', 'space', '_schema')
box.schema.user.grant('testuser','write', 'space', '_space')
As you can see, the ability to create spaces also requires write
access to certain system spaces.
To allow testuser
to drop a space that has associated objects, add the following privileges:
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')
In the example below, testuser
gets privileges allowing them to create indexes in the ‘writers’ space:
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')
To allow testuser
to alter indexes in the writers
space, grant the privileges below.
This example assumes that indexes in the writers
space are not created by 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')
If testuser
created indexes in the writers
space, granting the following privileges is enough to alter indexes:
box.schema.user.grant('testuser','read','space','_space_sequence')
box.schema.user.grant('testuser','read,write','space','_index')
In this example, testuser
gets privileges allowing them to select data from the ‘writers’ space:
box.schema.user.grant('testuser','read','space','writers')
In this example, testuser
is allowed to read and modify data in the ‘books’ space:
box.schema.user.grant('testuser','read,write','space','books')
In this example, testuser
gets privileges to create sequence generators:
box.schema.user.grant('testuser','create','sequence')
box.schema.user.grant('testuser', 'read,write', 'space', '_sequence')
To let testuser
drop a sequence, grant them the following privileges:
box.schema.user.grant('testuser','drop','sequence')
box.schema.user.grant('testuser','write','space','_sequence_data')
box.schema.user.grant('testuser','write','space','_sequence')
In this example, testuser
is allowed to use the id_seq:next()
function with a sequence named ‘id_seq’:
box.schema.user.grant('testuser','read,write','sequence','id_seq')
In the next example, testuser
is allowed to use the id_seq:set()
or id_seq:reset()
functions with a sequence named ‘id_seq’:
box.schema.user.grant('testuser','write','sequence','id_seq')
In this example, testuser
gets privileges to create functions:
box.schema.user.grant('testuser','create','function')
box.schema.user.grant('testuser','read,write','space','_func')
To let testuser
drop a function, grant them the following privileges:
box.schema.user.grant('testuser','drop','function')
box.schema.user.grant('testuser','write','space','_func')
In this example, testuser
gets privileges to create other users:
box.schema.user.grant('testuser','create','user')
box.schema.user.grant('testuser', 'read,write', 'space', '_user')
box.schema.user.grant('testuser', 'write', 'space', '_priv')
To let testuser
create new roles, grant the following privileges:
box.schema.user.grant('testuser','create','role')
box.schema.user.grant('testuser', 'read,write', 'space', '_user')
box.schema.user.grant('testuser', 'write', 'space', '_priv')
To let testuser
execute Lua code, grant the execute
privilege to the lua_eval
object:
box.schema.user.grant('testuser','execute','lua_eval')
Similarly, executing an arbitrary SQL expression requires the execute
privilege to the sql
object:
box.schema.user.grant('testuser','execute','sql')
In the example below, the created Lua function is executed on behalf of its creator, even if called by another user.
First, the two spaces (space1
and space2
) are created, and a no-password user (private_user
)
is granted full access to them. Then read_and_modify
is defined and private_user
becomes this function’s creator.
Finally, another user (public_user
) is granted access to execute Lua functions created by 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.space1
local space2 = box.space.space2
local fiber = require('fiber')
local t = space1:get{key}
if t ~= nil then
space1:put{key, box.session.uid()}
space2:put{key, fiber.time()}
end
end
box.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')
Whenever public_user
calls the function, it is executed on behalf of its creator, private_user
.
Object type | Description |
---|---|
universe |
A database (box.schema) that contains database objects, including spaces, indexes, users, roles, sequences, and functions. Granting privileges to universe gives a user access to any object in the database. |
user |
A user. |
role |
A role. |
space |
A space. |
function |
A function. |
sequence |
A sequence. |
lua_eval |
Executing arbitrary Lua code. |
lua_call |
Calling any global user-defined Lua function. |
sql |
Executing an arbitrary SQL expression. |
Permission | Object type | Granted to roles | Description |
---|---|---|---|
read |
All | Yes | Allows reading data of the specified object. For example, this permission can be used to allow a user to select data from the specified space. |
write |
All | Yes | Allows updating data of the specified object. For example, this permission can be used to allow a user to modify data in the specified space. |
create |
All | Yes | Allows creating objects of the specified type. For example, this permission can be used to allow a user to create new spaces. Note that this permission requires read and write access to certain system spaces. |
alter |
All | Yes | Allows altering objects of the specified type. Note that this permission requires read and write access to certain system spaces. |
drop |
All | Yes | Allows dropping objects of the specified type. Note that this permission requires read and write access to certain system spaces. |
execute |
role , universe , function , lua_eval , lua_call , sql |
Yes | For role , allows using the specified role.
For other object types, allows calling a function. |
session |
universe |
No | Allows a user to connect to an instance over IPROTO. |
usage |
universe |
No | Allows a user to use their privileges on database objects (for example, read, write, and alter spaces). |
Object type | Details |
---|---|
universe |
|
user |
|
role |
|
space |
|
function |
|
sequence |
|
lua_eval |
|
lua_call |
|
sql |
|