Skip to main content
Version: 8.2411.x.x RR

Session management

The session coordinator is responsible for managing the session lifecycle using the local and remote session stores.

The two key phases or states of the nevisAuth session are:

  1. Unauthenticated session (also mentioned as initial session)
  2. Authenticated session

The authentication engine uses two different inactivity timeouts depending on the authentication phase. The session coordinator enforces the current inactivity timeout and an absolute session lifetime.

The different timeouts are:

  • initial inactivity timeout

    The user is performing a multistep authentication and has not yet fully established an authenticated session. This timeout is compared against the last access time.

  • established inactivity timeout

    The user has successfully logged in. This timeout is compared against the last access time during the authentication process.

  • absolute session timeout

    The session cannot be valid longer than the specified timeout. This timeout is compared against the creation time. This timeout is overridden by the time-to-live attribute of the TokenAssembler, if the session was created via the AuthEngine.

When nevisAuth does not receive any session "hits", the established inactivity timeout needs to be equal to the absolute session timeout (the longest acceptable global session lifetime). Session hits may be performed by any client that gets requests from the authenticated user.

As nevisProxy instances know about every request a user is sending, the global authentication session is usually controlled by nevisProxy instances. The above session timeouts therefore are only relevant for limiting unauthenticated sessions and for clearing sessions where no termination notifications from nevisProxy instances arrive.

A simple session store configuration, in conjunction with the timeouts enforced by the AuthEngine, looks as follows:

Session store configuration example
<SessionCoordinator
sessionInitialInactivityTimeout="600"
sessionInactivityTimeout="28800"
sessionInitialMaxLifetime="1200"
sessionMaxLifetime="28800"
sessionIdRandomBytes="32">

<LocalSessionStore
maxSessions="100000"
reaperPeriod="60" />

<TokenAssembler name="default">
<Selector default="true"/>
<TokenSpec version="CSSO-1.0" ttl="28800" algorithm="SHA256withRSA">
...
</TokenSpec>

<Domain name="SSO1" default="true"
reauthInterval="0"
inactiveInterval="1800">
...
</Domain>

This example enforces:

  • A maximum inactivity timeout to perform a login step of 600 seconds (only relevant for stateful multistep authentications), to be enforced by nevisAuth.
  • The "screen lock" timeout (i.e., the inactivity timeout, that triggers a reauthentication on an existing session with some minimal credential requirements) is disabled.
  • An inactivity timeout of 1800 seconds (i.e., the reverse proxy or client terminates the session and notifies nevisAuth, if the user does not send any requests during this period), to be enforced by the nevisProxy. Note that this setting only applies if the InactivePolicy of the IdentityCreationFilter is set to "global".
  • A maximum session lifetime of 1200 seconds for Unauthenticated sessions.
  • A maximum session lifetime of 28800 seconds for authenticated sessions, enforced by the lifetime of the security token. This is also the maximum lifetime for the authentication state within the session in nevisProxy.
info

Re-authentication (lock/unlock) can be enabled by setting reauthInterval to a value higher than 0 (zero). However, note that spontaneously locking sessions during application usage is non-trivial for many modern web applications and compatibility with this behavior should therefore be examined for each application.

SessionCoordinator configuration

The following list provides an overview of the SessionCoordinator configuration attributes for the two main groups regarding session management: lifetime and identifier.

  • sessionInitialInactivityTimeout (Integer, seconds, optional, 600)

    Default value: 600

    The session's initial inactivity timeout, which is raised to inactivityTimeout as soon as the initial login is completed. A user needs to perform a login step within this period. Otherwise, the user is forced to start over.

  • sessionInactivityTimeout (Integer, seconds, optional, 28800)

    Default value: 28800

    The session's inactivity timeout. When nevisAuth does not get any session events during this period, the session is killed.

    Kill the session if: lastAccessTime + inactivityTimeout <= now

    caution

    This attribute must have the same value as the maxLifeTime attribute.

  • sessionInitialMaxLifetime (Integer, seconds, optional, 1200)

    Default value: 1200

    The session's initial absolute timeout (applies to unauthenticated sessions), which is raised to maxLifetime as soon as the initial login is completed (e.g. when the authentication flow reaches AUTH_DONE). A user needs to complete the login within this period. Otherwise, the user is forced to start over.

  • sessionMaxLifetime (Integer, seconds, optional, 28800)

    Default value: 28800

    The session's absolute timeout. (applies to authenticated sessions)

    Kill the session if: creationTime + maxLifeTime <= now

    This attribute must have a value that is greater than or equal to the maximum of all TokenAssembler's ttl attribute because expired SecTokens already define the maximum lifetime of an authentication session.

  • sessionIdRandomBytes (Integer, byte, optional, 32)

    Default value: 32

    The session generates session IDs that are being used as global session identification and are part of the SecToken. The ID is generated by base64 encoding the specified number of true random bytes. The default of 16 bytes therefore represents 128 true random bits. The session ID length therefore is: sessionIdRandomBytes * 4 / 3 + $instance name length$

    info

    The session ID inside nevisAuth is prefixed with the nevisAuth instance ID to distinguish sessions generated by different nevisAuth instances.

  • sessionIdPreGenerate (Boolean, optional, true)

    Default value: true

    Under normal circumstances, a session receives its final ID only after the state AUTH_DONE is reached for the first time. At this point, the previous (temporary) session ID is changed to a randomly generated ID as configured using sessionIdRandomBytes.

    Under some circumstances, it is desirable to know the final session ID during the authentication process. This can be achieved by setting sessionIdPreGenerate to "true". The session variable ch.nevis.esauth.sess.id.pregenerate will then be filled with the future session ID upon creation of the session.

