Security
(2Q19)
This article discusses eXist-db's security features and how to manage authentication, users, groups, passwords, permissions and access controls.
eXist-db's security infrastructure is built on a Unix permissions model (see Unix Model), which we have extended with Access Control Lists (ACL). As far as possible we try and adhere to the POSIX standard. The security infrastructure is very flexible and extensible, which enables the more advanced user, to provide custom Authentication Realms to enable different authentication models.
As well as the mechanisms described in this article, you should also note the presence of the Security Manager XQuery library module, which enables you to perform many user and security related tasks programmatically from XQuery.
Changing the Administrator Password
When the database is started for the first time, two default users are created:
admin
and guest
. The admin
user is a member of
the dba
group, and therefore has administrative privileges; the
guest
user is a member of the group guest
and is not an
administrator, in fact it has the least privileges.
At this point, the admin
password is empty, and so
access to the database is initially granted to everyone.
To set restrictions on database access, first set a password for the
admin
user in one of the following ways:
-
The Java Admin Client GUI has a dialog box for user management. To open this dialog box, enter Ctrl-U or select Tools, Edit Users:
-
At the top, select the
admin
user in the table of users -
Type in your password into the corresponding fields
-
Click the Modify User button to apply the changes
-
-
In the Dashboard's User manager, click on the user account that you wish to edit and type its password twice. Confirm by clicking save.
-
Set a new administrator password on the command line in the console mode of the Java Admin Client. Enter the following at the command prompt:
type help or ? for help. exist:/db>passwd admin password: somepass re-enter password: somepass exist:/db>quit
-
Change the password programmatically, for instance from within eXide, by using the
sm:passwd()
function of the Security Manager XQuery library module.
The other default user, guest
, has the default password
guest
. The guest identity is internally assigned to all clients that have
not authenticated themselves.
Creating Users
There are several ways to create new users:
-
Use the Dashboard's User manager to add a new user. Click on the big red + button in the lower right-hand corner and fill in the dialog.
-
Use the GUI of the Java Admin Client. In the Edit users dialog box, fill in the username, password, and home collection fields. Assign a group (or groups) for the new user. Finally, select Create User. The new user will appear in the list of users in the top panel.
-
The console
adduser
of the Java Admin Client command also allows you to create additional users. The command asks for a password and a list of groups to which the user should belong. An example is shown below:exist:/db/shakespeare>adduser wolf password: xxxxxxxx re-enter password: xxxxxxxx enter groups: users
You can list all the users using the
users
command. -
Create a user programmatically, for instance from within eXide, by using the
sm:create-account()
function of the Security Manager XQuery library module.
System Accounts and Groups
eXist-db has several built-in accounts which ensure the correct functioning of the system. These accounts and groups cannot be removed, however the admin
and guest
accounts can be disabled if required.
System Accounts
Name |
Description |
---|---|
SYSTEM |
This is a DBA account under which the database executes internal privileged opertaions. This account is not exported during backups. |
admin |
This is the default |
guest |
This is the account under which operations by un-authenticated users will be executed, for example users connecting to the REST Server without authenticating. |
nobody |
This is an internal account and should not be used directly. This account is not exported during backups. |
System Groups
Name |
Description |
---|---|
DBA |
This is the DBA group, all DBA users should be members of this group. |
guest |
This is the primary group of the |
nogroup |
This is an internal group and should not be used directly. In the case that users without a primary group are imported from an older eXist-db backup, the users will be restored with this as their primary group. This group is not exported during backups. |
Resource Permissions
eXist-db supports both a Unix like permissions model and simple Access Control Lists. It is important to understand the Unix permission model first. Access Control Lists are useful if the Unix Model is insufficient for your application.
Unix Model
The default that is based on the UNIX read, write and execute flags for owner, group and world.
In versions prior to eXist-db 2.0, there was no execute flag, rather an update flag was present.
Category |
Description |
---|---|
Owner |
These permissions work for the owner of the resource |
Group |
These permissions work for the members of the group of the resource |
World |
These permissions work for any user |
Be aware that permissions for collections are not inherited by their sub-collections: write permissions can be set for sub-collections, but you must also have write permissions for the parent collection for these to be effective!
Using the Java Admin Client or its
command line, you can list the permissions assigned to each resource (this assumes
the permissions
property in client.properties
is set to true
). For example:
exist:/db/shakespeare/plays>ls -rwxr-xr-- admin dba hamlet.xml -rwxr-xr-- admin dba macbeth.xml -rwxr-xr-- wolf users r_and_j.xml -rwxr-xr-- admin dba shakes.xsl exist:/db/shakespeare/plays>
The Java Admin Client displays resource permissions in a format similar to the
output of the Unix ls -l
command: a ten-character code.
-
The first character represents the type of resource:
-
-
(hyphen) for documents -
c
for collections
-
-
The next three characters are the permissions for the user. For normal usage these are (for special usage see below):
-
-
(hyphen) for denied permissions -
r
for read -
w
for write -
x
for execute
-
-
The next three characters (five through seven) set the permissions for groups
-
The last three for others (i.e. anyone else who can access the database).
The database root collection /db
initially has its permissions
set to drwxr-xr-x
, so full access is granted to everyone. You
might consider changing this.
Also note that the default setting for all newly created resources is
-rw-r--r--
: the owner has read/write permissions, not execute.
The group and others (world) have read permissions only.
Changing Resource Permissions and Ownership
Please note that only the owner of a resource or members of the
dba
group are allowed to change permissions. All other
users who attempt to change these settings will receive an error
message.
To change permissions and/or ownership, use one of the following methods:
-
Use the GUI of the Java Admin Client.
-
The command line of the Java Admin Client:
chmod [resource] [user|group|other]=[+|-][read|write|execute][, ...]
chown [user] [group] [resource]
If you do not specify a resource in the first argument of the
chmod
command, the permission string will be applied to the current collection. This is an important feature if you want to change permissions for the/db
root collection.For example, to deny write permissions to others for the entire database, change directory to the root collection (enter
cd /db
) and enter:chmod other=-write
-
Use the GUI interface of the Dashboard's User manager
-
Do it programmatically, for instance from within eXide, by using the one of the functions of the Security Manager XQuery library module.
Special permissions
eXist-db also supports the setuid/setgid and sticky bit permissions:
- setuid/setgid
-
The setuid and setgid permissions are meant for being able to run code as another user or group than what the query was started with.
For example: Assume a query was started by user "Fred" in group "normal-users". But the XQuery needs to do something that only the admin user can do, for instance reach out to disk using the
file
module, create a new user, change somebody else's password, etc. Fred needs temporary admin rights to do so. With the setuid/setgid mechanism, this can be implemented by creating a module owned by admin and place the setuid bit on its permissions. Now when code executes within this module, Fred is no longer Fred but is temporarily disguised as admin.In a permission string, the setuid/setgid permission replaces the execute (
x
) marker of the user and/or group. It has two possible values:-
s
for setuid and execute permission. -
S
for setuid permission only (without execute permission)
-
- Sticky bit
-
Sticky bits in eXist are useful on collections only. When a new collection or document is created in eXist, it is allocated a default permissions mode, based on the umask of the user which created it. However, if the sticky bit is set on the parent collection (the one in which the new document or collection is created), the new document or collection inherits the permissions of the Collection.
In a permission string, the sticky bit permission replaces the execute (
x
) marker of the others. It has two possible values:-
t
for sticky bit and execute permission -
T
for sticky bit permission only (without execute permission)
-
For more information:
Access Control Lists (ACL)
ACL's complement the Unix Model implemented by eXist-db. When ACL's are employed these are evaluated first. If no match is found, the Unix permission model is evaluated.
An ACL is a list of ACEs (Access Control Entries). When an ACL is evaluated, the
list of ACE's are evaluated from top to bottom. Each ACE allows you to grant or deny
additional permissions to a collection or document for a user or group. Each ACE has
3 permission bits: r
for read, w
for write,
and x
for execute.
ACL's are perhaps best illustrated by an example, consider the following document
somedoc.xml
which has the following permissions:
In this example, the document somedoc.xml
is owned by
fred
. However our ACL has three ACEs which:
-
Deny the user
bob
read and write to the document (even though he is an editor) (we won't pontificate about whyBob
is in disgrace with his fellow editors…). -
Allow the group
editors
read and write access to the document. -
Allow the group
reviewers
read only access to the document.
Because the ACL is evaluated from top-to-bottom, even though
bob
is in the editors
group and editors are
allowed read and write access to the document, there is an earlier rule which
prohibits access for just bob
.
More information about ACLs is available as slides (PDF) and a presentation on YouTube.
Permission Checks
eXist-db enforces permission checks on each operation. The details of the permissions required for a specific operation are detailed below.
Operation |
Collection |
Document |
---|---|---|
Open Collection |
|
|
List Collection Contents |
|
|
Add Document |
|
|
Remove Document |
|
|
Overwrite Existing Document |
|
|
Operation |
Source Collection |
Source Document |
Destination Collection (if exists) |
Destination Document (if exists) |
---|---|---|---|---|
Copy Document |
|
|
|
|
Move/Rename Document |
|
|
|
|
Operation |
Parent of Source Collection |
Source Collection |
Parent of Destination Collection |
Destination Collection (if exists) |
---|---|---|---|---|
Add Collection |
|
|
|
|
Remove Collection |
|
|
|
|
Copy Collection |
|
|
|
|
Move/Rename Collection |
|
|
|
|
When copying a collection, permissions are checked for each sub-collection and resource.
Copying a sub-collection requires r-x on the sub-collection and rwx on the destination collection. If the sub-collection already exists in the destination r-x is required on that.
Copying resources from a collection requires r-- on the resource, and -w- on the destination resource if it exists, otherwise -w- on the destination collection.
The Security Manager
eXist-db has a central Security Manager which is configured in the file
/db/system/security/config.xml
. This document, which is generated
during database start-up, defines what authentication realms are
available to the Security Manager.
For example, this Security Manager configuration file defines a URL for authentication:
<security-manager xmlns="http://exist-db.org/Configuration" last-account-id="11" last-group-id="10" version="2.0">
<Authentication-Entry-Point>
/authentication/login
</Authentication-Entry-Point>
</security-manager>
The Security Manager also features an authentication event listener that you can
configure to call a custom XQuery module on each authentication event. For example, this
configuration file would pass authentication events to module
/db/security-events.xq
:
<security-manager ... version="2.0">
...
<events script-uri="/db/security-events.xq"/>
...
</security-manager>
The XQuery module that receives the authentication events must be a library module in
the http://exist-db.org/security/events
namespace. It must have a
function authentication()
. This example sends a log message to the
console:
xquery version "3.0";
module namespace sec-ev="http://exist-db.org/security/events";
declare function sec-ev:authentication() {
util:log-system-out(concat("An authentication event has occurred for ", xmldb:get-current-user())))
};
Authentication Realms
eXist-db always has an internal authentication realm. It also supports multiple external authentication realms. This allows you to add one or more external realms which provide user and group authentication.
Default Internal Realm
The "eXist-db realm" is the default internal realm. By default this realm
handles the SYSTEM
, admin
and guest
users
and the DBA
and guest
groups. Any additional users or
groups created in eXist-db are added to this realm.
Every eXist-db realm user has an account with a username, password and other
metadata, stored in the database. This user information for the eXist-db realm
is maintained in the collection
/db/system/security/exist
.
Important:
The security collections in /db/system/security
should
not be manipulated or accessed directly . Only access
this information through the SecurityManager
class or the
SecurityManager
XQuery library module. Direct manipulation may
lead to inconsistent state and security issues.
The following is a sample user account document for aretter
in the eXist-db realm:
<account xmlns="http://exist-db.org/Configuration" id="11">
<name>
aretter
</name>
<password>
{RIPEMD160}Vi7e971INiGmyWGT1bm63bHj1gf=
</password>
<group name="dba"/>
<enabled>
true
</enabled>
<expired>
false
</expired>
<metadata/>
</account>
As you can see, eXist-db does not store passwords in the clear. It stores hashed values of the passwords (in Base64 encoding), using the RIPEMD-160 cryptographic hashing algorithm.
Whenever a user supplies account credentials for authentication, the database applies RIPEMD-160 hash to the password and compares it to the hash stored in the user's account document. Storing hashes of passwords is a best practice in security that provides a strong layer of security compared to storing passwords in the clear. The notion is that even if the hashed password is exposed to an attacker, it is too difficult to derive the original password from the hash.
Note that the /db/system/security
collection is (by
default) only readable and writable by the system or users in the
dba
group. The dba
group is reserved for
database administrators, and only dba
users are allowed to
create, remove or modify other users.
LDAP Realm
The LDAP Realm is enabled by default in the build configuration file
extensions/build.properties
(include.feature.security.ldap
property). To use the LDAP
realm, add an LDAP realm element to
/db/system/security/config.xml
. For example:
<realm id="LDAP" version="1.0" principals-are-case-insensitive="true">
<context>
<authentication>
simple
</authentication>
<url>
ldap://ad.server.url.here:389
</url>
<domain>
domain.here
</domain>
<search>
<base>
ou=group,dc=ad,dc=organiation-or-what-ever,dc=domain
</base>
<default-username>
account@domain.here
</default-username>
<default-password>
XXXXXXX
</default-password>
<account>
<search-filter-prefix>
objectClass=user
</search-filter-prefix>
<search-attribute key="objectSid">
objectSid
</search-attribute>
<search-attribute key="primaryGroupID">
primaryGroupID
</search-attribute>
<search-attribute key="name">
sAMAccountName
</search-attribute>
<search-attribute key="dn">
distinguishedName
</search-attribute>
<search-attribute key="memberOf">
memberOf
</search-attribute>
<metadata-search-attribute key="http://axschema.org/namePerson/first">
givenName
</metadata-search-attribute>
<metadata-search-attribute key="http://axschema.org/contact/email">
mail
</metadata-search-attribute>
<metadata-search-attribute key="http://axschema.org/namePerson/last">
sn
</metadata-search-attribute>
<metadata-search-attribute key="http://axschema.org/namePerson">
name
</metadata-search-attribute>
</account>
<group>
<search-filter-prefix>
objectClass=group
</search-filter-prefix>
<search-attribute key="member">
member
</search-attribute>
<search-attribute key="primaryGroupToken">
primaryGroupToken
</search-attribute>
<search-attribute key="objectSid">
objectSid
</search-attribute>
<search-attribute key="name">
sAMAccountName
</search-attribute>
<search-attribute key="dn">
distinguishedName
</search-attribute>
<whitelist>
<principal>
Domain Users
</principal>
<principal>
Users_GROUP
</principal>
</whitelist>
</group>
</search>
<transformation>
<add-group>
group.users
</add-group>
</transformation>
</context>
</realm>
-
The
<default-username>
and<default-password>
elements are used to communicate with the LDAP server if a non-LDAP user requests information from LDAP server. -
The
<search-*>
elements are mapping for names. -
The
<metadata-search-attribute>
elements are used for mapping LDAP account metadata onto eXist-db account metadata. -
The
<whitelist>
element contains the allowed groups for authentication. The<blacklist>
element contains groups that are not allowed. -
The
<transformation>
element contains actions to be performed after first authentication.
Legacy Internal Realm
Before eXist-db 2.0, the internal security realm was maintained in a different manner. The details are included here for the purpose of informing decisions on migration.
Every eXist-db database user has an account with a username, password and other
information that is stored in the database. Furthermore, every user belongs to one or
more groups - and respectively, every resource in the database is owned by a user and by
a group. By default, the owner is set to the user who created the resource, and his
primary group, but eXist-db allows for different permissions for the owner, the owner's
group and others. However, only the owner of the resource (or dba
users) can change these permissions.
User and group information is found in the designated XML file
/db/system/users.xml
located in collection
/db/system
. This file is generated during database start-up. The
following is a simple example of a users.xml
document:
<auth>
<groups last-id="3">
<group name="dba" id="1"/>
<group name="guest" id="2"/>
<group name="mygroup" id="3"/>
</groups>
<users last-id="3">
<user name="admin" uid="1">
<group>
dba
</group>
</user>
<user name="guest" uid="2" password="e55d929cdbc8d5a7ce3bda044bc69f59">
<group>
guest
</group>
</user>
<user name="user-1" uid="3" password="7f0261c14d7d1b8e51680a013daa623e" home="my-collection">
<group>
my-group
</group>
</user>
</users>
</auth>
As we see from this example, passwords are encrypted using an MD5
algorithm (e.g. user-1
has the MD5-encrypted
password "7f0261c14d7d1b8e51680a013daa623e"). Therefore, whenever a user enters his or
her password, the database generates an MD5 encryption and compares it to the encryption
stored in users.xml
. Since it is very difficult for users to guess
the original password from the MD5 string, passwords in eXist-db should be sufficiently
secure.
Note that the /db/system
collection is (by default) only readable
by dba
users (although it is possible to make it accessible by other
users). The dba
group is specially reserved for database
administrators, and only dba
users are allowed to create or remove
users, or change permissions for other users.