Authentication

Whenever a client connects to GraphDB, a session is created. The session stores various contextual information about the connection. Each session is always associated with a single user.

Authentication is the process of mapping this session to a specific user. Once the session is mapped to a user, a set of permissions can be associated with it, using authorization.

This diagram can be used as a quick reference for our security model:

_images/authentication-diagram.png

GraphDB supports two authentication providers natively, explained in detail below - local and Lightweight Directory Access Protocol (LDAP).

Local authentication

This is the default security access provider. Users stored inside the local database take precedence over LDAP users. For example, if both providers have a user called “leah”, the settings and roles for this user will be sourced from the local database.

The local database stores usernames, encrypted passwords, local user settings and remote LDAP user settings. Passwords are encrypted using the SHA-256 algorithm. When a user tries to access the database, they will be met with a security challenge. If they can provide a valid username/password combination, a JWT token will be generated.

The local database is located in the settings.js file under the GraphDB work - workbench directory. By default this is ${graphdb.home}/work/workbench. If you are worried about the security of this file, we recommend encrypting it (see Encryption at rest).

Note

JWT is serialized as “Authorization: GDB <token>” header in every request, so it is vulnerable to a man-in-the-middle attack. Everyone who intercepts the JWT can reuse the session. To prevent this, we recommend to always enable encryption in transit.

Tokens are by default valid for 30 days. Within this period, each communication between the client and the server will be carried out only after the token has been validated. If the token has expired or is not present, the users will be met with a security challenge. You can configure this period via the graphdb.auth.token.validity parameter, changing its default value of 30 to a number of your choice.

Note

During the token validity period, if the password is changed the user would still have access to the server. However, if the user is removed the token would stop working.

Local authentication does not need to be configured.

LDAP authentication

Lightweight Directory Access Protocol (LDAP) is a lightweight client-server protocol for accessing directory services implementing X.500 standards. All its records are organized around the LDAP Data Interchange Format (LDIF), which is represented in a standard plain text file format.

An optional identity provider. In the event that a given username/password combination cannot be validated by the local database, and a LDAP server has been configured, GraphDB security will attempt to fetch the user from the LDAP server.

Even with LDAP turned on, the local database is still used for storing user settings. This means that you can use the workbench or the GraphDB API to change them. All other administration operations need to be performed on the LDAP server side.

Unlike local authentication, LDAP needs to be configured in the graphdb.properties file, by passing the configuration details to the Java process or via the GDB_JAVA_OPTS parameter.

Note

The order of precedence for GraphDB configuration properties is: config file < GDB_JAVA_OPTS < command line supplied arguments.

LDAP is turned on by using the following setting:

graphdb.auth.module=ldap

When LDAP is turned on, the following security settings can be used to configure it:

Property Values Usage
graphdb.auth.ldap.url (required) ldap://<my-openldap-server> :389/<partition> LDAP endpoint
graphdb.auth.ldap.user.search.base <empty> Query to identify the directory where all authenticated users are located.
graphdb.auth.ldap.user.search.filter (required) (cn={0}) Matches the attribute to a GraphDB username.
graphdb.auth.ldap.role.search.base <empty> Query to identify the directory where roles/groups for authenticated users are located
graphdb.auth.ldap.role.search.filter (required) (uniqueMember={0}) Authorize a user by matching the manner in which they are listed within the group.
graphdb.auth.ldap.role.search.attribute cn (default) The attribute to identify the common name
graphdb.auth.ldap.role.map.administrator (required) my-group-name Map a single LDAP group to GDB administrator role
graphdb.auth.ldap.role.map. repositoryManager my-group-name Map a single LDAP group to GDB repository manager role
graphdb.auth.ldap.role.map.repository .read.<my-repo> my-group-name Map a single LDAP group to GDB repository-specific read permissions
graphdb.auth.ldap.role.map.repository .write.<my-repo> my-group-name Map a single LDAP group to GDB repository-specific write permissions
graphdb.auth.ldap.repository.read.base   Query to identify the directory where repository read groups for authenticated users are located
graphdb.auth.ldap.repository.read.filter (uniqueMember={0}) Authorize a user by matching the manner in which they are listed within the group
graphdb.auth.ldap.repository.read. attribute cn (default) Specify the mapping of a GraphDB repository id to a LDAP attribute
graphdb.auth.ldap.repository.write.base   Query to identify the directory where repository write groups for authenticated users are located.
graphdb.auth.ldap.repository.write.filter (uniqueMember={0}) Authorize a user by matching the manner in which they are listed within the group
graphdb.auth.ldap.repository.write. attribute cn (default) Specify the mapping of a GraphDB repository id to a LDAP attribute

