<chapter id="clustering">
    <title>Clustering</title>

    <para>To improve availability and scalability Keycloak can be deployed in a cluster.</para>

    <para>It's fairly straightforward to configure a Keycloak cluster, the steps required are:
        <itemizedlist>
            <listitem>
                <para>
                    Configure a shared database
                </para>
            </listitem>
            <listitem>
                <para>
                    Configure Infinispan
                </para>
            </listitem>
            <listitem>
                <para>
                    Enable realm and user cache invalidation
                </para>
            </listitem>
            <listitem>
                <para>
                    Enable distributed user sessions
                </para>
            </listitem>
            <listitem>
                <para>
                    Start in HA mode
                </para>
            </listitem>
        </itemizedlist>
    </para>

    <section>
        <title>Configure a shared database</title>
        <para>
            Keycloak doesn't replicate realms and users, but instead relies on all nodes using the same
            database. This can be a relational database or Mongo. To make sure your database doesn't become a single
            point of failure you may also want to deploy your database to a cluster.
        </para>
    </section>

    <section>
        <title id="cluster-configure-infinispan">Configure Infinispan</title>
        <para>
            Keycloak uses <ulink url="http://www.infinispan.org/">Infinispan</ulink> caches to share information between nodes.
        </para>
        <para>
            For realm and users Keycloak uses a invalidation cache. An invalidation cache doesn't share any data, but simply
            removes stale data from remote caches. This reduces network traffic, as well as preventing sensitive data (such as
            realm keys and password hashes) from being sent between the nodes.
        </para>
        <para>
            User sessions and login failures supports either distributed caches or fully replicated caches. We recommend using a distributed
            cache.
        </para>
        <para>
            To configure the required Infinspan caches open <literal>standalone/configuration/standalone-ha.xml</literal> and add:
<programlisting>
<![CDATA[
<subsystem xmlns="urn:jboss:domain:infinispan:2.0">
    <cache-container name="keycloak" jndi-name="infinispan/Keycloak" start="EAGER">
        <invalidation-cache name="realms" mode="SYNC"/>
        <invalidation-cache name="users" mode="SYNC"/>
        <distributed-cache name="sessions" mode="SYNC" owners="1" />
        <distributed-cache name="loginFailures" mode="SYNC" owners="1" />
    </cache-container>
    ...
</subsystem>
]]>
</programlisting>
        </para>
        <para>
            For more advanced options refer to the
            <ulink url="http://docs.jboss.org/author/display/WFLY8/Infinispan+Subsystem">Infinispan Subsystem</ulink>
            and
            <ulink url="http://www.infinispan.org/docs/6.0.x/user_guide/user_guide.html">Infinispan</ulink>
            documentation.
        </para>
        <para>
            Next open <literal>standalone/configuration/keycloak-server.json</literal> and add:
<programlisting>
"connectionsInfinispan": {
    "default" : {
        "cacheContainer" : "java:jboss/infinispan/Keycloak"
    }
}
</programlisting>
        </para>
    </section>

    <section>
        <title>Enable realm and user cache invalidation</title>
        <para>
            To reduce number of requests to the database Keycloak caches realm and user data. In cluster mode
            Keycloak uses an Infinispan invalidation cache to make sure all nodes re-load data from the database
            when it is changed. Using an invalidation cache instead of a replicated cache reduces the network traffic
            generated by the cluster, but more importantly prevents sensitive data from being sent.
        </para>
        <para>
            To enable realm and user cache invalidation open <literal>keycloak-server.json</literal> and change
            the <literal>realmCache</literal> and <literal>userCache</literal> providers to <literal>infinispan</literal>:
<programlisting>
"realmCache": {
    "provider": "infinispan"
},

