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