Keycloak Identity and Access Management on FreeBSD

Many times I wrote about FreeIPA/IDM – but I have one problem with it – its not currently possible to run FreeIPA on FreeBSD … so I searched for other open source alternatives and found Keycloak. What surprised me even more is that its even available in the FreeBSD Ports as net/keycloak port. So I wanted to check how it works/runs on FreeBSD … and this is exactly how this article happened.

keycloak.logo

My earlier FreeIPA/IDM attempts are below.

First – we will create new VM for our server. I will use sysutils/vm-bhyve-devel for Bhyve but feel free to use any other hypervisor (or even w/o one). To not waste time installing I will also use provided by FreeBSD project VM-IMAGE with ZFS enabled – FreeBSD-14.0-RELEASE-amd64-zfs.raw disk0.img

host # cat /vm/.templates/freebsd.conf
loader="bhyveload"
cpu=1
memory=256M
network0_type="virtio-net"
network0_switch="public"
disk0_type="nvme"
disk0_name="disk0.img"

host # vm create -t freebsd -c 2 -m 4G -s 10G keycloak

host # ls -lh /vm/keycloak
total 3402399
-rw-------  1 root wheel   10G Mar 10 10:47 disk0.img
-rw-r--r--  1 root wheel  209B Mar 10 07:20 keycloak.conf
-rw-r--r--  1 root wheel   96B Mar 10 07:22 vm-bhyve.log

host # cd /vm/keycloak

host # rm -f disk0.img

host # cp /vm/TEMPLATE/FreeBSD-14.0-RELEASE-amd64-zfs.raw disk0.img

host # truncate -s 10G disk0.img

host # vm start keycloak
Starting keycloak
  * found guest in /vm/keycloak
  * booting...

host # vm console keycloak

Type root as user and hit [ENTER] for empty password. Now the FreeBSD setup and needed packages.

root@freebsd:~ # :> ~/.hushlogin

root@freebsd:~ # cat << EOF > /etc/rc.conf
hostname="keycloak.lab.org"
ifconfig_DEFAULT="inet 10.1.1.211/24"
defaultrouter="10.1.1.1"
growfs_enable="YES"
zfs_enable="YES"
sshd_enable="YES"
postgresql_enable="YES"
keycloak_enable="YES"
keycloak_env="KEYCLOAK_ADMIN=admin KEYCLOAK_ADMIN_PASSWORD=password"
EOF

root@freebsd:~ # echo 10.1.1.211 keycloak.lab.org keycloak >> /etc/hosts

root@freebsd:~ # mkdir -p /usr/local/etc/pkg/repos

root@freebsd:~ # sed -e s/quarterly/latest/g /etc/pkg/FreeBSD.conf \
                   > /usr/local/etc/pkg/repos/FreeBSD.conf

root@freebsd:~ # echo nameserver 1.1.1.1 > /etc/resolv.conf

root@freebsd:~ # drill freebsd.org | grep '^[^;]'
freebsd.org.        799     IN      A       96.47.72.84

root@freebsd:~ # service netif restart

root@freebsd:~ # service routing restart

root@freebsd:~ # service hostname restart
Setting hostname: keycloak.lab.org.

root@keycloak:~ # passwd
Changing local password for root
New Password:
Retype New Password:

root@keycloak:~ # cat << EOF >> /etc/ssh/sshd_config
PermitRootLogin yes
UseDNS no
EOF

root@keycloak:~ # service sshd enable

root@keycloak:~ # service sshd start

root@keycloak:~ # exit

Now switch to ssh(1) for better experience – needed to paste larger blocks of configs/text.

host % ssh root@10.1.1.211

root@keycloak:~ # pkg install -y keycloak postgresql16-server postgresql16-client

root@keycloak:~ # service postgresql enable

root@keycloak:~ # service postgresql initdb

root@keycloak:~ # service postgresql start

root@keycloak:~ # sockstat -l4
USER     COMMAND    PID   FD  PROTO  LOCAL ADDRESS         FOREIGN ADDRESS      
postgres postgres    2265 7   tcp4   127.0.0.1:5432        *:*
root     syslogd      656 7   udp4   *:514                 *:*

