Configuration the keycloak server for SSO(Single-Sign-On) for a lab environment (1)

Sunho Song
7 min readOct 7, 2021

Continuing from the last time(Configuration the PostgreSQL database for metadata using PGPool on lab environment), this time, we will configure the keycloak service for SSO. keycloak provides various functions related to authentication and authority, but among them, it provides an OIDC function linked with LDAP. Using this, you can implement SSO very easily, and maintenance in the future is also very easy. Also, it can be seen that the extensibility is very good because the part where OIDC linkage is not possible can be linked with LDAP.

The architecture for continuous machine learning in a lab environment

As can be seen from the figure above, keycloak is located in the center of the system and manages authority and authentication. Since all services are basically configured in HA in the lab environment, keycloak is also going to be configured in HA by default.

Keycloak cluster information

Basic information of the keycloak cluster is as follows.

Hostname and IP address

+-----------+------------+-----------------------------------+
| Hostname | IP Address | Role |
+-----------+------------+-----------------------------------+
| keycloak1 | 10.0.0.168 | Domain Controller |
| keycloak2 | 10.0.0.169 | Host Controller |
| keycloak3 | 10.0.0.170 | Host Controller |
+-----------+------------+-----------------------------------+

Version and configuration

+-----------------------+----------------------+--------+
| Item | Value | Detail |
+-----------------------+----------------------+--------+
| Keycloak | 15.0.2 | |
| Port | 8080 | |
| Keycloak Home | /opt/keycloak | |
| Default Service Name | homelab | |
| Default Admin User | awslife | |
| Default Admin Pass | ChangeMe | |
| Default Mgmt User | awslife | |
| Default Mgmt Pass | ChangeMe | |
| Default Mgmt Group | homelab | |
+-----------------------+----------------------+--------+

Installation Keycloak

Since keycloak is developed in java, you need to install java first to install keycloak. Find and install the java version suitable for keycloak. Since keycloak version 15.0.2 uses JDK-8, install JDK-8.

$ dnf install -y java-1.8.0-openjdk-headless
$ java -version
openjdk version "1.8.0_302"
OpenJDK Runtime Environment (build 1.8.0_302-b08)
OpenJDK 64-Bit Server VM (build 25.302-b08, mixed mode)

After the java installation is complete, download keycloak 15.0.2 from the keycloak project page on Github and extract the compressed file. And for the convenience of version management, create a link with keycloak.

$ cd /opt/
$ wget https://github.com/keycloak/keycloak/releases/download/15.0.2/keycloak-15.0.2.tar.gz
$ tar zxf keycloak-15.0.2.tar.gz
$ ls -l keycloak-15.0.2
total 508
-rw-r--r--. 1 root root 11358 Aug 20 16:30 LICENSE.txt
drwxr-xr-x. 3 root root 4096 Oct 6 21:37 bin
drwxr-xr-x. 7 root root 92 Aug 20 16:30 docs
drwxr-xr-x. 4 root root 38 Aug 20 16:30 domain
-rw-r--r--. 1 root root 497210 Aug 20 16:30 jboss-modules.jar
drwxr-xr-x. 3 root root 39 Oct 6 21:37 modules
drwxr-xr-x. 6 root root 68 Aug 20 16:30 standalone
drwxr-xr-x. 5 root root 71 Oct 6 21:37 themes
-rw-r--r--. 1 root root 26 Aug 20 16:30 version.txt
drwxr-xr-x. 2 root root 42 Oct 6 21:37 welcome-content
$ ln -sf ./keycloak-15.0.2 ./keycloak

HA will be provided by default by configuring the keycloak configuration in domain clustered mode. So, let’s proceed with the domain setting first. Open the /opt/keycloak/domain/configuration/domain.xml file and edit the following part. The domain.xml file must be modified on all hosts.

  • Delete the auth-server-standalone element of profiles.
  • Delete the datasource jndi-name=”java:jboss/datasources/ExampleDS” of datasources in auth-server-clustered profile.
  • Edit the datasource jndi-name=”java:jboss/datasources/KeycloakDS” likes below.
<datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
<connection-url>{{ postgresql_connection_url }}</connection-url>
<driver>postgresql</driver>
<security>
<user-name>{{ postgresql_username }}</user-name>
<password>{{ postgresql_password }}</password>
</security>
</datasource>
  • Change the driver element likes below.