Choosing the session lifetime

It is recommended for nevisAuth and nevisProxy to have a same session lifetime. With this configuration, nevisAuth and nevisProxy sessions will be automatically in sync.

In case if different session lifetimes are configured in nevisAuth and nevisProxy the sessions between those components must be kept in sync. This synchronization can be enabled by setting EnablePollTerminatedCalls to true in the esauth4Connector in nevisProxy. In this usecase also the syncPullInitial must be enabled in the RemoteSessionStore in nevisAuth.

Note that as nevisProxy is coordinating the sessions at a higher level. Depending on the nevisProxy configuration RecheckAuthentication of the IdentityCreationFilter, nevisProxy might not contact nevisAuth again once a session becomes authenticated. This does not affect the EnablePollTerminatedCalls.

On the top of the session lifetime configuration the secToken also plays into the lifetime of a session. When the secToken expires the session practically ends. Therefore, the ttl of the TokenSpec in the TokenAssembler should match the configured session lifetimes in the SessionCoordinator and nevisProxy settings.

Additionally, the inactiveInterval attribute of the Domain element can be used to instruct nevisProxy to enforce an inactivity timeout on the nevisProxy level.

Terminated session polling

As mentioned before different session lifetime configuration in nevisProxy and nevisAuth requires syncrhonization.

An other use-case where nevisProxy is not aware of session modifications done in nevisAuth is when you use a nevisAuth REST service or nevisAdapt to kill sessions in nevisAuth. In such case you should set EnablePollTerminatedCalls to true in the esauth4Connector in nevisProxy to synchronize sessions.

Technically nevisProxy calls nevisAuth using the poll_terminated_sessions soap operation where nevisAuth will wait for maximum 30 seconds to respond. In case there are session terminations initiated by the caller nevisProxy instance, the reponse with terminated sessions will be returned immediately. In case there are no expired sessions, nevisAuth will send an empty response once 30 seconds passed. nevisProxy will remove the received sessions itself and it will reconnect again to listen for more sessions to be removed.

Session indexing

nevisAuth sessions can be indexed by a configurable session attribute. Enable indexing by adding the SessionIndexing element, see Session indexing for further information.

  • attribute (String, "$session attribute name$", optional, ch.nevis.session.loginid)

    Default value: ch.nevis.session.loginid

    Enable indexing on a configured session variable.

  • attributeMapping (Enum {none, unicodeEscape, hash}, optional, hash)

    Default value: hash

    Defines the format in which the session index value is written into the remote session store in case Session indexing is turned on. Available mappings are:

    • none

      The value of the session index is stored as it is in the session without any change (legacy behavior).

    • unicodeEscape

      Non-Latin 1 (ISO 8859-1) characters in the session index value are unicode-escaped (\uxxxx), for example, abçdefğ is stored as ab\u00E7def\u011F.

    • hash

      The SHA-256 (hex) hash of the session index value is stored, for example, abçdefğ is stored as 20c76ac7893952f0d6993b1977ec13893f76cc5fdf4eb4d00f788e89c9101785.

Local session store

nevisAuth uses an in-memory session store to hold authentication sessions. This session store uses an inactivity timeout mechanism with a session reaper mechanism to get rid of expired sessions. The session store has an upper limit to prevent uncontrolled session creation and heap space growth.

The use of the LocalSessionStore is required for the operation of nevisAuth, it cannot be disabled.

The following list provides an overview of the LocalSessionStore configuration attributes.

  • maxSessions (Integer, maximum number, optional, 100000)

    Default value: 100000

    The maximum number of session entries allowed. This parameter is the upper limit for parallel global authentication sessions. If the reverse proxy is solely using nevisAuth as an authentication infrastructure, only this number of authenticated clients are able to pass the reverse proxy at a time.

    Note that once the maximum amount is reached no new session will be created until an existing session expires. In kubernetes based deployments this might not be desired as nevisAuth might look healthy, but actually cannot receive new load.

  • reaperPeriod (Integer, seconds, optional, 60)

    Default value: 60

    The reaper thread locks the session cache regularly (defined by this period) to clean up expired sessions.