root@keycloak:~ # su - postgres -c psql
psql (16.2)
Type "help" for help.

postgres=# ALTER USER postgres WITH PASSWORD 'password';

postgres=# CREATE DATABASE keycloak with encoding 'UTF8';
CREATE DATABASE

postgres=# GRANT ALL ON DATABASE keycloak TO postgres;
GRANT

postgres=# \q

root@keycloak:~ # cd /usr/local/share/java/keycloak/conf

root@keycloak:~ # openssl req -x509 -newkey rsa:2048 -keyout server.key.pem -out server.crt.pem -days 36500 -nodes -subj "/C=PL/ST=lodzkie/L=Lodz/O=Vermaden/OU=HR/CN=keycloak.lab.org"

root@keycloak:~ # chmod 600 server.crt.pem server.key.pem

root@keycloak:~ # chown keycloak:keycloak server.crt.pem server.key.pem

root@keycloak:~ # cat << EOF > /usr/local/share/java/keycloak/conf/keycloak.conf               
db=postgres
db-username=postgres
db-password=password
db-url=jdbc:postgresql://localhost:5432/keycloak
hostname-strict-https=true
hostname-url=https://keycloak.lab.org:8443/
hostname-admin-url=https://keycloak.lab.org:8443/
https-certificate-file=/usr/local/share/java/keycloak/conf/server.crt.pem
https-certificate-key-file=/usr/local/share/java/keycloak/conf/server.key.pem
proxy=edge
EOF

root@keycloak:~ # echo quarkus.transaction-manager.enable-recovery=true \
                    > /usr/local/share/java/keycloak/conf/quarkus.properties

root@keycloak:~ # chown keycloak:keycloak /usr/local/share/java/keycloak/conf/quarkus.properties

root@keycloak:~ # service keycloak enable

root@keycloak:~ # service keycloak build
The following run time non-cli properties were found, but will be ignored during build time: kc.db-url, kc.db-username, kc.db-password, kc.hostname-url, kc.hostname-admin-url, kc.hostname-strict-https, kc.https-certificate-file, kc.https-certificate-key-file, kc.proxy
Updating the configuration and installing your custom providers, if any. Please wait.
2024-03-10 09:01:17,701 INFO  [io.quarkus.deployment.QuarkusAugmentor] (main) Quarkus augmentation completed in 29796ms
Server configuration updated and persisted. Run the following command to review the configuration:

        kc.sh show-config

root@keycloak:~ # /usr/local/share/java/keycloak/bin/kc.sh show-config
Current Mode: production
Current Configuration:
        kc.config.built =  true (SysPropConfigSource)
        kc.db =  postgres (PropertiesConfigSource)
        kc.db-password =  ******* (PropertiesConfigSource)
        kc.db-url =  jdbc:postgresql://localhost:5432/keycloak (PropertiesConfigSource)
        kc.db-username =  postgres (PropertiesConfigSource)
        kc.hostname-admin-url =  https://keycloak.lab.org:8443/ (PropertiesConfigSource)
        kc.hostname-strict-https =  true (PropertiesConfigSource)
        kc.hostname-url =  https://keycloak.lab.org:8443/ (PropertiesConfigSource)
        kc.https-certificate-file =  /usr/local/share/java/keycloak/conf/server.crt.pem (PropertiesConfigSource)
        kc.https-certificate-key-file =  /usr/local/share/java/keycloak/conf/server.key.pem (PropertiesConfigSource)
        kc.log-console-output =  default (PropertiesConfigSource)
        kc.log-file =  ${kc.home.dir:default}${file.separator}data${file.separator}log${file.separator}keycloak.log (PropertiesConfigSource)
        kc.optimized =  true (PersistedConfigSource)
        kc.proxy =  edge (PropertiesConfigSource)
        kc.spi-hostname-default-admin-url =  https://keycloak.lab.org:8443/ (PropertiesConfigSource)
        kc.spi-hostname-default-hostname-url =  https://keycloak.lab.org:8443/ (PropertiesConfigSource)
        kc.spi-hostname-default-strict-https =  true (PropertiesConfigSource)
        kc.version =  23.0.6 (SysPropConfigSource)