Note

Configuration changes happen only after restart.

Here is an example configuration:

# Turn on ldap authentication and configure the server.
graphdb.auth.module = ldap
graphdb.auth.ldap.url = ldap://localhost:10389/dc=example,dc=org

# Permit access for all users that are part of the “people” unit of the fictional “example.org” organisation.
graphdb.auth.ldap.user.search.base = ou=people
graphdb.auth.ldap.user.search.filter = (cn={0})

# Make all users in the Administration group GraphDB administrators as well.
graphdb.auth.ldap.role.search.base = ou=groups
graphdb.auth.ldap.role.search.filter = (member={0})
graphdb.auth.ldap.role.map.administrator = Administration

# Make all users in the Management group GraphDB Repository Managers as well.
graphdb.auth.ldap.role.map.repositoryManager = Management

# Enable all users in the Readers group to read the my_repo repository.
graphdb.auth.ldap.role.map.repository.read.my_repo = Readers

# Enable all users in the Writers group to write and read the my_repo repository.
graphdb.auth.ldap.role.map.repository.write.my_repo = Writers

# All entries located under the "groups" organizational unit that have members (i.e., groups), will be able to read repositories that share their common name.
graphdb.auth.ldap.repository.read.base = ou=groups
graphdb.auth.ldap.repository.read.filter = (member={0})
graphdb.auth.ldap.repository.read.attribute = cn

# All entries located under the "groups" organizational unit that have members (i.e., groups), will be able to read and write to repositories that share their common name.
graphdb.auth.ldap.repository.write.base = ou=groups
graphdb.auth.ldap.repository.write.filter = (member={0})
graphdb.auth.ldap.repository.write.attribute = cn

Basic authentication

Instead of using JWT, users can access GraphDB by passing valid base-64 encoded username:password combinations as a header, in the following fashion:

Authorization: Basic YWRtaW46cm9vdA==

Note

Basic Authentication is even more vulnerable to man-in-the-middle attacks than JWT! Anyone who intercepts your requests will be able to reuse your credentials indefinitely until you change them. Since the credentials are merely base-64 encoded, they will also get your username and password. This is why it is very important to always use encryption in transit.

Note

If you need to speed up a slow repository with enabled security when each request includes HTTP basic authentication, we recommend some options here.

Kerberos authentication

Kerberos is a highly secure single sign-on protocol that uses tickets for authentication, and avoids storing passwords locally or sending them over the Internet. The authentication mechanism involves communication between a trusted third-party connection encrypted with symmetric-key cryptography. Although considered a legacy technology, Kerberos is still the default single sign-on mechanism in big Windows-based enterprises, and is an alternative of the JWT authentication.

The basic support for authentication via Kerberos in GraphDB involves:

  • Validation of SPNEGO HTTP Authorization tokens. For example:

    • Authorization: Negotiate XXXXXXX
  • Extraction of the username from the SPNEGO token and matching the username against a user from the local database or a user from LDAP.

SPNEGO is the mechanism that integrates Kerberos with HTTP authentication.

After the token is validated and matched to an existing user, the process continues with authorization (assigning user roles) via the existing mechanism.