"userCache": {
    "provider": "infinispan"
}
</programlisting>
        </para>
    </section>

    <section>
        <title>Enable distributed user sessions</title>
        <para>
            To help distribute the load of user sessions Keycloak uses an Infinispan distributed cache. A distributed
            cache splits user sessions into segments where each node holds one or more segment. It is possible
            to replicate each segment to multiple nodes, but this is not strictly necessary since the failure of a node
            will only result in users having to log in again. If you need to prevent node failures from requiring users to
            log in again, set the <literal>owners</literal> attribute to 2 or more for the <literal>sessions</literal> cache
             (see <link linkend='cluster-configure-infinispan'>Configure Infinispan</link>).
        </para>
        <para>
            To enable the Infinispan user sessions provider open <literal>keycloak-server.json</literal> and change the
            userSessions provider to <literal>infinispan</literal>:
<programlisting>
"userSessions": {
    "provider": "infinispan"
}
</programlisting>
        </para>
    </section>

    <section>
        <title>Start in HA mode</title>
        <para>
            To start the server in HA mode, start it with:
            <programlisting># bin/standalone --server-config=standalone-ha.xml</programlisting>
        </para>
        <para>
            Alternatively you can copy <literal>standalone/config/standalone-ha.xml</literal> to <literal>standalone/config/standalone.xml</literal>
            to make it the default server config.
        </para>
    </section>

    <section>
        <title>Enabling cluster security</title>
        <para>
            By default there's nothing to prevent unauthorized nodes from joining the cluster and sending potentially malicious
            messages to the cluster. However, as there's no sensitive data sent there's not much that can be achieved.
            For realms and users all that can be done is to send invalidation messages to make nodes load data from the
            database more frequently. For user sessions it would be possible to modify existing user sessions, but creating
            new sessions would have no affect as they would not be linked to any access tokens. There's not to much that
            can be achieved by modifying user sessions. For example it would be possible to prevent sessions from expiring,
            by changing the creation time. However, it would for example have no effect adding additional permissions to the
            sessions as these are rechecked against the user and application when the token is created or refreshed.
        </para>
        <para>
            In either case your cluster nodes should be in a private network, with a firewall protecting them from outside
            attacks. Ideally isolated from workstations and laptops. You can also enable encryption of cluster messages,
            this could for example be useful if you can't isolate cluster nodes from workstations and laptops on your private
            network. However, encryption will obviously come at a cost of reduced performance.
        </para>
        <para>
            To enable encryption of cluster messages you first have to create a shared keystore (change the key and store passwords!):
<programlisting>
<![CDATA[
# keytool -genseckey -alias keycloak -keypass <PASSWORD> -storepass <PASSWORD> \
 -keyalg Blowfish -keysize 56 -keystore defaultStore.keystore -storetype JCEKS
]]>
</programlisting>
        </para>
        <para>
            Copy this keystore to all nodes (for example to standalone/configuration). Then configure JGroups to encrypt all
            messages by adding the <literal>ENCRYPT</literal> protocol to the JGroups sub-system (this should be added after
            the <literal>pbcast.GMS</literal> protocol):
<programlisting>
<![CDATA[
<subsystem xmlns="urn:jboss:domain:jgroups:2.0" default-stack="udp">
    <stack name="udp">
        ...
        <protocol type="pbcast.GMS"/>
        <protocol type="ENCRYPT">
            <property name="key_store_name">
                ${jboss.server.config.dir}/defaultStore.keystore
            </property>
            <property name="key_password">PASSWORD</property>
            <property name="store_password">PASSWORD</property>
            <property name="alias">keycloak</property>
        </protocol>
        ...
    </stack>
    <stack name="tcp">
        ...
        <protocol type="pbcast.GMS"/>
        <protocol type="ENCRYPT">
            <property name="key_store_name">
                ${jboss.server.config.dir}/defaultStore.keystore
            </property>
            <property name="key_password">PASSWORD</property>
            <property name="store_password">PASSWORD</property>
            <property name="alias">keycloak</property>
        </protocol>
        ...
    </stack>
    ...
</subsystem>
]]>
</programlisting>
            See the <ulink url="http://www.jgroups.org/manual/index.html#ENCRYPT">JGroups manual</ulink> for more details.
        </para>
    </section>

</chapter>