We now have needed packages installed. Self signed certificate for HTTPS generated. PostgreSQL database and Keycloak configured. We will need small patch to enable passing env(1) variables at the Keycloak daemon start. It will allow to use keycloak_env at the /etc/rc.conf main FreeBSD config file. This is needed to configure the initial admin user as sated in the Keycloak documentation.

keycloak-0-initial-admin-user

Now back to the patch.

root@keycloak:~ # cat /root/keycloak.patch
--- /root/keycloak      2024-03-08 11:46:21.847315000 +0000
+++ /usr/local/etc/rc.d/keycloak        2024-03-08 11:47:22.027102000 +0000
@@ -28,6 +28,7 @@
 : ${keycloak_enable:=NO}
 : ${keycloak_user:=keycloak}
 : ${keycloak_group:=keycloak}
+: ${keycloak_env:=""}
 : ${keycloak_flags="start"}
 : ${keycloak_java_home="/usr/local/openjdk17"}
 
@@ -54,6 +55,7 @@
 
        echo "Starting keycloak."
         ${command} ${command_args} \
+                env ${keycloak_env} \
                 /usr/local/share/java/keycloak/bin/kc.sh \
                 ${keycloak_flags}
 }

root@keycloak:~ # cd /usr/local/etc/rc.d

root@keycloak:/usr/local/etc/rc.d # patch < /root/keycloak.patch
Hmm...  Looks like a unified diff to me...
The text leading up to this was:
--------------------------
|--- /root/keycloak      2024-03-08 11:46:21.847315000 +0000
|+++ /usr/local/etc/rc.d/keycloak        2024-03-08 11:47:22.027102000 +0000
--------------------------
Patching file keycloak using Plan A...
Hunk #1 succeeded at 28.
Hunk #2 succeeded at 55 with fuzz 2.
Hmm...  Ignoring the trailing garbage.
done

Now we will start Keycloak. Its possible to track its startup process in the /var/log/keycloak/keycloak.out file. Below You will find last 4 lines that you want to see – with Keycloak 23.0.6 on JVM (powered by Quarkus 3.2.10.Final) started in 19.251s. message ๐Ÿ™‚

root@keycloak:~ # service keycloak start

root@keycloak:~ # tail -f /var/log/keycloak/keycloak.out
(...)
2024-03-10 09:12:15,550 INFO  [io.quarkus] (main) Keycloak 23.0.6 on JVM (powered by Quarkus 3.2.10.Final) started in 19.251s. Listening on: http://0.0.0.0:8080 and https://0.0.0.0:8443
2024-03-10 09:12:15,551 INFO  [io.quarkus] (main) Profile prod activated. 
2024-03-10 09:12:15,552 INFO  [io.quarkus] (main) Installed features: [agroal, cdi, hibernate-orm, jdbc-h2, jdbc-mariadb, jdbc-mssql, jdbc-mysql, jdbc-oracle, jdbc-postgresql, keycloak, logging-gelf, micrometer, narayana-jta, reactive-routes, resteasy-reactive, resteasy-reactive-jackson, smallrye-context-propagation, smallrye-health, vertx]
2024-03-10 09:12:16,303 INFO  [org.keycloak.services] (main) KC-SERVICES0009: Added user 'admin' to realm 'master'
[CTRL]-[C]

root@keycloak:~ # top -ab -o res 10
last pid:  3067;  load averages:  0.50,  0.47,  0.42  up 0+02:56:35    09:19:04
18 processes:  1 running, 17 sleeping
CPU:  1.4% user,  0.0% nice,  0.4% system,  0.2% interrupt, 98.0% idle
Mem: 299M Active, 176M Inact, 3247M Wired, 264K Buf, 202M Free
ARC: 2965M Total, 902M MFU, 1982M MRU, 4096B Anon, 12M Header, 50M Other
     2766M Compressed, 2934M Uncompressed, 1.06:1 Ratio
