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.
My earlier FreeIPA/IDM attempts are below.
- Connect FreeBSD to FreeIPA/Red Hat Identity Management
- Connect FreeBSD 13.2 to FreeIPA/IDM
- Connect FreeBSD 14.0-STABLE to FreeIPA/IDM
- FreeBSD on FreeIPA/IDM with Poudriere Repo
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.
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.
Next click the Administration Console link.
Login with admin and password (or your password if You used other one).
… and You can now create your new realm, add users, create groups etc. You have fully working Keycloak in production mode.
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.
The other one was Keycloak SSH PAM project on GitHub which provided more or less ready solution for Linux systems.
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.
Pingback: KeyCloak Identity and Access Management no FreeBSD – linux-BR.org
Pingback: FreeBSD ไธ็ Keycloak ่บซไปฝๅ่ฎฟ้ฎ็ฎก็ - ๅๆง็็ ๅ
Pingback: Valuable News – 2024/03/11 | ๐๐๐๐๐๐๐๐
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.
LikeLike
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.
LikeLike
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
LikeLike
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.
LikeLike
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.
LikeLike