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:

    1. At the top, select the admin user in the table of users

    2. Type in your password into the corresponding fields

    3. 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 DBA account.

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 guest.

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 why Bob 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

--x

---

List Collection Contents

r-x

---

Add Document

-wx

---

Remove Document

-wx

---

Overwrite Existing Document

--x

-w- (or owner)

Operation

Source Collection

Source Document

Destination Collection (if exists)

Destination Document (if exists)

Copy Document

--x

r--

-wx (w only if destination document does not exist)

-w-

Move/Rename Document

-wx

--x (or owner)

-wx

-w-

Operation

Parent of Source Collection

Source Collection

Parent of Destination Collection

Destination Collection (if exists)

Add Collection

---

---

-wx

---

Remove Collection

-wx

rwx (and on all sub-collections)

---

---

Copy Collection

---

r-x

---

---

Move/Rename Collection

-wx

-w-

---

-wx (When replacing an existing collection, permissions for removing that collection must be met first).

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.