<drivers>
<driver name="postgresql" module="org.postgresql">
<xa-datasource-class>org.postgresql.xa.PGXADataSource</xa-datasource-class>
</driver>
</drivers>
  • Change the datasource of default binding likes below.
<default-bindings context-service="java:jboss/ee/concurrency/context/default" datasource="java:jboss/datasources/KeycloakDS" managed-executor-service="java:jboss/ee/concurrency/executor/default" managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default" managed-thread-factory="java:jboss/ee/concurrency/factory/default"/>
  • Delete the load-balancer element in profiles.
  • Change inet-address value to host IP address in the interface. The default value is “127.0.0.1”.
  • add proxy-address-forwarding=”true” to server element. it is needed for the load balancer.
<server name="default-server">
<ajp-listener name="ajp" socket-binding="ajp"/>
<http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true" proxy-address-forwarding="true"/>
<https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true" proxy-address-forwarding="true"/>
<host name="default-host" alias="localhost">
<location name="/" handler="welcome-content"/>
<http-invoker security-realm="ApplicationRealm"/>
</host>
</server>
  • Delete the load-balancer-sockets element in socket-binding-group.
  • Delete the load-balancer-group server-group element in server-groups.

This time, edit the /opt/keycloak/domain/configuration/host-master.xml file. Likewise, it should be modified on all hosts.

  • Delete the name=”master” to the host element.
  • Change inet-address value to host IP address in the interface. management and public interface should be changed. The default value is “127.0.0.1”.
  • Delete the name=”load-balancer” element in servers.
  • Change the name=”server-one” to default service name. we defined the default service name to homelab above.

When the above settings are completed, add the default administrator. In domain clustered mode, the settings are saved in the directory of the service name, so create the directory first and execute the add administrator command. After the administrator addition is completed, the operator account is added. Since the operator account is used for connection between the master and slave nodes, copy the key output when adding the operator account.

$ mkdir -p /opt/keycloak/domain/servers/homelab/configuration
$ /opt/keycloak/bin/add-user-keycloak.sh --domain --dc /opt/keycloak/domain/servers/homelab/configuration --realm master --user awslife --password 'ChangeMe'
Added 'awslife' to '/opt/keycloak/domain/servers/homelab/configuration/keycloak-add-user.json', restart server to load user
$ /opt/keycloak/bin/add-user.sh --user awslife --password 'ChangeMe' --group 'homelab' --realm ManagementRealm --confirm-warning --display-secret
Added user 'awslife' to file '/opt/keycloak-15.0.2/domain/configuration/mgmt-users.properties'
Added user 'awslife' with groups homelab to file '/opt/keycloak-15.0.2/domain/configuration/mgmt-groups.properties'
To represent the user add the following to the server-identities definition <secret value="dGhkcm5zODEh" />
$

When the account addition is completed, the domain cluster master configuration is complete. Now let’s proceed with the slave setup. Slave configuration starts by editing the host-slave.xml file. The host-slave.xml file must be modified on all hosts as well.

  • Change the secret-value using printed value when adding a management user. This time the secret-value is “dGhkcm5zODEh”
<server-identities>
<secret value="dGhkcm5zODEh"/>
</server-identities>
  • Add the username tag in the remote element on the domain-controller element. An example is ‘username=awslife’.
<domain-controller>
<remote security-realm="ManagementRealm" username="awslife" >
<discovery-options>
<static-discovery name="primary" protocol="${jboss.domain.master.protocol:remote+http}" host="${jboss.domain.master.address}" port="${jboss.domain.master.port:9990}"/>
</discovery-options>
</remote>
</domain-controller>
  • Change the server name value to the default service name in the servers element.
<servers>
<server name="{{ default_service_name }}" group="auth-server-group" auto-start="true">
<jvm name="default"/>
<socket-bindings port-offset="250"/>
</server>
</servers>

The Master and slave setup is complete. Next, configure the keycloak module of each host so that the keycloak service can connect to the Postgres database. First, create a directory to copy the PostgreSQL JDBC driver, download the PostgreSQL JDBC driver, and copy it to the directory.