Swap: 1024M Total, 1024M Free

  PID USERNAME    THR PRI NICE   SIZE    RES STATE    C   TIME    WCPU COMMAND
 2981 keycloak     41  68    0  1425M   299M uwait    1   0:37   0.00% /usr/local/openjdk17/bin/java -Dkc.config.built=true -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.err.encoding=UTF-8 -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -XX:+ExitOnOutOfMemoryError -Djava.security.egd=file:/dev/urandom -XX:+UseParallelGC -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -XX:FlightRecorderOptions=stackdepth=512 --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED -Dkc.home.dir=/usr/local/share/java/keycloak/bin/.. -Djboss.server.config.dir=/usr/local/share/java/keycloak/bin/../conf -Djava.util.logging.manager=org.jboss.logmanager.LogManager -Dquarkus-log-max-startup-records=10000 -cp /usr/local/share/java/keycloak/bin/../lib/quarkus-run.jar io.quarkus.bootstrap.runner.QuarkusEntryPoint start
 3063 postgres      1  24    0   181M    49M kqread   1   0:00   0.00% postgres: postgres keycloak 127.0.0.1(21936) idle (postgres)
 2266 postgres      1  20    0   178M    48M kqread   1   0:00   0.00% postgres: checkpointer  (postgres)
 3062 postgres      1  20    0   181M    47M kqread   1   0:00   0.00% postgres: postgres keycloak 127.0.0.1(22820) idle (postgres)
 2270 postgres      1  20    0   179M    31M kqread   0   0:00   0.00% postgres: autovacuum launcher  (postgres)
 2271 postgres      1  20    0   179M    31M kqread   0   0:00   0.00% postgres: logical replication launcher  (postgres)
 2269 postgres      1  20    0   178M    31M kqread   0   0:00   0.00% postgres: walwriter  (postgres)
 2267 postgres      1  20    0   178M    31M kqread   0   0:00   0.00% postgres: background writer  (postgres)
 2265 postgres      1  20    0   178M    30M kqread   0   0:00   0.00% /usr/local/bin/postgres -D /var/db/postgres/data16
 2420 root          1  20    0    22M    11M select   1   0:01   0.00% sshd: root@pts/0 (sshd)

Add also on the host system the IP information to the /etc/hosts file and check https://keycloak.lab.org:8443 in your browser.

host # echo 10.1.1.211 keycloak.lab.org keycloak >> /etc/hosts

host % firefox 'https://keycloak.lab.org:8443'

As we use self signed certificate You will be warned by potential security risk. Hit ‘Advanced’ and then ‘Accept the Risk and Continue’ buttons.

keycloak-1-self-cert

Next click the Administration Console link.

keycloak-2-main-page

Login with admin and password (or your password if You used other one).

keycloak-3-admin-login

… and You can now create your new realm, add users, create groups etc. You have fully working Keycloak in production mode.

keycloak-4-admin-console

Now … like with FreeIPA/IDM – it would be nice to attach FreeBSD to it so one could login to FreeBSD system with Keycloak user … not so fast unfortunately. To make such things be possible You need a PAM module for Keycloak … and I was not able to find one that will work on FreeBSD … and the Keycloak package also comes without one.

root@keycloak:~ # pkg info -l keycloak | grep -i pam
root@keycloak:~ # 

After grepping the Internet I found two solutions … but only for Linux.

One of them was a step by step Keycloak PAM Module Development Tutorial guide which showed you how to write such PAM module.

pam-dev

The other one was Keycloak SSH PAM project on GitHub which provided more or less ready solution for Linux systems.

pam-kc

So while with FreeIPA/IDM we had server on Linux that allowed to connect FreeBSD systems to it – we now hat Keycloak server hosted on FreeBSD that allows connecting Linux systems ๐Ÿ™‚

Not much of an improvement – but maybe someone will find that guide useful.

EOF