Remote session store

Typical reasons for storing sessions remotely are, to provide resilience, and to share sessions between nevisAuth instances.

Whenever a session event is fired, for example when a session is deleted or updated, all session listeners are informed about the recent change. As a listener the remote session store will attempt to synchronize the current state of the session with the remote session database.

The RemoteSessionStore element encapsulates the configuration options for the database connection, synchronization features and the cleanup of expired sessions. The remote session store is enabled by defining the RemoteSessionStore element. To disable it, either remove the element or comment it out.

By default, setting both authenticated and unauthenticated sessions are synchronized. This can be changed, so only authenticated session are stored.

Session synchronization

The remote session store shares the sessions with the database in an ignorant way, even if the synchronization failed, the authentication flow will not be disrupted.

In case syncFailRetryCount is configured the synchronization will be attempted again the set number of times. Related configuration option is the syncFailRetryPeriod, in case it is configured to be greater than 0 the future retries of the synchronization will be handled with a delay period asynchronously, because of this, the authentication flow can continue without waiting for the retry.

Note, that it doesn't make sense to configure this retry delay period to be high in environments where multiple nevisAuth instances are used as the continued authentication flow can end up on the other instance which doesn't yet have state of the session.

Relation between the local and remote session store

When remote session store is not configured, the local session store acts as the "single source of truth".

In case the remote session store is configured, the local session store behaves the same, however the SessionCoordinator operates in a way that the overall behaviour changes. The local session store is only used for the following purposes:

  1. Cache sessions during the evaluation of an authentication step.
  2. Keep nevisProxy informed about expired sessions. (polled terminated sessions)

nevisAuth keeps track, if a session has been synchronized to the remote session store or not. In case the synchronization fails, the authentication flow is not disrupted. The session remains in the local session store as a not synchronized session. For those sessions the source of truth will always be the local session store, once all retries are exhausted.

Stateless behaviour

Default configuration values in the RemoteSessionStore are set to make nevisAuth stateless with some resilience in case there is a synchronization issue to the remote session store.

Configuration