Using Kerberos this way is equivalent to authenticating via Basic (username and password supplied every time), or via a JWT token (username supplied every time together with a timestamp and signature).

Kerberos terminology

Client - An entity on the network (a user, a host, or an application) that can receive a ticket from Kerberos.

Ticket - A temporary set of credentials that verify the identity of a client for a particular service.

Realm - A network that uses Kerberos, composed of one or more servers and a potentially large number of clients.

Principal - The principal name or principal is the unique name of a user (User Principal) or service (Service Principal) allowed to authenticate using Kerberos.

Kinit - The kinit command allows a principal who has already logged in to obtain and cache the initial ticket.

Ticket Granting Service (TGS) - A server that issues tickets for the desired service which are in turn given to users for access to the service. The TGS usually runs on the same host as the KDC.

Key Distribution Center (KDC) - A service that issues Kerberos tickets, usually run on the same host as the Ticket Granting Server.

Ticket Granting Ticket (TGT) - A special ticket that allows the client to obtain additional tickets without applying for them from the KDC.

SPNEGO - Kerberos is designed for TCP/UDP protocols. SPNEGO is a specification on how to serialize the Kerberos V token as an HTTP header and send it to the web server. SPNEGO for Kerberos is the same as “Authorization: Basic <credentials>” for HTTP Basic Authentication.

Configuring Kerberos in GraphDB

In order to validate incoming SPNEGO tokens, the Spring Security Kerberos module needs a Kerberos keytab (a set of keys associated with a particular Kerberos account) and a service principal (the username of the associated Kerberos account). This account is used only to validate and decrypt the incoming SPNEGO tokens and is not associated with any user in GraphDB. You can find more on how to create a keytab file here.

Kerberos is configured via several properties:

Property Default value Usage
graphdb.auth.kerberos.enabled false Whether Kerberos authentication is enabled.
graphdb.auth.kerberos.keytab no default value Full or relative (to the GraphDB config directory) path to where the keys of the Kerberos service principal are stored. Required if Kerberos is enabled.
graphdb.auth.kerberos.principal no default value Name of the Kerberos service principal. Required if Kerberos is enabled.
graphdb.auth.kerberos.debug false Whether some of the Spring Kerberos classes print extra messages related to Kerberos.

In addition, you might want to specify a custom krb5.conf file via the java.security.krb5.conf property but Java should be able to pick up the default system file automatically.

User matching

Kerberos principals (usernames) need to be matched to GraphDB usernames. A Kerberos principal consists of a username, followed by @, followed by a realm. The realm looks like a domain name and is usually written out in capital letters. The principals are converted by simply dropping the @ sign and the realm. However, the realm from incoming SPNEGO tokens must match the realm of the service principal. Some examples:

Service principal Principal from SPNEGO token Username in GraphDB
HTTP/data.example.com@EXAMPLE.COM john@EXAMPLE.COM john
HTTP/data.example.com@EXAMPLE.COM john@FOO.EXAMPLE.COM Invalid authentication because of realm mismatch

Using SPNEGO tokens with GraphDB

There are various ways to use SPNEGO when talking to GraphDB as a client. All methods add the Kerberos/SPNEGO authentication in the HTTP client used by the RDF4J libraries.

Native method

The native method does not require any third-party libraries and relies on the built-in Kerberos capabilities of Java and Apache’s HttpClient. However, it is a bit cumbersome to use since it requires wrapping calls into an authentication context. This method supports only non-preemptive authentication, i.e., the GraphDB server must explicitly say it needs Kerberos/SPNEGO by sending a WWW-Authenticate: Negotiate header to the client.

Third-party library method

There is a third-party library called kerb4j, which makes some things easier. It does not require wrapping the execution into an authentication context and supports preemptive authentication, i.e., sending the necessary headers without asking the server if it needs authentication.

Both methods are illustrated in this example project.