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:
- Unauthenticated session (also mentioned as initial session)
- 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:
<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.
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
cautionThis 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$
infoThe 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 usingsessionIdRandomBytes
.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 variablech.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 asab\u00E7def\u011F
.hash
The SHA-256 (hex) hash of the session index value is stored, for example,
abçdefğ
is stored as20c76ac7893952f0d6993b1977ec13893f76cc5fdf4eb4d00f788e89c9101785
.
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:
- Cache sessions during the evaluation of an authentication step.
- 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 withjdbc:postgresql://
for PostgreSQL.Examplejdbc: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-databaseinfonevisAuth 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.
- MariaDB requires to enable it on the database level or configure it in the JDBC driver url in this property using the query parameter
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 theconnectionAutomaticDbSchemaSetup
is configured to betrue
.infoIt 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 theconnectionAutomaticDbSchemaSetup
is configured to betrue
.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.
connectionMaxPoolSize
(Integer, optional, 20)Default value:
20
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:
Field | Type (MariaDB) | Null | Key | Default |
---|---|---|---|---|
SESSION_ID | varchar(255) | NO | PRIMARY KEY | NULL |
DATA | mediumtext | YES | NULL | |
ABSTO | timestamp | NO | NULL | |
SESSION_INDEX | varchar(255) | YES | MUL | NULL |
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
andABSTO
fields are updated whenever a session is synced, which is whenever a session is created or updated. Sessions that have a timestampABSTO
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 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;
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 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.
<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.
<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.
<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).
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.
infoReplication 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.
Username | Purpose | Required permissions |
---|---|---|
nss_auth | Account used by nevisAuth for session management. | ALL PRIVILEGES, on the replicated database to manage sessions. |
replication_user | Account used by the slave node to log into the master node. | REPLICATION SLAVE, to be able to replicate data. |
binarylog_user | Account 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.
Creation of the replication user
CREATE USER IF NOT EXISTS replication_user IDENTIFIED BY 'replicationpassword';
GRANT REPLICATION SLAVE ON *.* TO replication_user;Creation of the binary logs user
CREATE USER IF NOT EXISTS binarylog_user IDENTIFIED BY 'binarylogspassword';
GRANT SUPER ON *.* TO binarylog_user;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-COMMITTEDConfigure 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-COMMITTEDRestart 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
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.
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.
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.
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
(*) 1Context:
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
(*) 1Context:
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
(*) 1Context:
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
(*) 1Context:
auth
The Nevis reverse proxy may be identified by this attribute.
ch.nevis.session.esauthid
(*) 1Context:
auth
The nevisAuth instance that performed the authentication is specified by this attribute.
ch.nevis.session.sessid
(*) 1Context:
sess
The global Nevis session ID.
ch.nevis.esauth.sess.id.-pregenerate
Context:
sess
The pre-generated session ID, if
idPregenerate
is set totrue
.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. |
- The attributes marked with (*) are usually part of the SecToken.↩