The following table provides an overview of the component's configuration properties.

  • provider (String, required, "jdbc")

    Default value: jdbc

    Specifies the provider handling synchronization connections.

    Only jdbc provider is supported, synchronizing to MariaDB or PostgreSQL database.

  • connectionUrl (String, required, -)

    This attribute specifies the connection/address of the synchronization medium for the provider.

    The connection URL must start with jdbc:mariadb:// for the MariaDB JDBC driver and with jdbc:postgresql:// for PostgreSQL.

    Example
    jdbc:mariadb://authdb:3306/nevisauth?autocommit=true
    jdbc:postgresql://authdb:5432/nevisauth

    For more information visit the official connection parameter documentation for [MariaDB](https://mariadb.com/kb/en/about-mariadb-connector-j/#connection-strings) and [PostgreSQL](https://jdbc.postgresql.org/documentation/use/#connecting-to-the-database

    info

    nevisAuth relies on the autocommit feature of the database.

    • MariaDB requires to enable it on the database level or configure it in the JDBC driver url in this property using the query parameter autocommit=true.
    • PostgreSQL by default have autocommit enabled.
  • connectionUser (String, required, -)

    The username for DB access. You can use the same format as for passwords. For example, you can use the following syntax to specify the username from an environment variable: pipe://echo $SYNC_USER. For more information regarding the allowed syntax, see Passwords in the configuration.

  • connectionPassword (String, required, -)

    The password for DB access. This property accepts standard encryption syntax. See documentation of how to restrict disclosure of passphrases in Passwords in the configuration.

  • connectionSchemaUser (string)

    The name of the user that creates the schema and tables in the database. This user must have CREATE access rights to the database. If not provided, the system will fall back to use the connectionUser. Only used when the connectionAutomaticDbSchemaSetup is configured to be true.

    info

    It is recommended that separate users create the schema and access the data. You specify these users in the DB properties schemaUser and dataUser, respectively.

  • connectionSchemaPassword (string)

    The password of the user who creates the schema and the tables in the database. If not provided, the system will fall back to use the connectionPassword. Only used when the connectionAutomaticDbSchemaSetup is configured to be true.

  • connectionAutomaticDbSchemaSetup (boolean)

    Default value: true

    If set to "true", nevisAuth will automatically try to create the table used to store the data (with the CREATE TABLE IF NOT EXISTS syntax).

    Set this property to "false", if you want to handle this differently. Also set the property to "false", if you did not specify the schemaUser or if the specified user does not have the required CREATE access rights.

  • connectionMinPoolSize (Integer, optional)

    Default value: connectionMaxPoolSize

    Mininum number of connections in the connection pool used to connect to the database. In case this is set lower then connectionMaxPoolSize, connections will be created on demand.

  • connectionMaxPoolSize (Integer, optional, 10)

    Default value: 10

    Size of the connection pool used to communicate with the remote session store.

  • connectionTimeout (Integer, msec, optional, 30000)

    Default value: 30000

    This property controls the maximum number of milliseconds that nevisAuth will wait for a connection from the pool.

  • connectionMaxLifeTime (Integer, msec, optional, 1800000)

    Default value: 1800000 (30 minutes)

    The maximum time, in milliseconds, that a connection used in the connection pool.

  • syncPullInitial (Boolean, optional, false)

    Default value: false

    Configures if all sessions should be pulled from the database on nevisAuth startup. This can be disabled if the terminated session polling is disabled in nevisProxy. The loading of sessions happens in the background in a non-blocking way.

  • syncFailRetryPeriod (Integer, msec, optional, 100)

    Default value: 100

    This period is used to re-queue a session after a failed synchronization. The period should allow a server to recover from some temporary problem.

  • syncFailRetryCount (Integer, optional, 1)

    Default value: 1

    This counter defines how many retries should be performed after a failed synchronization. The counter is decremented with each try.

  • reaperPeriod (Integer, seconds, optional, 60)

    Default value: 60

    The period in seconds in which to execute the run method of the RemoteSessionReaper.

  • reaperTimeoutTolerance (Integer, seconds, optional, 10% of max session lifetime)

    Default value: calculated: 10% of max session lifetime

    The tolerance (in seconds) is added to the ABS_TO value of the session. It must ensure that the RemoteSessionReaper is only used to reap remote sessions that were not killed explicitly. Changing it will not affect existing sessions.

  • reaperInitialDelay (Integer, seconds, optional, -)

    Default value: reaperPeriod

    Time elapsed before the first clean up of the remote session store with the RemoteSessionReaper is triggered after startup.

  • reaperThreads (Integer, optional, 1)

    Default value: 1

    The number of threads to use to clean up the remote session store with the RemoteSessionReaper.

  • storeUnauthenticatedSessions (Boolean, optional, true)

    Default value: true

    When disabled, only authenticated session are stored in the remote session store.

Setup required for the remote SQL session DB using the automatic table creation

MariaDB

CREATE DATABASE IF NOT EXISTS NSS;

CREATE USER IF NOT EXISTS `nss_auth_schemauser`@`localhost` IDENTIFIED BY 'password';
GRANT CREATE ON NSS.* TO `nss_auth_schemauser`@`localhost`;

CREATE USER IF NOT EXISTS `nss_auth`@`localhost` IDENTIFIED BY 'password';
GRANT SELECT, INSERT, UPDATE, DELETE ON NSS.* TO `nss_auth`@`localhost`;

FLUSH PRIVILEGES;
Timezone setup

Timezone database should be initialized, in case the SELECT * FROM mysql.time_zone_name; returns nothing than this means the timezone database was not yet initialized. To fix that you have to run mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql -p.

PostgreSQL

CREATE USER "nss_auth_schemauser" WITH encrypted password 'password';
CREATE USER "nss_auth" WITH encrypted password 'password';

CREATE DATABASE nss
WITH
OWNER = "nss_auth_schemauser";

ALTER ROLE "nss_auth_schemauser" IN DATABASE nss SET search_path TO "nss_auth_schemauser";
ALTER ROLE "nss_auth" IN DATABASE nss SET search_path TO "nss_auth_schemauser";

\connect nss "nss_auth_schemauser";
CREATE SCHEMA "nss_auth_schemauser" AUTHORIZATION "nss_auth_schemauser";

GRANT USAGE ON SCHEMA "nss_auth_schemauser" to "nss_auth";
GRANT CONNECT ON DATABASE nss TO "nss_auth";

ALTER DEFAULT PRIVILEGES FOR USER "nss_auth_schemauser" IN SCHEMA "nss_auth_schemauser" GRANT SELECT, INSERT, UPDATE, DELETE, TRIGGER ON TABLES TO "nss_auth";
ALTER DEFAULT PRIVILEGES FOR USER "nss_auth_schemauser" IN SCHEMA "nss_auth_schemauser" GRANT USAGE, SELECT ON SEQUENCES TO "nss_auth";
ALTER DEFAULT PRIVILEGES FOR USER "nss_auth_schemauser" IN SCHEMA "nss_auth_schemauser" GRANT EXECUTE ON FUNCTIONS TO "nss_auth";

Setting up a remote SQL session DB Manually

nevisAuth synchronizes session properties as blobs into the database. The schema of the remote SQL session table TNSSA_AUTH_SESSION_CACHE, which holds the session entries, is described in the following table:

FieldType (MariaDB)NullKeyDefault
SESSION_IDvarchar(255)NOPRIMARY KEYNULL
DATAmediumtextYESNULL
ABSTOtimestampNONULL
SESSION_INDEXvarchar(255)YESMULNULL
info

The SESSION_INDEX column is not intended for indexing large data fields (exceeding 255 characters). The column data-type has to be sized differently in scenarios where this assumption does not hold.

  • The SESSION_ID field corresponds to the ID of the local session of a nevisAuth instance.
  • The DATA field contains the session properties as strings in form of a blob.
  • The ABSTO field corresponds to the expiration timestamp of the session.
  • The DATA and ABSTO fields are updated whenever a session is synced, which is whenever a session is created or updated. Sessions that have a timestamp ABSTO in the past will not be resynched to a nevisAuth instance. Instead, they will be removed regularly if the remote session reaper is running.
  • We recommend storing the ABSTO as it increases the reliability of the nevisAuth instances that are pulling and pushing updates from and to the remote session cache.

MariaDB

The following MariaDB script can be used to create the remote session table that can be accessed from localhost with the user nss_auth :

Create remote session table
CREATE DATABASE IF NOT EXISTS NSS;

-- table TNSSA_AUTH_SESSION_CACHE stores the sessions
CREATE TABLE NSS.TNSSA_AUTH_SESSION_CACHE (
SESSION_ID VARCHAR(255) PRIMARY KEY NOT NULL UNIQUE,
DATA MEDIUMTEXT,
ABSTO TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
SESSION_INDEX VARCHAR(255)
) ENGINE = InnoDB;
-- nss_auth indexes:
ALTER TABLE NSS.TNSSA_AUTH_SESSION_CACHE ADD INDEX (SESSION_INDEX);
ALTER TABLE NSS.TNSSA_AUTH_SESSION_CACHE ADD INDEX (ABSTO);

-- nss_auth user:
CREATE USER nss_auth@localhost IDENTIFIED BY 'nss_auth';
GRANT SELECT,UPDATE,INSERT,DELETE ON NSS.TNSSA_AUTH_SESSION_CACHE TO nss_auth@localhost;
info

If the MariaDB database is located in a different host than nevisAuth, make sure that the user can remotely connect to it, as described in the MariaDB documentation.

Timezone setup

Timezone database should be initialized, in case the SELECT * FROM mysql.time_zone_name; returns nothing than this means the timezone database was not yet initialized. To fix that you have to run mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql -p.

PostgreSQL

Create remote session table
CREATE USER "nss_auth" WITH encrypted password 'password';

CREATE DATABASE nss
WITH
OWNER = "nss_auth";

ALTER ROLE "nss_auth" IN DATABASE nss SET search_path TO "nss_auth";

\connect nss "nss_auth";
CREATE SCHEMA "nss_auth" AUTHORIZATION "nss_auth"

CREATE TABLE IF NOT EXISTS TNSSA_AUTH_SESSION_CACHE (
SESSION_ID VARCHAR(255) PRIMARY KEY,
DATA TEXT,
ABSTO TIMESTAMP WITH TIME ZONE NOT NULL,
SESSION_INDEX VARCHAR(255)
);
CREATE INDEX IF NOT EXISTS session_index_idx ON TNSSA_AUTH_SESSION_CACHE (ABSTO);
CREATE INDEX IF NOT EXISTS session_reap_timestamp_idx ON TNSSA_AUTH_SESSION_CACHE (SESSION_INDEX);

Session store example use-cases

Short living sessions where resilience is not important

The simplest and best performing configuration for short-lived sessions with no session sharing.

Minimal configuration example
<SessionCoordinator
...>

<LocalSessionStore
maxSessions="100000"/>

Multiple nevisAuth instances

In case you would like to share sessions between multiple nevisAuth instances therefore synchronization should be done to a database immediately to have most up-to-date information. To achieve stateless behaviour, enable storing of unauthenticated sessions.

Example of immediate synchronisation to remote session store
<SessionCoordinator
...>

<LocalSessionStore
maxSessions="100000"/>

<RemoteSessionStore
provider="jdbc"
connectionUser="nss_auth"
connectionPassword="nss_auth"
connectionUrl="jdbc:mariadb://localhost:3306/NSS"
syncPullInitial="false"
syncFailRetryPeriod="100"
syncFailRetryCount="1"
storeUnauthenticatedSessions="true"/>

Multiple nevisAuth instances with resilience

More resilience can be achieved by using a replicated MariaDB setup and increasing retry failure. Note that increasing the rerty period and number will have some affects on memory usage in case of high load and prolonged syncronization issues.

Example with replicated remote session store databases and retries
<SessionCoordinator
...>

<LocalSessionStore
maxSessions="100000"/>

<RemoteSessionStore
provider="jdbc"
connectionUser="nss_auth"
connectionPassword="nss_auth"
connectionUrl="jdbc:mariadb:sequential//host-db1:3306,host-db2:3306/NSS"
syncPullInitial="false"
syncFailRetryPeriod="200"
syncFailRetryCount="3"
storeUnauthenticatedSessions="true"/>

Configuring Resilient Session Sharing with MariaDB

This chapter is about resilience towards outages of session sharing database nodes, which differs from resilience towards failures of nevisAuth instances. To achieve resilience towards failures of nevisAuth instances, configure two nevisAuth instance addresses in nevisProxy. Such a configuration enables failover to the second nevisAuth instance if nevisProxy cannot connect to the first nevisAuth instance. For more information, see the nevisProxy reference guide (InetAddress parameter of the HttpConnectorServlet).

Resilience to Database Instance Outage

This chapter explains how to set up two MariaDB instances for session sharing with replication . The advantages of this solution are:

  • Sharing of sessions between nevisAuth instances and support of nevisProxy for failover for the nevisAuth instances.
  • Resilience towards outage of one of the MariaDB instances or its host (e.g., in case of a power outage).
  • Resilience towards a failure of the network infrastructure between nevisAuth and one of the MariaDB instances (e.g., in case of a data center outage).
info

The above statements hold only as long as at least one of the two MariaDB instances is running and reachable. Simultaneous outage of both database instances hinders nevisAuth from sharing sessions.

Known limitations

Most sessions will remain undisturbed in case of a database outage. However, sessions that are undergoing multi-step authentication operations at the moment of the database outage may experience failures. If so, the operations have to be restarted. There are two known reasons for such session failures:

  • Session information loss

    If nevisAuth cannot synchronize the session with the remote session store (because the database instance has become unavailable at that precise time), it will possibly read an older state of the same session later on. This will result in the loss of the step-up authorization.

  • Session loss

    Session loss may occur in case of recurring loss of connectivity to database instances, or when both database nodes are restarted in short succession. In such a case, nevisAuth may read session information from a database node that does not contain the session yet (because replication from the other node is not finished yet).

Therefore, when restarting database instances for maintenance, we recommend waiting a few minutes between restarting one database instance and stopping the other.

Components involved

This section describes the setup of a fail-safe remote session store for nevisAuth using two MariaDB database hosts with data replication. This setup is tested with MariaDB version 10.3.7.

For a discussion of the nevisProxy connection to the nevisAuth configuration, see the nevisProxy reference guide HttpConnectorServlet section.

The database hosts are called host-db1 and host-db2 in this guide. The replication is enabled in both directions (master/master setup), so that each host is a replication master and a replication slave at the same time. The replication is enabled for a single database called "NSS". Unless otherwise stated, the instructions have to be performed on both hosts.

Implementation overview

Regular clustering solutions provide all fault-tolerant features themselves. Therefore, the connecting application has no clue about the resilient setup. The suggested solution takes a different approach. In this setup, the connecting application becomes part of the resilient setup in terms of configuration.

Two key features to achieve resilience

  • Connectivity (MariaDB JDBC driver) - configure a JDBC URL, where you define the DB nodes in priority order:

    jdbc:mariadb:sequential//host-db1:3306,host-db2:3306/NSS
  • Data consistency (MariaDB replication) - configure master-to-master replication.

    info

    Replication is done by the database. The application and the JDBC driver are not aware of it at all.

Overview of database users

The replicated session store is managed by several database users to separate concerns. The creation of the users is explained below.

UsernamePurposeRequired permissions
nss_authAccount used by nevisAuth for session management.ALL PRIVILEGES, on the replicated database to manage sessions.
replication_userAccount used by the slave node to log into the master node.REPLICATION SLAVE, to be able to replicate data.
binarylog_userAccount used for binary log management.SUPER permission, to be able to purge the binary logs.

Step-by-step setup of the replicated session store

This chapters assumes that the remote session store is already set up based on the section: Setting up a remote SQL session DB.

  1. Creation of the replication user

    CREATE USER IF NOT EXISTS replication_user IDENTIFIED BY 'replicationpassword';
    GRANT REPLICATION SLAVE ON *.* TO replication_user;
  2. Creation of the binary logs user

    CREATE USER IF NOT EXISTS binarylog_user IDENTIFIED BY 'binarylogspassword';
    GRANT SUPER ON *.* TO binarylog_user;
  3. Configuration of MariaDB service. To configure the MariaDB service, add the following entries to the file /etc/my.cnf as super user. The two configuration files (host-db1 and host-db2) differ at some points. The different lines are marked with (*).

  • Configure the MariaDB service on host-db1:

    [mariadb]
    # Enabling binary log
    log-bin
    # The ID of this master (*)
    server_id=1
    # The ID of the replication stream created by this master (*)
    gtid-domain-id=1
    # The basename and format of the binary log
    log-basename=mariadbmaster
    binlog-format=MIXED
    # Setting which tables are replicated
    replicate_wild_do_table="NSS.TNSSA_AUTH_SESSION_CACHE"
    # Avoiding collisions of primary IDs for tables where the primary ID is auto-incremented
    # Auto-increment value
    auto_increment_increment=2
    # Auto-increment offset (*)
    auto_increment_offset=1
    # Suppressing duplicated keys errors for multi-master setup
    slave_exec_mode=IDEMPOTENT
    # Ignoring some data definition language errors
    slave-skip-errors=1452, 1062
    # Suppressing binary logs after a delay regardless of the replication status
    expire_logs_days=1
    # Maximum number of connections
    max_connections=1000
    # Size of each of the binary log files (default: 1GB)
    max_binlog_size=500M
    # Enabling writing to the DB in parallel threads for the replication
    slave-parallel-threads=10
    # enabling semi-synchronous replication
    rpl_semi_sync_master_enabled=ON
    rpl_semi_sync_slave_enabled=ON
    # change to READ COMMITTED
    transaction-isolation=READ-COMMITTED
  • Configure the MariaDB service on host-db2:

    [mariadb]
    # Enabling binary log
    log-bin
    # The ID of this master (*)
    server_id=2
    # The ID of the replication stream created by this master (*)
    gtid-domain-id=2
    # The basename and format of the binary log
    log-basename=mariadbmaster
    binlog-format=MIXED
    # Setting which tables are replicated
    replicate_wild_do_table="NSS.TNSSA_AUTH_SESSION_CACHE"
    # Avoiding collisions of primary IDs for tables where the primary ID is auto-incremented
    # Auto-increment value
    auto_increment_increment=2
    # Auto-increment offset (*)
    auto_increment_offset=2
    # Suppressing duplicated keys errors for multi-master setup
    slave_exec_mode=IDEMPOTENT
    # Ignoring some data definition language errors
    slave-skip-errors=1452, 1062
    # Suppressing binary logs after a delay regardless of the replication status
    expire_logs_days=1
    # Maximum number of connections
    max_connections=1000
    # Size of each of the binary log files (default: 1GB)
    max_binlog_size=500M
    # Enabling writing to the DB in parallel threads for the replication
    slave-parallel-threads=10
    # enabling semi-synchronous replication
    rpl_semi_sync_master_enabled=ON
    rpl_semi_sync_slave_enabled=ON
    # change to READ COMMITTED
    transaction-isolation=READ-COMMITTED
  • Restart the MariaDB servers on both hosts:

    sudo service mariadb restart

Semi-synchronous replication

By default, MariaDB uses asynchronous replication. To reach more consistency, it is recommended using semi-synchronous replication.

The database configurations previously shown enable semi-synchronous replication with the following lines:

rpl_semi_sync_master_enabled=ON
rpl_semi_sync_slave_enabled=ON
info

MariaDB versions before 10.3.3 require the installation of plug-ins for semi-synchronous replication and are not supported.

Start-up of replication

To start the replication, log in as root into your MariaDB client and run the following commands.

  • on host-db1 (master is host-db2):

    CHANGE MASTER TO
    MASTER_HOST='host-db2',
    MASTER_USER='replication_user',
    MASTER_PASSWORD='replicationpassword',
    MASTER_PORT=3306,
    MASTER_USE_GTID=current_pos,
    MASTER_CONNECT_RETRY=10;
  • on host-db2 (master is host-db1):

    CHANGE MASTER TO
    MASTER_HOST='host-db1',
    MASTER_USER='replication_user',
    MASTER_PASSWORD='replicationpassword',
    MASTER_PORT=3306,
    MASTER_USE_GTID=current_pos,
    MASTER_CONNECT_RETRY=10;
  • on host-db1:

    START SLAVE;
  • on host-db2:

    START SLAVE;

Setup of nevisAuth

Most of the nevisAuth configuration is the same for the replicated remote session cache as for the "single-node" (normal) remote session cache. The only difference is the syncTarget property of the SessionCache, with which you configure the two database hosts:

<property name="syncTarget" value="jdbc:mariadb:sequential//host-db1:3306,host-db2:3306/NSS"/>

You can use the full MariaDB syntax, because the value of the syncTarget property is handed over to the MariaDB jdbc driver as connection string without any changes.

info

For more information on the MariaDB jdbc driver, see the MariaDB documentation.

We recommend using sequential fail-over mode, which will sequentially use the DB nodes in the provided order, one at the time.

info

The connection handling to a database in nevisAuth is lazy. If one of the database nodes fails, the system continuously moves each connection to the other node. The same happens if the failing database node recovers. That is, the connections are continuously re-initiated to the recovered node as they age out.

Additional setup

Purging the binary logs

With the provided configuration (expire_logs_days=1 in the MariaDB settings), the system will automatically remove the binary logs that are older than one day, even if the logs were not copied by the slave. This prevents the disk of the master node from being filled up in case the slave is down for a long time. The automatic binary log removal takes place when

  • the master DB node starts,
  • the logs are flushed (nevisAuth does not use this feature),
  • the binary log rotates, or
  • the binary logs are purged manually (see below).

So binary logs older than one day can exist, if none of the listed actions occurred recently.

Complementary to this expiration feature, MariaDB provides the possibility to manually purge the binary logs. The purge action removes all binary logs that were already copied by the slave. This allows a safe removal of the binary logs on a regular basis. The nevisProxy package is delivered with an adaptable purging script, which is located at: /opt/nevisproxy/sql/mariadb/purgebinarylogs.sh

To use this script,

  • copy the script to a location of your choice, and
  • adapt it to your configuration.

The script takes care of both DB nodes, so that it only needs to be configured once.

info

Note that if different database server nodes are used for nevisProxy and nevisAuth, you have set them up separately.

You can schedule the script to run for example once per hour, with a cron job:

0 * * * * /var/opt/nevisproxy/instance/conf/purgebinarylogs.sh # Absolute path of your adapted script

Size the binary logs

The provided configuration (max_binlog_size=500M in the MariaDB settings) allows you to configure the maximal size of the binary log files before rotating. The smaller the size, the more often rotations will occur, which will slow down replication. The advantage is a more efficient purge process. The bigger the size, the less often rotations will occur, but the disk may be filled with old logs.

According to our experiences, a size less than 8K does stop replication completely under heavy load, because the slave keeps rotating the logs.

Troubleshooting

Usually the slave stops the replication if an error occurs. You can check the state of the slave with the following SQL command:

show slave status\G

Note that showing the slave status requires the REPLICATION CLIENT grant.

If the replication has stopped, usually the error that caused it will be displayed. First you should try to fix the error. If this is not possible, you can do a "forced" restart of the slave like this:

  • on the master call (to display the current state of the master):

    MariaDB [replicated_session_store]> show master status\G
    *************************** 1. row ***************************
    File: mariadbmaster-bin.000131
    Position: 194630804
    Binlog_Do_DB:
    Binlog_Ignore_DB:
    1 row in set (0.00 sec)
  • On the slave (using the values returned by the call show master status\G on the master):

    STOP SLAVE;
    CHANGE MASTER TO
    MASTER_LOG_FILE='mariadbmaster-bin.000131',
    MASTER_LOG_POS=194630804;
    START SLAVE;

In this way, the system will restart the slave, without replicating to the slave all the sessions that occurred from the moment the replication has stopped until now.

Configuring Resilient Session Sharing with PostgreSQL

Not supported during the experimental phase.

Session attributes

The authentication session inside nevisAuth supports a set of well-known session attributes as defined in the following list. The context specifies whether the attribute is relevant during authentication processing or session coordination.

  • ch.nevis.session.userid (*) 1

    Context: auth

    Authenticated user principal; the user's internal identification, usually the primary key within the user database. Needs to be set by authentication plug-in on successful authentication.

  • ch.nevis.session.loginid

    Context: auth

    The login ID, the user has presented for authentication. May be set by authentication plug-in at any time during login.

  • ch.nevis.session.authlevel (*) 1

    Context: auth

    The level of authentication as defined by the site's security role model. May be set after each successful authentication phase (initial login, stepup) by the authentication plug-in.

  • ch.nevis.session.secroles

    Context: auth

    The (comma separated) list of actual security roles a user currently has.

  • ch.nevis.session.loginresource

    Context: sess

    This attribute reflects the resource (URL) a user accessed when performing the initial authentication step.

  • ch.nevis.session.domain (*) 1

    Context: auth

    The SSO domain the user was logged in.

  • ch.nevis.session.clientid

    Context: auth

    The Nevis entry point (reverse proxy) detects the client's channel and associates a channel ID with it. This attribute is used to store the channel ID.

  • ch.nevis.session.entryid (*) 1

    Context: auth

    The Nevis reverse proxy may be identified by this attribute.

  • ch.nevis.session.esauthid (*) 1

    Context: auth

    The nevisAuth instance that performed the authentication is specified by this attribute.

  • ch.nevis.session.sessid (*) 1

    Context: sess

    The global Nevis session ID.

  • ch.nevis.esauth.sess.id.-pregenerate

    Context: sess

    The pre-generated session ID, if idPregenerate is set to true.

  • ch.nevis.session.time.creation, ch.nevis.session.time.access, ch.nevis.session.time.time.sync

    Context: sess

    These are timestamps, marking the suggested event in the session life cycle.

  • ch.nevis.session.timeout.idle, ch.nevis.session.timeout.absolute

    Context: sess

    These are the timeouts (in milliseconds) of the session that are enforced as described in Session cache. |


  1. The attributes marked with (*) are usually part of the SecToken.