$ mkdir -p /opt/keycloak/modules/system/layers/keycloak/org/postgresql/main
$ wget https://jdbc.postgresql.org/download/postgresql-42.2.22.jar -O /opt/keycloak/modules/system/layers/keycloak/org/postgresql/main/postgresql-42.2.22.jar
--2021-10-07 19:15:27-- https://jdbc.postgresql.org/download/postgresql-42.2.22.jar
Resolving jdbc.postgresql.org (jdbc.postgresql.org)... 72.32.157.228, 2001:4800:3e1:1::228
Connecting to jdbc.postgresql.org (jdbc.postgresql.org)|72.32.157.228|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1005173 (982K) [application/java-archive]
Saving to: ‘/opt/keycloak/modules/system/layers/keycloak/org/postgresql/main/postgresql-42.2.22.jar’
/opt/keycloak/modules/system/layers/keycloak/o 100%[=================================================================================================>] 981.61K 1.13MB/s in 0.8s2021-10-07 19:15:29 (1.13 MB/s) - ‘/opt/keycloak/modules/system/layers/keycloak/org/postgresql/main/postgresql-42.2.22.jar’ saved [1005173/1005173]

Next, copy the Postgres-related module.xml file to the directory where the JDBC driver is located. The details of the module.xml file are as follows.

<module xmlns="urn:jboss:module:1.3" name="org.postgresql">
<resources>
<resource-root path="postgresql-42.2.22.jar"/>
</resources>
<dependencies>
<module name="javax.api"/>
<module name="javax.transaction.api"/>
</dependencies>
</module>

Now, we will create and register a systemd service file so that keycloak can be serviced normally. Modify the contents of “keycloak.service” below to match the environment of each server and copy it to “/usr/lib/systemd/systemd/system/keycloak.service.”

  • keycloak.service for domain controller
[Unit]
Description=The Keycloak Server
After=syslog.target network.target
[Service]
Environment=BIND_ADDRESS="-Djboss.bind.address.management={{ ip4 }} -Djboss.bind.address.private={{ ip4 }} -Djboss.bind.address={{ ip4 }}"
Environment=LOG_DIR="-Djboss.server.log.dir=/var/log/keycloak -Djboss.domain.log.dir=/var/log/keycloak"
Environment=MASTER_ADDRESS="-Djboss.domain.master.address={{ master_ip4 }}"
Environment=DOMAIN_CONF="-Djboss.domain.temp.dir={{ default_domain_path }}/temp -Djboss.domain.log.dir={{ default_domain_path }}/log -Djboss.domain.data.dir={{ default_domain_path }}/data -Djboss.domain.servers.dir={{ default_domain_path }}/servers"
LimitNOFILE=102642
PIDFile=/var/run/keycloak/keycloak.pid
ExecStart=/opt/keycloak/bin/domain.sh --host-config=host-master.xml $BIND_ADDRESS $LOG_DIR $MASTER_ADDRESS $DOMAIN_CONF
StandardOutput=null
[Install]
WantedBy=multi-user.target
  • keycloak.service for host controller
[Unit]
Description=The Keycloak Server
After=syslog.target network.target
[Service]
Environment=BIND_ADDRESS="-Djboss.bind.address.management={{ ip4 }} -Djboss.bind.address.private={{ ip4 }} -Djboss.bind.address={{ ip4 }}"
Environment=LOG_DIR="-Djboss.server.log.dir=/var/log/keycloak -Djboss.domain.log.dir=/var/log/keycloak"
Environment=MASTER_ADDRESS="-Djboss.domain.master.address={{ master_ip4 }}"
LimitNOFILE=102642
PIDFile=/var/run/keycloak/keycloak.pid
ExecStart=/opt/keycloak/bin/domain.sh --host-config=host-slave.xml $BIND_ADDRESS $LOG_DIR $MASTER_ADDRESS
StandardOutput=null
[Install]
WantedBy=multi-user.target

Reload the systemd daemon and start the keycloak service.

$ systemdctl daemon-reload
$ systemctl start keycloak.service

Conclusion

I have summarized how to install in Keycloak domain clustered mode. Next time, we will connect LDAP to keycloak and take time to issue OIDC token key. Ansible playbook for installation has been published on Github. Please refer to Github for more details.

I hope it will be helpful to those who are looking for related content. And if you have saved a lot of time with this content, please donate a cup of coffee. (Please help me to write while eating ice americano at a local cafe.)

https://buymeacoffee.com/7ov2xm5

References

  • keycloak domain configuration
  • Installing and Configuring Keycloak — Domain Clustered Deployment

--

--

Sunho Song

I have developed an AI platform for semiconductor defect analysis. I am very interested in MLOps and love to learn new skills.