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 bcrypt 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

graphdb.auth.ldap.url
(required)

LDAP endpoint
ldap://<my-openldap-server>:389/<partition>
graphdb.auth.ldap.user.search.base
Query to identify the directory where all authenticated users are located.
<empty>
graphdb.auth.ldap.user.search.filter
(required)

Matches the attribute to a GraphDB username
(cn={0})
graphdb.auth.ldap.role.search.base

Query to identify the directory where roles/groups for authenticated users are located
<empty>
graphdb.auth.ldap.role.search.filter
(required)

Authorize a user by matching the manner in which they are listed within the group.
(uniqueMember={0})
graphdb.auth.ldap.role.search.attribute

The attribute to identify the common name
cn (default)
graphdb.auth.ldap.role.map.administrator
(required)

Map a single LDAP group to GDB administrator role
my-group-name
graphdb.auth.ldap.role.map.repositoryManager

Map a single LDAP group to GDB repository manager role
my-group-name
graphdb.auth.ldap.role.map.repository.read.<my-repo>

Map a single LDAP group to GDB repository-specific read permissions
my-group-name
graphdb.auth.ldap.role.map.repository.write.<my-repo>

Map a single LDAP group to GDB repository-specific write permissions
my-group-name
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

Authorize a user by matching the manner in which they are listed within the group
(uniqueMember={0})
graphdb.auth.ldap.repository.read.attribute

Specify the mapping of a GraphDB repository id to a LDAP attribute
cn (default)
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

Authorize a user by matching the manner in which they are listed within the group
(uniqueMember={0})
graphdb.auth.ldap.repository.write.attribute

Specify the mapping of a GraphDB repository id to a LDAP attribute
cn (default)
graphdb.auth.ldap.bind.userDn

Specify a userDN (distinguished name) allowing anonymous binds and anonymous access to a LDAP server
dn (default)
graphdb.auth.ldap.bind.userDn.password

UserDN password

Note

Configuration changes happen only after restart.

Mapping user type roles

GraphDB has three standard user roles: Administrator, Repository manager, and User. Every user authenticated over LDAP will be assigned one of these roles.

Mapping the Administrator role

Set the following property to the LDAP role that must receive this role:

graphdb.auth.ldap.role.map.administrator = gdbadmin

Mapping the Repository manager role

Set the following property to the LDAP role that must receive this role:

graphdb.auth.ldap.role.map.repositoryManager = gdbrepomanager

Mapping the User role

Unless a user has been assigned the Administrator or Repository manager role, they will receive the User role automatically.

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” organization.
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

# Required for accessing a LDAP server that does not allow anonymous binds and anonymous access.
graphdb.auth.ldap.bind.userDn = uid=userId,ou=people,dc=example,dc=org
graphdb.auth.ldap.bind.userDn.password = 123456

Basic authentication

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

Authorization: Basic YWRtaW46cm9vdA==

Warning

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.

To log in and obtain a JWT string:

  1. Log in and generate the authorization.

curl -X POST -I 'http://localhost:7200/rest/login/admin' -H 'X-GraphDB-Password: root'
  1. Pass the returned JWT key.

curl -H 'Authorization: GDB eyJ1c2VybmFtZSI6ImFkbWluIiwiYXV0aGVudGljYXRlZEF0IjoxNTU3OTIzNTkxNDA0fQ==.OwSkajbUoHHsQGfwvaCxbb1f7bn0PJUeL4VbGEmNcWY=' http://localhost:7200/repositories/SYSTEM/size

The JWT token expires every 30 days.

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.