8 thoughts on “Keycloak Identity and Access Management on FreeBSD

  1. Pingback: KeyCloak Identity and Access Management no FreeBSD – linux-BR.org

  2. Pingback: FreeBSD ไธŠ็š„ Keycloak ่บซไปฝๅ’Œ่ฎฟ้—ฎ็ฎก็† - ๅๆ‰ง็š„็ ๅ†œ

  3. Pingback: Valuable News – 2024/03/11 | ๐šŸ๐šŽ๐š›๐š–๐šŠ๐š๐šŽ๐š—

  4. Alexander Leidinger

    There’s a bug with hostname-*url. If they are different from each other (and if you want to use keycloak from the Internet, the admin url should be different and not accessible from the Internet) you can run into issues.

    You set proxy=edge, which assumes some reverse proxy in front of Keycloak, but this is not shown in your guide and I would expect a different hostname-*url setting in this case (rev proxy on the standard https code -> no port in the URL, else the probability of hitting the url bug increases).

    Keycloak is more a SSO/OIDC/Saml provider than an IAM. SSO via FIDO2 keys to various Web apps (wiki/webmail/wordpress/…) is more the domain of Keycloak than a login to the console.

    There’s also midpoint in ports, which is more of a IAM (but no SSO), but I didn’t have the time yet to make it work with FreeBSD+OpenLDAP or another way of have it manage FreeBSD accounts.

    Like

    Reply
  5. Alexander Leidinger

    Seems my first comment got eaten / lost somehow, so trying again:

    Keycloak is more of a SSO/OIDC/Saml provider than an IAM. If you want to do IAM, have a look at the midpoint port (I didn’t had the time to successfully marry it to my openldap, or create local accounts / homedirs on FreeBSD).

    You specify in the keycloak config that there is an edge-proxy, but you don’t show that. And with an edge proxy, I would expect a different hostname*url setup than you provide here.

    Attention, there are bugs in the implementation for the hostnames, if you use different hostnames for SSO and admin (which is the recommended way), and if you use port numbers in the hostname*url setting. https://github.com/keycloak/keycloak/issues/14666

    Keycloak works very well for SSO of OIDC and Saml stuff, e.g. wordpress, wiki, webmail, ….

    If you look for PAM stuff for keycloak, keep in mind that you can use OTP / webauthn / FIDO2 tokens to authenticate a keycloak account … I would suggest to check if the PAM solution supports that, else one of my big plus points for keycloack – the possibility to use MFA – will not work.

    Like

    Reply
    1. vermaden Post author

      Thank You for an interesting comment.

      About Keycloak in general – I was just looking for some FreeIPA/IDM replacement that could be hosted natively on FreeBSD w/o the need to reply on Linux systems – just an exercise – seem there would be a follow up with net-mgmt/midpoint attempt – I will definitely check that port later ๐Ÿ™‚

      About various Keycloak parameters I used … I am far from being a Keycloak expert – and to be honest – I needed to fight a little (several hours) to just make Keycloak run – because the documentation and options are not that straightforward unfortunately – so yes – some of these options may not be the best choice or are not suited for REAL production if You really know what You are doing with the Keycloak system.

      Hope that helps.

      Regards,
      vermaden

      Like

      Reply
  6. Alexander Leidinger

    I stumbled over keycloak as I wanted to have something which support MFA for my web related access (next step for me is to have web-stuff without user management protected by it instead of using basic http authentication). Works great for this kind of things. The setup you describe is already nearly production ready. A rev-proxy with correct forwarded headers in front of it, letsencrypt cert, hostname*url without port numbers, and once the bug with hostnames*url is fixed / a workaround is known, separate management and authentication URLs -> SSO over all the web stuff is possible, accessible from the world, admin stuff only accessible internally (well… MFA setup inside keycloak for good security practice too).

    If you get midpoint into an useful shape (note, the port is not on the most recent version), I would definitively be interested to read about a working setup.

    Like

    Reply
    1. vermaden Post author

      Thank You for useful tips.

      About Midpoint … I just tried their docs etc. and they do not even show how to configure Linux systems with PAM/LDAP/Kerberos configuration … not to even mention FreeBSD – so I think I will pass on Midpoint (for now) because it would be more or less the same post as this one – w/o any systems connections to it.

      Like

      Reply

Leave a comment