diff --git a/playbooks/keyclock_quarkus.yml b/playbooks/keyclock_quarkus.yml new file mode 100644 index 0000000..ebed89b --- /dev/null +++ b/playbooks/keyclock_quarkus.yml @@ -0,0 +1,9 @@ +--- +- name: Playbook for Keycloak X Hosts + hosts: all + vars: + keycloak_admin_password: "remembertochangeme" + collections: + - middleware_automation.keycloak + roles: + - keycloak_quarkus \ No newline at end of file diff --git a/roles/keycloak_quarkus/README.md b/roles/keycloak_quarkus/README.md index 19237d5..ed4fae3 100644 --- a/roles/keycloak_quarkus/README.md +++ b/roles/keycloak_quarkus/README.md @@ -7,6 +7,13 @@ Install [keycloak](https://keycloak.org/) >= 17.0.0 (quarkus) server configurati Role Defaults ------------- +* Installation options + +| Variable | Description | Default | +|:---------|:------------|:--------| +|`keycloak_quarkus_version`| keycloak.org package version | `17.0.1` | + + * Service configuration | Variable | Description | Default | @@ -20,13 +27,18 @@ Role Defaults |`keycloak_quarkus_https_port`| TLS HTTP port | `8443` | |`keycloak_quarkus_ajp_port`| AJP port | `8009` | |`keycloak_quarkus_jgroups_port`| jgroups cluster tcp port | `7600` | -|`keycloak_quarkus_java_opts`| Additional JVM options | `-Xms1024m -Xmx2048m` | |`keycloak_quarkus_service_user`| Posix account username | `keycloak` | |`keycloak_quarkus_service_group`| Posix account group | `keycloak` | |`keycloak_quarkus_service_pidfile`| Pid file path for service | `/run/keycloak.pid` | |`keycloak_quarkus_jvm_package`| RHEL java package runtime | `java-11-openjdk-headless` | +|`keycloak_quarkus_java_home`| JAVA_HOME of installed JRE, leave empty for using specified keycloak_quarkus_jvm_package RPM path | `None` | +|`keycloak_quarkus_java_opts`| Additional JVM options | `-Xms1024m -Xmx2048m` | |`keycloak_quarkus_frontend_url`| Service public URL | `http://localhost:8080/auth` | |`keycloak_quarkus_http_relative_path` | Service context path | `auth` | +|`keycloak_quarkus_http_enabled`| Enable listener on HTTP port | `True` | +|`keycloak_quarkus_https_enabled`| Enable listener on HTTPS port | `False` | +|`keycloak_quarkus_key_file`| The file path to a private key in PEM format | `conf/server.key.pem` | +|`keycloak_quarkus_cert_file`| The file path to a server certificate or certificate chain in PEM format | `conf/server.crt.pem` | * Database configuration @@ -70,6 +82,7 @@ Role Defaults | Variable | Description | Default | |:---------|:------------|:--------| |`keycloak_quarkus_metrics_enabled`| Whether to enable metrics | `False` | +|`keycloak_quarkus_health_enabled`| If the server should expose health check endpoints | `True` | |`keycloak_quarkus_archive` | keycloak install archive filename | `keycloak-{{ keycloak_quarkus_version }}.zip` | |`keycloak_quarkus_installdir` | Installation path | `{{ keycloak_quarkus_dest }}/keycloak-{{ keycloak_quarkus_version }}` | |`keycloak_quarkus_home` | Installation work directory | `{{ keycloak_quarkus_installdir }}` | @@ -79,14 +92,19 @@ Role Defaults |`keycloak_force_install` | Remove pre-existing versions of service | `False` | |`keycloak_url` | URL for configuration rest calls | `http://{{ keycloak_quarkus_host }}:{{ keycloak_http_port }}` | |`keycloak_management_url` | URL for management console rest calls | `http://{{ keycloak_quarkus_host }}:{{ keycloak_management_http_port }}` | +|`keycloak_quarkus_log`| Enable one or more log handlers in a comma-separated list | `file` | +|`keycloak_quarkus_log_level`| The log level of the root category or a comma-separated list of individual categories and their levels | `info` | +|`keycloak_quarkus_log_file`| Set the log file path and filename relative to keycloak home | `data/log/keycloak.log` | +|`keycloak_quarkus_log_format`| Set a format specific to file log entries | `%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n` | +|`keycloak_quarkus_proxy_mode`| The proxy address forwarding mode if the server is behind a reverse proxy | `edge` | Role Variables -------------- -| Variable | Description | -|:---------|:------------| -|`keycloak_quarkus_admin_pass`| Password of console admin account | +| Variable | Description | Required | +|:---------|:------------|----------| +|`keycloak_quarkus_admin_pass`| Password of console admin account | `yes` | License diff --git a/roles/keycloak_quarkus/defaults/main.yml b/roles/keycloak_quarkus/defaults/main.yml index 6778308..7d58fe3 100644 --- a/roles/keycloak_quarkus/defaults/main.yml +++ b/roles/keycloak_quarkus/defaults/main.yml @@ -1,6 +1,6 @@ --- ### Configuration specific to keycloak -keycloak_quarkus_version: 17.0.1 +keycloak_quarkus_version: 18.0.0 keycloak_quarkus_archive: "keycloak-{{ keycloak_quarkus_version }}.zip" keycloak_quarkus_download_url: "https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}" keycloak_quarkus_installdir: "{{ keycloak_quarkus_dest }}/keycloak-{{ keycloak_quarkus_version }}" @@ -10,6 +10,7 @@ keycloak_quarkus_offline_install: False ### Install location and service settings keycloak_quarkus_jvm_package: java-11-openjdk-headless +keycloak_quarkus_java_home: keycloak_quarkus_dest: /opt/keycloak keycloak_quarkus_home: "{{ keycloak_quarkus_installdir }}" keycloak_quarkus_config_dir: "{{ keycloak_quarkus_home }}/conf" @@ -26,12 +27,18 @@ keycloak_quarkus_master_realm: master ### Configuration settings keycloak_quarkus_bind_address: 0.0.0.0 keycloak_quarkus_host: localhost +keycloak_quarkus_http_enabled: True keycloak_quarkus_http_port: 8080 keycloak_quarkus_https_port: 8443 keycloak_quarkus_ajp_port: 8009 keycloak_quarkus_jgroups_port: 7600 keycloak_quarkus_java_opts: "-Xms1024m -Xmx2048m" +### TLS/HTTPS configuration +keycloak_quarkus_https_enabled: False +keycloak_quarkus_key_file: conf/server.key.pem +keycloak_quarkus_cert_file: conf/server.crt.pem + ### Enable configuration for database backend, clustering and remote caches on infinispan keycloak_quarkus_ha_enabled: False ### Enable database configuration, must be enabled when HA is configured @@ -41,7 +48,11 @@ keycloak_quarkus_db_enabled: "{{ True if keycloak_quarkus_ha_enabled else False keycloak_quarkus_http_relative_path: auth keycloak_quarkus_frontend_url: http://localhost:8080/auth +# proxy address forwarding mode if the server is behind a reverse proxy. [edge, reencrypt, passthrough] +keycloak_quarkus_proxy_mode: edge + keycloak_quarkus_metrics_enabled: False +keycloak_quarkus_health_enabled: True ### infinispan remote caches access (hotrod) keycloak_quarkus_ispn_user: supervisor @@ -67,4 +78,10 @@ keycloak_quarkus_default_jdbc: version: 9.4.1212 mariadb: url: 'jdbc:mariadb://localhost:3306/keycloak' - version: 2.7.4 \ No newline at end of file + version: 2.7.4 + +### logging configuration +keycloak_quarkus_log: file +keycloak_quarkus_log_level: info +keycloak_quarkus_log_file: data/log/keycloak.log +keycloak_quarkus_log_format: '%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n' diff --git a/roles/keycloak_quarkus/meta/argument_specs.yml b/roles/keycloak_quarkus/meta/argument_specs.yml index 78382f9..19fb8df 100644 --- a/roles/keycloak_quarkus/meta/argument_specs.yml +++ b/roles/keycloak_quarkus/meta/argument_specs.yml @@ -31,6 +31,9 @@ argument_specs: default: "java-11-openjdk-headless" description: "RHEL java package runtime" type: "str" + keycloak_quarkus_java_home: + description: "JAVA_HOME of installed JRE, leave empty for using specified keycloak_jvm_package RPM path" + type: "str" keycloak_quarkus_dest: # line 13 of defaults/main.yml default: "/opt/keycloak" @@ -91,11 +94,27 @@ argument_specs: default: "localhost" description: "hostname" type: "str" + keycloak_quarkus_http_enabled: + default: true + description: "Enable listener on HTTP port" + type: "bool" keycloak_quarkus_http_port: # line 29 of defaults/main.yml default: 8080 description: "HTTP port" type: "int" + keycloak_quarkus_https_enabled: + default: false + description: "Enable listener on HTTPS port" + type: "bool" + keycloak_quarkus_key_file: + default: "conf/server.key.pem" + description: "The file path to a private key in PEM format" + type: "str" + keycloak_quarkus_cert_file: + default: "conf/server.crt.pem" + description: "The file path to a server certificate or certificate chain in PEM format" + type: "str" keycloak_quarkus_https_port: # line 30 of defaults/main.yml default: 8443 @@ -141,6 +160,10 @@ argument_specs: default: false description: "Whether to enable metrics" type: "bool" + keycloak_quarkus_health_enabled: + default: true + description: "If the server should expose health check endpoints" + type: "bool" keycloak_quarkus_ispn_user: # line 46 of defaults/main.yml default: "supervisor" @@ -201,3 +224,23 @@ argument_specs: default: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].version }}" description: "Version for JDBC driver" type: "str" + keycloak_quarkus_log: + default: "file" + type: "str" + description: "Enable one or more log handlers in a comma-separated list" + keycloak_quarkus_log_level: + default: "info" + type: "str" + description: "The log level of the root category or a comma-separated list of individual categories and their levels" + keycloak_quarkus_log_file: + default: "data/log/keycloak.log" + type: "str" + description: "Set the log file path and filename relative to keycloak home" + keycloak_quarkus_log_format: + default: '%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n' + type: "str" + description: "Set a format specific to file log entries" + keycloak_quarkus_proxy_mode: + default: 'edge' + type: "str" + description: "The proxy address forwarding mode if the server is behind a reverse proxy" diff --git a/roles/keycloak_quarkus/tasks/fastpackages.yml b/roles/keycloak_quarkus/tasks/fastpackages.yml index 78bc556..3dd28e1 100644 --- a/roles/keycloak_quarkus/tasks/fastpackages.yml +++ b/roles/keycloak_quarkus/tasks/fastpackages.yml @@ -14,7 +14,7 @@ packages_to_install: "{{ packages_to_install | default([]) + rpm_info.stdout_lines | map('regex_findall', 'package (.+) is not installed$') | flatten }}" when: rpm_info.failed -- name: "Install packages: {{ packages_to_install }}" +- name: "Install packages: {{ packages_to_install | join(',') }}" become: yes ansible.builtin.yum: name: "{{ packages_to_install }}" diff --git a/roles/keycloak_quarkus/tasks/main.yml b/roles/keycloak_quarkus/tasks/main.yml index d350f6f..0ed3ece 100644 --- a/roles/keycloak_quarkus/tasks/main.yml +++ b/roles/keycloak_quarkus/tasks/main.yml @@ -1,6 +1,5 @@ --- # tasks file for keycloak - - name: Check prerequisites ansible.builtin.include_tasks: prereqs.yml tags: @@ -32,10 +31,27 @@ notify: - restart keycloak +- name: "Configure quarkus config for keycloak service" + ansible.builtin.template: + src: quarkus.properties.j2 + dest: "{{ keycloak.home }}/conf/quarkus.properties" + owner: "{{ keycloak.service_user }}" + group: "{{ keycloak.service_group }}" + mode: 0644 + notify: + - restart keycloak + - name: "Start and wait for keycloak service" ansible.builtin.include_tasks: start.yml - name: Check service status ansible.builtin.command: "systemctl status keycloak" register: keycloak_service_status - changed_when: False \ No newline at end of file + changed_when: False + +- name: Link default logs directory + ansible.builtin.file: + state: link + src: "{{ keycloak.home }}/{{ keycloak.log.file }}" + dest: /var/log/keycloak + force: yes diff --git a/roles/keycloak_quarkus/tasks/systemd.yml b/roles/keycloak_quarkus/tasks/systemd.yml index 578802c..ee8a1cc 100644 --- a/roles/keycloak_quarkus/tasks/systemd.yml +++ b/roles/keycloak_quarkus/tasks/systemd.yml @@ -1,4 +1,13 @@ --- +- name: Determine JAVA_HOME for selected JVM RPM # noqa blocked_modules + ansible.builtin.shell: | + set -o pipefail + rpm -ql {{ keycloak_quarkus_jvm_package }} | grep -Po '/usr/lib/jvm/.*(?=/bin/java$)' + args: + executable: /bin/bash + changed_when: False + register: rpm_java_home + - name: "Configure sysconfig file for keycloak service" become: yes ansible.builtin.template: @@ -7,6 +16,8 @@ owner: root group: root mode: 0644 + vars: + keycloak_rpm_java_home: "{{ rpm_java_home.stdout }}" notify: - restart keycloak diff --git a/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 b/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 index 13a589e..5315708 100644 --- a/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 +++ b/roles/keycloak_quarkus/templates/keycloak-sysconfig.j2 @@ -1,3 +1,4 @@ # {{ ansible_managed }} KEYCLOAK_ADMIN={{ keycloak_quarkus_admin_user }} -KEYCLOAK_ADMIN_PASSWORD='{{ keycloak_quarkus_admin_pass }}' \ No newline at end of file +KEYCLOAK_ADMIN_PASSWORD='{{ keycloak_quarkus_admin_pass }}' +JAVA_HOME={{ keycloak_java_home | default(keycloak_rpm_java_home, true) }} diff --git a/roles/keycloak_quarkus/templates/keycloak.conf.j2 b/roles/keycloak_quarkus/templates/keycloak.conf.j2 index 59d82a2..63fcae1 100644 --- a/roles/keycloak_quarkus/templates/keycloak.conf.j2 +++ b/roles/keycloak_quarkus/templates/keycloak.conf.j2 @@ -1,51 +1,47 @@ # {{ ansible_managed }} +{% if keycloak_quarkus_db_enabled %} # Database -# Database vendor [dev-file, dev-mem, mariadb, mssql, mysql, oracle, postgres] -#db=postgres -# The username of the database user. -#db-username=keycloak -# The password of the database user. -#db-password=password -# The full database JDBC URL. If not provided, a default URL is set based on the selected database vendor. -#db-url=jdbc:postgresql://localhost/keycloak +db={{ keycloak_quarkus_jdbc_engine }} +db-url={{ keycloak_quarkus_jdbc_url }} +db-username={{ keycloak_quarkus_db_user }} +db-password={{ keycloak_quarkus_db_pass }} +{% endif %} # Observability -# If the server should expose metrics and healthcheck endpoints. -#metrics-enabled=true +metrics-enabled={{ keycloak_quarkus_metrics_enabled }} +health-enabled={{ keycloak_quarkus_health_enabled }} # HTTP -http-enabled=true -http-port=8080 -https-port=8443 -# The file path to a server certificate or certificate chain in PEM format. -#https-certificate-file=${kc.home.dir}conf/server.crt.pem -# The file path to a private key in PEM format. -#https-certificate-key-file=${kc.home.dir}conf/server.key.pem -# The proxy address forwarding mode if the server is behind a reverse proxy. -#proxy=reencrypt -# Do not attach route to cookies and rely on the session affinity capabilities from reverse proxy -#spi-sticky-session-encoder-infinispan-should-attach-route=false +http-enabled={{ keycloak_quarkus_http_enabled }} +http-port={{ keycloak_quarkus_http_port }} + +# HTTPS +https-port={{ keycloak_quarkus_https_port }} +{% if keycloak_quarkus_https_enabled %} +https-certificate-file={{ keycloak.home }}/{{ keycloak_quarkus_cert_file}} +https-certificate-key-file={{ keycloak.home }}/{{ keycloak_quarkus_key_file }} +{% endif %} # Hostname for the Keycloak server. hostname={{ keycloak_quarkus_host }} hostname-path={{ keycloak_quarkus_http_relative_path }} # Cluster -#cache=ispn -#Defines the cache mechanism for high-availability. [local, ispn] -#cache-config-file=conf/cache-ispn.xml -#Defines the file from which cache configuration should be loaded from. -#cache-stack=tcp -#Define the default stack to use for cluster communication and node discovery. [tcp, udp, kubernetes, ec2, azure, google] +{% if keycloak_quarkus_ha_enabled %} +cache=ispn +cache-config-file=conf/cache-ispn.xml +cache-stack=tcp +{% endif %} # Proxy -# The proxy address forwarding mode if the server is behind a reverse proxy. [edge, reencrypt, passthrough] -#proxy= +proxy={{ keycloak_quarkus_proxy_mode }} +# Do not attach route to cookies and rely on the session affinity capabilities from reverse proxy +#spi-sticky-session-encoder-infinispan-should-attach-route=false # Logging -# The format of log entries. #log-format=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n -# The log level of the root category or a comma-separated list of individual categories and their levels. -#log-level=info - +log={{ keycloak_quarkus_log }} +log-level={{ keycloak.log.level }} +log-file={{ keycloak.log.file }} +log-file-format={{ keycloak.log.format }} diff --git a/roles/keycloak_quarkus/templates/keycloak.service.j2 b/roles/keycloak_quarkus/templates/keycloak.service.j2 index a710ee2..90242cf 100644 --- a/roles/keycloak_quarkus/templates/keycloak.service.j2 +++ b/roles/keycloak_quarkus/templates/keycloak.service.j2 @@ -7,8 +7,8 @@ After=network.target Type=simple EnvironmentFile=-/etc/sysconfig/keycloak PIDFile={{ keycloak_quarkus_service_pidfile }} -ExecStart={{ keycloak.home }}/bin/kc.sh start -#--http-relative-path={{ keycloak_quarkus_http_relative_path }} +ExecStart={{ keycloak.home }}/bin/kc.sh start --auto-build --log={{ keycloak_quarkus_log }} +User={{ keycloak.service_user }} [Install] WantedBy=multi-user.target diff --git a/roles/keycloak_quarkus/templates/quarkus.properties.j2 b/roles/keycloak_quarkus/templates/quarkus.properties.j2 new file mode 100644 index 0000000..cf133a3 --- /dev/null +++ b/roles/keycloak_quarkus/templates/quarkus.properties.j2 @@ -0,0 +1,19 @@ +# {{ ansible_managed }} +{% if keycloak_quarkus_ha_enabled %} +quarkus.infinispan-client.server-list={{ keycloak_quarkus_ispn_url }} +quarkus.infinispan-client.client-intelligence=HASH_DISTRIBUTION_AWARE +quarkus.infinispan-client.use-auth=true +quarkus.infinispan-client.auth-username={{ keycloak_quarkus_ispn_user }} +quarkus.infinispan-client.auth-password={{ keycloak_quarkus_ispn_pass }} +quarkus.infinispan-client.auth-realm=default +quarkus.infinispan-client.auth-server-name=infinispan +quarkus.infinispan-client.sasl-mechanism={{ keycloak_quarkus_ispn_sasl_mechanism }} +{% if keycloak_quarkus_ispn_use_ssl %} +quarkus.infinispan-client.trust-store={{ keycloak_quarkus_ispn_trust_store_path }} +quarkus.infinispan-client.trust-store-password={{ keycloak_quarkus_ispn_trust_store_password }} +quarkus.infinispan-client.trust-store-type=jks +{% endif %} +#quarkus.infinispan-client.use-schema-registration=true +#quarkus.infinispan-client.auth-client-subject +#quarkus.infinispan-client.auth-callback-handler +{% endif %} \ No newline at end of file diff --git a/roles/keycloak_quarkus/vars/main.yml b/roles/keycloak_quarkus/vars/main.yml index bca7d88..e5302f4 100644 --- a/roles/keycloak_quarkus/vars/main.yml +++ b/roles/keycloak_quarkus/vars/main.yml @@ -8,4 +8,8 @@ keycloak: cli_path: "{{ keycloak_quarkus_home }}/bin/kcadm.sh" service_user: "{{ keycloak_quarkus_service_user }}" service_group: "{{ keycloak_quarkus_service_group }}" - offline_install: "{{ keycloak_quarkus_offline_install }}" \ No newline at end of file + offline_install: "{{ keycloak_quarkus_offline_install }}" + log: + file: "{{ keycloak_quarkus_log_file }}" + level: "{{ keycloak_quarkus_log_level }}" + format: "{{ keycloak_quarkus_log_format }}" \ No newline at end of file diff --git a/roles/keycloak_realm/README.md b/roles/keycloak_realm/README.md index cf098a7..91e6b8f 100644 --- a/roles/keycloak_realm/README.md +++ b/roles/keycloak_realm/README.md @@ -8,9 +8,10 @@ Role Defaults ------------- | Variable | Description | Default | -|:---------|:------------|:---------| +|:---------|:------------|:--------| |`keycloak_admin_user`| Administration console user account | `admin` | |`keycloak_host`| hostname | `localhost` | +|`keycloak_context`| Context path for rest calls | `/auth` | |`keycloak_http_port`| HTTP port | `8080` | |`keycloak_https_port`| TLS HTTP port | `8443` | |`keycloak_auth_realm`| Name of the main authentication realm | `master` |