Keystone needs to work with multiple federation sources. Keycloak is a JBoss based project that provides, among other things, SAML and OpenID connect protocols. As part of my work in getting the two integrated, I needed to deploy Keycloak. The rest of my development setup is done via Ansible and I wanted to handle Keycloak the same way.
Unlike Ipsilon, Keycloak is not deployed via RPMs and Yum. Instead, the most common deployment method is to download and expand the tarball. This provides a great deal of flexibility to the deployer. While I am not going for a full live-deployment approach here, I did want to use best practices. Here were the decisions I made:
- Use the System deployed Java runtime
- Run as a non-root dedicated user named keycloak.
- Manage the process via systemd
- Put the majority of the files under /var/run/keycloak.
- Have all code and configuration owned by root and not be editable by the Keycloak user
- Use firewalld to open only the ports necessary (8080 and 9990) to communicate with the Keycloak server itself.
Here is the roles/keycloak/tasks/main.yml file that has the majority of the logic:
---
- name: install keycloak prerequisites
tags:
- keycloak
yum: name={{ item }} state=present
with_items:
- java-1.7.0-openjdk.x86_64
- firewalld
- name: create keycloak user
tags:
- keycloak
user: name=keycloak
- name: keycloak target directory
tags:
- keycloak
file: dest={{ keycloak_dir }}
mode=755
owner=root
group=root
state=directory
- name: get Keycloak distribution tarball
tags:
- keycloak
get_url: url={{ keycloak_url }}
dest={{ keycloak_dir }}
- name: unpack keycloak
tags:
- keycloak
unarchive: src={{ keycloak_dir }}/{{keycloak_archive}}
dest={{ keycloak_dir }}
copy=no
- name: keycloak log directory
tags:
- keycloak
file: dest={{ keycloak_log_dir }}
mode=755
owner=keycloak
group=keycloak
state=directory
- name: keycloak data directory
tags:
- keycloak
file: dest={{ keycloak_jboss_home }}/standalone/data
mode=755
owner=keycloak
group=keycloak
state=directory
- name: keycloak tmp directory
tags:
- keycloak
file: dest={{ keycloak_jboss_home }}/standalone/tmp
mode=755
owner=keycloak
group=keycloak
state=directory
- name: make keycloak configuration directory readable
tags:
- keycloak
file: dest={{ keycloak_jboss_home }}/standalone/configuration
mode=755
owner=keycloak
group=keycloak
state=directory
recurse=yes
- name: keycloak systemd setup
tags:
- keycloak
template:
owner=root group=root mode=0644
src=keycloak.service.j2
dest=/etc/systemd/system/keycloak.service
notify:
- reload systemd
- name: enable firewalld
tags:
- ipaserver
service: enabled=yes
state=started
name=firewalld
- name: Open Firewall for services
tags:
- keycloak
firewalld: port={{ item }}
permanent=true
state=enabled
immediate=yes
with_items:
- 8080/tcp
- 9990/tcp
- name: keycloak systemd service enable and start
tags:
- keycloak
service: name=keycloak
enabled=yes
state=started
It makes use of some variables that I expect to have to tweak as package versions increase. Here is the roles/keycloak/vars/main.yml file
---
keycloak_version: 1.6.1.Final
keycloak_dir: /var/lib/keycloak
keycloak_archive: keycloak-{{ keycloak_version }}.tar.gz
keycloak_url: http://downloads.jboss.org/keycloak/{{ keycloak_version }}/{{keycloak_archive }}
keycloak_jboss_home: "{{ keycloak_dir }}/keycloak-{{ keycloak_version }}"
keycloak_log_dir: "{{ keycloak_jboss_home }}/standalone/log"
For Systemd I started with the configuration as suggested by: Jens Krämer Which I tailored to reference Keycloak explicitly and also to listen on 0.0.0.0. Here is the template file roles/keycloak/templates/keycloak.service.j2
[Unit]
Description=Jboss Application Server
After=network.target
[Service]
Type=idle
Environment=JBOSS_HOME={{ keycloak_jboss_home }} JBOSS_LOG_DIR={{ keycloak_log_dir }} "JAVA_OPTS=-Xms1024m -Xmx20480m -XX:MaxPermSize=768m"
User=keycloak
Group=keycloak
ExecStart={{ keycloak_jboss_home }}/bin/standalone.sh -b 0.0.0.0
TimeoutStartSec=600
TimeoutStopSec=600
[Install]
WantedBy=multi-user.target
The top level playbook for this is somewhat muddied by having other roles, not relevant for this post. It looks like this:
- hosts: keycloak
remote_user: "{{ cloud_user }}"
tags: all
tasks: []
- hosts: keycloak
sudo: yes
remote_user: "{{ cloud_user }}"
tags:
- ipa
roles:
- common
- ipaclient
- keycloak
vars:
hostname: "{{ ansible_fqdn }}"
ipa_admin_password: "{{ ipa_admin_user_password }}"
And I call it is using:
ansible-playbook -i ~/.ossipee/deployments/ayoung.os1/inventory.ini keycloak.yml
I’m tempted to split this code off into its own repository; right now I have it as part of Rippowam.
Thanks, probably going to use some of this:)
For security I would separate the user that is running the service from the owner of the application files. So I prefer having everything owned by root, only give read-access to group keycloak (exception to some files/paths Keycloak requires write-access to).
keyclock != keycloak
tempaltes != templates
keycloak.service.js != keycloak.service.j2
Thanks. Changes made