-
ocelot-inc authoredocelot-inc authored
Access control
Understanding the details of security is primarily an issue for administrators, but ordinary users should at least skim this section so that they will have an idea of how Tarantool makes it possible for administrators to prevent unauthorized access to the database and to certain functions.
Briefly: there is a method to guarantee with password checks that users really are who they say they are ("authentication"). There is a _user space where user names and password-hashes are stored. There are functions for saying that certain users are allowed to do certain things ("privileges"). There is a _priv space where privileges are stored. Whenever a user tries to do an operation, there is a check whether the user has the privilege to do the operation ("access control").
Passwords
Each user may have a password. The password is any alphanumeric string. Administrators should advise users to choose long unobvious passwords, but it is ultimately up to the users to choose or change their own passwords.
Tarantool passwords are stored in the _user space with a Cryptographic hash function
so that, if the password is 'x', the stored hashed-password is a long string
like 'lL3OvhkIPOKh+Vn9Avlkx69M/Ck=
'. When a client connects to a Tarantool
server, the server sends a random Salt Value which the client must mix with the
hashed-password before sending to the server. Thus the original value 'x' is
never stored anywhere except in the user's head, and the hashed value is never
passed down a network wire except when mixed with a random salt. This system
prevents malicious onlookers from finding passwords by snooping in the log
files or snooping on the wire. It is the same system that MySQL introduced
several years ago which has proved adequate for medium-security installations.
Nevertheless administrators should warn users that no system is foolproof against
determined long-term attacks, so passwords should be guarded and changed occasionally.
Notes: To get the hash-password of a string 'X', say box.schema.user.password('X')
.
To see more about the details of the algorithm for the purpose of writing a new
client application, read the scramble.h header file.
Users and the _user space
The fields in the _user space are:
- the numeric id of the tuple
- the numeric id of the tuple's creator
- the user name
- the type
- optional password
There are four special tuples in the _user space: 'guest', 'admin', 'public', and 'replication'.
Name | ID | Type | Description |
---|---|---|---|
guest | 0 | user | Default when connecting remotely. Usually an untrusted user with few privileges. |
admin | 1 | user | Default when using tarantool as a console. Usually
an administrative user with all privileges. |
public | 2 | role | Not a user in the usual sense. Described later in section Roles. |
replication | 3 | role | Not a user in the usual sense. Described later in section Roles. |
To select a row from the _user space, use box.space._user:select
. For
example, here is what happens with a select for user id = 0, which is the
'guest' user, which by default has no password:
tarantool> box.space._user:select{0}
---
- - [0, 1, 'guest', 'user']
...
To change tuples in the _user space, do not use ordinary box.space
functions for insert or update or delete - the _user space is special so
there are special functions which have appropriate error checking.
To create a new user, say:
box.schema.user.create(user-name)
box.schema.user.create(user-name, {if_not_exists = true})
box.schema.user.create(user-name, {password = password}).
The :samp:`password={password}` specification is good because in a :ref:`URI` (Uniform Resource Identifier) it is usually illegal to include a user-name without a password.
To change the user's password, say:
-- To change the current user's password
box.schema.user.passwd(password)
-- To change a different user's password
box.schema.user.passwd(user-name, password)
(Usually it is only the admin user who can change a different user's password.)
To drop a user, say:
box.schema.user.drop(user-name).
To check whether a user exists, say:
box.schema.user.exists(user-name)
which returns true or false.
To find what privileges a user has, say:
box.schema.user.info(user-name)
Example:
Here is a session which creates a new user with a strong password, selects a tuple in the _user space, and then drops the user.
tarantool> box.schema.user.create('JeanMartin', {password = 'Iwtso_6_os$$'})
---
...
tarantool> box.space._user.index.name:select{'JeanMartin'}
---
- - [17, 1, 'JeanMartin', 'user', {'chap-sha1': 't3xjUpQdrt857O+YRvGbMY5py8Q='}]
...
tarantool> box.schema.user.drop('JeanMartin')
---
...
Note
The maximum number of users is 32.
Privileges and the _priv space
The fields in the _priv space are:
- the numeric id of the user who gave the privilege ("grantor_id"),
- the numeric id of the user who received the privilege ("grantee_id"),
- the type of object - "space" or "function" or "universe",
- the numeric id of the object,
- the type of operation - "read" = 1, or "write" = 2, or "execute" = 4, or a combination such as "read,write,execute".
The function for granting a privilege is:
box.schema.user.grant(grantee, operation, object-type, obejct-name*[, *options])
-- OR
box.schema.user.grant(grantee, operation, 'universe' [, nil, options])
where 'universe' means 'all objects', and the optional grant-option can be:
- :samp:`grantor={grantor_name_or_id}` - string or number, for custom grantor
- :samp:`if_not_exists=true|false` - bool, do not throw error if user already has the privilege
The function for revoking a privilege is:
box.schema.user.revoke(grantee, operation, object-type, object-name*[, *options])
box.schema.user.revoke(grantee, operation, 'universe'[, nil, options])
where 'universe' means 'all objects', and the optional grant-option can be:
- :samp:`if_not_exists=true|false` - bool, do not throw error if user already lacks the privilege
For example, here is a session where the admin user gave the guest user the
privilege to read from a space named space55
, and then took the privilege away:
tarantool> box.schema.user.grant('guest', 'read', 'space', 'space55')
---
...
tarantool> box.schema.user.revoke('guest', 'read', 'space', 'space55')
---
...
Note
Generally privileges are granted or revoked by the owner of the object (the user who created it), or by the 'admin' user. Before dropping any objects or users, steps should be taken to ensure that all their associated privileges have been revoked.
Note
Only the 'admin' user can grant privileges for the 'universe'.
Note
Only the creator of a space can drop, alter, or truncate the space. Only the creator of a user can change a different user's password.
Functions and _func space
The fields in the _func space are:
- the numeric function id, a number,
- the function name
- flag
- possibly a language name.
The _func space does not include the function's body. One continues to create Lua functions in the usual way, by saying ":samp:`function {function_name} () ... end`", without adding anything in the _func space. The _func space only exists for storing function tuples so that their names can be used within grant/revoke functions.
The function for creating a _func tuple is:
box.schema.func.create(function-name [, options])
The possible options are:
- :samp:`if_not_exists = {true|false}` - default = false,
- :samp:`setuid = {true|false}` - default = false,
- :samp:`language = {'LUA'|'C'}` - default = 'LUA'.
Example:
box.schema.func.create('f', {language = 'C', setuid = false})
Specifying if_not_exists=true
would cause error: Function '...' already
exists
if the function already exists.
Specifying setuid=true
would cause the setuid flag (the fourth field in
the _func tuple) to have a value meaning "true", and the effect of that is that
the function's caller is treated as the function's creator, with full privileges.
The setuid behavior does not apply for users who connect via console.connect
.
Specifying language='C'
would cause the language field (the fifth field
in the _func tuple) to have a value 'C', which means the function was written in
C. Tarantool functions are normally written in Lua but can be written in C as well.
The function for dropping a _func tuple is:
box.schema.func.drop(function-name)
The function for checking whether a _func tuple exists is:
box.schema.func.exists(function-name)
In the following example, a function named 'f7' is created, then it is put in
the _func space, then it is used in a box.schema.user.grant
function,
then it is dropped: