Vendor middleware_automation.keycloak collection

Needed for keycloak_authentication_v2, to manage Keycloak
authentication flows (e.g. the CCCHH browser flow) as YAML.
Chosen over community.general's own copy of the module (deprecated in
favor of this collection, and limited to 4 sub-flow levels there vs.
10 here).
This commit is contained in:
Stefan Bethke 2026-09-02 10:46:57 +02:00
commit 4832e5da5d
159 changed files with 34399 additions and 0 deletions

View file

@ -0,0 +1,314 @@
keycloak_quarkus
================
<!--start description -->
Install [keycloak](https://keycloak.org/) >= 20.0.0 (quarkus) server configurations.
<!--end description -->
Requirements
------------
This role requires the `python3-netaddr` and `lxml` library installed on the controller node.
* to install via yum/dnf: `dnf install python3-netaddr python3-lxml`
* to install via apt: `apt install python3-netaddr python3-lxml`
* or via the collection: `pip install -r requirements.txt`
Dependencies
------------
The roles depends on:
* [middleware_automation.common](https://github.com/ansible-middleware/common)
* [ansible-posix](https://docs.ansible.com/ansible/latest/collections/ansible/posix/index.html)
To install all the dependencies via galaxy:
ansible-galaxy collection install -r requirements.yml
Role Defaults
-------------
#### Installation options
| Variable | Description | Default |
|:---------|:------------|:--------|
|`keycloak_quarkus_version`| keycloak.org package version | `26.6.2` |
|`keycloak_quarkus_offline_install` | Perform an offline install | `False`|
|`keycloak_quarkus_dest`| Installation root path | `/opt/keycloak` |
|`keycloak_quarkus_download_url` | Download URL for keycloak | `https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}` |
|`keycloak_quarkus_download_path`| Path local to controller for offline/download of install archives | `{{ lookup('env', 'PWD') }}` |
#### Service configuration
| Variable | Description | Default |
|:---------|:------------|:--------|
|`keycloak_quarkus_bootstrap_admin_user`| Administration console user account | `admin` |
|`keycloak_quarkus_admin_user`| Deprecated, use `keycloak_quarkus_bootstrap_admin_user` instead. | |
|`keycloak_quarkus_bind_address`| Deprecated, use `keycloak_quarkus_http_host` instead | `0.0.0.0` |
|`keycloak_quarkus_host`| Deprecated, use `keycloak_quarkus_hostname` instead. | |
|`keycloak_quarkus_port`| Deprecated, use `keycloak_quarkus_hostname` instead. | |
|`keycloak_quarkus_path`| Deprecated, use `keycloak_quarkus_hostname` instead. | |
|`keycloak_quarkus_service_user`| Posix account username | `keycloak` |
|`keycloak_quarkus_service_group`| Posix account group | `keycloak` |
|`keycloak_quarkus_service_restart_always`| systemd restart always behavior activation | `False` |
|`keycloak_quarkus_service_restart_on_failure`| systemd restart on-failure behavior activation | `False` |
|`keycloak_quarkus_service_restartsec`| systemd RestartSec | `10s` |
|`keycloak_quarkus_jvm_package`| RHEL java package runtime | `java-21-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_heap_opts`| Heap memory JVM setting | `-Xms1024m -Xmx2048m` |
|`keycloak_quarkus_java_jvm_opts`| Other JVM settings | same as keycloak |
|`keycloak_quarkus_java_opts`| JVM arguments; if overridden, it takes precedence over `keycloak_quarkus_java_*` | `{{ keycloak_quarkus_java_heap_opts + ' ' + keycloak_quarkus_java_jvm_opts }}` |
|`keycloak_quarkus_additional_env_vars` | List of additional env variables of { key: str, value: str} to be put in sysconfig file, see https://www.keycloak.org/server/all-config | `[]` |
|`keycloak_quarkus_frontend_url`| Deprecated, use `keycloak_quarkus_hostname` instead. | |
|`keycloak_quarkus_admin_url`| Deprecated, use `keycloak_quarkus_hostname_admin` instead. | |
|`keycloak_quarkus_health_check_url`| Full URL (including scheme, host, path, fragment etc.) used for health check endpoint; keycloak_quarkus_hostname will NOT be prepended; helpful when health checks should happen against http port, but keycloak_quarkus_hostname uses https scheme per default | `{{ keycloak_quarkus_hostname ~ keycloak_quarkus_http_relative_path.rstrip('/') ~ '/' ~ keycloak_quarkus_health_check_url_path }}` |
|`keycloak_quarkus_health_check_url_path`| Path to the health check endpoint; keycloak_quarkus_hostname will be prepended automatically; Note that keycloak_quarkus_health_check_url takes precedence over this property | `realms/{{ keycloak_quarkus_master_realm }}/.well-known/openid-configuration` |
|`keycloak_quarkus_proxy_headers`| Parse reverse proxy headers (`forwarded` or `xforwarded`) | `""` |
|`keycloak_quarkus_config_key_store_file`| Path to the configuration key store; only used if `keycloak_quarkus_config_key_store_password` is not empty | `{{ keycloak.home }}/conf/conf_store.p12` if `keycloak_quarkus_config_key_store_password != ''`, else `''` |
|`keycloak_quarkus_config_key_store_password`| Password of the configuration keystore; if non-empty, `keycloak_quarkus_db_pass` will be saved to the keystore at `keycloak_quarkus_config_key_store_file` instead of being written to the configuration file in clear text | `""` |
|`keycloak_quarkus_configure_firewalld` | Ensure firewalld is running and configure keycloak ports | `False` |
|`keycloak_quarkus_configure_iptables` | Ensure iptables is configured for keycloak ports | `False` |
#### High-availability
| Variable | Description | Default |
|:---------|:------------|:--------|
|`keycloak_quarkus_ha_enabled`| Enable auto configuration for database backend, clustering and remote caches on infinispan | `False` |
|`keycloak_quarkus_ha_discovery`| Discovery protocol for HA cluster members | `JDBCPING` |
|`keycloak_quarkus_db_enabled`| Enable auto configuration for database backend | `True` if `keycloak_quarkus_ha_enabled` is True, else `False` |
|`keycloak_quarkus_jgroups_ip`| Host jgroups IP. If changing this variable you must make sure it is always set for all hosts in your cluster. | `{{ ansible_default_ipv4.address }}` |
|`keycloak_quarkus_jgroups_port`| jgroups cluster tcp port | `7800` |
|`keycloak_quarkus_systemd_wait_for_port` | Whether systemd unit should wait for keycloak port before returning | `{{ keycloak_quarkus_ha_enabled }}` |
|`keycloak_quarkus_systemd_wait_for_port_number`| Which port the systemd unit should wait for | `{{ keycloak_quarkus_https_port }}` |
|`keycloak_quarkus_systemd_wait_for_log` | Whether systemd unit should wait for service to be up in logs | `false` |
|`keycloak_quarkus_systemd_wait_for_timeout`| How long to wait for service to be alive (seconds) | `60` |
|`keycloak_quarkus_systemd_wait_for_delay`| Activation delay for service systemd unit (seconds) | `10` |
|`keycloak_quarkus_restart_strategy`| Strategy task file for restarting in HA (one of provided restart/['serial.yml','none.yml','serial_then_parallel.yml']) or path to file when providing custom strategy | `restart/serial.yml` |
|`keycloak_quarkus_restart_health_check`| Whether to wait for successful health check after restart | `true` |
|`keycloak_quarkus_restart_health_check_delay`| Seconds to let pass before starting healch checks | `10` |
|`keycloak_quarkus_restart_health_check_retries`| Number of attempts for successful health check before failing | `25` |
|`keycloak_quarkus_validate_certs`| Whether to validate SSL certificates when performing health checks | `true` |
|`keycloak_quarkus_restart_pause`| Seconds to wait between restarts in HA strategy | `15` |
#### Hostname configuration
| Variable | Description | Default |
|:---------|:------------|:--------|
|`keycloak_quarkus_hostname`| Address at which is the server exposed. Can be a full URL, or just a hostname. When only hostname is provided, scheme, port and context path are resolved from the request. | |
|`keycloak_quarkus_hostname_admin`| Set the base URL for accessing the administration console, including scheme, host, port and path | `` |
|`keycloak_quarkus_hostname_strict`| Disables dynamically resolving the hostname from request headers | `true` |
|`keycloak_quarkus_hostname_backchannel_dynamic`| Enables dynamic resolving of backchannel URLs, including hostname, scheme, port and context path. Set to true if your application accesses Keycloak via a private network. If set to true, hostname option needs to be specified as a full URL. | `false` |
|`keycloak_quarkus_hostname_strict_backchannel`| Deprecated, use (the inverted!)`keycloak_quarkus_hostname_backchannel_dynamic` instead. | |
#### HTTP(S) configuration
| Variable | Description | Default |
|:---------|:------------|:--------|
|`keycloak_quarkus_http_relative_path`| Set the path relative to / for serving resources. The path must start with a / | `/` |
|`keycloak_quarkus_http_host`| The http host, ie. the address used to bind the service | `0.0.0.0` |
|`keycloak_quarkus_http_port`| HTTP listening port | `8080` |
|`keycloak_quarkus_https_port`| TLS HTTP listening port | `8443` |
|`keycloak_quarkus_http_management_port`| Port of the management interface. Relevant only when something is exposed on the management interface - see the guide for details. | `9000` |
|`keycloak_quarkus_https_key_store_file`| The file path to the key store | `{{ keycloak.home }}/conf/key_store.p12` |
|`keycloak_quarkus_https_key_store_password`| Password for the key store | `""` |
|`keycloak_quarkus_https_trust_store_enabled`| Enable configuration of the https trust store | `False` |
|`keycloak_quarkus_https_trust_store_file`| The file path to the trust store | `{{ keycloak.home }}/conf/trust_store.p12` |
|`keycloak_quarkus_https_trust_store_password`| Password for the trust store | `""` |
|`keycloak_quarkus_https_key_file_enabled`| Enable listener on HTTPS port | `False` |
|`keycloak_quarkus_key_file_copy_enabled`| Enable copy of key file to target host | `False` |
|`keycloak_quarkus_key_content`| Content of the TLS private key. Use `"{{ lookup('file', 'server.key.pem') }}"` to lookup a file. | `""` |
|`keycloak_quarkus_key_file`| The file path to a private key in PEM format | `/etc/pki/tls/private/server.key.pem` |
|`keycloak_quarkus_cert_file_copy_enabled`| Enable copy of cert file to target host | `False`|
|`keycloak_quarkus_cert_file_src`| Set the source file path | `""` |
|`keycloak_quarkus_cert_file`| The file path to a server certificate or certificate chain in PEM format | `/etc/pki/tls/certs/server.crt.pem` |
|`keycloak_quarkus_https_key_store_enabled`| Enable configuration of HTTPS via a key store | `False` |
|`keycloak_quarkus_key_store_file`| Deprecated, use `keycloak_quarkus_https_key_store_file` instead. ||
|`keycloak_quarkus_key_store_password`| Deprecated, use `keycloak_quarkus_https_key_store_password` instead.||
|`keycloak_quarkus_http_relative_path` | Set the path relative to / for serving resources. The path must start with a / | `/` |
|`keycloak_quarkus_http_management_relative_path` | Set the path relative to / for serving resources from management interface. The path must start with a /. If not given, the value is inherited from HTTP options. Relevant only when something is exposed on the management interface - see the guide for details. | `/` |
|`keycloak_quarkus_http_enabled`| Enable listener on HTTP port | `True` |
#### Infinispan configuration
| Variable | Description | Default |
| :------------------------------------------------- | :------------------------------ | :----------------------------------------------------------- |
| `keycloak_quarkus_cache_managed_infinispan_config` | Manage infinispan configuration | `"{{ keycloak_quarkus_version is version('26.4.0', '<') }}"` |
| `keycloak_quarkus_cache_infinispan_template` | Infinispan cache template file | `cache-ispn.xml` |
As explained in the [official documentation](https://www.keycloak.org/server/caching#_modifying_cache_configuration_defaults), since version 26.4, it is recommended not to modify the XML configuration file but rather to configure the cache via the keycloak.properties file. By default, the role will no longer automatically deploy this file for versions higher than 26.4.
For earlier versions, it is possible to override the given template to customize the cache using the `keycloak_quarkus_cache_infinispan_template` variable.
#### Database configuration
| Variable | Description | Default |
|:---------|:------------|:--------|
|`keycloak_quarkus_db_engine` | Database engine [mariadb,postres,mssql] | `postgres` |
|`keycloak_quarkus_db_user` | User for database connection | `keycloak-user` |
|`keycloak_quarkus_db_pass` | Password for database connection | `keycloak-pass` |
|`keycloak_quarkus_db_url` | JDBC URL for connecting to database | `jdbc:postgresql://localhost:5432/keycloak` |
|`keycloak_quarkus_db_driver_version` | Version for JDBC engine driver | `9.4.1212` |
#### Cache configuration
| Variable | Description | Default |
|:---------|:------------|:--------|
|`keycloak_quarkus_cache_remote` | Whether to connect to remote cache infinispan server | `false` |
|`keycloak_quarkus_cache_remote_username` | Username for connecting to infinispan | `supervisor` |
|`keycloak_quarkus_cache_remote_password` | Password for connecting to infinispan | `supervisor` |
|`keycloak_quarkus_cache_remote_host` | Hostname for connecting to infinispan | `localhost` |
|`keycloak_quarkus_cache_remote_port`| Port for connecting to infinispan | `11222` |
|`keycloak_quarkus_cache_remote_sasl_mechanism` | Infinispan auth mechanism | `SCRAM-SHA-512` |
|`keycloak_quarkus_cache_remote_tls_enabled` | Whether infinispan uses TLS connection | `false` |
|`keycloak_quarkus_cache_embedded_properties` | Embedded cache properties | `` |
#### Logging configuration
| Variable | Description | Default |
|:---------|:------------|:--------|
|`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_log_target`| Set the destination of the keycloak log folder link | `/var/log/keycloak` |
|`keycloak_quarkus_log_max_file_size`| Set the maximum log file size before a log rotation happens; A size configuration option recognises string in this format (shown as a regular expression): `[0-9]+[KkMmGgTtPpEeZzYy]?`. If no suffix is given, assume bytes. | `10M` |
|`keycloak_quarkus_log_max_backup_index`| Set the maximum number of archived log files to keep | `10` |
|`keycloak_quarkus_log_file_suffix`| Set the log file handler rotation file suffix. When used, the file will be rotated based on its suffix; Note: If the suffix ends with `.zip` or `.gz`, the rotation file will also be compressed. | `.yyyy-MM-dd.zip` |
#### Miscellaneous configuration
| Variable | Description | Default |
|:---------|:------------|:--------|
|`keycloak_quarkus_metrics_enabled`| Whether to enable metrics | `False` |
|`keycloak_quarkus_health_enabled`| If the server should expose health check endpoints on the management interface | `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 }}` |
|`keycloak_quarkus_config_dir` | Path for configuration | `{{ keycloak_quarkus_home }}/conf` |
|`keycloak_quarkus_master_realm` | Name for rest authentication realm | `master` |
|`keycloak_auth_client` | Authentication client for configuration REST calls | `admin-cli` |
|`keycloak_quarkus_force_install` | Remove pre-existing versions of service | `False` |
|`keycloak_quarkus_proxy_mode`| The proxy address forwarding mode if the server is behind a reverse proxy (deprecated) | `none` |
|`keycloak_quarkus_start_dev`| Whether to start the service in development mode (start-dev) | `False` |
|`keycloak_quarkus_transaction_xa_enabled`| Whether to use XA transactions | `True` |
|`keycloak_quarkus_spi_sticky_session_encoder_infinispan_should_attach_route`| If the route should be attached to cookies to reflect the node that owns a particular session. If false, route is not attached to cookies and we rely on the session affinity capabilities from reverse proxy | `True` |
|`keycloak_quarkus_show_deprecation_warnings`| Whether deprecation warnings should be shown | `True` |
#### Vault configuration
| Variable | Description | Default |
|:---------|:------------|:--------|
|`keycloak_quarkus_ks_vault_enabled`| Whether to enable the vault SPI | `false` |
|`keycloak_quarkus_ks_vault_file`| The keystore path for the vault SPI | `{{ keycloak_quarkus_config_dir }}/keystore.p12` |
|`keycloak_quarkus_ks_vault_type`| Type of the keystore used for the vault SPI | `PKCS12` |
#### Configuring providers
| Variable | Description | Default |
|:---------|:------------|:--------|
|`keycloak_quarkus_providers`| List of provider definitions; see below | `[]` |
Providers support different sources:
* `url`: http download for providers not requiring authentication
* `maven`: maven download for providers hosted publicly on Apache Maven Central or private Maven repositories like Github Maven requiring authentication
* `local_path`: static providers to be uploaded
Provider definition:
```yaml
keycloak_quarkus_providers:
- id: http-client # required; "{{ id }}.jar" identifies the file name on RHBK
spi: connections # required if neither url, local_path nor maven are specified; required for setting properties
default: true # optional, whether to set default for spi, default false
restart: true # optional, whether to rebuild config and restart the service after deploying, default true
url: https://.../.../custom_spi.jar # optional, url for download via http
local_path: my_theme_spi.jar # optional, path on local controller for SPI to be uploaded
remote: true # optional, whether to copy from localhost or remotely, see https://docs.ansible.com/ansible/latest/collections/ansible/builtin/copy_module.html#parameter-remote_src, default false
maven: # optional, for download using maven
repository_url: https://maven.pkg.github.com/OWNER/REPOSITORY # optional, maven repo url
group_id: my.group # optional, maven group id
artifact_id: artifact # optional, maven artifact id
version: 24.0.5 # optional, defaults to latest
username: user # optional, cf. https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-apache-maven-registry#authenticating-to-github-packages
password: pat # optional, provide a PAT for accessing Github's Apache Maven registry
properties: # optional, list of key-values
- key: default-connection-pool-size
value: 10
checksum: sha256:D98291AC[...]B6DC7B97 # optional, checksum used to verify integrity:
# for `url` SPIs, use format: <algorithm>:<checksum|url>, cf. <https://docs.ansible.com/ansible/latest/collections/ansible/builtin/get_url_module.html#parameter-checksum>;
# for `local_path` SPIs, use SHA1 format <https://docs.ansible.com/ansible/latest/collections/ansible/builtin/copy_module.html#parameter-checksum>
# for `maven` SPIs, this field is ignored since maven has integrity verification methods enabled by default
```
the definition above will generate the following build command:
```
bin/kc.sh build --spi-connections-provider=http-client --spi-connections-http-client-default-connection-pool-size=10
```
#### Configuring policies
| Variable | Description | Default |
|:---------|:------------|:--------|
|`keycloak_quarkus_policies`| List of policy definitions; see below | `[]` |
Provider definition:
```yaml
keycloak_quarkus_policies:
- name: john-the-ripper.txt # required, resulting file name
url: https://github.com/danielmiessler/SecLists/raw/master/Passwords/Software/john-the-ripper.txt # required, url for download
type: password-blacklists # optional, defaults to `password-blacklists`; supported values: [`password-blacklists`]
```
Role Variables
--------------
| Variable | Description | Required |
|:---------|:------------|----------|
|`keycloak_quarkus_bootstrap_admin_password`| Password of console admin account | `yes` |
|`keycloak_quarkus_admin_pass`| Deprecated, use `keycloak_quarkus_bootstrap_admin_password` instead. | |
|`keycloak_quarkus_ks_vault_pass`| The password for accessing the keystore vault SPI | `no` |
|`keycloak_quarkus_alternate_download_url`| Alternate location with optional authentication for downloading RHBK | `no` |
|`keycloak_quarkus_download_user`| Optional username for http authentication | `no*` |
|`keycloak_quarkus_download_pass`| Optional password for http authentication | `no*` |
|`keycloak_quarkus_download_validate_certs`| Whether to validate certs for URL `keycloak_quarkus_alternate_download_url` | `no` |
|`keycloak_quarkus_jdbc_download_user`| Optional username for http authentication | `no*` |
|`keycloak_quarkus_jdbc_download_pass`| Optional password for http authentication | `no*` |
|`keycloak_quarkus_jdbc_download_validate_certs`| Whether to validate certs for URL `keycloak_quarkus_download_validate_certs` | `no` |
`*` username/password authentication credentials must be both declared or both undefined
Role custom facts
-----------------
The role uses the following [custom facts](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_vars_facts.html#adding-custom-facts) found in `/etc/ansible/facts.d/keycloak.fact` (and thus identified by the `ansible_local.keycloak.` prefix):
| Variable | Description |
|:---------|:------------|
|`general.bootstrapped` | A custom fact indicating whether this role has been used for bootstrapping keycloak on the respective host before; set to `false` (e.g., when starting off with a new, empty database) ensures that the initial admin user as defined by `keycloak_quarkus_bootstrap_admin_user[_password]` gets created |
License
-------
Apache License 2.0
Author Information
------------------
* [Guido Grazioli](https://github.com/guidograzioli)

View file

@ -0,0 +1,194 @@
---
### Configuration specific to keycloak
keycloak_quarkus_version: 26.6.2
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 }}"
# Authentication for Keycloak binary download (e.g. from internal artifact repository)
keycloak_quarkus_binary_download_user:
keycloak_quarkus_binary_download_pass:
# whether to install from local archive
keycloak_quarkus_offline_install: false
keycloak_quarkus_show_deprecation_warnings: true
### Install location and service settings
keycloak_quarkus_java_home:
keycloak_quarkus_dest: /opt/keycloak
keycloak_quarkus_home: "{{ keycloak_quarkus_installdir }}"
keycloak_quarkus_config_dir: "{{ keycloak_quarkus_home }}/conf"
keycloak_quarkus_download_path: "{{ lookup('env', 'PWD') }}"
keycloak_quarkus_start_dev: false
keycloak_quarkus_service_user: keycloak
keycloak_quarkus_service_group: keycloak
keycloak_quarkus_service_restart_always: false
keycloak_quarkus_service_restart_on_failure: false
keycloak_quarkus_service_restartsec: "10s"
keycloak_quarkus_configure_firewalld: false
keycloak_quarkus_configure_iptables: false
### administrator console password
keycloak_quarkus_bootstrap_admin_user: admin
keycloak_quarkus_bootstrap_admin_password:
keycloak_quarkus_master_realm: master
### Configuration settings
keycloak_quarkus_bind_address: 0.0.0.0 # deprecated use keycloak_quarkus_http_host
keycloak_quarkus_http_host: 0.0.0.0
keycloak_quarkus_http_enabled: true
keycloak_quarkus_http_port: 8080
keycloak_quarkus_https_port: 8443
keycloak_quarkus_http_management_port: 9000
keycloak_quarkus_jgroups_port: 7800
keycloak_quarkus_jgroups_bind_address: "{{ ansible_default_ipv4.address }}"
keycloak_quarkus_jgroups_external_addr: "{{ keycloak_quarkus_jgroups_bind_address }}"
keycloak_quarkus_jgroups_external_port: "{{ keycloak_quarkus_jgroups_port }}"
keycloak_quarkus_java_heap_opts: "-Xms1024m -Xmx2048m"
keycloak_quarkus_java_jvm_opts: >
-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:GCTimeRatio=4
-XX:AdaptiveSizePolicyWeight=90 -XX:FlightRecorderOptions=stackdepth=512
keycloak_quarkus_jgroups_opts: >
-Djgroups.bind.address={{ keycloak_quarkus_jgroups_bind_address }}
-Djgroups.external_port={{ keycloak_quarkus_jgroups_external_port }}
-Djgroups.external_addr={{ keycloak_quarkus_jgroups_external_addr }}
keycloak_quarkus_java_opts: "{{ ' '.join((keycloak_quarkus_jgroups_opts, keycloak_quarkus_java_heap_opts, keycloak_quarkus_java_jvm_opts)) }}"
keycloak_quarkus_additional_env_vars: []
### TLS/HTTPS configuration
keycloak_quarkus_https_key_file_enabled: false
keycloak_quarkus_key_file_copy_enabled: false
keycloak_quarkus_key_content: ""
keycloak_quarkus_key_file: "/etc/pki/tls/private/server.key.pem"
keycloak_quarkus_cert_file_copy_enabled: false
keycloak_quarkus_cert_file_src: ""
keycloak_quarkus_cert_file: "/etc/pki/tls/certs/server.crt.pem"
#### key store configuration
keycloak_quarkus_https_key_store_enabled: false
keycloak_quarkus_https_key_store_file: "{{ keycloak.home }}/conf/key_store.p12"
keycloak_quarkus_https_key_store_password: ''
##### trust store configuration
keycloak_quarkus_https_trust_store_enabled: false
keycloak_quarkus_https_trust_store_file: "{{ keycloak.home }}/conf/trust_store.p12"
keycloak_quarkus_https_trust_store_password: ''
### configuration key store configuration
keycloak_quarkus_config_key_store_file: "{{ keycloak.home }}/conf/conf_store.p12"
keycloak_quarkus_config_key_store_password: ''
### Enable configuration for database backend, clustering and remote caches on infinispan
keycloak_quarkus_ha_enabled: false
keycloak_quarkus_ha_discovery: "JDBCPING"
### Enable database configuration, must be enabled when HA is configured
keycloak_quarkus_db_enabled: "{{ keycloak_quarkus_ha_enabled }}"
keycloak_quarkus_systemd_wait_for_port: "{{ keycloak_quarkus_ha_enabled }}"
keycloak_quarkus_systemd_wait_for_port_number: "{{ keycloak_quarkus_https_port }}"
keycloak_quarkus_systemd_wait_for_log: false
keycloak_quarkus_systemd_wait_for_timeout: 60
keycloak_quarkus_systemd_wait_for_delay: 10
### keycloak frontend url
keycloak_quarkus_hostname:
keycloak_quarkus_hostname_admin: ""
### Set the path relative to / for serving resources. The path must start with a /
### (set to `/auth` for retrocompatibility with pre-quarkus releases)
keycloak_quarkus_http_relative_path: /
# Disables dynamically resolving the hostname from request headers.
# Should always be set to true in production, unless proxy verifies the Host header.
keycloak_quarkus_hostname_strict: true
# Enables dynamic resolving of backchannel URLs, including hostname, scheme, port and context path.
# Set to true if your application accesses Keycloak via a private network. If set to true, keycloak_quarkus_hostname option needs to be specified as a full URL.
keycloak_quarkus_hostname_backchannel_dynamic: false
# The proxy headers that should be accepted by the server. ['', 'forwarded', 'xforwarded']
keycloak_quarkus_proxy_headers: ""
# deprecated: proxy address forwarding mode if the server is behind a reverse proxy. [none, edge, reencrypt, passthrough]
keycloak_quarkus_proxy_mode: none
# disable xa transactions
keycloak_quarkus_transaction_xa_enabled: true
# If the route should be attached to cookies to reflect the node that owns a particular session.
# If false, route is not attached to cookies and we rely on the session affinity capabilities from reverse proxy
keycloak_quarkus_spi_sticky_session_encoder_infinispan_should_attach_route: true
keycloak_quarkus_metrics_enabled: false
keycloak_quarkus_health_enabled: true
### infinispan; must read: https://forum.keycloak.org/t/keycloak-26-4-7-ha/31202
keycloak_quarkus_cache_managed_infinispan_config: "{{ keycloak_quarkus_version is version('26.4.0', '<') }}"
keycloak_quarkus_cache_infinispan_template: cache-ispn.xml
### caches; must read: https://www.keycloak.org/2024/12/storing-sessions-in-kc26
### embedded caches
# https://www.keycloak.org/server/caching
keycloak_quarkus_cache_embedded_properties: ""
### infinispan remote caches access (hotrod)
# https://www.keycloak.org/server/caching#_remote_cache
keycloak_quarkus_cache_remote: false
keycloak_quarkus_cache_remote_username: supervisor
keycloak_quarkus_cache_remote_password: supervisor
keycloak_quarkus_cache_remote_host: localhost
keycloak_quarkus_cache_remote_port: 11222
keycloak_quarkus_cache_remote_tls_enabled: false
keycloak_quarkus_cache_remote_sasl_mechanism: SCRAM-SHA-512
### database backend engine: values [ 'postgres', 'mariadb' ]
keycloak_quarkus_db_engine: postgres
### database backend credentials
keycloak_quarkus_db_user: keycloak-user
keycloak_quarkus_db_pass: keycloak-pass
keycloak_quarkus_db_url: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_db_engine].url }}"
keycloak_quarkus_db_driver_version: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_db_engine].version }}"
# override the variables above, following defaults show recommended version as per
# https://access.redhat.com/articles/7033107
keycloak_quarkus_default_jdbc:
postgres:
url: 'jdbc:postgresql://localhost:5432/keycloak'
version: 42.7.7
mariadb:
url: 'jdbc:mariadb://localhost:3306/keycloak'
version: 3.5.2
mssql:
url: 'jdbc:sqlserver://localhost:1433;databaseName=keycloak;'
version: 13.2.0
driver_jar_url: "https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/13.2.0.jre11/mssql-jdbc-13.2.0.jre11.jar"
### 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'
keycloak_quarkus_log_target: /var/log/keycloak
keycloak_quarkus_log_max_file_size: 10M
keycloak_quarkus_log_max_backup_index: 10
keycloak_quarkus_log_file_suffix: '.yyyy-MM-dd.zip'
# keystore-based vault
keycloak_quarkus_ks_vault_enabled: false
keycloak_quarkus_ks_vault_file: "{{ keycloak_quarkus_config_dir }}/keystore.p12"
keycloak_quarkus_ks_vault_type: PKCS12
keycloak_quarkus_ks_vault_pass:
keycloak_quarkus_providers: []
keycloak_quarkus_policies: []
keycloak_quarkus_supported_policy_types: ['password-blacklists']
# files in restart directory (one of [ 'serial', 'none', 'serial_then_parallel' ]), or path to file when providing custom strategy
keycloak_quarkus_restart_strategy: restart/serial.yml
keycloak_quarkus_restart_health_check: true
keycloak_quarkus_restart_health_check_delay: 10
keycloak_quarkus_restart_health_check_retries: 25
keycloak_quarkus_validate_certs: true
keycloak_quarkus_health_check_url_path: "realms/{{ keycloak_quarkus_master_realm }}/.well-known/openid-configuration"
keycloak_quarkus_health_check_url: "{{ keycloak_quarkus_hostname ~ keycloak_quarkus_http_relative_path.rstrip('/') ~ '/' ~ keycloak_quarkus_health_check_url_path }}"
keycloak_quarkus_restart_pause: 15
keycloak_quarkus_force_install: false

View file

@ -0,0 +1,21 @@
---
- name: "Invalidate theme cache: {{ keycloak.service_name }}"
ansible.builtin.include_tasks: invalidate_theme_cache.yml
listen: "invalidate keycloak theme cache"
# handler should be invoked anytime a [build configuration](https://www.keycloak.org/server/all-config?f=build) changes
- name: "Rebuild config: {{ keycloak.service_name }}"
ansible.builtin.include_tasks: rebuild_config.yml
listen: "rebuild keycloak config"
- name: "Bootstrapped"
ansible.builtin.include_tasks: bootstrapped.yml
listen: bootstrapped
- name: "Restart {{ keycloak.service_name }}"
ansible.builtin.include_tasks:
file: "{{ keycloak_quarkus_restart_strategy if keycloak_quarkus_ha_enabled else 'restart.yml' }}"
listen: "restart keycloak"
- name: "Display deprecation warning"
ansible.builtin.fail:
msg: "Deprecation warning: you are using the deprecated variable '{{ keycloak_quarkus_deprecated_variable | d('NotSet') }}', check docs on how to upgrade."
failed_when: false
changed_when: true
listen: "print deprecation warning"

View file

@ -0,0 +1,573 @@
argument_specs:
main:
options:
keycloak_quarkus_version:
default: "26.6.2"
description: "keycloak.org package version"
type: "str"
keycloak_quarkus_archive:
default: "keycloak-{{ keycloak_quarkus_version }}.zip"
description: "keycloak install archive filename"
type: "str"
keycloak_quarkus_download_url:
default: "https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}"
description: "Download URL for keycloak"
type: "str"
keycloak_quarkus_installdir:
default: "{{ keycloak_quarkus_dest }}/keycloak-{{ keycloak_quarkus_version }}"
description: "Installation path"
type: "str"
keycloak_quarkus_offline_install:
default: false
description: "Perform an offline install"
type: "bool"
keycloak_quarkus_jvm_package:
default: "java-21-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:
default: "/opt/keycloak"
description: "Installation root path"
type: "str"
keycloak_quarkus_home:
default: "{{ keycloak_quarkus_installdir }}"
description: "Installation work directory"
type: "str"
keycloak_quarkus_config_dir:
default: "{{ keycloak_quarkus_home }}/conf"
description: "Path for configuration"
type: "str"
keycloak_quarkus_service_user:
default: "keycloak"
description: "Posix account username"
type: "str"
keycloak_quarkus_service_group:
default: "keycloak"
description: "Posix account group"
type: "str"
keycloak_quarkus_configure_firewalld:
default: false
description: "Ensure firewalld is running and configure keycloak ports"
type: "bool"
keycloak_quarkus_configure_iptables:
default: false
description: "Ensure firewalld is running and configure keycloak ports"
type: "bool"
keycloak_quarkus_service_restart_always:
default: false
description: "systemd restart always behavior of service; takes precedence over keycloak_service_restart_on_failure if true"
type: "bool"
keycloak_quarkus_service_restart_on_failure:
default: false
description: "systemd restart on-failure behavior of service"
type: "bool"
keycloak_quarkus_service_restartsec:
default: "10s"
description: "systemd RestartSec for service"
type: "str"
keycloak_quarkus_bootstrap_admin_user:
default: "admin"
description: "Administration user account, only for bootstrapping"
type: "str"
keycloak_quarkus_force_install:
default: false
description: "Remove pre-existing versions of service"
type: "bool"
keycloak_quarkus_bootstrap_admin_password:
required: true
description: "Password of admin account, only for bootstrapping"
type: "str"
keycloak_quarkus_master_realm:
default: "master"
description: "Name for rest authentication realm"
type: "str"
keycloak_quarkus_bind_address:
default: "0.0.0.0"
description: "Deprecated, use `keycloak_quarkus_http_host`"
type: "str"
keycloak_quarkus_hostname:
description: >-
Address at which is the server exposed.
Can be a full URL, or just a hostname. When only hostname is provided, scheme, port and context path are resolved from the request.
type: "str"
keycloak_quarkus_host:
description: "Deprecated in v26, use keycloak_quarkus_hostname instead."
type: "str"
keycloak_quarkus_port:
description: "Deprecated in v26, use keycloak_quarkus_hostname instead."
type: "int"
keycloak_quarkus_path:
description: "Deprecated in v26, use keycloak_quarkus_hostname instead."
type: "str"
keycloak_quarkus_http_enabled:
default: true
description: "Enable listener on HTTP port"
type: "bool"
keycloak_quarkus_http_host:
default: '0.0.0.0'
description: "HTTP host, address for binding service ports"
type: "str"
keycloak_quarkus_http_port:
default: 8080
description: "HTTP port"
type: "int"
keycloak_quarkus_health_check_url:
default: "{{ keycloak_quarkus_hostname ~ keycloak_quarkus_http_relative_path.rstrip('/') ~ '/' ~ keycloak_quarkus_health_check_url_path }}"
description: "Full URL (including scheme, host, path, fragment etc.) used for health check endpoint; keycloak_quarkus_hostname will NOT be prepended; helpful when health checks should happen against http port, but keycloak_quarkus_hostname uses https scheme per default"
type: "str"
keycloak_quarkus_health_check_url_path:
default: "realms/{{ keycloak_quarkus_master_realm }}/.well-known/openid-configuration"
description: "Path to the health check endpoint; keycloak_quarkus_hostname will be prepended automatically; Note that keycloak_quarkus_health_check_url takes precedence over this property"
type: "str"
keycloak_quarkus_https_key_file_enabled:
default: false
description: "Enable configuration of HTTPS via files in PEM format"
type: "bool"
keycloak_quarkus_key_file_copy_enabled:
default: false
description: "Enable copy of key file to target host"
type: "bool"
keycloak_quarkus_key_content:
default: ""
description: "Content of the TLS private key"
type: "str"
keycloak_quarkus_key_file:
default: "/etc/pki/tls/private/server.key.pem"
description: "The file path to a private key in PEM format"
type: "str"
keycloak_quarkus_cert_file_copy_enabled:
default: false
description: "Enable copy of cert file to target host"
type: "bool"
keycloak_quarkus_cert_file_src:
default: ""
description: "Set the source file path"
type: "str"
keycloak_quarkus_cert_file:
default: "/etc/pki/tls/certs/server.crt.pem"
description: "The file path to a server certificate or certificate chain in PEM format"
type: "str"
keycloak_quarkus_https_key_store_enabled:
default: false
description: "Enable configuration of HTTPS via a key store"
type: "bool"
keycloak_quarkus_key_store_file:
default: ""
description: "Deprecated, use `keycloak_quarkus_https_key_store_file` instead."
type: "str"
keycloak_quarkus_key_store_password:
default: ""
description: "Deprecated, use `keycloak_quarkus_https_key_store_password` instead."
type: "str"
keycloak_quarkus_https_key_store_file:
default: "{{ keycloak.home }}/conf/key_store.p12"
description: "The file path to the key store"
type: "str"
keycloak_quarkus_https_key_store_password:
default: ""
description: "Password for the key store"
type: "str"
keycloak_quarkus_https_trust_store_enabled:
default: false
description: "Enable configuration of the https trust store"
type: "bool"
keycloak_quarkus_https_trust_store_file:
default: "{{ keycloak.home }}/conf/trust_store.p12"
description: "The file path to the trust store"
type: "str"
keycloak_quarkus_https_trust_store_password:
default: ""
description: "Password for the trust store"
type: "str"
keycloak_quarkus_config_key_store_file:
default: "{{ keycloak.home }}/conf/conf_store.p12"
description: "Path to the configuration key store; only used if `keycloak_quarkus_config_key_store_password` is not empty"
type: "str"
keycloak_quarkus_config_key_store_password:
default: ""
description: >
Password of the configuration key store; if non-empty, `keycloak_quarkus_db_pass` will be saved to the key store
at `keycloak_quarkus_config_key_store_file` (instead of being written to the configuration file in clear text)
type: "str"
keycloak_quarkus_https_port:
default: 8443
description: "HTTPS port"
type: "int"
keycloak_quarkus_http_management_port:
default: 9000
description: "Port of the management interface. Relevant only when something is exposed on the management interface - see the guide for details."
type: "int"
keycloak_quarkus_jgroups_port:
description: 'jgroups bind port'
default: 7800
type: "int"
keycloak_quarkus_jgroups_bind_address:
description: 'jgroups bind address'
default: "{{ ansible_default_ipv4.address }}"
type: "str"
keycloak_quarkus_jgroups_external_addr:
description: 'IP address that other instances in the Keycloak should use to contact this node'
default: "{{ keycloak_quarkus_jgroups_bind_address }}"
type: "str"
keycloak_quarkus_jgroups_external_port:
description: 'Port that other instances in the Keycloak cluster should use to contact this node'
default: "{{ keycloak_quarkus_jgroups_port }}"
type: "int"
keycloak_quarkus_jgroups_opts:
description: "JVM arguments for jgroups configuration"
default: "-Djgroups.bind.address={{ keycloak_quarkus_jgroups_bind_address }} -Djgroups.external_port={{ keycloak_quarkus_jgroups_external_port }} -Djgroups.external_addr={{ keycloak_quarkus_jgroups_external_addr }}"
type: "str"
keycloak_quarkus_java_heap_opts:
default: "-Xms1024m -Xmx2048m"
description: "Heap memory JVM setting"
type: "str"
keycloak_quarkus_java_jvm_opts:
default: >
-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:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -XX:FlightRecorderOptions=stackdepth=512
description: "Other JVM settings"
type: "str"
keycloak_quarkus_java_opts:
default: "{{ ' '.join((keycloak_quarkus_jgroups_opts, keycloak_quarkus_java_heap_opts, keycloak_quarkus_java_jvm_opts)) }}"
description: "JVM arguments, by default heap_opts + jvm_opts, if overriden it takes precedence over them"
type: "str"
keycloak_quarkus_additional_env_vars:
default: "[]"
description: "List of additional env variables of { key: str, value: str} to be put in sysconfig file"
type: "list"
keycloak_quarkus_ha_enabled:
default: false
description: "Enable auto configuration for database backend, clustering and remote caches on infinispan"
type: "bool"
keycloak_quarkus_ha_discovery:
default: "TCPPING"
description: "Discovery protocol for HA cluster members"
type: "str"
keycloak_quarkus_db_enabled:
default: "{{ True if keycloak_quarkus_ha_enabled else False }}"
description: "Enable auto configuration for database backend"
type: "str"
keycloak_quarkus_http_relative_path:
required: false
default: /
description: "Set the path relative to / for serving resources. The path must start with a /"
type: "str"
keycloak_quarkus_http_management_relative_path:
required: false
description: "Set the path relative to / for serving resources from management interface. The path must start with a /. If not given, the value is inherited from HTTP options. Relevant only when something is exposed on the management interface - see the guide for details."
type: "str"
keycloak_quarkus_frontend_url:
required: false
description: "Deprecated in v26, use keycloak_quarkus_hostname instead."
type: "str"
keycloak_quarkus_hostname_admin:
required: false
description: "Service URL for the admin console"
type: "str"
keycloak_quarkus_admin_url:
required: false
description: "Deprecated in v26, use keycloak_quarkus_hostname_admin instead."
type: "str"
keycloak_quarkus_metrics_enabled:
default: false
description: "Whether to enable metrics"
type: "bool"
keycloak_quarkus_health_enabled:
default: true
description: "If the server should expose health check endpoints on the management interface"
type: "bool"
keycloak_quarkus_cache_remote:
description: "Whether to connect to remote cache infinispan server"
default: false
type: 'bool'
keycloak_quarkus_cache_remote_username:
default: "supervisor"
description: "Username for connecting to infinispan"
type: "str"
keycloak_quarkus_cache_remote_password:
default: "supervisor"
description: "Password for connecting to infinispan"
type: "str"
keycloak_quarkus_cache_remote_host:
default: "localhost"
description: "Hostname for connecting to infinispan"
type: "str"
keycloak_quarkus_cache_remote_port:
default: "11222"
description: "Port for connecting to infinispan"
type: "str"
keycloak_quarkus_cache_remote_sasl_mechanism:
default: "SCRAM-SHA-512"
description: "Infinispan auth mechanism"
type: "str"
keycloak_quarkus_cache_remote_tls_enabled:
default: false
description: "Whether infinispan uses TLS connection"
type: "bool"
keycloak_quarkus_db_engine:
default: "postgres"
description: "Database engine [mariadb,postres,mssql]"
type: "str"
keycloak_quarkus_db_user:
default: "keycloak-user"
description: "User for database connection"
type: "str"
keycloak_quarkus_db_pass:
default: "keycloak-pass"
description: "Password for database connection"
type: "str"
keycloak_quarkus_db_url:
default: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_db_engine].url }}"
description: "JDBC URL for connecting to database"
type: "str"
keycloak_quarkus_db_driver_version:
default: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_db_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_log_target:
default: '/var/log/keycloak'
type: "str"
description: "Set the destination of the keycloak log folder link"
keycloak_quarkus_log_max_file_size:
default: 10M
type: "str"
description: >
Set the maximum log file size before a log rotation happens; A size configuration option recognises string in this format (shown as a regular
expression): [0-9]+[KkMmGgTtPpEeZzYy]?. If no suffix is given, assume bytes.
keycloak_quarkus_log_max_backup_index:
default: 10
type: "str"
description: "Set the maximum number of archived log files to keep"
keycloak_quarkus_log_file_suffix:
default: '.yyyy-MM-dd.zip'
type: "str"
description: >
Set the log file handler rotation file suffix. When used, the file will be rotated based on its suffix. Note: If the suffix ends
with .zip or .gz, the rotation file will also be compressed.
keycloak_quarkus_proxy_mode:
default: 'none'
type: "str"
description: "The proxy address forwarding mode if the server is behind a reverse proxy. Set to 'none' as it is deprecated according to Keycloak documentation"
keycloak_quarkus_proxy_headers:
default: ""
type: "str"
description: "Parse reverse proxy headers (`forwarded` or `xforwarded`), overrides the deprecated keycloak_quarkus_proxy_mode argument"
keycloak_quarkus_start_dev:
default: false
type: "bool"
description: "Whether to start the service in development mode (start-dev)"
keycloak_quarkus_transaction_xa_enabled:
default: true
type: "bool"
description: "Enable or disable XA transactions which may not be supported by some DBMS"
keycloak_quarkus_hostname_strict:
default: true
type: "bool"
description: >
Disables dynamically resolving the hostname from request headers. Should always be set to true in production, unless
proxy verifies the Host header.
keycloak_quarkus_hostname_backchannel_dynamic:
default: false
type: "bool"
description: >
Enables dynamic resolving of backchannel URLs, including hostname, scheme, port and context path.
Set to true if your application accesses Keycloak via a private network. If set to true, hostname option needs to be specified as a full URL.
keycloak_quarkus_spi_sticky_session_encoder_infinispan_should_attach_route:
default: true
type: "bool"
description: >
If the route should be attached to cookies to reflect the node that owns a particular session. If false, route is not attached to cookies
and we rely on the session affinity capabilities from reverse proxy
keycloak_quarkus_ks_vault_enabled:
default: false
type: "bool"
description: "Whether to enable vault SPI"
keycloak_quarkus_ks_vault_file:
default: "{{ keycloak_quarkus_config_dir }}/keystore.p12"
type: "str"
description: "The keystore path for the vault SPI"
keycloak_quarkus_ks_vault_type:
default: "PKCS12"
type: "str"
description: "Type of the keystore used for the vault SPI"
keycloak_quarkus_ks_vault_pass:
required: false
type: "str"
description: "The password for accessing the keystore vault SPI"
keycloak_quarkus_systemd_wait_for_port:
description: 'Whether systemd unit should wait for keycloak port before returning'
default: "{{ keycloak_quarkus_ha_enabled }}"
type: "bool"
keycloak_quarkus_systemd_wait_for_port_number:
default: "{{ keycloak_quarkus_https_port }}"
description: "The port the systemd unit should wait for, by default the https port"
type: "int"
keycloak_quarkus_systemd_wait_for_log:
description: 'Whether systemd unit should wait for service to be up in logs'
default: false
type: "bool"
keycloak_quarkus_systemd_wait_for_timeout:
description: "How long to wait for service to be alive (seconds)"
default: 60
type: 'int'
keycloak_quarkus_systemd_wait_for_delay:
description: "Activation delay for service systemd unit (seconds)"
default: 10
type: 'int'
keycloak_quarkus_providers:
description: >
List of provider definition dicts: { 'id': str, 'spi': str, 'url': str, 'local_path': str,
'maven': {
'repository_url': str, 'group_id': str, 'artifact_id': str, 'version': str, 'username': str, optional, 'password': str, optional
},
'default': bool,
'properties': list of key/value }
default: []
type: "list"
keycloak_quarkus_supported_policy_types:
description: "List of str of supported policy types"
default: ['password-blacklists']
type: "list"
keycloak_quarkus_policies:
description: "List of policy definition dicts: { 'name': str, 'url': str, 'type': str }"
default: []
type: "list"
keycloak_quarkus_jdbc_download_url:
description: "Override the default Maven Central download URL for the JDBC driver"
type: "str"
keycloak_quarkus_jdbc_download_user:
description: "Set a username with which to authenticate when downloading JDBC drivers from an alternative location"
type: "str"
keycloak_quarkus_jdbc_download_pass:
description: >
Set a password with which to authenticate when downloading JDBC drivers from an alternative location
(requires `keycloak_quarkus_jdbc_download_user``)
type: "str"
keycloak_quarkus_jdbc_download_validate_certs:
default: true
description: "Allow the option to ignore invalid certificates when downloading JDBC drivers from a custom URL"
type: "bool"
keycloak_quarkus_restart_health_check:
default: true
description: "Whether to wait for successful health check after restart"
type: "bool"
keycloak_quarkus_restart_strategy:
description: >
Strategy task file for restarting in HA, one of restart/[ 'serial', 'none', 'serial_then_parallel' ].yml, or path to
file when providing custom strategy; when keycloak_quarkus_ha_enabled and keycloak_quarkus_restart_health_check == true
default: "restart/serial.yml"
type: "str"
keycloak_quarkus_restart_pause:
description: "Seconds to wait between restarts in HA strategy"
default: 15
type: int
keycloak_quarkus_restart_health_check_delay:
description: "Seconds to let pass before starting healch checks"
default: 10
type: 'int'
keycloak_quarkus_restart_health_check_retries:
description: "Number of attempts for successful health check before failing"
default: 25
type: 'int'
keycloak_quarkus_validate_certs:
description: "Whether to validate SSL certificates when performing health checks"
default: true
type: "bool"
keycloak_quarkus_show_deprecation_warnings:
default: true
description: "Whether or not deprecation warnings should be shown"
type: "bool"
keycloak_quarkus_download_path:
description: "Path local to controller for offline/download of install archives"
default: "{{ lookup('env', 'PWD') }}"
type: "str"
keycloak_quarkus_cache_managed_infinispan_config:
description: "Manage infinispan configuration"
default: "{{ keycloak_quarkus_version is version('26.4.0', '<') }}"
type: bool
keycloak_quarkus_cache_infinispan_template:
description: "Infinispan cache template file"
default: "cache-ispn.xml"
type: str
keycloak_quarkus_cache_embedded_properties:
description: Embedded cache properties
default: ""
type: str
keycloak_quarkus_binary_download_user:
description: "Username for HTTP Basic Auth when downloading Keycloak binary"
type: "str"
required: false
keycloak_quarkus_binary_download_pass:
description: "Password for HTTP Basic Auth when downloading Keycloak binary"
type: "str"
required: false
downstream:
options:
rhbk_version:
default: "26.4.11"
description: "Red Hat Build of Keycloak version"
type: "str"
rhbk_archive:
default: "rhbk-{{ rhbk_version }}.zip"
description: "Red Hat Build of Keycloak install archive filename"
type: "str"
rhbk_dest:
default: "/opt/rhbk"
description: "Root installation directory"
type: "str"
rhbk_installdir:
default: "{{ rhbk_dest }}/rhbk-{{ rhbk_version }}"
description: "Installation path for Red Hat Build of Keycloak"
type: "str"
rhbk_apply_patches:
default: false
description: "Install Red Hat Build of Keycloak most recent cumulative patch"
type: "bool"
rhbk_enable:
default: true
description: "Enable Red Hat Build of Keycloak installation"
type: "bool"
rhbk_offline_install:
default: false
description: "Perform an offline install"
type: "bool"
rhbk_service_name:
default: "rhbk"
description: "systemd service name for Red Hat Build of Keycloak"
type: "str"
rhbk_service_desc:
default: "Red Hat Build of Keycloak"
description: "systemd description for Red Hat Build of Keycloak"
type: "str"
rhbk_patch_version:
required: false
description: "Red Hat Build of Keycloak latest cumulative patch version to apply; defaults to latest version when rhbk_apply_patches is True"
type: "str"
rhbk_patch_bundle:
default: "rhbk-{{ rhbk_patch_version | default('[0-9]+[.][0-9]+[.][0-9]+') }}-patch.zip"
description: "Red Hat Build of Keycloak patch archive filename"
type: "str"
rhbk_product_category:
default: "RHBK"
description: "Unified Downloads API category for Red Hat Build of Keycloak"
type: "str"

View file

@ -0,0 +1,33 @@
---
galaxy_info:
role_name: keycloak_quarkus
namespace: middleware_automation
author: Guido Grazioli
description: Install keycloak on quarkus server configurations
company: Red Hat, Inc.
license: Apache License 2.0
min_ansible_version: "2.16"
platforms:
- name: EL
versions:
- "8"
- "9"
- name: Fedora
- name: Debian
- name: Ubuntu
galaxy_tags:
- keycloak
- quarkus
- redhat
- rhel
- sso
- authentication
- identity
- security
- rhbk
- debian

View file

@ -0,0 +1,16 @@
---
- name: Save ansible custom facts
become: "{{ keycloak_quarkus_bootstrapped_require_privilege_escalation | default(true) }}"
ansible.builtin.template:
src: keycloak.fact.j2
dest: /etc/ansible/facts.d/keycloak.fact
mode: '0644'
vars:
bootstrapped: true
- name: Refresh custom facts
ansible.builtin.setup:
filter: ansible_local
- name: Ensure that `KEYCLOAK_ADMIN[_PASSWORD]` get purged
ansible.builtin.include_tasks: systemd.yml

View file

@ -0,0 +1,52 @@
---
- name: "Initialize configuration key store variables to be written"
ansible.builtin.set_fact:
keycloak_quarkus_store_items:
- key: "kc.db-password"
value: "{{ keycloak_quarkus_db_pass }}"
- name: "Initialize empty configuration key store"
become: "{{ keycloak_quarkus_config_store_require_privilege_escalation | default(true) }}"
# keytool doesn't allow creating an empty key store, so this is a hacky way around it
ansible.builtin.shell: | # noqa blocked_modules shell is necessary here
set -o nounset # abort on unbound variable
set -o pipefail # do not hide errors within pipes
set -o errexit # abort on nonzero exit status
echo dummy | keytool -noprompt -importpass -alias dummy -keystore {{ keycloak_quarkus_config_key_store_file | quote }} -storepass {{ keycloak_quarkus_config_key_store_password | quote }} -storetype PKCS12
keytool -delete -alias dummy -keystore {{ keycloak_quarkus_config_key_store_file | quote }} -storepass {{ keycloak_quarkus_config_key_store_password | quote }}
args:
creates: "{{ keycloak_quarkus_config_key_store_file }}"
- name: "Set configuration key store using keytool"
ansible.builtin.shell: | # noqa blocked_modules shell is necessary here
set -o nounset # abort on unbound variable
set -o pipefail # do not hide errors within pipes
keytool -list -alias {{ item.key | quote }} -keystore {{ keycloak_quarkus_config_key_store_file | quote }} -storepass {{ keycloak_quarkus_config_key_store_password | quote }}
retVal=$?
set -o errexit # abort on nonzero exit status
if [ $retVal -eq 0 ]; then
# value is already in keystore, but keytool has no replace function: delete and re-create instead
# note that we can not read whether the value has changed either[^1], so we need to override it
# [^1]: https://stackoverflow.com/a/37491400
keytool -delete -alias {{ item.key | quote }} -keystore {{ keycloak_quarkus_config_key_store_file | quote }} -storepass {{ keycloak_quarkus_config_key_store_password | quote }}
fi
echo {{ item.value | quote }} | keytool -noprompt -importpass -alias {{ item.key | quote }} -keystore {{ keycloak_quarkus_config_key_store_file | quote }} -storepass {{ keycloak_quarkus_config_key_store_password | quote }} -storetype PKCS12
loop: "{{ keycloak_quarkus_store_items }}"
no_log: true
become: "{{ keycloak_quarkus_config_store_require_privilege_escalation | default(true) }}"
changed_when: true
notify:
- restart keycloak
- name: "Set owner of configuration key store {{ keycloak_quarkus_config_key_store_file }}"
ansible.builtin.file:
path: "{{ keycloak_quarkus_config_key_store_file }}"
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
mode: '0400'
become: "{{ keycloak_quarkus_config_store_require_privilege_escalation | default(true) }}"

View file

@ -0,0 +1,10 @@
---
- name: Include firewall config tasks
ansible.builtin.include_tasks:
file: iptables.yml
apply:
tags:
- firewall
when: keycloak_quarkus_configure_iptables
tags:
- firewall

View file

@ -0,0 +1,162 @@
---
- name: Check deprecation keycloak_quarkus_key_store -> keycloak_quarkus_http_key_store
delegate_to: localhost
run_once: true
when:
- keycloak_quarkus_https_key_store_enabled
block:
- name: Ensure backward compatibility for `keycloak_quarkus_key_store_file`, superseded by `keycloak_quarkus_https_key_store_file`
when:
- keycloak_quarkus_key_store_file is defined
- keycloak_quarkus_key_store_file != ''
- keycloak_quarkus_https_key_store_file == keycloak.home + "/conf/key_store.p12" # default value
changed_when: keycloak_quarkus_show_deprecation_warnings
ansible.builtin.set_fact:
keycloak_quarkus_https_key_store_file: "{{ keycloak_quarkus_key_store_file }}"
keycloak_quarkus_deprecated_variable: "keycloak_quarkus_key_store_file" # read in deprecation handler
notify:
- print deprecation warning
- name: Flush handlers
ansible.builtin.meta: flush_handlers
- name: Ensure backward compatibility for `keycloak_quarkus_key_store_password`, superseded by `keycloak_quarkus_https_key_store_password`
when:
- keycloak_quarkus_key_store_password is defined
- keycloak_quarkus_key_store_password != ''
- keycloak_quarkus_https_key_store_password == "" # default value
changed_when: keycloak_quarkus_show_deprecation_warnings
ansible.builtin.set_fact:
keycloak_quarkus_https_key_store_password: "{{ keycloak_quarkus_key_store_password }}"
keycloak_quarkus_deprecated_variable: "keycloak_quarkus_key_store_password" # read in deprecation handler
notify:
- print deprecation warning
- name: Flush handlers
ansible.builtin.meta: flush_handlers
# https://access.redhat.com/documentation/en-us/red_hat_build_of_keycloak/24.0/html-single/upgrading_guide/index#deprecated_literal_proxy_literal_option
- name: Check deprecation of keycloak_quarkus_proxy_mode
when:
- keycloak_quarkus_proxy_mode is defined
- keycloak_quarkus_proxy_headers is defined and keycloak_quarkus_proxy_headers | length == 0
- keycloak_quarkus_version.split('.') | first | int >= 24
delegate_to: localhost
run_once: true
changed_when: keycloak_quarkus_show_deprecation_warnings
ansible.builtin.set_fact:
keycloak_quarkus_deprecated_variable: "keycloak_quarkus_proxy_mode" # read in deprecation handler
notify:
- print deprecation warning
# https://docs.redhat.com/en/documentation/red_hat_build_of_keycloak/26.0/html-single/upgrading_guide/index#new_hostname_options
- name: Check deprecation of keycloak_quarkus_frontend_url -> keycloak_quarkus_hostname
when:
- keycloak_quarkus_hostname is not defined
- keycloak_quarkus_frontend_url is defined
- keycloak_quarkus_frontend_url != ''
delegate_to: localhost
run_once: true
changed_when: keycloak_quarkus_show_deprecation_warnings
ansible.builtin.set_fact:
keycloak_quarkus_hostname: "{{ keycloak_quarkus_frontend_url }}"
keycloak_quarkus_deprecated_variable: "keycloak_quarkus_frontend_url" # read in deprecation handler
notify:
- print deprecation warning
# https://docs.redhat.com/en/documentation/red_hat_build_of_keycloak/26.0/html-single/upgrading_guide/index#new_hostname_options
- name: Check deprecation of keycloak_quarkus_hostname_strict_https + keycloak_quarkus_host + keycloak_quarkus_port + keycloak_quarkus_path -> keycloak_quarkus_hostname
when:
- keycloak_quarkus_hostname is not defined
- keycloak_quarkus_hostname_strict_https is defined or keycloak_quarkus_frontend_url is defined or keycloak_quarkus_port is defined or keycloak_quarkus_path is defined
delegate_to: localhost
run_once: true
changed_when: keycloak_quarkus_show_deprecation_warnings
ansible.builtin.set_fact:
keycloak_quarkus_hostname: >-
{% set protocol = '' %}
{% if keycloak_quarkus_hostname_strict_https %}
{% set protocol = 'https://' %}
{% elif keycloak_quarkus_hostname_strict_https is defined and keycloak_quarkus_hostname_strict_https is False %}
{% set protocol = 'http://' %}
{% endif %}
{{ protocol }}{{ keycloak_quarkus_host }}:{{ keycloak_quarkus_port }}/{{ keycloak_quarkus_path }}
keycloak_quarkus_deprecated_variable: "keycloak_quarkus_hostname_strict_https or keycloak_quarkus_frontend_url or keycloak_quarkus_frontend_url or keycloak_quarkus_hostname" # read in deprecation handler
notify:
- print deprecation warning
# https://docs.redhat.com/en/documentation/red_hat_build_of_keycloak/26.0/html-single/upgrading_guide/index#new_hostname_options
- name: Check deprecation of keycloak_quarkus_admin_url -> keycloak_quarkus_hostname_admin
when:
- keycloak_quarkus_hostname_admin is not defined
- keycloak_quarkus_admin_url is defined
- keycloak_quarkus_admin_url != ''
delegate_to: localhost
run_once: true
changed_when: keycloak_quarkus_show_deprecation_warnings
ansible.builtin.set_fact:
keycloak_quarkus_hostname_admin: "{{ keycloak_quarkus_admin_url }}"
keycloak_quarkus_deprecated_variable: "keycloak_quarkus_admin_url" # read in deprecation handler
notify:
- print deprecation warning
# https://docs.redhat.com/en/documentation/red_hat_build_of_keycloak/26.0/html-single/upgrading_guide/index#new_hostname_options
- name: Check deprecation of keycloak_quarkus_hostname_strict_backchannel -> keycloak_quarkus_hostname_backchannel_dynamic
when:
- keycloak_quarkus_hostname_backchannel_dynamic is not defined
- keycloak_quarkus_hostname_strict_backchannel is defined
- keycloak_quarkus_hostname_strict_backchannel != ''
delegate_to: localhost
run_once: true
changed_when: keycloak_quarkus_show_deprecation_warnings
ansible.builtin.set_fact:
keycloak_quarkus_hostname_backchannel_dynamic: "{{ keycloak_quarkus_hostname_strict_backchannel == False }}"
keycloak_quarkus_deprecated_variable: "keycloak_quarkus_hostname_backchannel_dynamic" # read in deprecation handler
notify:
- print deprecation warning
# https://github.com/keycloak/keycloak/issues/30009
- name: Check deprecation of keycloak_quarkus_admin_user -> keycloak_quarkus_bootstrap_admin_user
when:
- keycloak_quarkus_bootstrap_admin_user is not defined
- keycloak_quarkus_admin_user is defined
- keycloak_quarkus_admin_user != ''
delegate_to: localhost
run_once: true
changed_when: keycloak_quarkus_show_deprecation_warnings
ansible.builtin.set_fact:
keycloak_quarkus_bootstrap_admin_user: "{{ keycloak_quarkus_admin_user }}"
keycloak_quarkus_deprecated_variable: "keycloak_quarkus_admin_user" # read in deprecation handler
notify:
- print deprecation warning
# https://github.com/keycloak/keycloak/issues/30009
- name: Check deprecation of keycloak_quarkus_admin_pass -> keycloak_quarkus_bootstrap_admin_password
when:
- keycloak_quarkus_bootstrap_admin_password is not defined
- keycloak_quarkus_admin_pass is defined
- keycloak_quarkus_admin_pass != ''
delegate_to: localhost
run_once: true
changed_when: keycloak_quarkus_show_deprecation_warnings
ansible.builtin.set_fact:
keycloak_quarkus_bootstrap_admin_user: "{{ keycloak_quarkus_admin_pass }}"
keycloak_quarkus_deprecated_variable: "keycloak_quarkus_admin_pass" # read in deprecation handler
notify:
- print deprecation warning
- name: Check deprecation of keycloak_quarkus_bind_address -> keycloak_quarkus_http_host
when:
- keycloak_quarkus_bind_address is defined
- keycloak_quarkus_bind_address != '0.0.0.0'
delegate_to: localhost
run_once: true
changed_when: keycloak_quarkus_show_deprecation_warnings
ansible.builtin.set_fact:
keycloak_quarkus_http_host: "{{ keycloak_quarkus_bind_address }}"
keycloak_quarkus_deprecated_variable: "keycloak_quarkus_bind_address" # read in deprecation handler
notify:
- print deprecation warning
- name: Flush handlers
ansible.builtin.meta: flush_handlers

View file

@ -0,0 +1,31 @@
---
- name: "Check if packages are already installed" # noqa command-instead-of-module this runs faster
ansible.builtin.command: "rpm -q {{ packages_list | join(' ') }}"
register: keycloak_quarkus_rpm_info
changed_when: false
failed_when: false
when: ansible_facts.os_family == "RedHat"
- name: "Add missing packages to the yum install list"
ansible.builtin.set_fact:
keycloak_quarkus_packages_to_install: "{{ keycloak_quarkus_packages_to_install | default([]) + keycloak_quarkus_rpm_info.stdout_lines | \
map('regex_findall', 'package (.+) is not installed$') | default([]) | flatten }}"
when: ansible_facts.os_family == "RedHat"
- name: "Install packages: {{ keycloak_quarkus_packages_to_install }}"
become: "{{ keycloak_quarkus_fastpackages_require_privilege_escalation | default(true) }}"
ansible.builtin.dnf:
name: "{{ keycloak_quarkus_packages_to_install }}"
state: present
when:
- keycloak_quarkus_packages_to_install | default([]) | length > 0
- ansible_facts.os_family == "RedHat"
- name: "Install packages: {{ packages_list }}"
become: "{{ keycloak_quarkus_fastpackages_require_privilege_escalation | default(true) }}"
ansible.builtin.package:
name: "{{ packages_list }}"
state: present
when:
- packages_list | default([]) | length > 0
- ansible_facts.os_family == "Debian"

View file

@ -0,0 +1,36 @@
---
- name: Ensure required package firewalld are installed
ansible.builtin.include_tasks: fastpackages.yml
vars:
packages_list:
- firewalld
- name: Enable and start the firewalld service
become: "{{ keycloak_quarkus_firewalld_require_privilege_escalation | default(true) }}"
ansible.builtin.systemd:
name: firewalld
enabled: true
state: started
- name: "Configure firewall for http port: {{ keycloak.service_name }}"
become: "{{ keycloak_quarkus_firewalld_require_privilege_escalation | default(true) }}"
ansible.posix.firewalld:
port: "{{ item }}"
permanent: true
state: enabled
immediate: true
loop:
- "{{ keycloak_quarkus_http_port }}/tcp"
when: keycloak_quarkus_http_enabled | bool
- name: "Configure firewall for ports: {{ keycloak.service_name }}"
become: "{{ keycloak_quarkus_firewalld_require_privilege_escalation | default(true) }}"
ansible.posix.firewalld:
port: "{{ item }}"
permanent: true
state: enabled
immediate: true
loop:
- "{{ keycloak_quarkus_https_port }}/tcp"
- "{{ keycloak_quarkus_http_management_port }}/tcp"
- "{{ keycloak_quarkus_jgroups_port }}/tcp"

View file

@ -0,0 +1,319 @@
---
- name: Validate parameters
ansible.builtin.assert:
that:
- keycloak.home is defined
- keycloak_quarkus_service_user is defined
- keycloak_quarkus_dest is defined
- keycloak_quarkus_archive is defined
- keycloak_quarkus_download_url is defined
- keycloak_quarkus_version is defined
- keycloak_quarkus_local_path is defined
quiet: true
- name: Check for an existing deployment
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
ansible.builtin.stat:
path: "{{ keycloak.home }}"
register: keycloak_quarkus_existing_deploy
- name: Stop and restart if existing deployment exists and install forced
when: keycloak_quarkus_existing_deploy.stat.exists and keycloak_quarkus_force_install | bool
block:
- name: "Stop the old service: {{ keycloak.service_name }}"
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
failed_when: false
ansible.builtin.systemd:
name: keycloak
state: stopped
- name: "Remove the old deployment: {{ keycloak.service_name }}"
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
ansible.builtin.file:
path: "{{ keycloak_quarkus_home }}"
state: absent
- name: Check for an existing deployment after possible forced removal
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
ansible.builtin.stat:
path: "{{ keycloak_quarkus_home }}"
register: keycloak_quarkus_existing_deploy
- name: "Create service user/group: {{ keycloak.service_name }}"
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
ansible.builtin.user:
name: "{{ keycloak.service_user }}"
home: /opt/keycloak
system: true
create_home: false
- name: "Create install location: {{ keycloak.service_name }}"
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
ansible.builtin.file:
dest: "{{ keycloak_quarkus_dest }}"
state: directory
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
mode: '0750'
- name: Create directory for ansible custom facts
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
ansible.builtin.file:
state: directory
recurse: true
path: /etc/ansible/facts.d
## check remote archive
- name: Set download archive path
ansible.builtin.set_fact:
keycloak_quarkus_archive_full_path: "{{ keycloak_quarkus_dest }}/{{ keycloak.bundle }}"
- name: Check download archive path
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
ansible.builtin.stat:
path: "{{ keycloak_quarkus_archive_full_path }}"
register: keycloak_quarkus_archive_path
## download to controller
- name: Download keycloak archive
ansible.builtin.get_url: # noqa risky-file-permissions delegated, uses controller host user
url: "{{ keycloak_quarkus_download_url }}"
dest: "{{ keycloak_quarkus_local_path.stat.path }}/{{ keycloak.bundle }}"
mode: '0640'
url_username: "{{ keycloak_quarkus_binary_download_user | default(omit) }}"
url_password: "{{ keycloak_quarkus_binary_download_pass | default(omit) }}"
validate_certs: "{{ keycloak_quarkus_download_validate_certs | default(omit) }}"
delegate_to: localhost
become: false
run_once: true
when:
- keycloak_quarkus_archive_path is defined
- keycloak_quarkus_archive_path.stat is defined
- not keycloak_quarkus_archive_path.stat.exists
- not keycloak.offline_install
- not rhbk_enable is defined or not rhbk_enable
- name: Perform download from RHN using the Unified Downloads API
delegate_to: localhost
run_once: true
when:
- keycloak_quarkus_archive_path is defined
- keycloak_quarkus_archive_path.stat is defined
- not keycloak_quarkus_archive_path.stat.exists
- rhbk_enable is defined and rhbk_enable
- not keycloak.offline_install
- keycloak_quarkus_alternate_download_url is undefined
block:
- name: Retrieve product download using Unified Downloads API
middleware_automation.common.product_search:
client_id: "{{ rhn_username }}"
client_secret: "{{ rhn_password }}"
product_type: DISTRIBUTION
product_version: "{{ rhbk_version }}"
product_category: "{{ rhbk_product_category }}"
validate_certs: "{{ keycloak_quarkus_download_validate_certs | default(omit) }}"
register: keycloak_quarkus_rhn_products
no_log: "{{ omit_rhn_output | default(true) }}"
delegate_to: localhost
run_once: true
- name: Determine install zipfile from search results
ansible.builtin.set_fact:
keycloak_quarkus_rhn_filtered_products: "{{ keycloak_quarkus_rhn_products.results | selectattr('file_name', 'match', '[^/]*/' + rhbk_archive + '$') }}"
delegate_to: localhost
run_once: true
- name: Download Red Hat Build of Keycloak
middleware_automation.common.product_download: # noqa risky-file-permissions delegated, uses controller host user
client_id: "{{ rhn_username }}"
client_secret: "{{ rhn_password }}"
product_id: "{{ (keycloak_quarkus_rhn_filtered_products | first).id }}"
dest: "{{ keycloak_quarkus_local_path.stat.path }}/{{ keycloak.bundle }}"
mode: '0640'
validate_certs: "{{ keycloak_quarkus_download_validate_certs | default(omit) }}"
no_log: "{{ omit_rhn_output | default(true) }}"
delegate_to: localhost
run_once: true
become: false
- name: Perform download of RHBK from alternate download location
delegate_to: localhost
run_once: true
become: false
when:
- keycloak_quarkus_archive_path is defined
- keycloak_quarkus_archive_path.stat is defined
- not keycloak_quarkus_archive_path.stat.exists
- rhbk_enable is defined and rhbk_enable
- not keycloak.offline_install
- keycloak_quarkus_alternate_download_url is defined
ansible.builtin.get_url: # noqa risky-file-permissions delegated, uses controller host user
url: "{{ keycloak_quarkus_alternate_download_url }}"
dest: "{{ keycloak_quarkus_local_path.stat.path }}/{{ keycloak.bundle }}"
mode: '0640'
url_username: "{{ keycloak_quarkus_download_user | default(omit) }}"
url_password: "{{ keycloak_quarkus_download_pass | default(omit) }}"
validate_certs: "{{ keycloak_quarkus_download_validate_certs | default(omit) }}"
- name: Check downloaded archive
ansible.builtin.stat:
path: "{{ keycloak_quarkus_local_path.stat.path }}/{{ keycloak.bundle }}"
register: keycloak_quarkus_local_archive_path
delegate_to: localhost
become: false
run_once: true
## copy and unpack
- name: Copy archive to target nodes
ansible.builtin.copy:
src: "{{ keycloak_quarkus_local_path.stat.path }}/{{ keycloak.bundle }}"
dest: "{{ keycloak_quarkus_archive_full_path }}"
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
mode: '0640'
register: keycloak_quarkus_new_version_downloaded
when:
- not keycloak_quarkus_archive_path.stat.exists
- keycloak_quarkus_local_archive_path.stat is defined
- keycloak_quarkus_local_archive_path.stat.exists
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
- name: "Check target directory /bin/: {{ keycloak.home }}"
ansible.builtin.stat:
path: "{{ keycloak.home }}/bin/"
register: keycloak_quarkus_path_to_workdir
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
- name: "Extract Keycloak archive on target" # noqa no-handler need to run this here
ansible.builtin.unarchive:
remote_src: true
src: "{{ keycloak_quarkus_archive_full_path }}"
dest: "{{ keycloak_quarkus_dest }}"
creates: "{{ keycloak.home }}/bin/"
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
when:
- (not keycloak_quarkus_path_to_workdir.stat.exists) or keycloak_quarkus_new_version_downloaded.changed
notify:
- restart keycloak
- name: Inform decompression was not executed
ansible.builtin.debug:
msg: "{{ keycloak.home }} already exists and version unchanged, skipping decompression"
when:
- (not keycloak_quarkus_new_version_downloaded.changed) and keycloak_quarkus_path_to_workdir.stat.exists
- name: "Copy private key to target"
ansible.builtin.copy:
content: "{{ keycloak_quarkus_key_content }}"
dest: "{{ keycloak_quarkus_key_file }}"
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
mode: '0640'
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
when:
- keycloak_quarkus_https_key_file_enabled is defined and keycloak_quarkus_https_key_file_enabled
- keycloak_quarkus_key_file_copy_enabled is defined and keycloak_quarkus_key_file_copy_enabled
- keycloak_quarkus_key_content | length > 0
- name: "Copy certificate to target"
ansible.builtin.copy:
src: "{{ keycloak_quarkus_cert_file_src }}"
dest: "{{ keycloak_quarkus_cert_file }}"
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
mode: '0644'
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
when:
- keycloak_quarkus_https_key_file_enabled is defined and keycloak_quarkus_https_key_file_enabled
- keycloak_quarkus_cert_file_copy_enabled is defined and keycloak_quarkus_cert_file_copy_enabled
- keycloak_quarkus_cert_file_src | length > 0
- name: "Install JDBC driver: {{ keycloak_quarkus_db_engine }}"
ansible.builtin.include_tasks: jdbc_driver.yml
when:
- rhbk_enable is defined and rhbk_enable
- keycloak_quarkus_default_jdbc[keycloak_quarkus_db_engine].driver_jar_url is defined
- name: "Download custom providers via http"
ansible.builtin.get_url:
url: "{{ item.url }}"
dest: "{{ keycloak.home }}/providers/{{ item.id }}.jar"
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
mode: '0640'
checksum: "{{ item.checksum | default(omit) }}"
validate_certs: "{{ keycloak_quarkus_download_validate_certs | default(omit) }}"
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
loop: "{{ keycloak_quarkus_providers }}"
when: item.url is defined and item.url | length > 0
no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}"
notify: "{{ ['invalidate keycloak theme cache', 'rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}"
# this requires the `lxml` package to be installed; we redirect this step to localhost such that we do need to install it on the remote hosts
- name: "Download custom providers to localhost using maven"
middleware_automation.common.maven_artifact:
repository_url: "{{ item.maven.repository_url }}"
group_id: "{{ item.maven.group_id }}"
artifact_id: "{{ item.maven.artifact_id }}"
version: "{{ item.maven.version | default(omit) }}"
username: "{{ item.maven.username | default(omit) }}"
password: "{{ item.maven.password | default(omit) }}"
dest: "{{ keycloak_quarkus_local_path.stat.path }}/{{ item.id }}.jar"
validate_certs: "{{ keycloak_quarkus_download_validate_certs | default(omit) }}"
delegate_to: "localhost"
run_once: true
loop: "{{ keycloak_quarkus_providers }}"
when: item.maven is defined
no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}"
- name: "Copy maven providers"
ansible.builtin.copy:
src: "{{ keycloak_quarkus_local_path.stat.path }}/{{ item.id }}.jar"
dest: "{{ keycloak.home }}/providers/{{ item.id }}.jar"
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
mode: '0640'
checksum: "{{ item.checksum | default(omit) }}"
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
loop: "{{ keycloak_quarkus_providers }}"
when: item.maven is defined
no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}"
notify: "{{ ['invalidate keycloak theme cache', 'rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}"
- name: "Copy local providers"
ansible.builtin.copy:
src: "{{ item.local_path }}"
dest: "{{ keycloak.home }}/providers/{{ item.id }}.jar"
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
mode: '0640'
remote_src: "{{ item.remote | default(false) }}"
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
loop: "{{ keycloak_quarkus_providers }}"
when: item.local_path is defined
no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}"
notify: "{{ ['invalidate keycloak theme cache', 'rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}"
- name: Ensure required folder structure for policies exists
ansible.builtin.file:
path: "{{ keycloak.home }}/data/{{ item | lower }}"
state: directory
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
mode: '0750'
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
loop: "{{ keycloak_quarkus_supported_policy_types }}"
- name: "Install custom policies"
ansible.builtin.get_url:
url: "{{ item.url }}"
dest: "{{ keycloak.home }}/data/{{ item.type | default(keycloak_quarkus_supported_policy_types | first) | lower }}/{{ item.name }}"
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
mode: '0640'
validate_certs: "{{ keycloak_quarkus_download_validate_certs | default(omit) }}"
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
loop: "{{ keycloak_quarkus_policies }}"
when: item.url is defined and item.url | length > 0
notify: "restart keycloak"

View file

@ -0,0 +1,11 @@
---
# From https://docs.redhat.com/en/documentation/red_hat_build_of_keycloak/24.0/html/server_developer_guide/themes#creating_a_theme:
# If you want to manually delete the content of the themes cache,
# you can do so by deleting the data/tmp/kc-gzip-cache directory of the server distribution
# It can be useful for instance if you redeployed custom providers or custom themes without
# disabling themes caching in the previous server executions.
- name: "Delete theme cache directory: {{ keycloak.service_name }}"
ansible.builtin.file:
path: "{{ keycloak.home }}/data/tmp/kc-gzip-cache"
state: absent
become: "{{ keycloak_quarkus_invalidate_theme_cache_require_privilege_escalation | default(true) }}"

View file

@ -0,0 +1,20 @@
---
- name: Ensure required package iptables are installed
ansible.builtin.include_tasks: fastpackages.yml
vars:
packages_list:
- iptables
- name: "Configure firewall ports for {{ keycloak.service_name }}"
become: "{{ keycloak_quarkus_iptables_require_privilege_escalation | default(true) }}"
ansible.builtin.iptables:
destination_port: "{{ item }}"
action: "insert"
rule_num: 6 # magic number I forget why
chain: "INPUT"
policy: "ACCEPT"
protocol: tcp
loop:
- "{{ keycloak_quarkus_http_port }}"
- "{{ keycloak_quarkus_https_port }}"
- "{{ keycloak_quarkus_jgroups_port }}"

View file

@ -0,0 +1,22 @@
---
- name: "Verify valid parameters for download credentials when specified"
ansible.builtin.fail:
msg: >-
When JDBC driver download credentials are set, both the username and the password MUST be set
when: >
(keycloak_quarkus_jdbc_download_user is undefined and keycloak_quarkus_jdbc_download_pass is not undefined) or
(keycloak_quarkus_jdbc_download_pass is undefined and keycloak_quarkus_jdbc_download_user is not undefined)
- name: "Retrieve JDBC Driver from {{ keycloak_jdbc_download_url | default(keycloak_quarkus_default_jdbc[keycloak_quarkus_db_engine].driver_jar_url) }}"
ansible.builtin.get_url:
url: "{{ keycloak_quarkus_jdbc_download_url | default(keycloak_quarkus_default_jdbc[keycloak_quarkus_db_engine].driver_jar_url) }}"
dest: "{{ keycloak.home }}/providers"
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
url_username: "{{ keycloak_quarkus_jdbc_download_user | default(omit) }}"
url_password: "{{ keycloak_quarkus_jdbc_download_pass | default(omit) }}"
validate_certs: "{{ keycloak_quarkus_jdbc_download_validate_certs | default(omit) }}"
mode: '0640'
become: "{{ keycloak_quarkus_jdbc_driver_require_privilege_escalation | default(true) }}"
notify:
- restart keycloak

View file

@ -0,0 +1,139 @@
---
# tasks file for keycloak
- name: Check prerequisites
ansible.builtin.include_tasks:
file: prereqs.yml
apply:
tags:
- prereqs
tags:
- prereqs
- always
- name: Check for deprecations
ansible.builtin.include_tasks:
file: deprecations.yml
apply:
tags:
- always
tags:
- always
- name: Distro specific tasks
ansible.builtin.include_tasks:
file: "{{ ansible_os_family | lower }}.yml"
apply:
tags:
- unbound
tags:
- unbound
- name: Include install tasks
ansible.builtin.include_tasks:
file: install.yml
apply:
tags:
- install
tags:
- install
- name: Include systemd tasks
ansible.builtin.include_tasks:
file: systemd.yml
apply:
tags:
- systemd
tags:
- systemd
- name: Include configuration key store tasks
ansible.builtin.include_tasks:
file: config_store.yml
apply:
tags:
- install
when: keycloak.config_key_store_enabled
tags:
- install
- name: Create tcpping cluster node list
ansible.builtin.set_fact:
keycloak_quarkus_cluster_nodes: >
{{ keycloak_quarkus_cluster_nodes | default([]) + [
{
"name": item,
"address": 'jgroups-' + item,
"inventory_host": hostvars[item].keycloak_quarkus_jgroups_ip | default(item) + '[' + (keycloak_quarkus_jgroups_port | string) + ']',
"value": hostvars[item].keycloak_quarkus_jgroups_ip | default(item)
}
] }}
loop: "{{ ansible_play_batch }}"
when: keycloak_quarkus_ha_enabled and keycloak_quarkus_ha_discovery == 'TCPPING'
- name: Determine the config files
ansible.builtin.set_fact:
keycloak_quarkus_config_files: "{{ ['keycloak.conf', 'quarkus.properties'] + (keycloak_quarkus_cache_managed_infinispan_config | ternary([keycloak_quarkus_cache_infinispan_template], [])) }}"
- name: "Configure config files for keycloak service"
ansible.builtin.template:
src: "{{ item }}.j2"
dest: "{{ keycloak.home }}/conf/{{ item }}"
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
mode: '0640'
become: "{{ keycloak_quarkus_require_privilege_escalation | default(true) }}"
loop: "{{ keycloak_quarkus_config_files }}"
notify:
- rebuild keycloak config
- restart keycloak
- name: Ensure logdirectory exists
ansible.builtin.file:
state: directory
path: "{{ keycloak.log.file | dirname }}"
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
mode: '0775'
become: "{{ keycloak_quarkus_require_privilege_escalation | default(true) }}"
- name: Ensure tmp-directory exists
ansible.builtin.file:
state: directory
path: "{{ keycloak.home }}/data/tmp"
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
mode: '0755'
become: "{{ keycloak_quarkus_require_privilege_escalation | default(true) }}"
- name: Flush pending handlers
ansible.builtin.meta: flush_handlers
- name: "Start and wait for keycloak service"
ansible.builtin.include_tasks: start.yml
- name: Link default logs directory
ansible.builtin.file:
state: link
src: "{{ keycloak.log.file | dirname }}"
dest: "{{ keycloak_quarkus_log_target }}"
force: true
become: "{{ keycloak_quarkus_require_privilege_escalation | default(true) }}"
- name: Check service status
ansible.builtin.systemd_service:
name: "{{ keycloak.service_name }}"
register: keycloak_quarkus_service_status
changed_when: false
- name: "Notify to remove `keycloak_quarkus_bootstrap_admin_user[_password]` env vars"
when:
- not ansible_local.keycloak.general.bootstrapped | default(false) | bool # it was not bootstrapped prior to the current role's execution
- keycloak_quarkus_service_status.status.ActiveState == "active" # but it is now
ansible.builtin.assert: { that: true, quiet: true }
changed_when: true
notify:
- bootstrapped
- name: Flush pending handlers
ansible.builtin.meta: flush_handlers

View file

@ -0,0 +1,154 @@
---
- name: Validate admin console password
ansible.builtin.assert:
that:
- keycloak_quarkus_bootstrap_admin_password is defined
- keycloak_quarkus_bootstrap_admin_password is not none
- keycloak_quarkus_bootstrap_admin_password | length > 12
quiet: true
fail_msg: "The console administrator password is empty or invalid. Please set the keycloak_quarkus_bootstrap_admin_password to a 12+ char long string"
success_msg: "{{ 'Console administrator password OK' }}"
- name: Validate http_relative_path
ansible.builtin.assert:
that:
- keycloak_quarkus_http_relative_path is regex('^/.*')
quiet: true
fail_msg: "The relative path for keycloak_quarkus_http_relative_path must begin with /"
success_msg: "{{ 'Relative path OK' }}"
- name: Validate http_management_relative_path
ansible.builtin.assert:
that:
- keycloak_quarkus_http_management_relative_path is regex('^/.*')
quiet: true
fail_msg: "The relative path for keycloak_quarkus_http_management_relative_path must begin with /"
success_msg: "{{ 'Relative mgmt path OK' }}"
when: keycloak_quarkus_http_management_relative_path is defined
- name: Validate configuration
ansible.builtin.assert:
that:
- (keycloak_quarkus_ha_enabled and keycloak_quarkus_db_enabled) or
(not keycloak_quarkus_ha_enabled and keycloak_quarkus_db_enabled) or
(not keycloak_quarkus_ha_enabled and not keycloak_quarkus_db_enabled)
quiet: true
fail_msg: "HA setup requires a backend database service. Check keycloak_quarkus_ha_enabled and keycloak_quarkus_db_enabled"
success_msg: "{{ 'Configuring HA' if keycloak_quarkus_ha_enabled else 'Configuring standalone' }}"
- name: Validate OS family
ansible.builtin.assert:
that:
- ansible_os_family in ["RedHat", "Debian"]
quiet: true
fail_msg: "Can only install on RedHat or Debian OS families; found {{ ansible_os_family }}"
success_msg: "Installing on {{ ansible_os_family }}"
- name: Load OS specific variables
ansible.builtin.include_vars: "vars/{{ ansible_os_family | lower }}.yml"
tags:
- always
- name: Ensure required packages are installed
ansible.builtin.include_tasks: fastpackages.yml
vars:
packages_list: "{{ keycloak_quarkus_prereq_package_list }}"
- name: Check local download archive path
ansible.builtin.stat:
path: "{{ keycloak_quarkus_download_path }}"
register: keycloak_quarkus_local_path
delegate_to: localhost
run_once: true
become: false
- name: Validate local download path
ansible.builtin.assert:
that:
- keycloak_quarkus_local_path.stat.exists
- keycloak_quarkus_local_path.stat.readable
- keycloak_quarkus_offline_install or keycloak_quarkus_local_path.stat.writeable
quiet: true
fail_msg: "Defined controller path for downloading resources is incorrect or unreadable: {{ keycloak_quarkus_download_path }}"
success_msg: "Will download resource to controller path: {{ keycloak_quarkus_download_path }}"
delegate_to: localhost
run_once: true
- name: Check downloaded archive if offline
ansible.builtin.stat:
path: "{{ keycloak_quarkus_local_path.stat.path }}/{{ keycloak.bundle }}"
when: keycloak_quarkus_offline_install
register: keycloak_quarkus_local_archive_path_check
delegate_to: localhost
run_once: true
- name: Validate local downloaded archive if offline
ansible.builtin.assert:
that:
- keycloak_quarkus_local_archive_path_check.stat.exists
- keycloak_quarkus_local_archive_path_check.stat.readable
quiet: true
fail_msg: "Configured for offline install but install archive not found at: {{ keycloak_quarkus_local_path.stat.path }}/{{ keycloak.bundle }}"
success_msg: "Will install offline with expected archive: {{ keycloak_quarkus_local_path.stat.path }}/{{ keycloak.bundle }}"
when: keycloak_quarkus_offline_install
delegate_to: localhost
run_once: true
- name: "Validate keytool"
when: keycloak_quarkus_config_key_store_password | length > 0
block:
- name: "Check run keytool"
changed_when: false
ansible.builtin.command: keytool -help
register: keycloak_quarkus_keytool_check
ignore_errors: true
- name: "Fail when no keytool found"
when: keycloak_quarkus_keytool_check.rc != 0
ansible.builtin.fail:
msg: "keytool NOT found in the PATH, but is required for setting up the configuration key store"
- name: "Validate providers"
ansible.builtin.assert:
that: >
item.id is defined and item.id | length > 0 and
( (item.spi is defined and item.spi | length > 0) or
(item.url is defined and item.url | length > 0) or
( item.maven is defined and item.maven.repository_url is defined and item.maven.repository_url | length > 0 and
item.maven.group_id is defined and item.maven.group_id | length > 0 and
item.maven.artifact_id is defined and item.maven.artifact_id | length > 0) or
(item.local_path is defined and item.local_path | length > 0)
)
quiet: true
fail_msg: >
Providers definition incorrect; `id` and one of `spi`, `url`, `local_path`, or `maven` are mandatory. `key` and `value` are mandatory for each property
loop: "{{ keycloak_quarkus_providers }}"
no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}"
- name: "Validate policies"
ansible.builtin.assert:
that:
- item.name is defined and item.name | length > 0
- item.url is defined and item.url | length > 0
- item.type is not defined or item.type | lower in keycloak_quarkus_supported_policy_types
quiet: true
fail_msg: >
Policy definition is incorrect: `name` and one of `url` are mandatory, `type` needs to be left empty or one of {{ keycloak_quarkus_supported_policy_types }}.
loop: "{{ keycloak_quarkus_policies }}"
- name: "Validate additional env variables"
ansible.builtin.assert:
that:
- item.key is defined and item.key | length > 0
- item.value is defined and item.value | length > 0
quiet: true
fail_msg: "Additional env variable definition is incorrect: `key` and `value` are mandatory."
no_log: true
loop: "{{ keycloak_quarkus_additional_env_vars }}"
- name: "Validate proxy-headers"
ansible.builtin.assert:
that:
- keycloak_quarkus_proxy_headers | lower in ['', 'forwarded', 'xforwarded']
quiet: true
fail_msg: "keycloak_quarkus_proxy_headers must be either '', 'forwarded' or 'xforwarded'"

View file

@ -0,0 +1,7 @@
---
# cf. https://www.keycloak.org/server/configuration#_optimize_the_keycloak_startup
- name: "Rebuild config: {{ keycloak.service_name }}"
ansible.builtin.shell: | # noqa blocked_modules shell is necessary here
env -i bash -c "set -a ; source {{ keycloak_quarkus_sysconf_file }} ; {{ keycloak.home }}/bin/kc.sh build "
become: "{{ keycloak_quarkus_rebuild_config_require_privilege_escalation | default(true) }}"
changed_when: true

View file

@ -0,0 +1,10 @@
---
- name: Include firewall config tasks
ansible.builtin.include_tasks:
file: firewalld.yml
apply:
tags:
- firewall
when: keycloak_quarkus_configure_firewalld
tags:
- firewall

View file

@ -0,0 +1,24 @@
---
- name: "Restart and enable service: {{ keycloak.service_name }}"
ansible.builtin.systemd:
name: "{{ keycloak.service_name }}"
enabled: true
state: restarted
daemon_reload: true
become: "{{ keycloak_quarkus_restart_require_privilege_escalation | default(true) }}"
- name: "Wait until service becomes active: {{ keycloak.service_name }}"
ansible.builtin.uri:
url: "{{ keycloak.health_url }}"
validate_certs: "{{ keycloak_quarkus_validate_certs }}"
register: keycloak_quarkus_status
until: keycloak_quarkus_status.status == 200
retries: "{{ keycloak_quarkus_restart_health_check_retries }}"
delay: "{{ keycloak_quarkus_restart_health_check_delay }}"
when: internal_force_health_check | default(keycloak_quarkus_restart_health_check)
- name: Wait to give distributed ispn caches time to (re-)replicate back onto first host
ansible.builtin.pause:
seconds: "{{ keycloak_quarkus_restart_pause }}"
when:
- keycloak_quarkus_ha_enabled

View file

@ -0,0 +1,4 @@
---
- name: "Display message"
ansible.builtin.debug:
msg: "keycloak_quarkus_restart_strategy is none, skipping restart"

View file

@ -0,0 +1,11 @@
---
- name: "Restart services in serial, with optional healtch check (keycloak_quarkus_restart_health_check)"
throttle: 1
block:
- name: "Restart and enable service: {{ keycloak.service_name }}"
ansible.builtin.include_tasks:
file: restart.yml
apply:
delegate_to: "{{ item }}"
run_once: true
loop: "{{ ansible_play_hosts }}"

View file

@ -0,0 +1,20 @@
---
- name: Verify first restarted service with health URL, then rest restart in parallel
block:
- name: "Restart and enable service on initial host: {{ keycloak.service_name }}"
ansible.builtin.include_tasks:
file: restart.yml
apply:
delegate_to: "{{ ansible_play_hosts | first }}"
run_once: true
vars:
internal_force_health_check: true
- name: "Restart and enable service on other hosts: {{ keycloak.service_name }}"
ansible.builtin.systemd:
name: "{{ keycloak.service_name }}"
enabled: true
state: restarted
daemon_reload: true
become: "{{ keycloak_quarkus_restart_require_privilege_escalation | default(true) }}"
when: inventory_hostname != ansible_play_hosts | first

View file

@ -0,0 +1,18 @@
---
- name: "Start service: {{ keycloak.service_name }}"
ansible.builtin.systemd:
name: keycloak
enabled: true
state: started
daemon_reload: true
become: "{{ keycloak_quarkus_start_require_privilege_escalation | default(true) }}"
- name: "Wait until service becomes active: {{ keycloak.service_name }}"
ansible.builtin.uri:
url: "{{ keycloak.health_url }}"
validate_certs: "{{ keycloak_quarkus_validate_certs }}"
register: keycloak_quarkus_status
until: keycloak_quarkus_status.status == 200
retries: 25
delay: 10
when: internal_force_health_check | default(keycloak_quarkus_restart_health_check)

View file

@ -0,0 +1,27 @@
---
- name: "Configure sysconfig file for service: {{ keycloak.service_name }}"
become: "{{ keycloak_quarkus_systemd_require_privilege_escalation | default(true) }}"
ansible.builtin.template:
src: keycloak-sysconfig.j2
dest: "{{ keycloak_quarkus_sysconf_file }}"
owner: root
group: root
mode: '0640'
vars:
keycloak_sys_pkg_java_home: "{{ keycloak_quarkus_pkg_java_home }}"
notify:
- rebuild keycloak config
- restart keycloak
- name: "Configure systemd unit file for keycloak service"
ansible.builtin.template:
src: keycloak.service.j2
dest: /etc/systemd/system/keycloak.service
owner: root
group: root
mode: '0644'
become: "{{ keycloak_quarkus_systemd_require_privilege_escalation | default(true) }}"
register: keycloak_quarkus_systemdunit
notify:
- rebuild keycloak config
- restart keycloak

View file

@ -0,0 +1,119 @@
{{ ansible_managed | comment('xml') }}
<!--
~ Copyright 2019 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<infinispan
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:infinispan:config:15.0 http://www.infinispan.org/schemas/infinispan-config-15.0.xsd"
xmlns="urn:infinispan:config:15.0">
{% set stack_expression='' %}
{% if keycloak_quarkus_version is version_compare('26.2.0', '<') %}
{% if keycloak_quarkus_ha_enabled %}
{% if keycloak_quarkus_ha_discovery == 'TCPPING' %}
{% set stack_expression='stack="tcpping"' %}
<jgroups>
<stack name="tcpping" extends="tcp">
<!-- <TCP external_addr="${env.KC_EXTERNAL_ADDR}" bind_addr="{{ keycloak_quarkus_http_host }}" bind_port="{{ keycloak_quarkus_jgroups_port }}" /> -->
<TCPPING
initial_hosts="{{ keycloak_quarkus_cluster_nodes | map(attribute='inventory_host') | join (',') }}"
port_range="0"
stack.combine="REPLACE"
stack.position="MPING"
/>
</stack>
</jgroups>
{% elif keycloak_quarkus_ha_discovery == 'JDBCPING' %}
{% set stack_expression='stack="JDBC_PING2"' %}
{% endif %}
{% endif %}
{% endif %}
<cache-container name="keycloak">
<transport lock-timeout="60000" {{ stack_expression }}/>
<local-cache name="realms" simple-cache="true">
<encoding>
<key media-type="application/x-java-object"/>
<value media-type="application/x-java-object"/>
</encoding>
<memory max-count="10000"/>
</local-cache>
<local-cache name="users" simple-cache="true">
<encoding>
<key media-type="application/x-java-object"/>
<value media-type="application/x-java-object"/>
</encoding>
<memory max-count="10000"/>
</local-cache>
<distributed-cache name="sessions" owners="2">
<expiration lifespan="-1"/>
<memory max-count="10000"/>
</distributed-cache>
<distributed-cache name="authenticationSessions" owners="2">
<expiration lifespan="-1"/>
</distributed-cache>
<distributed-cache name="offlineSessions" owners="2">
<expiration lifespan="-1"/>
<memory max-count="10000"/>
</distributed-cache>
<distributed-cache name="clientSessions" owners="2">
<expiration lifespan="-1"/>
<memory max-count="10000"/>
</distributed-cache>
<distributed-cache name="offlineClientSessions" owners="2">
<expiration lifespan="-1"/>
<memory max-count="10000"/>
</distributed-cache>
<distributed-cache name="loginFailures" owners="2">
<expiration lifespan="-1"/>
</distributed-cache>
<local-cache name="authorization" simple-cache="true">
<encoding>
<key media-type="application/x-java-object"/>
<value media-type="application/x-java-object"/>
</encoding>
<memory max-count="10000"/>
</local-cache>
<replicated-cache name="work">
<expiration lifespan="-1"/>
</replicated-cache>
<local-cache name="keys" simple-cache="true">
<encoding>
<key media-type="application/x-java-object"/>
<value media-type="application/x-java-object"/>
</encoding>
<expiration max-idle="3600000"/>
<memory max-count="1000"/>
</local-cache>
<local-cache name="crl" simple-cache="true">
<encoding>
<key media-type="application/x-java-object"/>
<value media-type="application/x-java-object"/>
</encoding>
<expiration lifespan="-1"/>
<memory max-count="1000"/>
</local-cache>
<distributed-cache name="actionTokens" owners="2">
<encoding>
<key media-type="application/x-java-object"/>
<value media-type="application/x-java-object"/>
</encoding>
<expiration max-idle="-1" lifespan="-1" interval="300000"/>
<memory max-count="-1"/>
</distributed-cache>
</cache-container>
</infinispan>

View file

@ -0,0 +1,15 @@
{{ ansible_managed | comment }}
{% if not ansible_local.keycloak.general.bootstrapped | default(false) | bool %}
KC_BOOTSTRAP_ADMIN_USERNAME={{ keycloak_quarkus_bootstrap_admin_user }}
KC_BOOTSTRAP_ADMIN_PASSWORD='{{ keycloak_quarkus_bootstrap_admin_password }}'
{% else %}
{{ keycloak.bootstrap_mnemonic }}
{% endif %}
PATH="{{ keycloak_quarkus_java_home | default(keycloak_sys_pkg_java_home, true) }}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
JAVA_HOME="{{ keycloak_quarkus_java_home | default(keycloak_sys_pkg_java_home, true) }}"
JAVA_OPTS="{{ keycloak_quarkus_java_opts }}"
# Custom ENV variables
{% for env in keycloak_quarkus_additional_env_vars %}
{{ env.key }}={{ env.value }}
{% endfor %}

View file

@ -0,0 +1,110 @@
{{ ansible_managed | comment }}
{% if keycloak_quarkus_db_enabled %}
# Database
db={{ keycloak_quarkus_db_engine }}
db-url={{ keycloak_quarkus_db_url }}
db-username={{ keycloak_quarkus_db_user }}
{% if not keycloak.config_key_store_enabled %}
db-password={{ keycloak_quarkus_db_pass }}
{% endif %}
{% endif %}
{% if keycloak.config_key_store_enabled %}
# Config store
config-keystore={{ keycloak_quarkus_config_key_store_file }}
config-keystore-password={{ keycloak_quarkus_config_key_store_password }}
{% endif %}
# Observability
metrics-enabled={{ keycloak_quarkus_metrics_enabled | lower }}
health-enabled={{ keycloak_quarkus_health_enabled | lower }}
# HTTP
http-enabled={{ keycloak_quarkus_http_enabled | lower }}
{% if keycloak_quarkus_http_enabled %}
http-port={{ keycloak_quarkus_http_port }}
{% endif %}
http-relative-path={{ keycloak_quarkus_http_relative_path }}
http-host={{ keycloak_quarkus_http_host }}
# Management
http-management-port={{ keycloak_quarkus_http_management_port }}
{% if keycloak_quarkus_http_management_relative_path is defined and keycloak_quarkus_http_management_relative_path | length > 0 %}
http-management-relative-path={{ keycloak_quarkus_http_management_relative_path }}
{% endif %}
# HTTPS
https-port={{ keycloak_quarkus_https_port }}
{% if keycloak_quarkus_https_key_file_enabled %}
https-certificate-file={{ keycloak_quarkus_cert_file}}
https-certificate-key-file={{ keycloak_quarkus_key_file }}
{% endif %}
{% if keycloak_quarkus_https_key_store_enabled %}
https-key-store-file={{ keycloak_quarkus_https_key_store_file }}
https-key-store-password={{ keycloak_quarkus_https_key_store_password }}
{% endif %}
{% if keycloak_quarkus_https_trust_store_enabled %}
https-trust-store-file={{ keycloak_quarkus_https_trust_store_file }}
https-trust-store-password={{ keycloak_quarkus_https_trust_store_password }}
{% endif %}
# Client URL configuration
hostname={{ keycloak_quarkus_hostname }}
hostname-admin={{ keycloak_quarkus_hostname_admin }}
hostname-strict={{ keycloak_quarkus_hostname_strict | lower }}
hostname-backchannel-dynamic={{ keycloak_quarkus_hostname_backchannel_dynamic | lower }}
# Cluster
{% if keycloak_quarkus_ha_enabled %}
cache=ispn
{% if keycloak_quarkus_cache_managed_infinispan_config %}
cache-config-file=cache-ispn.xml
{% endif %}
{% if keycloak_quarkus_cache_remote %}
cache-remote-username={{ keycloak_quarkus_cache_remote_username }}
cache-remote-password={{ keycloak_quarkus_cache_remote_password }}
cache-remote-host={{ keycloak_quarkus_cache_remote_host }}
cache-remote-port={{ keycloak_quarkus_cache_remote_port }}
cache-remote-tls-enabled={{ keycloak_quarkus_cache_remote_tls_enabled | lower }}
{% endif %}
{{ keycloak_quarkus_cache_embedded_properties }}
{% endif %}
{% if keycloak_quarkus_proxy_headers | length > 0 %}
proxy-headers={{ keycloak_quarkus_proxy_headers | lower }}
{% elif keycloak_quarkus_proxy_mode is defined and keycloak_quarkus_proxy_mode != "none" %}
# Deprecated Proxy configuration
proxy={{ keycloak_quarkus_proxy_mode }}
{% endif %}
spi-sticky-session-encoder-infinispan-should-attach-route={{ keycloak_quarkus_spi_sticky_session_encoder_infinispan_should_attach_route | d(true) | lower }}
# Transaction
transaction-xa-enabled={{ keycloak_quarkus_transaction_xa_enabled | lower }}
# Logging
#log-format=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n
log={{ keycloak_quarkus_log }}
log-level={{ keycloak.log.level }}
log-file={{ keycloak.log.file }}
log-file-format={{ keycloak.log.format }}
# Vault
{% if keycloak_quarkus_ks_vault_enabled %}
vault=keystore
vault-file={{ keycloak_quarkus_ks_vault_file }}
vault-type={{ keycloak_quarkus_ks_vault_type }}
vault-pass={{ keycloak_quarkus_ks_vault_pass }}
{% endif %}
# Providers
{% for provider in keycloak_quarkus_providers %}
{% if provider.default is defined and provider.default %}
spi-{{ provider.spi }}-provider={{ provider.id }}
{% endif %}
{% if provider.properties is defined %}{% for property in provider.properties %}
spi-{{ provider.spi }}-{{ provider.id }}-{{ property.key }}={{ property.value }}
{% endfor %}{% endif %}
{% endfor %}

View file

@ -0,0 +1,2 @@
[general]
bootstrapped={{ bootstrapped | lower }}

View file

@ -0,0 +1,33 @@
{{ ansible_managed | comment }}
[Unit]
Description=Keycloak Server
After=network.target
[Service]
EnvironmentFile=-{{ keycloak_quarkus_sysconf_file }}
{% if keycloak_quarkus_start_dev %}
ExecStart={{ keycloak.home }}/bin/kc.sh start-dev
{% else %}
ExecStart={{ keycloak.home }}/bin/kc.sh start --optimized
{% endif %}
User={{ keycloak.service_user }}
Group={{ keycloak.service_group }}
SuccessExitStatus=0 143
{% if keycloak_quarkus_service_restart_always %}
Restart=always
{% elif keycloak_quarkus_service_restart_on_failure %}
Restart=on-failure
{% endif %}
RestartSec={{ keycloak_quarkus_service_restartsec }}
{% if keycloak_quarkus_http_port | int < 1024 or keycloak_quarkus_https_port | int < 1024 %}
AmbientCapabilities=CAP_NET_BIND_SERVICE
{% endif %}
{% if keycloak_quarkus_systemd_wait_for_port %}
ExecStartPost=/usr/bin/timeout {{ keycloak_quarkus_systemd_wait_for_timeout }} sh -c 'while ! ss -H -t -l -n sport = :{{ keycloak_quarkus_systemd_wait_for_port_number }} | grep -q "^LISTEN.*:{{ keycloak_quarkus_systemd_wait_for_port_number }}"; do sleep 1; done && /bin/sleep {{ keycloak_quarkus_systemd_wait_for_delay }}'
{% endif %}
{% if keycloak_quarkus_systemd_wait_for_log %}
ExecStartPost=/usr/bin/timeout {{ keycloak_quarkus_systemd_wait_for_timeout }} sh -c 'cat {{ keycloak.log.file }} | sed "/Profile.*activated/ q" && /bin/sleep {{ keycloak_quarkus_systemd_wait_for_delay }}'
{% endif %}
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,29 @@
{{ ansible_managed | comment }}
{% if keycloak_quarkus_ha_enabled %}
{% if keycloak_quarkus_version.split('.')[0] | int < 22 %}
quarkus.infinispan-client.server-list={{ keycloak_quarkus_cache_remote_host }}:{{ keycloak_quarkus_cache_remote_port }}
quarkus.infinispan-client.auth-username={{ keycloak_quarkus_cache_remote_username }}
quarkus.infinispan-client.auth-password={{ keycloak_quarkus_cache_remote_password }}
{% else %}
quarkus.infinispan-client.hosts={{ keycloak_quarkus_cache_remote_host }}:{{ keycloak_quarkus_cache_remote_port }}
quarkus.infinispan-client.username={{ keycloak_quarkus_cache_remote_username }}
quarkus.infinispan-client.password={{ keycloak_quarkus_cache_remote_password }}
{% endif %}
quarkus.infinispan-client.client-intelligence=HASH_DISTRIBUTION_AWARE
quarkus.infinispan-client.use-auth=true
quarkus.infinispan-client.auth-realm=default
quarkus.infinispan-client.auth-server-name=infinispan
quarkus.infinispan-client.sasl-mechanism={{ keycloak_quarkus_cache_remote_sasl_mechanism }}
{% if keycloak_quarkus_cache_remote_tls_enabled %}
quarkus.infinispan-client.trust-store={{ keycloak_quarkus_https_trust_store_file }}
quarkus.infinispan-client.trust-store-password={{ keycloak_quarkus_https_trust_store_password }}
quarkus.infinispan-client.trust-store-type=jks
{% endif %}
#quarkus.infinispan-client.use-schema-registration=true
{% endif %}
quarkus.log.file.rotation.max-file-size={{ keycloak_quarkus_log_max_file_size }}
quarkus.log.file.rotation.max-backup-index={{ keycloak_quarkus_log_max_backup_index }}
quarkus.log.file.rotation.file-suffix={{ keycloak_quarkus_log_file_suffix }}
{% if keycloak_quarkus_db_enabled %}
quarkus.transaction-manager.enable-recovery=true
{% endif %}

View file

@ -0,0 +1,15 @@
{{ ansible_managed | comment }}
{% if not ansible_local.keycloak.general.bootstrapped | default(false) | bool %}
KC_BOOTSTRAP_ADMIN_USERNAME={{ keycloak_quarkus_bootstrap_admin_user }}
KC_BOOTSTRAP_ADMIN_PASSWORD='{{ keycloak_quarkus_bootstrap_admin_password }}'
{% else %}
{{ keycloak.bootstrap_mnemonic }}
{% endif %}
PATH="{{ keycloak_quarkus_java_home | default(keycloak_sys_pkg_java_home, true) }}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
JAVA_HOME="{{ keycloak_quarkus_java_home | default(keycloak_sys_pkg_java_home, true) }}"
JAVA_OPTS="{{ keycloak_quarkus_java_opts }}"
# Custom ENV variables
{% for env in keycloak_quarkus_additional_env_vars %}
{{ env.key }}={{ env.value }}
{% endfor %}

View file

@ -0,0 +1,110 @@
{{ ansible_managed | comment }}
{% if keycloak_quarkus_db_enabled %}
# Database
db={{ keycloak_quarkus_db_engine }}
db-url={{ keycloak_quarkus_db_url }}
db-username={{ keycloak_quarkus_db_user }}
{% if not keycloak.config_key_store_enabled %}
db-password={{ keycloak_quarkus_db_pass }}
{% endif %}
{% endif %}
{% if keycloak.config_key_store_enabled %}
# Config store
config-keystore={{ keycloak_quarkus_config_key_store_file }}
config-keystore-password={{ keycloak_quarkus_config_key_store_password }}
{% endif %}
# Observability
metrics-enabled={{ keycloak_quarkus_metrics_enabled | lower }}
health-enabled={{ keycloak_quarkus_health_enabled | lower }}
# HTTP
http-enabled={{ keycloak_quarkus_http_enabled | lower }}
{% if keycloak_quarkus_http_enabled %}
http-port={{ keycloak_quarkus_http_port }}
{% endif %}
http-relative-path={{ keycloak_quarkus_http_relative_path }}
http-host={{ keycloak_quarkus_http_host }}
# Management
http-management-port={{ keycloak_quarkus_http_management_port }}
{% if keycloak_quarkus_http_management_relative_path is defined and keycloak_quarkus_http_management_relative_path | length > 0 %}
http-management-relative-path={{ keycloak_quarkus_http_management_relative_path }}
{% endif %}
# HTTPS
https-port={{ keycloak_quarkus_https_port }}
{% if keycloak_quarkus_https_key_file_enabled %}
https-certificate-file={{ keycloak_quarkus_cert_file}}
https-certificate-key-file={{ keycloak_quarkus_key_file }}
{% endif %}
{% if keycloak_quarkus_https_key_store_enabled %}
https-key-store-file={{ keycloak_quarkus_https_key_store_file }}
https-key-store-password={{ keycloak_quarkus_https_key_store_password }}
{% endif %}
{% if keycloak_quarkus_https_trust_store_enabled %}
https-trust-store-file={{ keycloak_quarkus_https_trust_store_file }}
https-trust-store-password={{ keycloak_quarkus_https_trust_store_password }}
{% endif %}
# Client URL configuration
hostname={{ keycloak_quarkus_hostname }}
hostname-admin={{ keycloak_quarkus_hostname_admin }}
hostname-strict={{ keycloak_quarkus_hostname_strict | lower }}
hostname-backchannel-dynamic={{ keycloak_quarkus_hostname_backchannel_dynamic | lower }}
# Cluster
{% if keycloak_quarkus_ha_enabled %}
cache=ispn
{% if keycloak_quarkus_cache_managed_infinispan_config %}
cache-config-file=cache-ispn.xml
{% endif %}
{% if keycloak_quarkus_cache_remote %}
cache-remote-username={{ keycloak_quarkus_cache_remote_username }}
cache-remote-password={{ keycloak_quarkus_cache_remote_password }}
cache-remote-host={{ keycloak_quarkus_cache_remote_host }}
cache-remote-port={{ keycloak_quarkus_cache_remote_port }}
cache-remote-tls-enabled={{ keycloak_quarkus_cache_remote_tls_enabled | lower }}
{% endif %}
{{ keycloak_quarkus_cache_embedded_properties }}
{% endif %}
{% if keycloak_quarkus_proxy_headers | length > 0 %}
proxy-headers={{ keycloak_quarkus_proxy_headers | lower }}
{% elif keycloak_quarkus_proxy_mode is defined and keycloak_quarkus_proxy_mode != "none" %}
# Deprecated Proxy configuration
proxy={{ keycloak_quarkus_proxy_mode }}
{% endif %}
spi-sticky-session-encoder-infinispan-should-attach-route={{ keycloak_quarkus_spi_sticky_session_encoder_infinispan_should_attach_route | d(true) | lower }}
# Transaction
transaction-xa-enabled={{ keycloak_quarkus_transaction_xa_enabled | lower }}
# Logging
#log-format=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n
log={{ keycloak_quarkus_log }}
log-level={{ keycloak.log.level }}
log-file={{ keycloak.log.file }}
log-file-format={{ keycloak.log.format }}
# Vault
{% if keycloak_quarkus_ks_vault_enabled %}
vault=keystore
vault-file={{ keycloak_quarkus_ks_vault_file }}
vault-type={{ keycloak_quarkus_ks_vault_type }}
vault-pass={{ keycloak_quarkus_ks_vault_pass }}
{% endif %}
# Providers
{% for provider in keycloak_quarkus_providers %}
{% if provider.default is defined and provider.default %}
spi-{{ provider.spi }}-provider={{ provider.id }}
{% endif %}
{% if provider.properties is defined %}{% for property in provider.properties %}
spi-{{ provider.spi }}-{{ provider.id }}-{{ property.key }}={{ property.value }}
{% endfor %}{% endif %}
{% endfor %}

View file

@ -0,0 +1,2 @@
[general]
bootstrapped={{ bootstrapped | lower }}

View file

@ -0,0 +1,33 @@
{{ ansible_managed | comment }}
[Unit]
Description=Keycloak Server
After=network.target
[Service]
EnvironmentFile=-{{ keycloak_quarkus_sysconf_file }}
{% if keycloak_quarkus_start_dev %}
ExecStart={{ keycloak.home }}/bin/kc.sh start-dev
{% else %}
ExecStart={{ keycloak.home }}/bin/kc.sh start --optimized
{% endif %}
User={{ keycloak.service_user }}
Group={{ keycloak.service_group }}
SuccessExitStatus=0 143
{% if keycloak_quarkus_service_restart_always %}
Restart=always
{% elif keycloak_quarkus_service_restart_on_failure %}
Restart=on-failure
{% endif %}
RestartSec={{ keycloak_quarkus_service_restartsec }}
{% if keycloak_quarkus_http_port | int < 1024 or keycloak_quarkus_https_port | int < 1024 %}
AmbientCapabilities=CAP_NET_BIND_SERVICE
{% endif %}
{% if keycloak_quarkus_systemd_wait_for_port %}
ExecStartPost=/usr/bin/timeout {{ keycloak_quarkus_systemd_wait_for_timeout }} sh -c 'while ! ss -H -t -l -n sport = :{{ keycloak_quarkus_systemd_wait_for_port_number }} | grep -q "^LISTEN.*:{{ keycloak_quarkus_systemd_wait_for_port_number }}"; do sleep 1; done && /bin/sleep {{ keycloak_quarkus_systemd_wait_for_delay }}'
{% endif %}
{% if keycloak_quarkus_systemd_wait_for_log %}
ExecStartPost=/usr/bin/timeout {{ keycloak_quarkus_systemd_wait_for_timeout }} sh -c 'cat {{ keycloak.log.file }} | sed "/Profile.*activated/ q" && /bin/sleep {{ keycloak_quarkus_systemd_wait_for_delay }}'
{% endif %}
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,12 @@
---
keycloak_quarkus_varjvm_package: "{{ keycloak_quarkus_jvm_package | default('openjdk-21-jdk-headless') }}"
keycloak_quarkus_prereq_package_list:
- "{{ keycloak_quarkus_varjvm_package }}"
- bash
- unzip
- procps
- apt
- tzdata
keycloak_quarkus_sysconf_file: /etc/default/keycloak
keycloak_quarkus_pkg_java_home: "/usr/lib/jvm/java-{{ keycloak_quarkus_varjvm_package | \
regex_search('(?!:openjdk-)[0-9.]+') }}-openjdk-{{ 'arm64' if ansible_architecture == 'aarch64' else 'amd64' }}"

View file

@ -0,0 +1,17 @@
---
keycloak: # noqa var-naming this is an internal dict of interpolated values
home: "{{ keycloak_quarkus_home }}"
config_dir: "{{ keycloak_quarkus_config_dir }}"
bundle: "{{ keycloak_quarkus_archive }}"
service_name: "keycloak"
health_url: "{{ keycloak_quarkus_health_check_url }}"
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 }}"
config_key_store_enabled: "{{ keycloak_quarkus_config_key_store_password != '' }}"
log:
file: "{{ keycloak_quarkus_home }}/{{ keycloak_quarkus_log_file }}"
level: "{{ keycloak_quarkus_log_level }}"
format: "{{ keycloak_quarkus_log_format }}"
bootstrap_mnemonic: "# ansible-middleware/keycloak: bootstrapped"

View file

@ -0,0 +1,11 @@
---
keycloak_quarkus_varjvm_package: "{{ keycloak_quarkus_jvm_package | default('java-21-openjdk-headless') }}"
keycloak_quarkus_prereq_package_list:
- "{{ keycloak_quarkus_varjvm_package }}"
- bash
- unzip
- procps-ng
- initscripts
- tzdata-java
keycloak_quarkus_sysconf_file: /etc/sysconfig/keycloak
keycloak_quarkus_pkg_java_home: "/etc/alternatives/jre_{{ keycloak_quarkus_varjvm_package | regex_search('(?<=java-)[0-9.]+') }}"