Compare commits

..

88 Commits
3.0.1 ... 3.0.4

Author SHA1 Message Date
ansible-middleware-core
c6e3337778 Update changelog for release 3.0.4
Signed-off-by: ansible-middleware-core <ansible-middleware-core@redhat.com>
2026-05-20 13:38:01 +00:00
Harsha Cherukuri
d1b295f551 Merge pull request #332 from cihlamar/AMW-522
Fix certification requirements for Keycloak
2026-05-20 09:36:11 -04:00
Martin Cihlar
5e13f4ea50 Fix certification requirements for Keycloak
- Add .ansible-lint, .DS_Store to build_ignore in galaxy.yml
- Add Release and Upgrade Notes section to README

[AMW-522](https://redhat.atlassian.net/browse/AMW-522)
2026-05-20 11:00:58 +02:00
Harsha Cherukuri
06cf664b08 Merge pull request #331 from RanabirChakraborty/SET-1341
SET-1341 Without ansible-core tag tests are failing in keycloak
2026-04-30 03:33:48 -04:00
Ranabir Chakraborty
e5690d7513 SET-1341 Without ansible-core tag tests are failing in keycloak 2026-04-28 22:15:14 +05:30
Ranabir Chakraborty
fb76736441 Merge pull request #330 from nwintering/main
Check that `data/tmp` directory has correct ownership
2026-04-28 18:24:19 +05:30
nwintering
6d00dcff48 check that tmp directory has correct permissions 2026-04-27 15:35:03 +02:00
Harsha Cherukuri
eaf9964aab Fix docs pipeline 2026-04-24 10:50:10 -04:00
Harsha Cherukuri
180f075a9f Merge pull request #324 from RanabirChakraborty/AMW-518
AMW-518 Validating arguments against arg spec 'main' fails unexpectedly.
2026-04-24 10:34:55 -04:00
Harsha Cherukuri
1013a05f8c Merge pull request #328 from hcherukuri/main
Fix sanity and molecule tests
2026-04-24 10:26:26 -04:00
Harsha Cherukuri
22f1ce516d Fix sanity and molecule tests 2026-04-24 10:07:24 -04:00
Harsha Cherukuri
7be872cc48 Merge pull request #326 from paulomenon/add/example-playbooks-client-scope-auth-flow
Add/example playbooks client scope auth flow
2026-04-24 08:29:16 -04:00
Harsha Cherukuri
55248de9ae Merge pull request #325 from paulomenon/fix/keycloak-context-default
Fix keycloak_context default from /auth to empty string for Quarkus-based Keycloak
2026-04-24 08:29:00 -04:00
Harsha Cherukuri
c6d4dfb8bb Merge pull request #327 from hcherukuri/main
Fix CI
2026-04-24 08:16:25 -04:00
Harsha Cherukuri
c8f4065eb5 Fix CI 2026-04-24 08:00:45 -04:00
pamenon
06e096ac50 Add module documentation to collection and role READMEs
Document all six modules (including the two new ones) in the main
collection README under a new 'Included modules' section. Add the
three new example playbooks to the Config Playbooks section. Update
the keycloak_realm role README with a 'Related Modules' table and
inline examples for keycloak_client_scope and
keycloak_authentication_flow usage.

Made-with: Cursor
2026-04-23 12:54:22 +01:00
pamenon
c6189bfc51 Add keycloak_client_scope and keycloak_authentication_flow modules with example playbooks
The collection was missing modules for managing client scopes and
authentication flows, forcing users to write raw uri calls against
the Keycloak Admin REST API. This adds two new modules that leverage
the existing KeycloakAPI helper methods:

- keycloak_client_scope: create/update/delete client scopes with
  protocol mappers (supports check_mode and diff)
- keycloak_authentication_flow: create/delete authentication flows
  with execution steps, or copy existing flows (supports check_mode
  and diff)

Also adds three example playbooks using the new modules:
- keycloak_client_scope.yml
- keycloak_authentication_flow.yml
- keycloak_realm_client.yml

Made-with: Cursor
2026-04-23 12:53:03 +01:00
pamenon
03fffaaf5f Fix keycloak_context default from /auth to empty string
The /auth context path was used by legacy WildFly-based Keycloak but
is no longer needed for Quarkus-based Keycloak (17+) or RHBK. The
current default of /auth forces users to explicitly pass an empty
keycloak_context to avoid broken API URLs.

This changes the default to an empty string, updates argument_specs
and README documentation, and removes the now-unnecessary
keycloak_context: '' overrides from all molecule converge files.

Users on legacy WildFly-based Keycloak can still set
keycloak_context: /auth explicitly.

Made-with: Cursor
2026-04-23 12:25:03 +01:00
Ranabir Chakraborty
a337a1d70c AMW-518 Validating arguments against arg spec 'main' fails unexpectedly. 2026-04-17 19:24:46 +05:30
Ranabir Chakraborty
28168a9a4f Merge pull request #307 from sgoericke/fix-client-id
manage_client_roles.yml: use "client.id" instead of "client.name" to fix client role creation.
2026-04-09 23:37:31 +05:30
Helmut Wolf
64469b6fac Merge pull request #320 from world-direct/fix/ispn_config
fix: include ispn config file conditionally
2026-01-15 08:05:26 +01:00
Ranabir Chakraborty
75e308b710 Merge pull request #321 from RanabirChakraborty/AMW-467
AMW-467 Download keycloak binary from password protected HTTP location
2026-01-14 21:59:45 +05:30
Ranabir Chakraborty
9cdf24ce28 AMW-467 Download keycloak binary from password protected HTTP location 2026-01-13 23:48:01 +05:30
Helmut Wolf
a00a602c3c fix: include ispn config file conditionally 2026-01-13 10:17:04 +01:00
Ranabir Chakraborty
a5a75c6d46 Merge pull request #317 from SLedunois/main
v26.4.x compability
2026-01-12 23:34:28 +05:30
Helmut Wolf
7212e572cd chore(defaults): raise default keycloak/rhbk versions to 26.4.7 2026-01-12 10:28:47 +01:00
Helmut Wolf
bc669ce0cd chore(deps): Update default SQL driver versions
As per https://access.redhat.com/articles/7027683
2026-01-12 10:28:47 +01:00
Simon LEDUNOIS
3c097ebf09 chore: add molecule test for quarkus ha 26.4- 2026-01-12 10:28:47 +01:00
Simon Ledunois
9562bf727e chore: manage infinispan configuration file 2026-01-12 10:28:47 +01:00
Simon Ledunois
6c3e327294 chore: upgrade to 26.4.7 2026-01-12 10:28:47 +01:00
Ranabir Chakraborty
be0c8a4ae3 Merge pull request #319 from RanabirChakraborty/fixing-lint
Removing parseable from lint file as Additional properties are not allowed
2026-01-09 22:16:00 +05:30
Ranabir Chakraborty
6bf10cc3e9 Removing parseable from lint file as Additional properties are not allowed 2026-01-09 22:14:03 +05:30
ansible-middleware-core
d0161dbeef Bump version to 3.0.4 2025-12-16 15:53:14 +00:00
ansible-middleware-core
bf5c805fcd Update changelog for release 3.0.3
Signed-off-by: ansible-middleware-core <ansible-middleware-core@redhat.com>
2025-12-16 15:52:59 +00:00
Ranabir Chakraborty
2b1c07d87e Merge pull request #306 from fxwgr/patch0
Declared proxy_mode as deprecated, updated quarkus and realm readme
2025-12-16 20:08:58 +05:30
Andreas Wagner
f1305e5aac Updated quarkus and realm readme, declared proxy_mode as deprecated
Updated argument_specs and declared keycloak_quarkus_proxy_mode as deprecated
2025-11-14 11:55:24 +01:00
Ranabir Chakraborty
412e17e9ea Merge pull request #312 from RanabirChakraborty/ci_label_fix
keycloak collection CI label is showing no status
2025-10-04 20:46:52 +05:30
Ranabir Chakraborty
fa87c004e3 keycloak collection CI label is showing no status 2025-10-04 20:19:09 +05:30
Ranabir Chakraborty
6c9bddbd61 Merge pull request #308 from tinsjourney/fix_config_key_store_password
Fix config_key_store_file description to match variable name
2025-09-25 21:47:46 +05:30
Ranabir Chakraborty
4602d254cf Merge pull request #310 from world-direct/fix/309
ansible-core 2.19 compatibility
2025-09-18 18:58:54 +05:30
Helmut Wolf
8b2ef22023 fix ansible-core v2.19.0: initialize keycloak_quarkus_hostname_admin to an empty string 2025-07-22 12:11:09 +02:00
Helmut Wolf
66228c3a13 ansible 2.19.0: fix error
'item' is undefined error, https://github.com/ansible-middleware/keycloak/issues/309#issuecomment-3101960407
2025-07-22 12:09:14 +02:00
Stephane Vigan
556d155533 Fix config_key_store_file description to match variable name 2025-07-21 16:15:59 +02:00
Sven Goericke
07063353b8 fixed wrong variable lookup 2025-07-16 13:50:07 +02:00
Guido Grazioli
c1bf9727f9 Merge pull request #293 from world-direct/fix/292
Update to keycloak 26.3.0
2025-07-09 11:38:56 +02:00
Helmut Wolf
f79fd227eb chore: bump KC/RHBK to v26.3.0/v26.2.5 2025-07-07 11:09:35 +02:00
Helmut Wolf
19564987ca fix(quarkus): update infinispan-client configuration to include port in server-list and hosts 2025-07-07 11:05:44 +02:00
Helmut Wolf
1ff25325a7 fix(ispn): use legacy JGroups stack configuration for < 26.2 only 2025-07-07 11:05:44 +02:00
Guido Grazioli
0099f1cf07 Merge pull request #303 from fxwgr/main
Allow to install provider jars from remote paths
2025-07-04 12:47:10 +02:00
Guido Grazioli
725ec8e37b Merge pull request #304 from SLedunois/client_secret
keycloak_realm: allow secret in keycloak_clients
2025-07-04 12:46:40 +02:00
Andreas Wagner
bbe568baa5 Added support for copy remote_src function for providers 2025-07-02 16:39:49 +02:00
LEDUNOIS Simon
dcd448443f feat: allow secret in keycloak_clients 2025-07-02 14:36:25 +00:00
ansible-middleware-core
3780a4e3c0 Bump version to 3.0.3 2025-07-01 16:56:26 +00:00
ansible-middleware-core
e60a5b7cf6 Update changelog for release 3.0.2
Signed-off-by: ansible-middleware-core <ansible-middleware-core@redhat.com>
2025-07-01 16:56:10 +00:00
Guido Grazioli
6143ae25e2 Merge pull request #296 from RanabirChakraborty/keycloak_force_install_quarkus_role
Fix `keycloak_quarkus_force_install` parameter being ignored by install
2025-07-01 18:45:34 +02:00
Ranabir Chakraborty
ef6d8890fb keycloak_quarkus_force_install does not ignore bootstrapped 2025-07-01 21:55:39 +05:30
Guido Grazioli
55185a1439 Merge pull request #302 from tinsjourney/fix_federation_provider_type
keycloak_realm: federation default provider type should be a string
2025-07-01 17:50:34 +02:00
Guido Grazioli
bb64b97e43 Merge pull request #298 from tinsjourney/alternate_download_fix
Fix alternate download location being ignored (JBossNeworkAPI always used)
2025-07-01 16:10:35 +02:00
Stephane Vigan
a9c9e05569 Default provider type should be a string 2025-07-01 10:27:00 +02:00
Stephane Vigan
8b27cb0706 Fix case where archive is always downalod from JBoss Nework API
Even is we set keycloak_quarkus_alternate_download_url
previous block was run and we always try to download
archive from JBoss Network Api.
2025-06-26 10:42:25 +02:00
Guido Grazioli
41127504dc Merge pull request #287 from guidograzioli/kc_26_caches
Session storage / distributed caches
2025-06-13 11:43:00 +02:00
Guido Grazioli
bcc961999c implement Single site - Sessions stored in external Infinispan 2025-06-05 12:02:43 +02:00
Guido Grazioli
b8907d765d Merge pull request #289 from guidograzioli/288_jdk_21
Use jdk21 as default in debian
2025-06-05 09:30:26 +02:00
Guido Grazioli
5c5e84b63e Use jdk21 as default in debian 2025-06-04 20:53:28 +02:00
Guido Grazioli
3d4bd734f1 document new parameters 2025-05-29 22:20:08 +02:00
Guido Grazioli
3de96a6666 single site remote cache 2025-05-29 21:37:11 +02:00
Guido Grazioli
de0ea02272 ci: move intermittent test back 2025-05-29 21:03:03 +02:00
Guido Grazioli
b6e585f503 Merge pull request #284 from guidograzioli/caches
Refactor test scenarios
2025-05-29 20:45:37 +02:00
Guido Grazioli
18de37706f tune Xmx Xms jvm args 2025-05-29 20:37:39 +02:00
Guido Grazioli
b569e4e713 update remote cache test 2025-05-29 20:14:04 +02:00
Guido Grazioli
919d55f806 Merge pull request #286 from RanabirChakraborty/AMW-398
Use tags to decorate the role workflow
2025-05-27 11:12:07 +02:00
Guido Grazioli
476bc0ec0b update test config 2025-05-23 14:59:50 +02:00
Helmut Wolf
2954bf81e8 Merge pull request #285 from world-direct/fix/keycloak_config_rebuild
Run config rebuild after SPI providers update
2025-05-21 09:26:07 +02:00
Ranabir Chakraborty
0403939c03 AMW-398 Use tags to decorate the role workflow 2025-05-20 21:40:32 +05:30
Helmut Wolf
88e4ea8d99 fix: MVN provider invokes KC config rebuild 2025-05-20 13:53:56 +02:00
Guido Grazioli
0a5fc3ae25 restructure molecule tests 2025-05-20 10:35:01 +02:00
Guido Grazioli
f4a1798f26 Merge pull request #283 from world-direct/feature/282
RHBK v26.2 (#282)
2025-05-19 18:37:01 +02:00
Helmut Wolf
d23ae39c25 chore(molecule): RHBK v26.2 (#282) 2025-05-19 14:44:34 +02:00
Helmut Wolf
8f95bcb9e6 feat(HA): Change default ispn discovery mechanism to JDBCPING as per v26.2.* (#282) 2025-05-19 14:44:34 +02:00
Helmut Wolf
f8c75de5d5 chore: RHBK v26.2: Bump KC version to v26.2.4 2025-05-19 14:44:34 +02:00
Helmut Wolf
8093b1af2a chore: RHBK v26.2: Bump RHBK version to v26.2.4 2025-05-19 14:12:22 +02:00
Helmut Wolf
a70aece0d9 chore: RHBK v26.2: Update recommended JDBC driver versions 2025-05-19 14:12:22 +02:00
Guido Grazioli
d427a6b721 Merge pull request #281 from jonathanspw/keycloak_quarkus_jgroups_ip
New parameter to set the jgroups host IP address
2025-05-13 19:04:56 +02:00
Jonathan Wright
c614af127e Add var to set the jgroups IP per host
This is useful if the default route does not
represent the network you want/need to use for
cluster communication.
2025-05-13 09:48:46 -05:00
Guido Grazioli
0936d415c7 ci: update test linking removed url 2025-05-09 15:20:57 +02:00
Guido Grazioli
a120b1c9b5 Merge pull request #280 from world-direct/feature/279_provider_checksums
New `checksum` property for keycloak_quarkus_providers
2025-05-06 17:49:41 +02:00
Helmut Wolf
5cd400b053 feat: introduce checksum for keycloak_quarkus_providers (#279) 2025-05-06 15:16:50 +02:00
ansible-middleware-core
e0c4b1e1ff Bump version to 3.0.2 2025-05-02 09:49:08 +00:00
100 changed files with 3421 additions and 198 deletions

View File

@@ -40,4 +40,3 @@ skip_list:
- var-naming[no-role-prefix] - var-naming[no-role-prefix]
use_default_rules: true use_default_rules: true
parseable: true

View File

@@ -6,14 +6,20 @@ on:
- main - main
pull_request: pull_request:
workflow_dispatch: workflow_dispatch:
inputs:
debug_verbosity:
description: 'ANSIBLE_VERBOSITY envvar value'
required: false
schedule: schedule:
- cron: '15 6 * * *' - cron: '15 6 * * *'
jobs: jobs:
ci: ci:
uses: ansible-middleware/github-actions/.github/workflows/ci.yml@main uses: ansible-middleware/github-actions/.github/workflows/ci.yml@rootperm
secrets: inherit secrets: inherit
with: with:
fqcn: 'middleware_automation/keycloak' fqcn: 'middleware_automation/keycloak'
root_permission_varname: 'keycloak_install_requires_become'
debug_verbosity: "${{ github.event.inputs.debug_verbosity }}"
molecule_tests: >- molecule_tests: >-
[ "default", "overridexml", "https_revproxy", "quarkus", "quarkus-devmode", "quarkus_upgrade", "debian", "quarkus_ha" ] [ "debian", "quarkus", "quarkus_ha", "quarkus_ha_remote", "quarkus_ha_26.4_below", "default", "quarkus_devmode", "quarkus_upgrade" ]

1
.gitignore vendored
View File

@@ -14,3 +14,4 @@ changelogs/.plugin-cache.yaml
*.pem *.pem
*.key *.key
*.p12 *.p12
.ansible/

View File

@@ -6,6 +6,67 @@ middleware\_automation.keycloak Release Notes
This changelog describes changes after version 0.2.6. This changelog describes changes after version 0.2.6.
v3.0.4
======
Major Changes
-------------
- AMW-467 Download keycloak binary from password protected HTTP location `#321 <https://github.com/ansible-middleware/keycloak/pull/321>`_
- v26.4.x compability `#317 <https://github.com/ansible-middleware/keycloak/pull/317>`_
Minor Changes
-------------
- AMW-518 Validating arguments against arg spec 'main' fails unexpectedly. `#324 <https://github.com/ansible-middleware/keycloak/pull/324>`_
Bugfixes
--------
- Removing parseable from lint file as Additional properties are not allowed `#319 <https://github.com/ansible-middleware/keycloak/pull/319>`_
v3.0.3
======
Major Changes
-------------
- Update to keycloak 26.3.0 `#293 <https://github.com/ansible-middleware/keycloak/pull/293>`_
- ansible-core 2.19 compatibility `#310 <https://github.com/ansible-middleware/keycloak/pull/310>`_
Minor Changes
-------------
- Allow to install provider jars from remote paths `#303 <https://github.com/ansible-middleware/keycloak/pull/303>`_
- Declared proxy_mode as deprecated, updated quarkus and realm readme `#306 <https://github.com/ansible-middleware/keycloak/pull/306>`_
- Fix config_key_store_file description to match variable name `#308 <https://github.com/ansible-middleware/keycloak/pull/308>`_
Bugfixes
--------
- keycloak collection CI label is showing no status `#312 <https://github.com/ansible-middleware/keycloak/pull/312>`_
- keycloak_realm: allow secret in keycloak_clients `#304 <https://github.com/ansible-middleware/keycloak/pull/304>`_
v3.0.2
======
Minor Changes
-------------
- New ``checksum`` property for keycloak_quarkus_providers `#280 <https://github.com/ansible-middleware/keycloak/pull/280>`_
- New parameter to set the jgroups host IP address `#281 <https://github.com/ansible-middleware/keycloak/pull/281>`_
- Session storage / distributed caches `#287 <https://github.com/ansible-middleware/keycloak/pull/287>`_
- Update keycloak/RHBK to v26.2.4 `#283 <https://github.com/ansible-middleware/keycloak/pull/283>`_
Bugfixes
--------
- Fix ``keycloak_quarkus_force_install`` parameter being ignored by install `#296 <https://github.com/ansible-middleware/keycloak/pull/296>`_
- Fix alternate download location being ignored (JBossNeworkAPI always used) `#298 <https://github.com/ansible-middleware/keycloak/pull/298>`_
- Run config rebuild after SPI providers update `#285 <https://github.com/ansible-middleware/keycloak/pull/285>`_
- Use jdk21 as default in debian `#289 <https://github.com/ansible-middleware/keycloak/pull/289>`_
- keycloak_realm: federation default provider type should be a string `#302 <https://github.com/ansible-middleware/keycloak/pull/302>`_
v3.0.1 v3.0.1
====== ======

View File

@@ -1,7 +1,7 @@
# Ansible Collection - middleware_automation.keycloak # Ansible Collection - middleware_automation.keycloak
<!--start build_status --> <!--start build_status -->
[![Build Status](https://github.com/ansible-middleware/keycloak/workflows/CI/badge.svg?branch=main)](https://github.com/ansible-middleware/keycloak/actions/workflows/ci.yml) [![Build Status](https://github.com/ansible-middleware/keycloak/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/ansible-middleware/keycloak/actions/workflows/ci.yml)
> **_NOTE:_ If you are Red Hat customer, install `redhat.rhbk` (for Red Hat Build of Keycloak) or `redhat.sso` (for Red Hat Single Sign-On) from [Automation Hub](https://console.redhat.com/ansible/ansible-dashboard) as the certified version of this collection.** > **_NOTE:_ If you are Red Hat customer, install `redhat.rhbk` (for Red Hat Build of Keycloak) or `redhat.sso` (for Red Hat Single Sign-On) from [Automation Hub](https://console.redhat.com/ansible/ansible-dashboard) as the certified version of this collection.**
@@ -55,6 +55,15 @@ A requirement file is provided to install:
<!--end roles_paths --> <!--end roles_paths -->
### Included modules
* `keycloak_realm`: module for managing Keycloak realms (create/update/delete).
* `keycloak_client`: module for managing Keycloak clients (create/update/delete).
* `keycloak_role`: module for managing Keycloak roles — realm roles and client roles (create/update/delete).
* `keycloak_user_federation`: module for managing user federations such as LDAP/AD (create/update/delete).
* `keycloak_client_scope`: module for managing client scopes and protocol mappers (create/update/delete).
* `keycloak_authentication_flow`: module for managing authentication flows and execution steps (create/delete, copy existing flows).
## Usage ## Usage
@@ -109,10 +118,13 @@ Note: when deploying clustered configurations, all hosts belonging to the cluste
## Configuration ## Configuration
### Config Playbook ### Config Playbooks
<!--start rhbk_realm_playbook --> <!--start rhbk_realm_playbook -->
[`playbooks/keycloak_realm.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak_realm.yml) creates or updates provided realm, user federation(s), client(s), client role(s) and client user(s). * [`playbooks/keycloak_realm.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak_realm.yml) creates or updates provided realm, user federation(s), client(s), client role(s) and client user(s).
<!--end rhbk_realm_playbook --> <!--end rhbk_realm_playbook -->
* [`playbooks/keycloak_realm_client.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak_realm_client.yml) creates a realm with clients, roles and users using the `keycloak_realm` role.
* [`playbooks/keycloak_client_scope.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak_client_scope.yml) creates a client scope with protocol mappers using the `keycloak_client_scope` module.
* [`playbooks/keycloak_authentication_flow.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak_authentication_flow.yml) creates a custom authentication flow with execution steps using the `keycloak_authentication_flow` module.
### Example configuration command ### Example configuration command
@@ -134,10 +146,21 @@ ansible-playbook -i <ansible_hosts> playbooks/keycloak_realm.yml -e keycloak_adm
For full configuration details, refer to the [keycloak_realm role README](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak_realm/README.md). For full configuration details, refer to the [keycloak_realm role README](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak_realm/README.md).
<!--end rhbk_realm_readme --> <!--end rhbk_realm_readme -->
## Support
<!--start support --> <!--start support -->
For bug reports and feature requests, use [GitHub Issues](https://github.com/ansible-middleware/keycloak/issues).
<!--end support --> <!--end support -->
## Release and Upgrade Notes
For details on changes between versions, please see the [CHANGELOG](https://github.com/ansible-middleware/keycloak/blob/main/CHANGELOG.rst) for this collection.
## License ## License
Apache License v2.0 or later Apache License v2.0 or later

View File

@@ -674,3 +674,110 @@ releases:
- 276.yaml - 276.yaml
- 277.yaml - 277.yaml
release_date: '2025-05-02' release_date: '2025-05-02'
3.0.2:
changes:
bugfixes:
- 'Fix ``keycloak_quarkus_force_install`` parameter being ignored by install
`#296 <https://github.com/ansible-middleware/keycloak/pull/296>`_
'
- 'Fix alternate download location being ignored (JBossNeworkAPI always used)
`#298 <https://github.com/ansible-middleware/keycloak/pull/298>`_
'
- 'Run config rebuild after SPI providers update `#285 <https://github.com/ansible-middleware/keycloak/pull/285>`_
'
- 'Use jdk21 as default in debian `#289 <https://github.com/ansible-middleware/keycloak/pull/289>`_
'
- 'keycloak_realm: federation default provider type should be a string `#302
<https://github.com/ansible-middleware/keycloak/pull/302>`_
'
minor_changes:
- 'New ``checksum`` property for keycloak_quarkus_providers `#280 <https://github.com/ansible-middleware/keycloak/pull/280>`_
'
- 'New parameter to set the jgroups host IP address `#281 <https://github.com/ansible-middleware/keycloak/pull/281>`_
'
- 'Session storage / distributed caches `#287 <https://github.com/ansible-middleware/keycloak/pull/287>`_
'
- 'Update keycloak/RHBK to v26.2.4 `#283 <https://github.com/ansible-middleware/keycloak/pull/283>`_
'
fragments:
- 280.yaml
- 281.yaml
- 283.yaml
- 285.yaml
- 287.yaml
- 289.yaml
- 296.yaml
- 298.yaml
- 302.yaml
release_date: '2025-07-01'
3.0.3:
changes:
bugfixes:
- 'keycloak collection CI label is showing no status `#312 <https://github.com/ansible-middleware/keycloak/pull/312>`_
'
- 'keycloak_realm: allow secret in keycloak_clients `#304 <https://github.com/ansible-middleware/keycloak/pull/304>`_
'
major_changes:
- 'Update to keycloak 26.3.0 `#293 <https://github.com/ansible-middleware/keycloak/pull/293>`_
'
- 'ansible-core 2.19 compatibility `#310 <https://github.com/ansible-middleware/keycloak/pull/310>`_
'
minor_changes:
- 'Allow to install provider jars from remote paths `#303 <https://github.com/ansible-middleware/keycloak/pull/303>`_
'
- 'Declared proxy_mode as deprecated, updated quarkus and realm readme `#306
<https://github.com/ansible-middleware/keycloak/pull/306>`_
'
- 'Fix config_key_store_file description to match variable name `#308 <https://github.com/ansible-middleware/keycloak/pull/308>`_
'
fragments:
- 293.yaml
- 303.yaml
- 304.yaml
- 306.yaml
- 308.yaml
- 310.yaml
- 312.yaml
release_date: '2025-12-16'
3.0.4:
changes:
bugfixes:
- 'Removing parseable from lint file as Additional properties are not allowed
`#319 <https://github.com/ansible-middleware/keycloak/pull/319>`_
'
major_changes:
- 'AMW-467 Download keycloak binary from password protected HTTP location `#321
<https://github.com/ansible-middleware/keycloak/pull/321>`_
'
- 'v26.4.x compability `#317 <https://github.com/ansible-middleware/keycloak/pull/317>`_
'
minor_changes:
- 'AMW-518 Validating arguments against arg spec ''main'' fails unexpectedly.
`#324 <https://github.com/ansible-middleware/keycloak/pull/324>`_
'
fragments:
- 317.yaml
- 319.yaml
- 321.yaml
- 324.yaml
release_date: '2026-05-20'

View File

@@ -64,7 +64,7 @@ master_doc = 'index'
# #
# This is also used if you do content translation via gettext catalogs. # This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases. # Usually you set "language" from the command line for these cases.
language = None language = 'en'
# List of patterns, relative to source directory, that match files and # List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files. # directories to ignore when looking for source files.

View File

@@ -1,3 +1,5 @@
# ansible_basic_sphinx_ext still imports pkg_resources (removed in setuptools 82+).
setuptools>=70.0.0,<81.0.0
antsibull>=0.17.0 antsibull>=0.17.0
antsibull-docs antsibull-docs
antsibull-changelog antsibull-changelog

View File

@@ -1,7 +1,7 @@
--- ---
namespace: middleware_automation namespace: middleware_automation
name: keycloak name: keycloak
version: "3.0.1" version: "3.0.4"
readme: README.md readme: README.md
authors: authors:
- Romain Pelisse <rpelisse@redhat.com> - Romain Pelisse <rpelisse@redhat.com>
@@ -35,7 +35,9 @@ issues: https://github.com/ansible-middleware/keycloak/issues
build_ignore: build_ignore:
- .gitignore - .gitignore
- .github - .github
- .ansible-lint
- .yamllint - .yamllint
- .DS_Store
- '*.tar.gz' - '*.tar.gz'
- '*.zip' - '*.zip'
- molecule - molecule

View File

@@ -1,6 +1,8 @@
--- ---
- name: Converge - name: Converge
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
vars: vars:
keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_show_deprecation_warnings: false
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_password: "remembertochangeme"
@@ -13,7 +15,6 @@
- role: keycloak_quarkus - role: keycloak_quarkus
- role: keycloak_realm - role: keycloak_realm
keycloak_url: "{{ keycloak_quarkus_hostname }}" keycloak_url: "{{ keycloak_quarkus_hostname }}"
keycloak_context: ''
keycloak_admin_user: "{{ keycloak_quarkus_bootstrap_admin_user }}" keycloak_admin_user: "{{ keycloak_quarkus_bootstrap_admin_user }}"
keycloak_admin_password: "{{ keycloak_quarkus_bootstrap_admin_password }}" keycloak_admin_password: "{{ keycloak_quarkus_bootstrap_admin_password }}"
keycloak_client_users: keycloak_client_users:

View File

@@ -3,7 +3,7 @@ driver:
name: docker name: docker
platforms: platforms:
- name: instance - name: instance
image: ghcr.io/hspaans/molecule-containers:debian-11 image: ghcr.io/hspaans/molecule-containers:debian-13
pre_build_image: true pre_build_image: true
privileged: true privileged: true
port_bindings: port_bindings:

View File

@@ -1,12 +1,13 @@
--- ---
- name: Prepare - name: Prepare
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
gather_facts: yes gather_facts: yes
tasks: tasks:
- name: Install sudo - name: Install sudo
ansible.builtin.apt: ansible.builtin.apt:
name: name:
- sudo - sudo
# - openjdk-21-jdk-headless # this is not available in ghcr.io/hspaans/molecule-containers:debian-11 (neither in debian-12) since the images are using outdated package sources - openjdk-21-jdk-headless
- openjdk-17-jdk-headless - iproute2
state: present

View File

@@ -1,6 +1,8 @@
--- ---
- name: Converge - name: Converge
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
vars: vars:
keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_show_deprecation_warnings: false
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_password: "remembertochangeme"
@@ -13,11 +15,11 @@
keycloak_quarkus_proxy_mode: none keycloak_quarkus_proxy_mode: none
keycloak_quarkus_offline_install: true keycloak_quarkus_offline_install: true
keycloak_quarkus_download_path: /tmp/keycloak/ keycloak_quarkus_download_path: /tmp/keycloak/
keycloak_quarkus_java_heap_opts: "-Xms640m -Xmx640m "
roles: roles:
- role: keycloak_quarkus - role: keycloak_quarkus
- role: keycloak_realm - role: keycloak_realm
keycloak_url: "{{ keycloak_quarkus_hostname }}" keycloak_url: "{{ keycloak_quarkus_hostname }}"
keycloak_context: ''
keycloak_admin_user: "{{ keycloak_quarkus_bootstrap_admin_user }}" keycloak_admin_user: "{{ keycloak_quarkus_bootstrap_admin_user }}"
keycloak_admin_password: "{{ keycloak_quarkus_bootstrap_admin_password }}" keycloak_admin_password: "{{ keycloak_quarkus_bootstrap_admin_password }}"
keycloak_client_users: keycloak_client_users:

View File

@@ -1,6 +1,6 @@
--- ---
driver: driver:
name: docker name: podman
platforms: platforms:
- name: instance - name: instance
image: registry.access.redhat.com/ubi9/ubi-init:latest image: registry.access.redhat.com/ubi9/ubi-init:latest
@@ -24,11 +24,16 @@ provisioner:
converge: converge.yml converge: converge.yml
verify: verify.yml verify: verify.yml
inventory: inventory:
group_vars:
all:
keycloak_install_requires_become: true
host_vars: host_vars:
localhost: localhost:
ansible_python_interpreter: "{{ ansible_playbook_python }}" ansible_python_interpreter: "{{ ansible_playbook_python }}"
env: env:
ANSIBLE_FORCE_COLOR: "true" ANSIBLE_FORCE_COLOR: "true"
PROXY: "${PROXY}"
NO_PROXY: "${NO_PROXY}"
verifier: verifier:
name: ansible name: ansible
scenario: scenario:

View File

@@ -1,6 +1,8 @@
--- ---
- name: Prepare - name: Prepare
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
gather_facts: yes gather_facts: yes
vars: vars:
sudo_pkg_name: sudo sudo_pkg_name: sudo
@@ -18,7 +20,7 @@
- name: Download keycloak archive to controller directory - name: Download keycloak archive to controller directory
ansible.builtin.get_url: # noqa risky-file-permissions delegated, uses controller host user ansible.builtin.get_url: # noqa risky-file-permissions delegated, uses controller host user
url: https://github.com/keycloak/keycloak/releases/download/26.0.8/keycloak-26.0.8.zip url: https://github.com/keycloak/keycloak/releases/download/26.4.7/keycloak-26.4.7.zip
dest: /tmp/keycloak dest: /tmp/keycloak
mode: '0640' mode: '0640'
delegate_to: localhost delegate_to: localhost

View File

@@ -0,0 +1,26 @@
---
keycloak_quarkus_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_systemd_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_install_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_firewalld_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_iptables_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_jdbc_driver_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_config_store_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_restart_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_start_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_rebuild_config_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_fastpackages_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_bootstrapped_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_invalidate_theme_cache_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_systemd_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_install_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_firewalld_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_iptables_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_jdbc_driver_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_restart_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_start_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_stop_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_fastpackages_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_rhsso_patch_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
molecule_prepare_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"

View File

@@ -1,6 +1,8 @@
--- ---
- name: Converge - name: Converge
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
vars: vars:
keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_show_deprecation_warnings: false
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_password: "remembertochangeme"

View File

@@ -1,6 +1,8 @@
--- ---
- name: Prepare - name: Prepare
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
tasks: tasks:
- name: Install sudo - name: Install sudo
ansible.builtin.dnf: ansible.builtin.dnf:
@@ -27,6 +29,8 @@
pre_tasks: pre_tasks:
- name: Create certificate request - name: Create certificate request
ansible.builtin.command: openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes -subj '/CN=proxy' ansible.builtin.command: openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes -subj '/CN=proxy'
args:
chdir: "{{ playbook_dir }}"
delegate_to: localhost delegate_to: localhost
changed_when: false changed_when: false
- name: Make certificate directory - name: Make certificate directory
@@ -39,11 +43,11 @@
src: "{{ item.name }}" src: "{{ item.name }}"
dest: "{{ item.dest }}" dest: "{{ item.dest }}"
mode: 0444 mode: 0444
become: true become: "{{ molecule_prepare_require_privilege_escalation }}"
loop: loop:
- { name: 'cert.pem', dest: '/etc/nginx/tls/certificate.crt' } - { name: 'cert.pem', dest: '/etc/nginx/tls/certificate.crt' }
- { name: 'key.pem', dest: '/etc/nginx/tls/certificate.key' } - { name: 'key.pem', dest: '/etc/nginx/tls/certificate.key' }
- name: Update CA trust - name: Update CA trust
ansible.builtin.command: update-ca-trust ansible.builtin.command: update-ca-trust
changed_when: false changed_when: false
become: true become: "{{ molecule_prepare_require_privilege_escalation }}"

View File

@@ -1,6 +1,8 @@
--- ---
- name: Converge - name: Converge
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
vars: vars:
keycloak_admin_password: "remembertochangeme" keycloak_admin_password: "remembertochangeme"
keycloak_config_override_template: custom.xml.j2 keycloak_config_override_template: custom.xml.j2

View File

@@ -1,6 +1,8 @@
--- ---
- name: Prepare - name: Prepare
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
gather_facts: yes gather_facts: yes
vars: vars:
sudo_pkg_name: sudo sudo_pkg_name: sudo

View File

@@ -25,11 +25,12 @@
fail_msg: "sudo is not installed on target system" fail_msg: "sudo is not installed on target system"
- name: "Install iproute" - name: "Install iproute"
become: true
ansible.builtin.yum: ansible.builtin.yum:
name: name:
- iproute - iproute
state: present state: present
when:
- ansible_user_id == 'root'
- name: "Retrieve assets server from env" - name: "Retrieve assets server from env"
ansible.builtin.set_fact: ansible.builtin.set_fact:

View File

@@ -1,6 +1,8 @@
--- ---
- name: Converge - name: Converge
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
vars: vars:
keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_show_deprecation_warnings: false
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_password: "remembertochangeme"
@@ -23,6 +25,8 @@
keycloak_quarkus_systemd_wait_for_delay: 2 keycloak_quarkus_systemd_wait_for_delay: 2
keycloak_quarkus_systemd_wait_for_log: true keycloak_quarkus_systemd_wait_for_log: true
keycloak_quarkus_restart_health_check: false # would fail because of self-signed cert keycloak_quarkus_restart_health_check: false # would fail because of self-signed cert
keycloak_quarkus_version: 26.4.7
keycloak_quarkus_java_heap_opts: "-Xms1024m -Xmx1024m"
keycloak_quarkus_additional_env_vars: keycloak_quarkus_additional_env_vars:
- key: KC_FEATURES_DISABLED - key: KC_FEATURES_DISABLED
value: impersonation,kerberos value: impersonation,kerberos
@@ -36,27 +40,29 @@
value: 10 value: 10
- id: spid-saml - id: spid-saml
url: https://github.com/italia/spid-keycloak-provider/releases/download/24.0.2/spid-provider.jar url: https://github.com/italia/spid-keycloak-provider/releases/download/24.0.2/spid-provider.jar
- id: spid-saml-w-checksum
url: https://github.com/italia/spid-keycloak-provider/releases/download/24.0.2/spid-provider.jar
checksum: sha256:fbb50e73739d7a6d35b5bff611b1c01668b29adf6f6259624b95e466a305f377
- id: keycloak-kerberos-federation - id: keycloak-kerberos-federation
maven: maven:
repository_url: https://repo1.maven.org/maven2/ # https://mvnrepository.com/artifact/org.keycloak/keycloak-kerberos-federation/24.0.4 repository_url: https://repo1.maven.org/maven2/ # https://mvnrepository.com/artifact/org.keycloak/keycloak-kerberos-federation/24.0.4
group_id: org.keycloak group_id: org.keycloak
artifact_id: keycloak-kerberos-federation artifact_id: keycloak-kerberos-federation
version: 26.0.7 # optional version: 26.4.7 # optional
# username: myUser # optional # username: myUser # optional
# password: myPAT # optional # password: myPAT # optional
# - id: my-static-theme # - id: my-static-theme
# local_path: /tmp/my-static-theme.jar # local_path: /tmp/my-static-theme.jar
keycloak_quarkus_policies: keycloak_quarkus_policies:
- name: "xato-net-10-million-passwords.txt" - name: "cain-and-abel.txt"
url: "https://github.com/danielmiessler/SecLists/raw/master/Passwords/xato-net-10-million-passwords.txt" url: "https://github.com/danielmiessler/SecLists/raw/master/Passwords/Software/cain-and-abel.txt"
- name: "xato-net-10-million-passwords-10.txt" - name: "john-the-ripper.txt"
url: "https://github.com/danielmiessler/SecLists/raw/master/Passwords/xato-net-10-million-passwords-10.txt" url: "https://github.com/danielmiessler/SecLists/raw/master/Passwords/Software/john-the-ripper.txt"
type: password-blacklists type: password-blacklists
roles: roles:
- role: keycloak_quarkus - role: keycloak_quarkus
- role: keycloak_realm - role: keycloak_realm
keycloak_url: http://instance:8080 keycloak_url: http://instance:8080
keycloak_context: ''
keycloak_admin_user: "{{ keycloak_quarkus_bootstrap_admin_user }}" keycloak_admin_user: "{{ keycloak_quarkus_bootstrap_admin_user }}"
keycloak_admin_password: "{{ keycloak_quarkus_bootstrap_admin_password }}" keycloak_admin_password: "{{ keycloak_quarkus_bootstrap_admin_password }}"
keycloak_client_default_roles: keycloak_client_default_roles:

View File

@@ -32,6 +32,8 @@ provisioner:
env: env:
ANSIBLE_FORCE_COLOR: "true" ANSIBLE_FORCE_COLOR: "true"
PYTHONHTTPSVERIFY: 0 PYTHONHTTPSVERIFY: 0
PROXY: "${PROXY}"
NO_PROXY: "${NO_PROXY}"
verifier: verifier:
name: ansible name: ansible
scenario: scenario:

View File

@@ -1,6 +1,8 @@
--- ---
- name: Prepare - name: Prepare
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
tasks: tasks:
- name: "Display hera_home if defined." - name: "Display hera_home if defined."
ansible.builtin.set_fact: ansible.builtin.set_fact:
@@ -11,11 +13,13 @@
- name: Create certificate request - name: Create certificate request
ansible.builtin.command: openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes -subj '/CN=instance' ansible.builtin.command: openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes -subj '/CN=instance'
args:
chdir: "{{ playbook_dir }}"
delegate_to: localhost delegate_to: localhost
changed_when: false changed_when: false
- name: Create vault directory - name: Create vault directory
become: true become: "{{ molecule_prepare_require_privilege_escalation }}"
ansible.builtin.file: ansible.builtin.file:
state: directory state: directory
path: "/opt/keycloak/vault" path: "/opt/keycloak/vault"
@@ -24,20 +28,22 @@
- name: Make sure a jre is available (for keytool to prepare keystore) - name: Make sure a jre is available (for keytool to prepare keystore)
delegate_to: localhost delegate_to: localhost
ansible.builtin.package: ansible.builtin.package:
name: "{{ 'java-21-openjdk-headless' if hera_home | length > 0 else 'openjdk-21-jdk-headless' }}" name: java-21-openjdk-headless
state: present state: present
become: true become: "{{ molecule_prepare_require_privilege_escalation }}"
failed_when: false failed_when: false
- name: Create vault keystore - name: Create vault keystore
ansible.builtin.command: keytool -importpass -alias TestRealm_testalias -keystore keystore.p12 -storepass keystorepassword ansible.builtin.command: keytool -importpass -alias TestRealm_testalias -keystore keystore.p12 -storepass keystorepassword
args:
chdir: "{{ playbook_dir }}"
delegate_to: localhost delegate_to: localhost
register: keytool_cmd register: keytool_cmd
changed_when: False changed_when: False
failed_when: not 'already exists' in keytool_cmd.stdout and keytool_cmd.rc != 0 failed_when: not 'already exists' in keytool_cmd.stdout and keytool_cmd.rc != 0
- name: Copy certificates and vault - name: Copy certificates and vault
become: true become: "{{ molecule_prepare_require_privilege_escalation }}"
ansible.builtin.copy: ansible.builtin.copy:
src: keystore.p12 src: keystore.p12
dest: /opt/keycloak/vault/keystore.p12 dest: /opt/keycloak/vault/keystore.p12

View File

@@ -1,6 +1,8 @@
--- ---
- name: Verify - name: Verify
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
vars: vars:
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_password: "remembertochangeme"
keycloak_quarkus_bootstrap_admin_user: "remembertochangeme" keycloak_quarkus_bootstrap_admin_user: "remembertochangeme"
@@ -56,7 +58,7 @@
fail_msg: "Service log symlink not correctly created" fail_msg: "Service log symlink not correctly created"
- name: Check log file - name: Check log file
become: true become: "{{ molecule_prepare_require_privilege_escalation }}"
ansible.builtin.stat: ansible.builtin.stat:
path: /tmp/keycloak/keycloak.log path: /tmp/keycloak/keycloak.log
register: keycloak_log_file register: keycloak_log_file
@@ -68,7 +70,7 @@
- not keycloak_log_file.stat.isdir - not keycloak_log_file.stat.isdir
- name: Check default log folder - name: Check default log folder
become: yes become: "{{ molecule_prepare_require_privilege_escalation }}"
ansible.builtin.stat: ansible.builtin.stat:
path: /var/log/keycloak path: /var/log/keycloak
register: keycloak_default_log_folder register: keycloak_default_log_folder
@@ -80,7 +82,7 @@
- not keycloak_default_log_folder.stat.exists - not keycloak_default_log_folder.stat.exists
- name: Verify vault SPI in logfile - name: Verify vault SPI in logfile
become: true become: "{{ molecule_prepare_require_privilege_escalation }}"
ansible.builtin.shell: | ansible.builtin.shell: |
set -o pipefail set -o pipefail
zgrep 'Configured KeystoreVaultProviderFactory with the keystore file' /opt/keycloak/keycloak-*/data/log/keycloak.log*zip zgrep 'Configured KeystoreVaultProviderFactory with the keystore file' /opt/keycloak/keycloak-*/data/log/keycloak.log*zip

View File

@@ -1,6 +1,8 @@
--- ---
- name: Converge - name: Converge
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
vars: vars:
keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_show_deprecation_warnings: false
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_password: "remembertochangeme"
@@ -11,11 +13,12 @@
keycloak_quarkus_start_dev: True keycloak_quarkus_start_dev: True
keycloak_quarkus_proxy_mode: none keycloak_quarkus_proxy_mode: none
keycloak_quarkus_java_home: /opt/openjdk/ keycloak_quarkus_java_home: /opt/openjdk/
keycloak_quarkus_java_heap_opts: "-Xms640m -Xmx640m"
roles: roles:
- role: keycloak_quarkus - role: keycloak_quarkus
- role: keycloak_realm - role: keycloak_realm
keycloak_url: "{{ keycloak_quarkus_hostname }}" keycloak_url: "{{ keycloak_quarkus_hostname }}"
keycloak_context: ''
keycloak_admin_user: "{{ keycloak_quarkus_bootstrap_admin_user }}" keycloak_admin_user: "{{ keycloak_quarkus_bootstrap_admin_user }}"
keycloak_admin_password: "{{ keycloak_quarkus_bootstrap_admin_password }}" keycloak_admin_password: "{{ keycloak_quarkus_bootstrap_admin_password }}"
keycloak_client_default_roles: keycloak_client_default_roles:

View File

@@ -1,6 +1,6 @@
--- ---
driver: driver:
name: docker name: podman
platforms: platforms:
- name: instance - name: instance
image: registry.access.redhat.com/ubi9/ubi-init:latest image: registry.access.redhat.com/ubi9/ubi-init:latest
@@ -31,6 +31,8 @@ provisioner:
ansible_python_interpreter: "{{ ansible_playbook_python }}" ansible_python_interpreter: "{{ ansible_playbook_python }}"
env: env:
ANSIBLE_FORCE_COLOR: "true" ANSIBLE_FORCE_COLOR: "true"
PROXY: "${PROXY}"
NO_PROXY: "${NO_PROXY}"
verifier: verifier:
name: ansible name: ansible
scenario: scenario:

View File

@@ -1,6 +1,8 @@
--- ---
- name: Prepare - name: Prepare
hosts: all hosts: all
vars_files:
- ../group_vars/all/vars.yml
tasks: tasks:
- name: Install sudo - name: Install sudo
ansible.builtin.apt: ansible.builtin.apt:
@@ -15,7 +17,7 @@
ansible.builtin.include_tasks: ../prepare.yml ansible.builtin.include_tasks: ../prepare.yml
- name: Install JDK17 - name: Install JDK17
become: yes become: "{{ molecule_prepare_require_privilege_escalation }}"
ansible.builtin.yum: ansible.builtin.yum:
name: name:
- java-17-openjdk-headless - java-17-openjdk-headless
@@ -24,7 +26,7 @@
- ansible_facts.os_family == 'RedHat' - ansible_facts.os_family == 'RedHat'
- name: Link default logs directory - name: Link default logs directory
become: yes become: "{{ molecule_prepare_require_privilege_escalation }}"
ansible.builtin.file: ansible.builtin.file:
state: link state: link
src: "{{ item }}" src: "{{ item }}"

View File

@@ -1,6 +1,8 @@
--- ---
- name: Converge - name: Converge
hosts: keycloak hosts: keycloak
vars_files:
- ../group_vars/all/vars.yml
vars: vars:
keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_show_deprecation_warnings: false
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme" keycloak_quarkus_bootstrap_admin_password: "remembertochangeme"

View File

@@ -42,7 +42,7 @@ platforms:
mounts: mounts:
- type: bind - type: bind
target: /etc/postgresql/postgresql.conf target: /etc/postgresql/postgresql.conf
source: ${PWD}/molecule/quarkus_ha/postgresql/postgresql.conf source: ${MOLECULE_PROJECT_DIRECTORY}/molecule/quarkus_ha/postgresql/postgresql.conf
env: env:
POSTGRES_USER: keycloak POSTGRES_USER: keycloak
POSTGRES_PASSWORD: mysecretpass POSTGRES_PASSWORD: mysecretpass

View File

@@ -1,6 +1,8 @@
--- ---
- name: Prepare - name: Prepare
hosts: keycloak hosts: keycloak
vars_files:
- ../group_vars/all/vars.yml
tasks: tasks:
- name: "Display hera_home if defined." - name: "Display hera_home if defined."
ansible.builtin.set_fact: ansible.builtin.set_fact:
@@ -11,11 +13,13 @@
- name: Create certificate request - name: Create certificate request
ansible.builtin.command: "openssl req -x509 -newkey rsa:4096 -keyout {{ inventory_hostname }}.key -out {{ inventory_hostname }}.pem -sha256 -days 365 -nodes -subj '/CN={{ inventory_hostname }}'" ansible.builtin.command: "openssl req -x509 -newkey rsa:4096 -keyout {{ inventory_hostname }}.key -out {{ inventory_hostname }}.pem -sha256 -days 365 -nodes -subj '/CN={{ inventory_hostname }}'"
args:
chdir: "{{ playbook_dir }}"
delegate_to: localhost delegate_to: localhost
changed_when: False changed_when: False
- name: Create vault directory - name: Create vault directory
become: true become: "{{ molecule_prepare_require_privilege_escalation }}"
ansible.builtin.file: ansible.builtin.file:
state: directory state: directory
path: "/opt/keycloak/vault" path: "/opt/keycloak/vault"
@@ -26,7 +30,7 @@
ansible.builtin.package: ansible.builtin.package:
name: "{{ 'java-17-openjdk-headless' if hera_home | length > 0 else 'openjdk-17-jdk-headless' }}" name: "{{ 'java-17-openjdk-headless' if hera_home | length > 0 else 'openjdk-17-jdk-headless' }}"
state: present state: present
become: true become: "{{ molecule_prepare_require_privilege_escalation }}"
failed_when: false failed_when: false
- name: Create vault keystore - name: Create vault keystore
@@ -37,7 +41,7 @@
failed_when: not 'already exists' in keytool_cmd.stdout and keytool_cmd.rc != 0 failed_when: not 'already exists' in keytool_cmd.stdout and keytool_cmd.rc != 0
- name: Copy certificates and vault - name: Copy certificates and vault
become: true become: "{{ molecule_prepare_require_privilege_escalation }}"
ansible.builtin.copy: ansible.builtin.copy:
src: keystore.p12 src: keystore.p12
dest: /opt/keycloak/vault/keystore.p12 dest: /opt/keycloak/vault/keystore.p12

View File

@@ -1,6 +1,8 @@
--- ---
- name: Verify - name: Verify
hosts: keycloak hosts: keycloak
vars_files:
- ../group_vars/all/vars.yml
tasks: tasks:
- name: Populate service facts - name: Populate service facts
ansible.builtin.service_facts: ansible.builtin.service_facts:
@@ -17,7 +19,7 @@
hera_home: "{{ lookup('env', 'HERA_HOME') }}" hera_home: "{{ lookup('env', 'HERA_HOME') }}"
- name: Check log file - name: Check log file
become: true become: "{{ molecule_prepare_require_privilege_escalation }}"
ansible.builtin.stat: ansible.builtin.stat:
path: /var/log/keycloak/keycloak.log path: /var/log/keycloak/keycloak.log
register: keycloak_log_file register: keycloak_log_file

View File

@@ -0,0 +1,32 @@
---
- name: Converge
hosts: keycloak
vars_files:
- ../group_vars/all/vars.yml
vars:
keycloak_quarkus_show_deprecation_warnings: false
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme"
keycloak_quarkus_bootstrap_admin_user: "remembertochangeme"
keycloak_quarkus_hostname: "http://{{ inventory_hostname }}:8080"
keycloak_quarkus_log: file
keycloak_quarkus_log_level: info
keycloak_quarkus_https_key_file_enabled: true
keycloak_quarkus_key_file_copy_enabled: true
keycloak_quarkus_key_content: "{{ lookup('file', inventory_hostname + '.key') }}"
keycloak_quarkus_cert_file_copy_enabled: true
keycloak_quarkus_cert_file_src: "{{ inventory_hostname }}.pem"
keycloak_quarkus_ks_vault_enabled: true
keycloak_quarkus_ks_vault_file: "/opt/keycloak/vault/keystore.p12"
keycloak_quarkus_ks_vault_pass: keystorepassword
keycloak_quarkus_systemd_wait_for_port: true
keycloak_quarkus_systemd_wait_for_timeout: 20
keycloak_quarkus_systemd_wait_for_delay: 2
keycloak_quarkus_systemd_wait_for_log: true
keycloak_quarkus_ha_enabled: true
keycloak_quarkus_restart_strategy: restart/serial.yml
keycloak_quarkus_db_user: keycloak
keycloak_quarkus_db_pass: mysecretpass
keycloak_quarkus_db_url: jdbc:postgresql://postgres:5432/keycloak
keycloak_quarkus_version: 26.3.5
roles:
- role: keycloak_quarkus

View File

@@ -0,0 +1,82 @@
---
driver:
name: docker
platforms:
- name: instance1
image: registry.access.redhat.com/ubi9/ubi-init:latest
pre_build_image: true
privileged: true
command: "/usr/sbin/init"
groups:
- keycloak
networks:
- name: rhbk
port_bindings:
- "8080/tcp"
- "8443/tcp"
- "9000/tcp"
- name: instance2
image: registry.access.redhat.com/ubi9/ubi-init:latest
pre_build_image: true
privileged: true
command: "/usr/sbin/init"
groups:
- keycloak
networks:
- name: rhbk
port_bindings:
- "8080/tcp"
- "8443/tcp"
- "9000/tcp"
- name: postgres
image: ubuntu/postgres:14-22.04_beta
pre_build_image: true
privileged: true
command: postgres
groups:
- database
networks:
- name: rhbk
port_bindings:
- "5432/tcp"
mounts:
- type: bind
target: /etc/postgresql/postgresql.conf
source: ${MOLECULE_PROJECT_DIRECTORY}/molecule/quarkus_ha/postgresql/postgresql.conf
env:
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: mysecretpass
POSTGRES_DB: keycloak
POSTGRES_HOST_AUTH_METHOD: trust
provisioner:
name: ansible
config_options:
defaults:
interpreter_python: auto_silent
ssh_connection:
pipelining: false
playbooks:
prepare: prepare.yml
converge: converge.yml
verify: verify.yml
inventory:
host_vars:
localhost:
ansible_python_interpreter: "{{ ansible_playbook_python }}"
env:
ANSIBLE_FORCE_COLOR: "true"
PYTHONHTTPSVERIFY: 0
verifier:
name: ansible
scenario:
test_sequence:
- cleanup
- destroy
- create
- prepare
- converge
- idempotence
- side_effect
- verify
- cleanup
- destroy

View File

@@ -0,0 +1,750 @@
# -----------------------------
# PostgreSQL configuration file
# -----------------------------
#
# This file consists of lines of the form:
#
# name = value
#
# (The "=" is optional.) Whitespace may be used. Comments are introduced with
# "#" anywhere on a line. The complete list of parameter names and allowed
# values can be found in the PostgreSQL documentation.
#
# The commented-out settings shown in this file represent the default values.
# Re-commenting a setting is NOT sufficient to revert it to the default value;
# you need to reload the server.
#
# This file is read on server startup and when the server receives a SIGHUP
# signal. If you edit the file on a running system, you have to SIGHUP the
# server for the changes to take effect, run "pg_ctl reload", or execute
# "SELECT pg_reload_conf()". Some parameters, which are marked below,
# require a server shutdown and restart to take effect.
#
# Any parameter can also be given as a command-line option to the server, e.g.,
# "postgres -c log_connections=on". Some parameters can be changed at run time
# with the "SET" SQL command.
#
# Memory units: kB = kilobytes Time units: ms = milliseconds
# MB = megabytes s = seconds
# GB = gigabytes min = minutes
# TB = terabytes h = hours
# d = days
#------------------------------------------------------------------------------
# FILE LOCATIONS
#------------------------------------------------------------------------------
# The default values of these variables are driven from the -D command-line
# option or PGDATA environment variable, represented here as ConfigDir.
#data_directory = 'ConfigDir' # use data in another directory
# (change requires restart)
#hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file
# (change requires restart)
#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file
# (change requires restart)
# If external_pid_file is not explicitly set, no extra PID file is written.
#external_pid_file = '' # write an extra PID file
# (change requires restart)
#------------------------------------------------------------------------------
# CONNECTIONS AND AUTHENTICATION
#------------------------------------------------------------------------------
# - Connection Settings -
listen_addresses = '*' # what IP address(es) to listen on;
# comma-separated list of addresses;
# defaults to 'localhost'; use '*' for all
# (change requires restart)
#port = 5432 # (change requires restart)
#max_connections = 100 # (change requires restart)
#superuser_reserved_connections = 3 # (change requires restart)
#unix_socket_directories = '/tmp' # comma-separated list of directories
# (change requires restart)
#unix_socket_group = '' # (change requires restart)
#unix_socket_permissions = 0777 # begin with 0 to use octal notation
# (change requires restart)
#bonjour = off # advertise server via Bonjour
# (change requires restart)
#bonjour_name = '' # defaults to the computer name
# (change requires restart)
# - TCP settings -
# see "man 7 tcp" for details
#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds;
# 0 selects the system default
#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds;
# 0 selects the system default
#tcp_keepalives_count = 0 # TCP_KEEPCNT;
# 0 selects the system default
#tcp_user_timeout = 0 # TCP_USER_TIMEOUT, in milliseconds;
# 0 selects the system default
# - Authentication -
#authentication_timeout = 1min # 1s-600s
#password_encryption = md5 # md5 or scram-sha-256
#db_user_namespace = off
# GSSAPI using Kerberos
#krb_server_keyfile = ''
#krb_caseins_users = off
# - SSL -
#ssl = off
#ssl_ca_file = ''
#ssl_cert_file = 'server.crt'
#ssl_crl_file = ''
#ssl_key_file = 'server.key'
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
#ssl_prefer_server_ciphers = on
#ssl_ecdh_curve = 'prime256v1'
#ssl_min_protocol_version = 'TLSv1'
#ssl_max_protocol_version = ''
#ssl_dh_params_file = ''
#ssl_passphrase_command = ''
#ssl_passphrase_command_supports_reload = off
#------------------------------------------------------------------------------
# RESOURCE USAGE (except WAL)
#------------------------------------------------------------------------------
# - Memory -
#shared_buffers = 32MB # min 128kB
# (change requires restart)
#huge_pages = try # on, off, or try
# (change requires restart)
#temp_buffers = 8MB # min 800kB
#max_prepared_transactions = 0 # zero disables the feature
# (change requires restart)
# Caution: it is not advisable to set max_prepared_transactions nonzero unless
# you actively intend to use prepared transactions.
#work_mem = 4MB # min 64kB
#maintenance_work_mem = 64MB # min 1MB
#autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem
#max_stack_depth = 2MB # min 100kB
#shared_memory_type = mmap # the default is the first option
# supported by the operating system:
# mmap
# sysv
# windows
# (change requires restart)
#dynamic_shared_memory_type = posix # the default is the first option
# supported by the operating system:
# posix
# sysv
# windows
# mmap
# (change requires restart)
# - Disk -
#temp_file_limit = -1 # limits per-process temp file space
# in kB, or -1 for no limit
# - Kernel Resources -
#max_files_per_process = 1000 # min 25
# (change requires restart)
# - Cost-Based Vacuum Delay -
#vacuum_cost_delay = 0 # 0-100 milliseconds (0 disables)
#vacuum_cost_page_hit = 1 # 0-10000 credits
#vacuum_cost_page_miss = 10 # 0-10000 credits
#vacuum_cost_page_dirty = 20 # 0-10000 credits
#vacuum_cost_limit = 200 # 1-10000 credits
# - Background Writer -
#bgwriter_delay = 200ms # 10-10000ms between rounds
#bgwriter_lru_maxpages = 100 # max buffers written/round, 0 disables
#bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round
#bgwriter_flush_after = 0 # measured in pages, 0 disables
# - Asynchronous Behavior -
#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching
#max_worker_processes = 8 # (change requires restart)
#max_parallel_maintenance_workers = 2 # taken from max_parallel_workers
#max_parallel_workers_per_gather = 2 # taken from max_parallel_workers
#parallel_leader_participation = on
#max_parallel_workers = 8 # maximum number of max_worker_processes that
# can be used in parallel operations
#old_snapshot_threshold = -1 # 1min-60d; -1 disables; 0 is immediate
# (change requires restart)
#backend_flush_after = 0 # measured in pages, 0 disables
#------------------------------------------------------------------------------
# WRITE-AHEAD LOG
#------------------------------------------------------------------------------
# - Settings -
#wal_level = replica # minimal, replica, or logical
# (change requires restart)
#fsync = on # flush data to disk for crash safety
# (turning this off can cause
# unrecoverable data corruption)
#synchronous_commit = on # synchronization level;
# off, local, remote_write, remote_apply, or on
#wal_sync_method = fsync # the default is the first option
# supported by the operating system:
# open_datasync
# fdatasync (default on Linux)
# fsync
# fsync_writethrough
# open_sync
#full_page_writes = on # recover from partial page writes
#wal_compression = off # enable compression of full-page writes
#wal_log_hints = off # also do full page writes of non-critical updates
# (change requires restart)
#wal_init_zero = on # zero-fill new WAL files
#wal_recycle = on # recycle WAL files
#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers
# (change requires restart)
#wal_writer_delay = 200ms # 1-10000 milliseconds
#wal_writer_flush_after = 1MB # measured in pages, 0 disables
#commit_delay = 0 # range 0-100000, in microseconds
#commit_siblings = 5 # range 1-1000
# - Checkpoints -
#checkpoint_timeout = 5min # range 30s-1d
#max_wal_size = 1GB
#min_wal_size = 80MB
#checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0
#checkpoint_flush_after = 0 # measured in pages, 0 disables
#checkpoint_warning = 30s # 0 disables
# - Archiving -
#archive_mode = off # enables archiving; off, on, or always
# (change requires restart)
#archive_command = '' # command to use to archive a logfile segment
# placeholders: %p = path of file to archive
# %f = file name only
# e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f'
#archive_timeout = 0 # force a logfile segment switch after this
# number of seconds; 0 disables
# - Archive Recovery -
# These are only used in recovery mode.
#restore_command = '' # command to use to restore an archived logfile segment
# placeholders: %p = path of file to restore
# %f = file name only
# e.g. 'cp /mnt/server/archivedir/%f %p'
# (change requires restart)
#archive_cleanup_command = '' # command to execute at every restartpoint
#recovery_end_command = '' # command to execute at completion of recovery
# - Recovery Target -
# Set these only when performing a targeted recovery.
#recovery_target = '' # 'immediate' to end recovery as soon as a
# consistent state is reached
# (change requires restart)
#recovery_target_name = '' # the named restore point to which recovery will proceed
# (change requires restart)
#recovery_target_time = '' # the time stamp up to which recovery will proceed
# (change requires restart)
#recovery_target_xid = '' # the transaction ID up to which recovery will proceed
# (change requires restart)
#recovery_target_lsn = '' # the WAL LSN up to which recovery will proceed
# (change requires restart)
#recovery_target_inclusive = on # Specifies whether to stop:
# just after the specified recovery target (on)
# just before the recovery target (off)
# (change requires restart)
#recovery_target_timeline = 'latest' # 'current', 'latest', or timeline ID
# (change requires restart)
#recovery_target_action = 'pause' # 'pause', 'promote', 'shutdown'
# (change requires restart)
#------------------------------------------------------------------------------
# REPLICATION
#------------------------------------------------------------------------------
# - Sending Servers -
# Set these on the master and on any standby that will send replication data.
#max_wal_senders = 10 # max number of walsender processes
# (change requires restart)
#wal_keep_segments = 0 # in logfile segments; 0 disables
#wal_sender_timeout = 60s # in milliseconds; 0 disables
#max_replication_slots = 10 # max number of replication slots
# (change requires restart)
#track_commit_timestamp = off # collect timestamp of transaction commit
# (change requires restart)
# - Master Server -
# These settings are ignored on a standby server.
#synchronous_standby_names = '' # standby servers that provide sync rep
# method to choose sync standbys, number of sync standbys,
# and comma-separated list of application_name
# from standby(s); '*' = all
#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed
# - Standby Servers -
# These settings are ignored on a master server.
#primary_conninfo = '' # connection string to sending server
# (change requires restart)
#primary_slot_name = '' # replication slot on sending server
# (change requires restart)
#promote_trigger_file = '' # file name whose presence ends recovery
#hot_standby = on # "off" disallows queries during recovery
# (change requires restart)
#max_standby_archive_delay = 30s # max delay before canceling queries
# when reading WAL from archive;
# -1 allows indefinite delay
#max_standby_streaming_delay = 30s # max delay before canceling queries
# when reading streaming WAL;
# -1 allows indefinite delay
#wal_receiver_status_interval = 10s # send replies at least this often
# 0 disables
#hot_standby_feedback = off # send info from standby to prevent
# query conflicts
#wal_receiver_timeout = 60s # time that receiver waits for
# communication from master
# in milliseconds; 0 disables
#wal_retrieve_retry_interval = 5s # time to wait before retrying to
# retrieve WAL after a failed attempt
#recovery_min_apply_delay = 0 # minimum delay for applying changes during recovery
# - Subscribers -
# These settings are ignored on a publisher.
#max_logical_replication_workers = 4 # taken from max_worker_processes
# (change requires restart)
#max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers
#------------------------------------------------------------------------------
# QUERY TUNING
#------------------------------------------------------------------------------
# - Planner Method Configuration -
#enable_bitmapscan = on
#enable_hashagg = on
#enable_hashjoin = on
#enable_indexscan = on
#enable_indexonlyscan = on
#enable_material = on
#enable_mergejoin = on
#enable_nestloop = on
#enable_parallel_append = on
#enable_seqscan = on
#enable_sort = on
#enable_tidscan = on
#enable_partitionwise_join = off
#enable_partitionwise_aggregate = off
#enable_parallel_hash = on
#enable_partition_pruning = on
# - Planner Cost Constants -
#seq_page_cost = 1.0 # measured on an arbitrary scale
#random_page_cost = 4.0 # same scale as above
#cpu_tuple_cost = 0.01 # same scale as above
#cpu_index_tuple_cost = 0.005 # same scale as above
#cpu_operator_cost = 0.0025 # same scale as above
#parallel_tuple_cost = 0.1 # same scale as above
#parallel_setup_cost = 1000.0 # same scale as above
#jit_above_cost = 100000 # perform JIT compilation if available
# and query more expensive than this;
# -1 disables
#jit_inline_above_cost = 500000 # inline small functions if query is
# more expensive than this; -1 disables
#jit_optimize_above_cost = 500000 # use expensive JIT optimizations if
# query is more expensive than this;
# -1 disables
#min_parallel_table_scan_size = 8MB
#min_parallel_index_scan_size = 512kB
#effective_cache_size = 4GB
# - Genetic Query Optimizer -
#geqo = on
#geqo_threshold = 12
#geqo_effort = 5 # range 1-10
#geqo_pool_size = 0 # selects default based on effort
#geqo_generations = 0 # selects default based on effort
#geqo_selection_bias = 2.0 # range 1.5-2.0
#geqo_seed = 0.0 # range 0.0-1.0
# - Other Planner Options -
#default_statistics_target = 100 # range 1-10000
#constraint_exclusion = partition # on, off, or partition
#cursor_tuple_fraction = 0.1 # range 0.0-1.0
#from_collapse_limit = 8
#join_collapse_limit = 8 # 1 disables collapsing of explicit
# JOIN clauses
#force_parallel_mode = off
#jit = on # allow JIT compilation
#plan_cache_mode = auto # auto, force_generic_plan or
# force_custom_plan
#------------------------------------------------------------------------------
# REPORTING AND LOGGING
#------------------------------------------------------------------------------
# - Where to Log -
#log_destination = 'stderr' # Valid values are combinations of
# stderr, csvlog, syslog, and eventlog,
# depending on platform. csvlog
# requires logging_collector to be on.
# This is used when logging to stderr:
#logging_collector = off # Enable capturing of stderr and csvlog
# into log files. Required to be on for
# csvlogs.
# (change requires restart)
# These are only used if logging_collector is on:
#log_directory = 'log' # directory where log files are written,
# can be absolute or relative to PGDATA
#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern,
# can include strftime() escapes
#log_file_mode = 0600 # creation mode for log files,
# begin with 0 to use octal notation
#log_truncate_on_rotation = off # If on, an existing log file with the
# same name as the new log file will be
# truncated rather than appended to.
# But such truncation only occurs on
# time-driven rotation, not on restarts
# or size-driven rotation. Default is
# off, meaning append to existing files
# in all cases.
#log_rotation_age = 1d # Automatic rotation of logfiles will
# happen after that time. 0 disables.
#log_rotation_size = 10MB # Automatic rotation of logfiles will
# happen after that much log output.
# 0 disables.
# These are relevant when logging to syslog:
#syslog_facility = 'LOCAL0'
#syslog_ident = 'postgres'
#syslog_sequence_numbers = on
#syslog_split_messages = on
# This is only relevant when logging to eventlog (win32):
# (change requires restart)
#event_source = 'PostgreSQL'
# - When to Log -
#log_min_messages = warning # values in order of decreasing detail:
# debug5
# debug4
# debug3
# debug2
# debug1
# info
# notice
# warning
# error
# log
# fatal
# panic
#log_min_error_statement = error # values in order of decreasing detail:
# debug5
# debug4
# debug3
# debug2
# debug1
# info
# notice
# warning
# error
# log
# fatal
# panic (effectively off)
#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements
# and their durations, > 0 logs only
# statements running at least this number
# of milliseconds
#log_transaction_sample_rate = 0.0 # Fraction of transactions whose statements
# are logged regardless of their duration. 1.0 logs all
# statements from all transactions, 0.0 never logs.
# - What to Log -
#debug_print_parse = off
#debug_print_rewritten = off
#debug_print_plan = off
#debug_pretty_print = on
#log_checkpoints = off
#log_connections = off
#log_disconnections = off
#log_duration = off
#log_error_verbosity = default # terse, default, or verbose messages
#log_hostname = off
#log_line_prefix = '%m [%p] ' # special values:
# %a = application name
# %u = user name
# %d = database name
# %r = remote host and port
# %h = remote host
# %p = process ID
# %t = timestamp without milliseconds
# %m = timestamp with milliseconds
# %n = timestamp with milliseconds (as a Unix epoch)
# %i = command tag
# %e = SQL state
# %c = session ID
# %l = session line number
# %s = session start timestamp
# %v = virtual transaction ID
# %x = transaction ID (0 if none)
# %q = stop here in non-session
# processes
# %% = '%'
# e.g. '<%u%%%d> '
#log_lock_waits = off # log lock waits >= deadlock_timeout
#log_statement = 'none' # none, ddl, mod, all
#log_replication_commands = off
#log_temp_files = -1 # log temporary files equal or larger
# than the specified size in kilobytes;
# -1 disables, 0 logs all temp files
#log_timezone = 'GMT'
#------------------------------------------------------------------------------
# PROCESS TITLE
#------------------------------------------------------------------------------
#cluster_name = '' # added to process titles if nonempty
# (change requires restart)
#update_process_title = on
#------------------------------------------------------------------------------
# STATISTICS
#------------------------------------------------------------------------------
# - Query and Index Statistics Collector -
#track_activities = on
#track_counts = on
#track_io_timing = off
#track_functions = none # none, pl, all
#track_activity_query_size = 1024 # (change requires restart)
#stats_temp_directory = 'pg_stat_tmp'
# - Monitoring -
#log_parser_stats = off
#log_planner_stats = off
#log_executor_stats = off
#log_statement_stats = off
#------------------------------------------------------------------------------
# AUTOVACUUM
#------------------------------------------------------------------------------
#autovacuum = on # Enable autovacuum subprocess? 'on'
# requires track_counts to also be on.
#log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and
# their durations, > 0 logs only
# actions running at least this number
# of milliseconds.
#autovacuum_max_workers = 3 # max number of autovacuum subprocesses
# (change requires restart)
#autovacuum_naptime = 1min # time between autovacuum runs
#autovacuum_vacuum_threshold = 50 # min number of row updates before
# vacuum
#autovacuum_analyze_threshold = 50 # min number of row updates before
# analyze
#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum
#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze
#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
# (change requires restart)
#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age
# before forced vacuum
# (change requires restart)
#autovacuum_vacuum_cost_delay = 2ms # default vacuum cost delay for
# autovacuum, in milliseconds;
# -1 means use vacuum_cost_delay
#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for
# autovacuum, -1 means use
# vacuum_cost_limit
#------------------------------------------------------------------------------
# CLIENT CONNECTION DEFAULTS
#------------------------------------------------------------------------------
# - Statement Behavior -
#client_min_messages = notice # values in order of decreasing detail:
# debug5
# debug4
# debug3
# debug2
# debug1
# log
# notice
# warning
# error
#search_path = '"$user", public' # schema names
#row_security = on
#default_tablespace = '' # a tablespace name, '' uses the default
#temp_tablespaces = '' # a list of tablespace names, '' uses
# only default tablespace
#default_table_access_method = 'heap'
#check_function_bodies = on
#default_transaction_isolation = 'read committed'
#default_transaction_read_only = off
#default_transaction_deferrable = off
#session_replication_role = 'origin'
#statement_timeout = 0 # in milliseconds, 0 is disabled
#lock_timeout = 0 # in milliseconds, 0 is disabled
#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled
#vacuum_freeze_min_age = 50000000
#vacuum_freeze_table_age = 150000000
#vacuum_multixact_freeze_min_age = 5000000
#vacuum_multixact_freeze_table_age = 150000000
#vacuum_cleanup_index_scale_factor = 0.1 # fraction of total number of tuples
# before index cleanup, 0 always performs
# index cleanup
#bytea_output = 'hex' # hex, escape
#xmlbinary = 'base64'
#xmloption = 'content'
#gin_fuzzy_search_limit = 0
#gin_pending_list_limit = 4MB
# - Locale and Formatting -
#datestyle = 'iso, mdy'
#intervalstyle = 'postgres'
#timezone = 'GMT'
#timezone_abbreviations = 'Default' # Select the set of available time zone
# abbreviations. Currently, there are
# Default
# Australia (historical usage)
# India
# You can create your own file in
# share/timezonesets/.
#extra_float_digits = 1 # min -15, max 3; any value >0 actually
# selects precise output mode
#client_encoding = sql_ascii # actually, defaults to database
# encoding
# These settings are initialized by initdb, but they can be changed.
#lc_messages = 'C' # locale for system error message
# strings
#lc_monetary = 'C' # locale for monetary formatting
#lc_numeric = 'C' # locale for number formatting
#lc_time = 'C' # locale for time formatting
# default configuration for text search
#default_text_search_config = 'pg_catalog.simple'
# - Shared Library Preloading -
#shared_preload_libraries = '' # (change requires restart)
#local_preload_libraries = ''
#session_preload_libraries = ''
#jit_provider = 'llvmjit' # JIT library to use
# - Other Defaults -
#dynamic_library_path = '$libdir'
#------------------------------------------------------------------------------
# LOCK MANAGEMENT
#------------------------------------------------------------------------------
#deadlock_timeout = 1s
#max_locks_per_transaction = 64 # min 10
# (change requires restart)
#max_pred_locks_per_transaction = 64 # min 10
# (change requires restart)
#max_pred_locks_per_relation = -2 # negative values mean
# (max_pred_locks_per_transaction
# / -max_pred_locks_per_relation) - 1
#max_pred_locks_per_page = 2 # min 0
#------------------------------------------------------------------------------
# VERSION AND PLATFORM COMPATIBILITY
#------------------------------------------------------------------------------
# - Previous PostgreSQL Versions -
#array_nulls = on
#backslash_quote = safe_encoding # on, off, or safe_encoding
#escape_string_warning = on
#lo_compat_privileges = off
#operator_precedence_warning = off
#quote_all_identifiers = off
#standard_conforming_strings = on
#synchronize_seqscans = on
# - Other Platforms and Clients -
#transform_null_equals = off
#------------------------------------------------------------------------------
# ERROR HANDLING
#------------------------------------------------------------------------------
#exit_on_error = off # terminate session on any error?
#restart_after_crash = on # reinitialize after backend crash?
#data_sync_retry = off # retry or panic on failure to fsync
# data?
# (change requires restart)
#------------------------------------------------------------------------------
# CONFIG FILE INCLUDES
#------------------------------------------------------------------------------
# These options allow settings to be loaded from files other than the
# default postgresql.conf. Note that these are directives, not variable
# assignments, so they can usefully be given more than once.
#include_dir = '...' # include files ending in '.conf' from
# a directory, e.g., 'conf.d'
#include_if_exists = '...' # include file only if it exists
#include = '...' # include file
#------------------------------------------------------------------------------
# CUSTOMIZED OPTIONS
#------------------------------------------------------------------------------
# Add settings for extensions here

View File

@@ -0,0 +1,48 @@
---
- name: Prepare
hosts: keycloak
vars_files:
- ../group_vars/all/vars.yml
tasks:
- name: "Display hera_home if defined."
ansible.builtin.set_fact:
hera_home: "{{ lookup('env', 'HERA_HOME') }}"
- name: "Ensure common prepare phase are set."
ansible.builtin.include_tasks: ../prepare.yml
- name: Create certificate request
ansible.builtin.command: "openssl req -x509 -newkey rsa:4096 -keyout {{ inventory_hostname }}.key -out {{ inventory_hostname }}.pem -sha256 -days 365 -nodes -subj '/CN={{ inventory_hostname }}'"
args:
chdir: "{{ playbook_dir }}"
delegate_to: localhost
changed_when: False
- name: Create vault directory
become: "{{ molecule_prepare_require_privilege_escalation }}"
ansible.builtin.file:
state: directory
path: "/opt/keycloak/vault"
mode: 0755
- name: Make sure a jre is available (for keytool to prepare keystore)
delegate_to: localhost
ansible.builtin.package:
name: "{{ 'java-17-openjdk-headless' if hera_home | length > 0 else 'openjdk-17-jdk-headless' }}"
state: present
become: "{{ molecule_prepare_require_privilege_escalation }}"
failed_when: false
- name: Create vault keystore
ansible.builtin.command: keytool -importpass -alias TestRealm_testalias -keystore keystore.p12 -storepass keystorepassword
delegate_to: localhost
register: keytool_cmd
changed_when: False
failed_when: not 'already exists' in keytool_cmd.stdout and keytool_cmd.rc != 0
- name: Copy certificates and vault
become: "{{ molecule_prepare_require_privilege_escalation }}"
ansible.builtin.copy:
src: keystore.p12
dest: /opt/keycloak/vault/keystore.p12
mode: 0444

View File

@@ -0,0 +1 @@
../../roles

View File

@@ -0,0 +1,31 @@
---
- name: Verify
hosts: keycloak
vars_files:
- ../group_vars/all/vars.yml
tasks:
- name: Populate service facts
ansible.builtin.service_facts:
- name: Check if keycloak service started
ansible.builtin.assert:
that:
- ansible_facts.services["keycloak.service"]["state"] == "running"
- ansible_facts.services["keycloak.service"]["status"] == "enabled"
fail_msg: "Service not running"
- name: Set internal envvar
ansible.builtin.set_fact:
hera_home: "{{ lookup('env', 'HERA_HOME') }}"
- name: Check log file
become: "{{ molecule_prepare_require_privilege_escalation }}"
ansible.builtin.stat:
path: /var/log/keycloak/keycloak.log
register: keycloak_log_file
- name: Check if keycloak file exists
ansible.builtin.assert:
that:
- keycloak_log_file.stat.exists
- not keycloak_log_file.stat.isdir

View File

@@ -0,0 +1,63 @@
---
- name: Converge
hosts: infinispan
vars_files:
- ../group_vars/all/vars.yml
vars:
ansible_become: "{{ keycloak_install_requires_become | default(true) }}"
roles:
- role: middleware_automation.infinispan.infinispan
infinispan_service_name: infinispan
infinispan_supervisor_password: remembertochangeme
infinispan_keycloak_caches: true
infinispan_keycloak_persistence: False
infinispan_jdbc_engine: postgres
infinispan_jdbc_url: jdbc:postgresql://postgres:5432/keycloak
infinispan_jdbc_driver_version: 9.4.1212
infinispan_jdbc_user: keycloak
infinispan_jdbc_pass: mysecretpass
infinispan_bind_address: "{{ ansible_default_ipv4.address }}"
infinispan_users:
- { name: 'testuser', password: 'test', roles: 'observer' }
- name: Converge
hosts: keycloak
vars_files:
- ../group_vars/all/vars.yml
vars:
keycloak_quarkus_show_deprecation_warnings: false
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme"
keycloak_quarkus_bootstrap_admin_user: "remembertochangeme"
keycloak_quarkus_hostname: "http://{{ inventory_hostname }}:8080"
keycloak_quarkus_log: file
keycloak_quarkus_log_level: info
keycloak_quarkus_https_key_file_enabled: true
keycloak_quarkus_key_file_copy_enabled: true
keycloak_quarkus_key_content: "{{ lookup('file', inventory_hostname + '.key') }}"
keycloak_quarkus_cert_file_copy_enabled: true
keycloak_quarkus_cert_file_src: "{{ inventory_hostname }}.pem"
keycloak_quarkus_ks_vault_enabled: true
keycloak_quarkus_ks_vault_file: "/opt/keycloak/vault/keystore.p12"
keycloak_quarkus_ks_vault_pass: keystorepassword
keycloak_quarkus_systemd_wait_for_port: true
keycloak_quarkus_systemd_wait_for_timeout: 20
keycloak_quarkus_systemd_wait_for_delay: 2
keycloak_quarkus_systemd_wait_for_log: true
keycloak_quarkus_ha_enabled: true
keycloak_quarkus_restart_strategy: restart/serial.yml
keycloak_quarkus_db_user: keycloak
keycloak_quarkus_db_pass: mysecretpass
keycloak_quarkus_db_url: jdbc:postgresql://postgres:5432/keycloak
keycloak_quarkus_cache_remote: true
keycloak_quarkus_cache_remote_username: supervisor
keycloak_quarkus_cache_remote_password: remembertochangeme
keycloak_quarkus_cache_remote_host: "infinispan1"
keycloak_quarkus_cache_remote_port: 11222
keycloak_quarkus_cache_remote_tls_enabled: false
keycloak_quarkus_additional_env_vars:
- key: KC_FEATURES
value: clusterless
- key: KC_FEATURES_DISABLED
value: persistent-user-sessions
roles:
- role: keycloak_quarkus

View File

@@ -0,0 +1,80 @@
---
driver:
name: docker
platforms:
- name: keycloak1
image: registry.access.redhat.com/ubi9/ubi-init:latest
pre_build_image: true
privileged: true
command: "/usr/sbin/init"
groups:
- keycloak
networks:
- name: rhbk
port_bindings:
- "8080/tcp"
- "8443/tcp"
- "9000/tcp"
- name: infinispan1
image: registry.access.redhat.com/ubi9/ubi-init:latest
pre_build_image: true
privileged: true
command: "/usr/sbin/init"
groups:
- infinispan
networks:
- name: rhbk
port_bindings:
- "11222/tcp"
- name: postgres
image: ubuntu/postgres:14-22.04_beta
pre_build_image: true
privileged: true
command: postgres
groups:
- database
networks:
- name: rhbk
port_bindings:
- "5432/tcp"
mounts:
- type: bind
target: /etc/postgresql/postgresql.conf
source: ${MOLECULE_PROJECT_DIRECTORY}/molecule/quarkus_ha/postgresql/postgresql.conf
env:
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: mysecretpass
POSTGRES_DB: keycloak
POSTGRES_HOST_AUTH_METHOD: trust
provisioner:
name: ansible
config_options:
defaults:
interpreter_python: auto_silent
ssh_connection:
pipelining: false
playbooks:
prepare: prepare.yml
converge: converge.yml
verify: verify.yml
inventory:
host_vars:
localhost:
ansible_python_interpreter: "{{ ansible_playbook_python }}"
env:
ANSIBLE_FORCE_COLOR: "true"
PYTHONHTTPSVERIFY: 0
verifier:
name: ansible
scenario:
test_sequence:
- cleanup
- destroy
- create
- prepare
- converge
- idempotence
- side_effect
- verify
- cleanup
- destroy

View File

@@ -0,0 +1,750 @@
# -----------------------------
# PostgreSQL configuration file
# -----------------------------
#
# This file consists of lines of the form:
#
# name = value
#
# (The "=" is optional.) Whitespace may be used. Comments are introduced with
# "#" anywhere on a line. The complete list of parameter names and allowed
# values can be found in the PostgreSQL documentation.
#
# The commented-out settings shown in this file represent the default values.
# Re-commenting a setting is NOT sufficient to revert it to the default value;
# you need to reload the server.
#
# This file is read on server startup and when the server receives a SIGHUP
# signal. If you edit the file on a running system, you have to SIGHUP the
# server for the changes to take effect, run "pg_ctl reload", or execute
# "SELECT pg_reload_conf()". Some parameters, which are marked below,
# require a server shutdown and restart to take effect.
#
# Any parameter can also be given as a command-line option to the server, e.g.,
# "postgres -c log_connections=on". Some parameters can be changed at run time
# with the "SET" SQL command.
#
# Memory units: kB = kilobytes Time units: ms = milliseconds
# MB = megabytes s = seconds
# GB = gigabytes min = minutes
# TB = terabytes h = hours
# d = days
#------------------------------------------------------------------------------
# FILE LOCATIONS
#------------------------------------------------------------------------------
# The default values of these variables are driven from the -D command-line
# option or PGDATA environment variable, represented here as ConfigDir.
#data_directory = 'ConfigDir' # use data in another directory
# (change requires restart)
#hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file
# (change requires restart)
#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file
# (change requires restart)
# If external_pid_file is not explicitly set, no extra PID file is written.
#external_pid_file = '' # write an extra PID file
# (change requires restart)
#------------------------------------------------------------------------------
# CONNECTIONS AND AUTHENTICATION
#------------------------------------------------------------------------------
# - Connection Settings -
listen_addresses = '*' # what IP address(es) to listen on;
# comma-separated list of addresses;
# defaults to 'localhost'; use '*' for all
# (change requires restart)
#port = 5432 # (change requires restart)
#max_connections = 100 # (change requires restart)
#superuser_reserved_connections = 3 # (change requires restart)
#unix_socket_directories = '/tmp' # comma-separated list of directories
# (change requires restart)
#unix_socket_group = '' # (change requires restart)
#unix_socket_permissions = 0777 # begin with 0 to use octal notation
# (change requires restart)
#bonjour = off # advertise server via Bonjour
# (change requires restart)
#bonjour_name = '' # defaults to the computer name
# (change requires restart)
# - TCP settings -
# see "man 7 tcp" for details
#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds;
# 0 selects the system default
#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds;
# 0 selects the system default
#tcp_keepalives_count = 0 # TCP_KEEPCNT;
# 0 selects the system default
#tcp_user_timeout = 0 # TCP_USER_TIMEOUT, in milliseconds;
# 0 selects the system default
# - Authentication -
#authentication_timeout = 1min # 1s-600s
#password_encryption = md5 # md5 or scram-sha-256
#db_user_namespace = off
# GSSAPI using Kerberos
#krb_server_keyfile = ''
#krb_caseins_users = off
# - SSL -
#ssl = off
#ssl_ca_file = ''
#ssl_cert_file = 'server.crt'
#ssl_crl_file = ''
#ssl_key_file = 'server.key'
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
#ssl_prefer_server_ciphers = on
#ssl_ecdh_curve = 'prime256v1'
#ssl_min_protocol_version = 'TLSv1'
#ssl_max_protocol_version = ''
#ssl_dh_params_file = ''
#ssl_passphrase_command = ''
#ssl_passphrase_command_supports_reload = off
#------------------------------------------------------------------------------
# RESOURCE USAGE (except WAL)
#------------------------------------------------------------------------------
# - Memory -
#shared_buffers = 32MB # min 128kB
# (change requires restart)
#huge_pages = try # on, off, or try
# (change requires restart)
#temp_buffers = 8MB # min 800kB
#max_prepared_transactions = 0 # zero disables the feature
# (change requires restart)
# Caution: it is not advisable to set max_prepared_transactions nonzero unless
# you actively intend to use prepared transactions.
#work_mem = 4MB # min 64kB
#maintenance_work_mem = 64MB # min 1MB
#autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem
#max_stack_depth = 2MB # min 100kB
#shared_memory_type = mmap # the default is the first option
# supported by the operating system:
# mmap
# sysv
# windows
# (change requires restart)
#dynamic_shared_memory_type = posix # the default is the first option
# supported by the operating system:
# posix
# sysv
# windows
# mmap
# (change requires restart)
# - Disk -
#temp_file_limit = -1 # limits per-process temp file space
# in kB, or -1 for no limit
# - Kernel Resources -
#max_files_per_process = 1000 # min 25
# (change requires restart)
# - Cost-Based Vacuum Delay -
#vacuum_cost_delay = 0 # 0-100 milliseconds (0 disables)
#vacuum_cost_page_hit = 1 # 0-10000 credits
#vacuum_cost_page_miss = 10 # 0-10000 credits
#vacuum_cost_page_dirty = 20 # 0-10000 credits
#vacuum_cost_limit = 200 # 1-10000 credits
# - Background Writer -
#bgwriter_delay = 200ms # 10-10000ms between rounds
#bgwriter_lru_maxpages = 100 # max buffers written/round, 0 disables
#bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round
#bgwriter_flush_after = 0 # measured in pages, 0 disables
# - Asynchronous Behavior -
#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching
#max_worker_processes = 8 # (change requires restart)
#max_parallel_maintenance_workers = 2 # taken from max_parallel_workers
#max_parallel_workers_per_gather = 2 # taken from max_parallel_workers
#parallel_leader_participation = on
#max_parallel_workers = 8 # maximum number of max_worker_processes that
# can be used in parallel operations
#old_snapshot_threshold = -1 # 1min-60d; -1 disables; 0 is immediate
# (change requires restart)
#backend_flush_after = 0 # measured in pages, 0 disables
#------------------------------------------------------------------------------
# WRITE-AHEAD LOG
#------------------------------------------------------------------------------
# - Settings -
#wal_level = replica # minimal, replica, or logical
# (change requires restart)
#fsync = on # flush data to disk for crash safety
# (turning this off can cause
# unrecoverable data corruption)
#synchronous_commit = on # synchronization level;
# off, local, remote_write, remote_apply, or on
#wal_sync_method = fsync # the default is the first option
# supported by the operating system:
# open_datasync
# fdatasync (default on Linux)
# fsync
# fsync_writethrough
# open_sync
#full_page_writes = on # recover from partial page writes
#wal_compression = off # enable compression of full-page writes
#wal_log_hints = off # also do full page writes of non-critical updates
# (change requires restart)
#wal_init_zero = on # zero-fill new WAL files
#wal_recycle = on # recycle WAL files
#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers
# (change requires restart)
#wal_writer_delay = 200ms # 1-10000 milliseconds
#wal_writer_flush_after = 1MB # measured in pages, 0 disables
#commit_delay = 0 # range 0-100000, in microseconds
#commit_siblings = 5 # range 1-1000
# - Checkpoints -
#checkpoint_timeout = 5min # range 30s-1d
#max_wal_size = 1GB
#min_wal_size = 80MB
#checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0
#checkpoint_flush_after = 0 # measured in pages, 0 disables
#checkpoint_warning = 30s # 0 disables
# - Archiving -
#archive_mode = off # enables archiving; off, on, or always
# (change requires restart)
#archive_command = '' # command to use to archive a logfile segment
# placeholders: %p = path of file to archive
# %f = file name only
# e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f'
#archive_timeout = 0 # force a logfile segment switch after this
# number of seconds; 0 disables
# - Archive Recovery -
# These are only used in recovery mode.
#restore_command = '' # command to use to restore an archived logfile segment
# placeholders: %p = path of file to restore
# %f = file name only
# e.g. 'cp /mnt/server/archivedir/%f %p'
# (change requires restart)
#archive_cleanup_command = '' # command to execute at every restartpoint
#recovery_end_command = '' # command to execute at completion of recovery
# - Recovery Target -
# Set these only when performing a targeted recovery.
#recovery_target = '' # 'immediate' to end recovery as soon as a
# consistent state is reached
# (change requires restart)
#recovery_target_name = '' # the named restore point to which recovery will proceed
# (change requires restart)
#recovery_target_time = '' # the time stamp up to which recovery will proceed
# (change requires restart)
#recovery_target_xid = '' # the transaction ID up to which recovery will proceed
# (change requires restart)
#recovery_target_lsn = '' # the WAL LSN up to which recovery will proceed
# (change requires restart)
#recovery_target_inclusive = on # Specifies whether to stop:
# just after the specified recovery target (on)
# just before the recovery target (off)
# (change requires restart)
#recovery_target_timeline = 'latest' # 'current', 'latest', or timeline ID
# (change requires restart)
#recovery_target_action = 'pause' # 'pause', 'promote', 'shutdown'
# (change requires restart)
#------------------------------------------------------------------------------
# REPLICATION
#------------------------------------------------------------------------------
# - Sending Servers -
# Set these on the master and on any standby that will send replication data.
#max_wal_senders = 10 # max number of walsender processes
# (change requires restart)
#wal_keep_segments = 0 # in logfile segments; 0 disables
#wal_sender_timeout = 60s # in milliseconds; 0 disables
#max_replication_slots = 10 # max number of replication slots
# (change requires restart)
#track_commit_timestamp = off # collect timestamp of transaction commit
# (change requires restart)
# - Master Server -
# These settings are ignored on a standby server.
#synchronous_standby_names = '' # standby servers that provide sync rep
# method to choose sync standbys, number of sync standbys,
# and comma-separated list of application_name
# from standby(s); '*' = all
#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed
# - Standby Servers -
# These settings are ignored on a master server.
#primary_conninfo = '' # connection string to sending server
# (change requires restart)
#primary_slot_name = '' # replication slot on sending server
# (change requires restart)
#promote_trigger_file = '' # file name whose presence ends recovery
#hot_standby = on # "off" disallows queries during recovery
# (change requires restart)
#max_standby_archive_delay = 30s # max delay before canceling queries
# when reading WAL from archive;
# -1 allows indefinite delay
#max_standby_streaming_delay = 30s # max delay before canceling queries
# when reading streaming WAL;
# -1 allows indefinite delay
#wal_receiver_status_interval = 10s # send replies at least this often
# 0 disables
#hot_standby_feedback = off # send info from standby to prevent
# query conflicts
#wal_receiver_timeout = 60s # time that receiver waits for
# communication from master
# in milliseconds; 0 disables
#wal_retrieve_retry_interval = 5s # time to wait before retrying to
# retrieve WAL after a failed attempt
#recovery_min_apply_delay = 0 # minimum delay for applying changes during recovery
# - Subscribers -
# These settings are ignored on a publisher.
#max_logical_replication_workers = 4 # taken from max_worker_processes
# (change requires restart)
#max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers
#------------------------------------------------------------------------------
# QUERY TUNING
#------------------------------------------------------------------------------
# - Planner Method Configuration -
#enable_bitmapscan = on
#enable_hashagg = on
#enable_hashjoin = on
#enable_indexscan = on
#enable_indexonlyscan = on
#enable_material = on
#enable_mergejoin = on
#enable_nestloop = on
#enable_parallel_append = on
#enable_seqscan = on
#enable_sort = on
#enable_tidscan = on
#enable_partitionwise_join = off
#enable_partitionwise_aggregate = off
#enable_parallel_hash = on
#enable_partition_pruning = on
# - Planner Cost Constants -
#seq_page_cost = 1.0 # measured on an arbitrary scale
#random_page_cost = 4.0 # same scale as above
#cpu_tuple_cost = 0.01 # same scale as above
#cpu_index_tuple_cost = 0.005 # same scale as above
#cpu_operator_cost = 0.0025 # same scale as above
#parallel_tuple_cost = 0.1 # same scale as above
#parallel_setup_cost = 1000.0 # same scale as above
#jit_above_cost = 100000 # perform JIT compilation if available
# and query more expensive than this;
# -1 disables
#jit_inline_above_cost = 500000 # inline small functions if query is
# more expensive than this; -1 disables
#jit_optimize_above_cost = 500000 # use expensive JIT optimizations if
# query is more expensive than this;
# -1 disables
#min_parallel_table_scan_size = 8MB
#min_parallel_index_scan_size = 512kB
#effective_cache_size = 4GB
# - Genetic Query Optimizer -
#geqo = on
#geqo_threshold = 12
#geqo_effort = 5 # range 1-10
#geqo_pool_size = 0 # selects default based on effort
#geqo_generations = 0 # selects default based on effort
#geqo_selection_bias = 2.0 # range 1.5-2.0
#geqo_seed = 0.0 # range 0.0-1.0
# - Other Planner Options -
#default_statistics_target = 100 # range 1-10000
#constraint_exclusion = partition # on, off, or partition
#cursor_tuple_fraction = 0.1 # range 0.0-1.0
#from_collapse_limit = 8
#join_collapse_limit = 8 # 1 disables collapsing of explicit
# JOIN clauses
#force_parallel_mode = off
#jit = on # allow JIT compilation
#plan_cache_mode = auto # auto, force_generic_plan or
# force_custom_plan
#------------------------------------------------------------------------------
# REPORTING AND LOGGING
#------------------------------------------------------------------------------
# - Where to Log -
#log_destination = 'stderr' # Valid values are combinations of
# stderr, csvlog, syslog, and eventlog,
# depending on platform. csvlog
# requires logging_collector to be on.
# This is used when logging to stderr:
#logging_collector = off # Enable capturing of stderr and csvlog
# into log files. Required to be on for
# csvlogs.
# (change requires restart)
# These are only used if logging_collector is on:
#log_directory = 'log' # directory where log files are written,
# can be absolute or relative to PGDATA
#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern,
# can include strftime() escapes
#log_file_mode = 0600 # creation mode for log files,
# begin with 0 to use octal notation
#log_truncate_on_rotation = off # If on, an existing log file with the
# same name as the new log file will be
# truncated rather than appended to.
# But such truncation only occurs on
# time-driven rotation, not on restarts
# or size-driven rotation. Default is
# off, meaning append to existing files
# in all cases.
#log_rotation_age = 1d # Automatic rotation of logfiles will
# happen after that time. 0 disables.
#log_rotation_size = 10MB # Automatic rotation of logfiles will
# happen after that much log output.
# 0 disables.
# These are relevant when logging to syslog:
#syslog_facility = 'LOCAL0'
#syslog_ident = 'postgres'
#syslog_sequence_numbers = on
#syslog_split_messages = on
# This is only relevant when logging to eventlog (win32):
# (change requires restart)
#event_source = 'PostgreSQL'
# - When to Log -
#log_min_messages = warning # values in order of decreasing detail:
# debug5
# debug4
# debug3
# debug2
# debug1
# info
# notice
# warning
# error
# log
# fatal
# panic
#log_min_error_statement = error # values in order of decreasing detail:
# debug5
# debug4
# debug3
# debug2
# debug1
# info
# notice
# warning
# error
# log
# fatal
# panic (effectively off)
#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements
# and their durations, > 0 logs only
# statements running at least this number
# of milliseconds
#log_transaction_sample_rate = 0.0 # Fraction of transactions whose statements
# are logged regardless of their duration. 1.0 logs all
# statements from all transactions, 0.0 never logs.
# - What to Log -
#debug_print_parse = off
#debug_print_rewritten = off
#debug_print_plan = off
#debug_pretty_print = on
#log_checkpoints = off
#log_connections = off
#log_disconnections = off
#log_duration = off
#log_error_verbosity = default # terse, default, or verbose messages
#log_hostname = off
#log_line_prefix = '%m [%p] ' # special values:
# %a = application name
# %u = user name
# %d = database name
# %r = remote host and port
# %h = remote host
# %p = process ID
# %t = timestamp without milliseconds
# %m = timestamp with milliseconds
# %n = timestamp with milliseconds (as a Unix epoch)
# %i = command tag
# %e = SQL state
# %c = session ID
# %l = session line number
# %s = session start timestamp
# %v = virtual transaction ID
# %x = transaction ID (0 if none)
# %q = stop here in non-session
# processes
# %% = '%'
# e.g. '<%u%%%d> '
#log_lock_waits = off # log lock waits >= deadlock_timeout
#log_statement = 'none' # none, ddl, mod, all
#log_replication_commands = off
#log_temp_files = -1 # log temporary files equal or larger
# than the specified size in kilobytes;
# -1 disables, 0 logs all temp files
#log_timezone = 'GMT'
#------------------------------------------------------------------------------
# PROCESS TITLE
#------------------------------------------------------------------------------
#cluster_name = '' # added to process titles if nonempty
# (change requires restart)
#update_process_title = on
#------------------------------------------------------------------------------
# STATISTICS
#------------------------------------------------------------------------------
# - Query and Index Statistics Collector -
#track_activities = on
#track_counts = on
#track_io_timing = off
#track_functions = none # none, pl, all
#track_activity_query_size = 1024 # (change requires restart)
#stats_temp_directory = 'pg_stat_tmp'
# - Monitoring -
#log_parser_stats = off
#log_planner_stats = off
#log_executor_stats = off
#log_statement_stats = off
#------------------------------------------------------------------------------
# AUTOVACUUM
#------------------------------------------------------------------------------
#autovacuum = on # Enable autovacuum subprocess? 'on'
# requires track_counts to also be on.
#log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and
# their durations, > 0 logs only
# actions running at least this number
# of milliseconds.
#autovacuum_max_workers = 3 # max number of autovacuum subprocesses
# (change requires restart)
#autovacuum_naptime = 1min # time between autovacuum runs
#autovacuum_vacuum_threshold = 50 # min number of row updates before
# vacuum
#autovacuum_analyze_threshold = 50 # min number of row updates before
# analyze
#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum
#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze
#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
# (change requires restart)
#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age
# before forced vacuum
# (change requires restart)
#autovacuum_vacuum_cost_delay = 2ms # default vacuum cost delay for
# autovacuum, in milliseconds;
# -1 means use vacuum_cost_delay
#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for
# autovacuum, -1 means use
# vacuum_cost_limit
#------------------------------------------------------------------------------
# CLIENT CONNECTION DEFAULTS
#------------------------------------------------------------------------------
# - Statement Behavior -
#client_min_messages = notice # values in order of decreasing detail:
# debug5
# debug4
# debug3
# debug2
# debug1
# log
# notice
# warning
# error
#search_path = '"$user", public' # schema names
#row_security = on
#default_tablespace = '' # a tablespace name, '' uses the default
#temp_tablespaces = '' # a list of tablespace names, '' uses
# only default tablespace
#default_table_access_method = 'heap'
#check_function_bodies = on
#default_transaction_isolation = 'read committed'
#default_transaction_read_only = off
#default_transaction_deferrable = off
#session_replication_role = 'origin'
#statement_timeout = 0 # in milliseconds, 0 is disabled
#lock_timeout = 0 # in milliseconds, 0 is disabled
#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled
#vacuum_freeze_min_age = 50000000
#vacuum_freeze_table_age = 150000000
#vacuum_multixact_freeze_min_age = 5000000
#vacuum_multixact_freeze_table_age = 150000000
#vacuum_cleanup_index_scale_factor = 0.1 # fraction of total number of tuples
# before index cleanup, 0 always performs
# index cleanup
#bytea_output = 'hex' # hex, escape
#xmlbinary = 'base64'
#xmloption = 'content'
#gin_fuzzy_search_limit = 0
#gin_pending_list_limit = 4MB
# - Locale and Formatting -
#datestyle = 'iso, mdy'
#intervalstyle = 'postgres'
#timezone = 'GMT'
#timezone_abbreviations = 'Default' # Select the set of available time zone
# abbreviations. Currently, there are
# Default
# Australia (historical usage)
# India
# You can create your own file in
# share/timezonesets/.
#extra_float_digits = 1 # min -15, max 3; any value >0 actually
# selects precise output mode
#client_encoding = sql_ascii # actually, defaults to database
# encoding
# These settings are initialized by initdb, but they can be changed.
#lc_messages = 'C' # locale for system error message
# strings
#lc_monetary = 'C' # locale for monetary formatting
#lc_numeric = 'C' # locale for number formatting
#lc_time = 'C' # locale for time formatting
# default configuration for text search
#default_text_search_config = 'pg_catalog.simple'
# - Shared Library Preloading -
#shared_preload_libraries = '' # (change requires restart)
#local_preload_libraries = ''
#session_preload_libraries = ''
#jit_provider = 'llvmjit' # JIT library to use
# - Other Defaults -
#dynamic_library_path = '$libdir'
#------------------------------------------------------------------------------
# LOCK MANAGEMENT
#------------------------------------------------------------------------------
#deadlock_timeout = 1s
#max_locks_per_transaction = 64 # min 10
# (change requires restart)
#max_pred_locks_per_transaction = 64 # min 10
# (change requires restart)
#max_pred_locks_per_relation = -2 # negative values mean
# (max_pred_locks_per_transaction
# / -max_pred_locks_per_relation) - 1
#max_pred_locks_per_page = 2 # min 0
#------------------------------------------------------------------------------
# VERSION AND PLATFORM COMPATIBILITY
#------------------------------------------------------------------------------
# - Previous PostgreSQL Versions -
#array_nulls = on
#backslash_quote = safe_encoding # on, off, or safe_encoding
#escape_string_warning = on
#lo_compat_privileges = off
#operator_precedence_warning = off
#quote_all_identifiers = off
#standard_conforming_strings = on
#synchronize_seqscans = on
# - Other Platforms and Clients -
#transform_null_equals = off
#------------------------------------------------------------------------------
# ERROR HANDLING
#------------------------------------------------------------------------------
#exit_on_error = off # terminate session on any error?
#restart_after_crash = on # reinitialize after backend crash?
#data_sync_retry = off # retry or panic on failure to fsync
# data?
# (change requires restart)
#------------------------------------------------------------------------------
# CONFIG FILE INCLUDES
#------------------------------------------------------------------------------
# These options allow settings to be loaded from files other than the
# default postgresql.conf. Note that these are directives, not variable
# assignments, so they can usefully be given more than once.
#include_dir = '...' # include files ending in '.conf' from
# a directory, e.g., 'conf.d'
#include_if_exists = '...' # include file only if it exists
#include = '...' # include file
#------------------------------------------------------------------------------
# CUSTOMIZED OPTIONS
#------------------------------------------------------------------------------
# Add settings for extensions here

View File

@@ -0,0 +1,50 @@
---
- name: Prepare
hosts: 'keycloak:infinispan'
vars_files:
- ../group_vars/all/vars.yml
tasks:
- name: "Display hera_home if defined."
ansible.builtin.set_fact:
hera_home: "{{ lookup('env', 'HERA_HOME') }}"
- name: "Ensure common prepare phase are set."
ansible.builtin.include_tasks: ../prepare.yml
- name: Create certificate request
ansible.builtin.command: "openssl req -x509 -newkey rsa:4096 -keyout {{ inventory_hostname }}.key -out {{ inventory_hostname }}.pem -sha256 -days 365 -nodes -subj '/CN={{ inventory_hostname }}'"
args:
chdir: "{{ playbook_dir }}"
delegate_to: localhost
changed_when: False
- name: Create vault directory
become: "{{ molecule_prepare_require_privilege_escalation }}"
ansible.builtin.file:
state: directory
path: "/opt/keycloak/vault"
mode: 0755
- name: Make sure a jre is available (for keytool to prepare keystore)
delegate_to: localhost
ansible.builtin.package:
name: "{{ 'java-17-openjdk-headless' if hera_home | length > 0 else 'openjdk-17-jdk-headless' }}"
state: present
become: "{{ molecule_prepare_require_privilege_escalation }}"
failed_when: false
- name: Create vault keystore
ansible.builtin.command: keytool -importpass -alias TestRealm_testalias -keystore keystore.p12 -storepass keystorepassword
args:
chdir: "{{ playbook_dir }}"
delegate_to: localhost
register: keytool_cmd
changed_when: False
failed_when: not 'already exists' in keytool_cmd.stdout and keytool_cmd.rc != 0
- name: Copy certificates and vault
become: "{{ molecule_prepare_require_privilege_escalation }}"
ansible.builtin.copy:
src: keystore.p12
dest: /opt/keycloak/vault/keystore.p12
mode: 0444

View File

@@ -0,0 +1 @@
../../roles

View File

@@ -0,0 +1,31 @@
---
- name: Verify
hosts: keycloak
vars_files:
- ../group_vars/all/vars.yml
tasks:
- name: Populate service facts
ansible.builtin.service_facts:
- name: Check if keycloak service started
ansible.builtin.assert:
that:
- ansible_facts.services["keycloak.service"]["state"] == "running"
- ansible_facts.services["keycloak.service"]["status"] == "enabled"
fail_msg: "Service not running"
- name: Set internal envvar
ansible.builtin.set_fact:
hera_home: "{{ lookup('env', 'HERA_HOME') }}"
- name: Check log file
become: "{{ molecule_prepare_require_privilege_escalation }}"
ansible.builtin.stat:
path: /var/log/keycloak/keycloak.log
register: keycloak_log_file
- name: Check if keycloak file exists
ansible.builtin.assert:
that:
- keycloak_log_file.stat.exists
- not keycloak_log_file.stat.isdir

View File

@@ -2,6 +2,7 @@
- name: Converge - name: Converge
hosts: all hosts: all
vars_files: vars_files:
- ../group_vars/all/vars.yml
- vars.yml - vars.yml
vars: vars:
keycloak_quarkus_show_deprecation_warnings: false keycloak_quarkus_show_deprecation_warnings: false

View File

@@ -4,7 +4,7 @@ dependency:
options: options:
requirements-file: molecule/requirements.yml requirements-file: molecule/requirements.yml
driver: driver:
name: docker name: podman
platforms: platforms:
- name: instance - name: instance
image: registry.access.redhat.com/ubi9/ubi-init:latest image: registry.access.redhat.com/ubi9/ubi-init:latest
@@ -27,6 +27,10 @@ provisioner:
host_vars: host_vars:
localhost: localhost:
ansible_python_interpreter: "{{ ansible_playbook_python }}" ansible_python_interpreter: "{{ ansible_playbook_python }}"
env:
ANSIBLE_FORCE_COLOR: "true"
PROXY: "${PROXY}"
NO_PROXY: "${NO_PROXY}"
verifier: verifier:
name: ansible name: ansible
scenario: scenario:

View File

@@ -2,6 +2,7 @@
- name: Prepare - name: Prepare
hosts: all hosts: all
vars_files: vars_files:
- ../group_vars/all/vars.yml
- vars.yml - vars.yml
vars: vars:
sudo_pkg_name: sudo sudo_pkg_name: sudo
@@ -43,6 +44,8 @@
- name: Create certificate request - name: Create certificate request
ansible.builtin.command: openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes -subj '/CN=instance' ansible.builtin.command: openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes -subj '/CN=instance'
args:
chdir: "{{ playbook_dir }}"
delegate_to: localhost delegate_to: localhost
changed_when: false changed_when: false
roles: roles:
@@ -53,4 +56,4 @@
ansible.builtin.file: ansible.builtin.file:
path: /etc/ansible/facts.d/keycloak.fact path: /etc/ansible/facts.d/keycloak.fact
state: absent state: absent
become: true become: "{{ molecule_prepare_require_privilege_escalation }}"

View File

@@ -14,10 +14,9 @@
- ansible_facts.services["keycloak.service"]["state"] == "running" - ansible_facts.services["keycloak.service"]["state"] == "running"
- ansible_facts.services["keycloak.service"]["status"] == "enabled" - ansible_facts.services["keycloak.service"]["status"] == "enabled"
- name: Verify we are running on requested jvm - name: Verify Java 21 runtime is installed (UBI/RHEL)
ansible.builtin.shell: | ansible.builtin.command:
set -eo pipefail cmd: rpm -q java-21-openjdk-headless
ps -ef | grep 'etc/alternatives/.*21' | grep -v grep
changed_when: false changed_when: false
- name: Verify token api call - name: Verify token api call
@@ -28,5 +27,5 @@
validate_certs: no validate_certs: no
register: keycloak_auth_response register: keycloak_auth_response
until: keycloak_auth_response.status == 200 until: keycloak_auth_response.status == 200
retries: 2 retries: 45
delay: 2 delay: 5

View File

@@ -2,10 +2,13 @@
collections: collections:
- name: middleware_automation.common - name: middleware_automation.common
- name: middleware_automation.jbcs - name: middleware_automation.jbcs
- name: middleware_automation.infinispan
- name: community.general - name: community.general
- name: ansible.posix - name: ansible.posix
- name: community.docker - name: community.docker
version: ">=3.8.0" version: ">=3.8.0"
- name: containers.podman
version: ">=1.8.1"
roles: roles:
- name: elan.simple_nginx_reverse_proxy - name: elan.simple_nginx_reverse_proxy

View File

@@ -0,0 +1,27 @@
---
- name: Playbook for Keycloak Authentication Flow Configuration
hosts: all
vars:
keycloak_admin_user: admin
keycloak_admin_password: "remembertochangeme"
keycloak_url: "http://localhost:8080"
keycloak_realm: TestRealm
tasks:
- name: Create authentication flow with executions
middleware_automation.keycloak.keycloak_authentication_flow:
auth_keycloak_url: "{{ keycloak_url }}"
auth_realm: master
auth_username: "{{ keycloak_admin_user }}"
auth_password: "{{ keycloak_admin_password }}"
realm: "{{ keycloak_realm }}"
alias: my-browser-flow
description: "Custom browser authentication flow"
provider_id: basic-flow
executions:
- provider_id: auth-cookie
requirement: ALTERNATIVE
- provider_id: auth-password
requirement: REQUIRED
- provider_id: auth-otp-form
requirement: ALTERNATIVE
state: present

View File

@@ -0,0 +1,48 @@
---
- name: Playbook for Keycloak Client Scope Configuration
hosts: all
vars:
keycloak_admin_user: admin
keycloak_admin_password: "remembertochangeme"
keycloak_url: "http://localhost:8080"
keycloak_realm: TestRealm
tasks:
- name: Create client scope with protocol mappers
middleware_automation.keycloak.keycloak_client_scope:
auth_keycloak_url: "{{ keycloak_url }}"
auth_realm: master
auth_username: "{{ keycloak_admin_user }}"
auth_password: "{{ keycloak_admin_password }}"
realm: "{{ keycloak_realm }}"
name: TestClientScope
description: "Client scope created via Ansible"
protocol: openid-connect
protocol_mappers:
- name: email
protocolMapper: oidc-usermodel-attribute-mapper
config:
user.attribute: email
claim.name: email
jsonType.label: String
id.token.claim: "true"
access.token.claim: "true"
userinfo.token.claim: "true"
- name: firstName
protocolMapper: oidc-usermodel-attribute-mapper
config:
user.attribute: firstName
claim.name: given_name
jsonType.label: String
id.token.claim: "true"
access.token.claim: "true"
userinfo.token.claim: "true"
- name: username
protocolMapper: oidc-usermodel-attribute-mapper
config:
user.attribute: username
claim.name: preferred_username
jsonType.label: String
id.token.claim: "true"
access.token.claim: "true"
userinfo.token.claim: "true"
state: present

View File

@@ -0,0 +1,39 @@
---
- name: Playbook for Keycloak Realm and Client Configuration
hosts: all
tasks:
- name: Keycloak Realm Role
ansible.builtin.include_role:
name: middleware_automation.keycloak.keycloak_realm
vars:
keycloak_admin_password: "remembertochangeme"
keycloak_realm: TestRealm
keycloak_client_default_roles:
- TestRoleAdmin
- TestRoleUser
keycloak_client_users:
- username: TestUser
password: password
client_roles:
- client: TestClient1
role: TestRoleUser
realm: TestRealm
- username: TestAdmin
password: password
client_roles:
- client: TestClient1
role: TestRoleUser
realm: TestRealm
- client: TestClient1
role: TestRoleAdmin
realm: TestRealm
keycloak_clients:
- name: TestClient1
client_id: TestClient1
roles: "{{ keycloak_client_default_roles }}"
realm: TestRealm
public_client: true
web_origins:
- http://testclient1origin/application
- http://testclient1origin/other
users: "{{ keycloak_client_users }}"

View File

@@ -0,0 +1,296 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2024, Contributors to the middleware_automation.keycloak collection
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: keycloak_authentication_flow
short_description: Allows administration of Keycloak authentication flows via Keycloak API
description:
- This module allows you to add, remove or modify Keycloak authentication flows via the Keycloak REST API.
It requires access to the REST API via OpenID Connect; the user connecting and the client being
used must have the requisite access rights. In a default Keycloak installation, admin-cli
and an admin user would work, as would a separate client definition with the scope tailored
to your needs and a user having the expected roles.
- This module supports creating new top-level authentication flows, copying existing flows,
and adding execution steps to a flow.
attributes:
check_mode:
support: full
diff_mode:
support: full
options:
state:
description:
- State of the authentication flow.
- On V(present), the flow will be created if it does not yet exist.
- On V(absent), the flow will be removed if it exists.
default: 'present'
type: str
choices:
- present
- absent
alias:
type: str
required: true
description:
- Alias (name) of the authentication flow.
description:
type: str
description:
- Description of the authentication flow.
default: ''
realm:
type: str
description:
- The Keycloak realm under which this authentication flow resides.
default: 'master'
provider_id:
type: str
description:
- The provider ID for the flow.
default: 'basic-flow'
aliases:
- providerId
copy_from:
type: str
description:
- If set, the new flow is created as a copy of the flow with this alias.
- Cannot be used together with O(executions).
aliases:
- copyFrom
executions:
type: list
elements: dict
description:
- A list of executions (authenticator steps) to add to the flow.
- Each execution is a dict with keys C(provider_id) (or C(providerId)) and C(requirement).
- Executions are only added when the flow is first created.
default: []
suboptions:
provider_id:
type: str
required: true
description:
- The authenticator provider ID (e.g. V(auth-cookie), V(auth-password), V(auth-otp-form)).
aliases:
- providerId
requirement:
type: str
required: true
description:
- The requirement level for this execution.
choices:
- REQUIRED
- ALTERNATIVE
- DISABLED
- CONDITIONAL
extends_documentation_fragment:
- middleware_automation.keycloak.keycloak
- middleware_automation.keycloak.attributes
author:
- Paulo Menon (@paulomenon)
'''
EXAMPLES = '''
- name: Create an authentication flow with executions
middleware_automation.keycloak.keycloak_authentication_flow:
auth_keycloak_url: http://localhost:8080
auth_realm: master
auth_username: admin
auth_password: password
realm: TestRealm
alias: my-browser-flow
description: "Custom browser flow"
provider_id: basic-flow
executions:
- provider_id: auth-cookie
requirement: ALTERNATIVE
- provider_id: auth-password
requirement: REQUIRED
- provider_id: auth-otp-form
requirement: ALTERNATIVE
state: present
delegate_to: localhost
- name: Create an authentication flow by copying an existing one
middleware_automation.keycloak.keycloak_authentication_flow:
auth_keycloak_url: http://localhost:8080
auth_realm: master
auth_username: admin
auth_password: password
realm: TestRealm
alias: my-copy-of-browser
copy_from: browser
state: present
delegate_to: localhost
- name: Create a flow using token authentication
middleware_automation.keycloak.keycloak_authentication_flow:
auth_keycloak_url: http://localhost:8080
token: MY_TOKEN
realm: TestRealm
alias: my-flow
state: present
delegate_to: localhost
- name: Delete an authentication flow
middleware_automation.keycloak.keycloak_authentication_flow:
auth_keycloak_url: http://localhost:8080
auth_realm: master
auth_username: admin
auth_password: password
realm: TestRealm
alias: my-browser-flow
state: absent
delegate_to: localhost
'''
RETURN = '''
msg:
description: Message as to what action was taken.
returned: always
type: str
sample: "Authentication flow my-browser-flow has been created"
end_state:
description: Representation of the authentication flow after module execution.
returned: on success
type: dict
sample: {
"id": "uuid-here",
"alias": "my-browser-flow",
"providerId": "basic-flow",
"topLevel": true,
"builtIn": false
}
'''
from ansible_collections.middleware_automation.keycloak.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, \
keycloak_argument_spec, get_token, KeycloakError
from ansible.module_utils.basic import AnsibleModule
def main():
argument_spec = keycloak_argument_spec()
execution_spec = dict(
provider_id=dict(type='str', required=True, aliases=['providerId']),
requirement=dict(type='str', required=True, choices=['REQUIRED', 'ALTERNATIVE', 'DISABLED', 'CONDITIONAL']),
)
meta_args = dict(
state=dict(type='str', default='present', choices=['present', 'absent']),
alias=dict(type='str', required=True),
description=dict(type='str', default=''),
realm=dict(type='str', default='master'),
provider_id=dict(type='str', default='basic-flow', aliases=['providerId']),
copy_from=dict(type='str', aliases=['copyFrom']),
executions=dict(type='list', default=[], options=execution_spec, elements='dict'),
)
argument_spec.update(meta_args)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True,
required_one_of=([['token', 'auth_realm', 'auth_username', 'auth_password']]),
required_together=([['auth_realm', 'auth_username', 'auth_password']]),
mutually_exclusive=[['copy_from', 'executions']])
result = dict(changed=False, msg='', diff={}, end_state={})
try:
connection_header = get_token(module.params)
except KeycloakError as e:
module.fail_json(msg=str(e))
kc = KeycloakAPI(module, connection_header)
realm = module.params.get('realm')
alias = module.params.get('alias')
state = module.params.get('state')
description = module.params.get('description')
provider_id = module.params.get('provider_id')
copy_from = module.params.get('copy_from')
executions = module.params.get('executions')
before_flow = kc.get_authentication_flow_by_alias(alias, realm=realm)
flow_exists = bool(before_flow)
if state == 'absent':
if flow_exists:
result['changed'] = True
if module._diff:
result['diff'] = dict(before=before_flow, after='')
if module.check_mode:
module.exit_json(**result)
kc.delete_authentication_flow_by_id(before_flow['id'], realm=realm)
result['msg'] = "Authentication flow {alias} has been deleted".format(alias=alias)
else:
result['msg'] = "Authentication flow {alias} does not exist, doing nothing".format(alias=alias)
result['end_state'] = {}
module.exit_json(**result)
if flow_exists:
result['changed'] = False
result['end_state'] = before_flow
result['msg'] = "Authentication flow {alias} already exists".format(alias=alias)
module.exit_json(**result)
result['changed'] = True
flow_config = {
'alias': alias,
'description': description,
'providerId': provider_id,
}
if module._diff:
result['diff'] = dict(before='', after=flow_config)
if module.check_mode:
module.exit_json(**result)
if copy_from:
flow_config['copyFrom'] = copy_from
after_flow = kc.copy_auth_flow(flow_config, realm=realm)
result['msg'] = "Authentication flow {alias} has been created (copied from {src})".format(alias=alias, src=copy_from)
else:
after_flow = kc.create_empty_auth_flow(flow_config, realm=realm)
if executions:
for execution in executions:
exec_rep = {
'providerId': execution['provider_id'],
'requirement': execution['requirement'],
}
kc.create_execution(exec_rep, alias, realm=realm)
result['msg'] = "Authentication flow {alias} has been created".format(alias=alias)
after_flow = kc.get_authentication_flow_by_alias(alias, realm=realm)
result['end_state'] = after_flow
module.exit_json(**result)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,324 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2024, Contributors to the middleware_automation.keycloak collection
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: keycloak_client_scope
short_description: Allows administration of Keycloak client scopes via Keycloak API
description:
- This module allows you to add, remove or modify Keycloak client scopes via the Keycloak REST API.
It requires access to the REST API via OpenID Connect; the user connecting and the client being
used must have the requisite access rights. In a default Keycloak installation, admin-cli
and an admin user would work, as would a separate client definition with the scope tailored
to your needs and a user having the expected roles.
- This module also supports managing protocol mappers within a client scope.
attributes:
check_mode:
support: full
diff_mode:
support: full
options:
state:
description:
- State of the client scope.
- On V(present), the client scope will be created if it does not yet exist, or updated with the parameters you provide.
- On V(absent), the client scope will be removed if it exists.
default: 'present'
type: str
choices:
- present
- absent
name:
type: str
required: true
description:
- Name of the client scope.
description:
type: str
default: ''
description:
- Description of the client scope.
realm:
type: str
description:
- The Keycloak realm under which this client scope resides.
default: 'master'
protocol:
type: str
description:
- The protocol associated with the client scope.
default: 'openid-connect'
choices:
- openid-connect
- saml
attributes:
type: dict
description:
- A dict of key/value pairs to set as attributes for the client scope.
protocol_mappers:
type: list
elements: dict
description:
- A list of protocol mappers to associate with the client scope.
- Each mapper is a dict with the keys C(name), C(protocol), C(protocolMapper), and C(config).
default: []
suboptions:
name:
type: str
required: true
description:
- Name of the protocol mapper.
protocol:
type: str
description:
- Protocol for the mapper.
default: 'openid-connect'
protocolMapper:
type: str
required: true
description:
- The mapper type (e.g. V(oidc-usermodel-attribute-mapper), V(oidc-audience-mapper)).
aliases:
- protocol_mapper_type
config:
type: dict
required: true
description:
- Configuration for the protocol mapper.
extends_documentation_fragment:
- middleware_automation.keycloak.keycloak
- middleware_automation.keycloak.attributes
author:
- Paulo Menon (@paulomenon)
'''
EXAMPLES = '''
- name: Create a client scope with protocol mappers
middleware_automation.keycloak.keycloak_client_scope:
auth_keycloak_url: http://localhost:8080
auth_realm: master
auth_username: admin
auth_password: password
realm: TestRealm
name: my-client-scope
description: "A custom client scope"
protocol: openid-connect
protocol_mappers:
- name: email
protocol: openid-connect
protocolMapper: oidc-usermodel-attribute-mapper
config:
user.attribute: email
claim.name: email
jsonType.label: String
id.token.claim: "true"
access.token.claim: "true"
userinfo.token.claim: "true"
state: present
delegate_to: localhost
- name: Create a client scope using token authentication
middleware_automation.keycloak.keycloak_client_scope:
auth_keycloak_url: http://localhost:8080
token: MY_TOKEN
realm: TestRealm
name: my-scope
state: present
delegate_to: localhost
- name: Delete a client scope
middleware_automation.keycloak.keycloak_client_scope:
auth_keycloak_url: http://localhost:8080
auth_realm: master
auth_username: admin
auth_password: password
realm: TestRealm
name: my-client-scope
state: absent
delegate_to: localhost
'''
RETURN = '''
msg:
description: Message as to what action was taken.
returned: always
type: str
sample: "Client scope my-scope has been created"
end_state:
description: Representation of the client scope after module execution.
returned: on success
type: dict
sample: {
"id": "uuid-here",
"name": "my-scope",
"protocol": "openid-connect",
"description": "A custom scope"
}
'''
from ansible_collections.middleware_automation.keycloak.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, \
keycloak_argument_spec, get_token, KeycloakError
from ansible.module_utils.basic import AnsibleModule
def main():
argument_spec = keycloak_argument_spec()
mapper_spec = dict(
name=dict(type='str', required=True),
protocol=dict(type='str', default='openid-connect'),
protocolMapper=dict(type='str', required=True, aliases=['protocol_mapper_type']),
config=dict(type='dict', required=True),
)
meta_args = dict(
state=dict(type='str', default='present', choices=['present', 'absent']),
name=dict(type='str', required=True),
description=dict(type='str', default=''),
realm=dict(type='str', default='master'),
protocol=dict(type='str', default='openid-connect', choices=['openid-connect', 'saml']),
attributes=dict(type='dict'),
protocol_mappers=dict(type='list', default=[], options=mapper_spec, elements='dict'),
)
argument_spec.update(meta_args)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True,
required_one_of=([['token', 'auth_realm', 'auth_username', 'auth_password']]),
required_together=([['auth_realm', 'auth_username', 'auth_password']]))
result = dict(changed=False, msg='', diff={}, end_state={})
try:
connection_header = get_token(module.params)
except KeycloakError as e:
module.fail_json(msg=str(e))
kc = KeycloakAPI(module, connection_header)
realm = module.params.get('realm')
name = module.params.get('name')
state = module.params.get('state')
protocol = module.params.get('protocol')
description = module.params.get('description')
attributes = module.params.get('attributes')
protocol_mappers = module.params.get('protocol_mappers')
before_scope = kc.get_clientscope_by_name(name, realm=realm)
if state == 'absent':
if before_scope:
result['changed'] = True
if module._diff:
result['diff'] = dict(before=before_scope, after='')
if module.check_mode:
module.exit_json(**result)
kc.delete_clientscope(cid=before_scope['id'], realm=realm)
result['msg'] = "Client scope {name} has been deleted".format(name=name)
else:
result['msg'] = "Client scope {name} does not exist, doing nothing".format(name=name)
result['end_state'] = {}
module.exit_json(**result)
scope_rep = {
'name': name,
'protocol': protocol,
'description': description,
}
if attributes:
scope_rep['attributes'] = attributes
if not before_scope:
result['changed'] = True
if module._diff:
result['diff'] = dict(before='', after=scope_rep)
if module.check_mode:
module.exit_json(**result)
kc.create_clientscope(scope_rep, realm=realm)
after_scope = kc.get_clientscope_by_name(name, realm=realm)
if protocol_mappers:
for mapper in protocol_mappers:
mapper_rep = {
'name': mapper['name'],
'protocol': mapper.get('protocol', protocol),
'protocolMapper': mapper['protocolMapper'],
'config': mapper['config'],
}
kc.create_clientscope_protocolmapper(after_scope['id'], mapper_rep, realm=realm)
after_scope = kc.get_clientscope_by_name(name, realm=realm)
result['end_state'] = after_scope
result['msg'] = "Client scope {name} has been created".format(name=name)
module.exit_json(**result)
else:
changed = False
for key in ('protocol', 'description'):
if scope_rep.get(key) and scope_rep[key] != before_scope.get(key):
changed = True
break
if attributes and attributes != before_scope.get('attributes', {}):
changed = True
if changed:
result['changed'] = True
scope_rep['id'] = before_scope['id']
if module._diff:
result['diff'] = dict(before=before_scope, after=scope_rep)
if module.check_mode:
module.exit_json(**result)
kc.update_clientscope(scope_rep, realm=realm)
if protocol_mappers:
existing_mappers = kc.get_clientscope_protocolmappers(before_scope['id'], realm=realm)
existing_mapper_names = {m['name'] for m in existing_mappers}
for mapper in protocol_mappers:
if mapper['name'] not in existing_mapper_names:
result['changed'] = True
if not module.check_mode:
mapper_rep = {
'name': mapper['name'],
'protocol': mapper.get('protocol', protocol),
'protocolMapper': mapper['protocolMapper'],
'config': mapper['config'],
}
kc.create_clientscope_protocolmapper(before_scope['id'], mapper_rep, realm=realm)
after_scope = kc.get_clientscope_by_name(name, realm=realm)
result['end_state'] = after_scope
if result['changed']:
result['msg'] = "Client scope {name} has been updated".format(name=name)
else:
result['msg'] = "No changes required to client scope {name}".format(name=name)
module.exit_json(**result)
if __name__ == '__main__':
main()

View File

@@ -7,6 +7,10 @@ keycloak_download_url_9x: "https://downloads.jboss.org/keycloak/{{ keycloak_vers
keycloak_installdir: "{{ keycloak_dest }}/keycloak-{{ keycloak_version }}" keycloak_installdir: "{{ keycloak_dest }}/keycloak-{{ keycloak_version }}"
keycloak_offline_install: false keycloak_offline_install: false
# Authentication for Keycloak binary download (e.g. from internal artifact repository)
keycloak_binary_download_user:
keycloak_binary_download_pass:
### Install location and service settings ### Install location and service settings
keycloak_java_home: keycloak_java_home:
keycloak_dest: /opt/keycloak keycloak_dest: /opt/keycloak

View File

@@ -333,6 +333,14 @@ argument_specs:
default: true default: true
description: "Allow the option to ignore invalid certificates when downloading JDBC drivers from a custom URL" description: "Allow the option to ignore invalid certificates when downloading JDBC drivers from a custom URL"
type: "bool" type: "bool"
keycloak_binary_download_user:
description: "Username for HTTP Basic Auth when downloading Keycloak binary"
type: "str"
required: false
keycloak_binary_download_pass:
description: "Password for HTTP Basic Auth when downloading Keycloak binary"
type: "str"
required: false
downstream: downstream:
options: options:
sso_version: sso_version:

View File

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

View File

@@ -13,7 +13,7 @@
when: ansible_facts.os_family == "RedHat" when: ansible_facts.os_family == "RedHat"
- name: "Install packages: {{ packages_to_install }}" - name: "Install packages: {{ packages_to_install }}"
become: true become: "{{ keycloak_fastpackages_require_privilege_escalation }}"
ansible.builtin.dnf: ansible.builtin.dnf:
name: "{{ packages_to_install }}" name: "{{ packages_to_install }}"
state: present state: present
@@ -22,7 +22,7 @@
- ansible_facts.os_family == "RedHat" - ansible_facts.os_family == "RedHat"
- name: "Install packages: {{ packages_list }}" - name: "Install packages: {{ packages_list }}"
become: true become: "{{ keycloak_fastpackages_require_privilege_escalation }}"
ansible.builtin.package: ansible.builtin.package:
name: "{{ packages_list }}" name: "{{ packages_list }}"
state: present state: present

View File

@@ -6,14 +6,14 @@
- firewalld - firewalld
- name: Enable and start the firewalld service - name: Enable and start the firewalld service
become: true become: "{{ keycloak_firewalld_require_privilege_escalation }}"
ansible.builtin.systemd: ansible.builtin.systemd:
name: firewalld name: firewalld
enabled: true enabled: true
state: started state: started
- name: "Configure firewall ports for {{ keycloak.service_name }}" - name: "Configure firewall ports for {{ keycloak.service_name }}"
become: true become: "{{ keycloak_firewalld_require_privilege_escalation }}"
ansible.posix.firewalld: ansible.posix.firewalld:
port: "{{ item }}" port: "{{ item }}"
permanent: true permanent: true

View File

@@ -11,7 +11,7 @@
quiet: true quiet: true
- name: Check for an existing deployment - name: Check for an existing deployment
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
ansible.builtin.stat: ansible.builtin.stat:
path: "{{ keycloak_jboss_home }}" path: "{{ keycloak_jboss_home }}"
register: existing_deploy register: existing_deploy
@@ -20,24 +20,24 @@
when: existing_deploy.stat.exists and keycloak_force_install | bool when: existing_deploy.stat.exists and keycloak_force_install | bool
block: block:
- name: "Stop the old {{ keycloak.service_name }} service" - name: "Stop the old {{ keycloak.service_name }} service"
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
failed_when: false failed_when: false
ansible.builtin.systemd: ansible.builtin.systemd:
name: keycloak name: keycloak
state: stopped state: stopped
- name: "Remove the old {{ keycloak.service_name }} deployment" - name: "Remove the old {{ keycloak.service_name }} deployment"
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
ansible.builtin.file: ansible.builtin.file:
path: "{{ keycloak_jboss_home }}" path: "{{ keycloak_jboss_home }}"
state: absent state: absent
- name: Check for an existing deployment after possible forced removal - name: Check for an existing deployment after possible forced removal
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
ansible.builtin.stat: ansible.builtin.stat:
path: "{{ keycloak_jboss_home }}" path: "{{ keycloak_jboss_home }}"
- name: "Create service user/group for {{ keycloak.service_name }}" - name: "Create service user/group for {{ keycloak.service_name }}"
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
ansible.builtin.user: ansible.builtin.user:
name: "{{ keycloak_service_user }}" name: "{{ keycloak_service_user }}"
home: /opt/keycloak home: /opt/keycloak
@@ -45,7 +45,7 @@
create_home: false create_home: false
- name: "Create install location for {{ keycloak.service_name }}" - name: "Create install location for {{ keycloak.service_name }}"
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
ansible.builtin.file: ansible.builtin.file:
dest: "{{ keycloak_dest }}" dest: "{{ keycloak_dest }}"
state: directory state: directory
@@ -54,7 +54,7 @@
mode: '0750' mode: '0750'
- name: Create pidfile folder - name: Create pidfile folder
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
ansible.builtin.file: ansible.builtin.file:
dest: "{{ keycloak_service_pidfile | dirname }}" dest: "{{ keycloak_service_pidfile | dirname }}"
state: directory state: directory
@@ -68,7 +68,7 @@
archive: "{{ keycloak_dest }}/{{ keycloak.bundle }}" archive: "{{ keycloak_dest }}/{{ keycloak.bundle }}"
- name: Check download archive path - name: Check download archive path
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
ansible.builtin.stat: ansible.builtin.stat:
path: "{{ archive }}" path: "{{ archive }}"
register: archive_path register: archive_path
@@ -85,6 +85,8 @@
url: "{{ keycloak_download_url }}" url: "{{ keycloak_download_url }}"
dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}" dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}"
mode: '0644' mode: '0644'
url_username: "{{ keycloak_binary_download_user | default(omit) }}"
url_password: "{{ keycloak_binary_download_pass | default(omit) }}"
delegate_to: localhost delegate_to: localhost
run_once: true run_once: true
when: when:
@@ -166,13 +168,13 @@
- not archive_path.stat.exists - not archive_path.stat.exists
- local_archive_path.stat is defined - local_archive_path.stat is defined
- local_archive_path.stat.exists - local_archive_path.stat.exists
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
- name: "Check target directory: {{ keycloak.home }}" - name: "Check target directory: {{ keycloak.home }}"
ansible.builtin.stat: ansible.builtin.stat:
path: "{{ keycloak.home }}" path: "{{ keycloak.home }}"
register: path_to_workdir register: path_to_workdir
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
- name: "Extract {{ keycloak_service_desc }} archive on target" - name: "Extract {{ keycloak_service_desc }} archive on target"
ansible.builtin.unarchive: ansible.builtin.unarchive:
@@ -182,7 +184,7 @@
creates: "{{ keycloak.home }}" creates: "{{ keycloak.home }}"
owner: "{{ keycloak_service_user }}" owner: "{{ keycloak_service_user }}"
group: "{{ keycloak_service_group }}" group: "{{ keycloak_service_group }}"
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
when: when:
- new_version_downloaded.changed or not path_to_workdir.stat.exists - new_version_downloaded.changed or not path_to_workdir.stat.exists
notify: notify:
@@ -200,13 +202,13 @@
owner: "{{ keycloak_service_user }}" owner: "{{ keycloak_service_user }}"
group: "{{ keycloak_service_group }}" group: "{{ keycloak_service_group }}"
recurse: true recurse: true
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
changed_when: false changed_when: false
- name: Ensure permissions are correct on existing deploy - name: Ensure permissions are correct on existing deploy
ansible.builtin.command: chown -R "{{ keycloak_service_user }}:{{ keycloak_service_group }}" "{{ keycloak.home }}" ansible.builtin.command: chown -R "{{ keycloak_service_user }}:{{ keycloak_service_group }}" "{{ keycloak.home }}"
when: keycloak_service_runas when: keycloak_service_runas
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
changed_when: false changed_when: false
# driver and configuration # driver and configuration
@@ -215,7 +217,7 @@
when: keycloak_jdbc[keycloak_jdbc_engine].enabled when: keycloak_jdbc[keycloak_jdbc_engine].enabled
- name: "Deploy custom {{ keycloak.service_name }} config to {{ keycloak_config_path_to_standalone_xml }} from {{ keycloak_config_override_template }}" - name: "Deploy custom {{ keycloak.service_name }} config to {{ keycloak_config_path_to_standalone_xml }} from {{ keycloak_config_override_template }}"
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
ansible.builtin.template: ansible.builtin.template:
src: "templates/{{ keycloak_config_override_template }}" src: "templates/{{ keycloak_config_override_template }}"
dest: "{{ keycloak_config_path_to_standalone_xml }}" dest: "{{ keycloak_config_path_to_standalone_xml }}"
@@ -227,7 +229,7 @@
when: keycloak_config_override_template | length > 0 when: keycloak_config_override_template | length > 0
- name: "Deploy standalone {{ keycloak.service_name }} config to {{ keycloak_config_path_to_standalone_xml }}" - name: "Deploy standalone {{ keycloak.service_name }} config to {{ keycloak_config_path_to_standalone_xml }}"
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
ansible.builtin.template: ansible.builtin.template:
src: templates/standalone.xml.j2 src: templates/standalone.xml.j2
dest: "{{ keycloak_config_path_to_standalone_xml }}" dest: "{{ keycloak_config_path_to_standalone_xml }}"
@@ -255,7 +257,7 @@
when: keycloak_ha_enabled and keycloak_ha_discovery == 'TCPPING' when: keycloak_ha_enabled and keycloak_ha_discovery == 'TCPPING'
- name: "Deploy HA {{ keycloak.service_name }} config to {{ keycloak_config_path_to_standalone_xml }}" - name: "Deploy HA {{ keycloak.service_name }} config to {{ keycloak_config_path_to_standalone_xml }}"
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
ansible.builtin.template: ansible.builtin.template:
src: templates/standalone-ha.xml.j2 src: templates/standalone-ha.xml.j2
dest: "{{ keycloak_config_path_to_standalone_xml }}" dest: "{{ keycloak_config_path_to_standalone_xml }}"
@@ -270,7 +272,7 @@
- keycloak_config_override_template | length == 0 - keycloak_config_override_template | length == 0
- name: "Deploy HA {{ keycloak.service_name }} config with infinispan remote cache store to {{ keycloak_config_path_to_standalone_xml }}" - name: "Deploy HA {{ keycloak.service_name }} config with infinispan remote cache store to {{ keycloak_config_path_to_standalone_xml }}"
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
ansible.builtin.template: ansible.builtin.template:
src: templates/standalone-infinispan.xml.j2 src: templates/standalone-infinispan.xml.j2
dest: "{{ keycloak_config_path_to_standalone_xml }}" dest: "{{ keycloak_config_path_to_standalone_xml }}"
@@ -285,7 +287,7 @@
- keycloak_config_override_template | length == 0 - keycloak_config_override_template | length == 0
- name: "Deploy profile.properties file to {{ keycloak_config_path_to_properties }}" - name: "Deploy profile.properties file to {{ keycloak_config_path_to_properties }}"
become: true become: "{{ keycloak_install_require_privilege_escalation }}"
ansible.builtin.template: ansible.builtin.template:
src: keycloak-profile.properties.j2 src: keycloak-profile.properties.j2
dest: "{{ keycloak_config_path_to_properties }}" dest: "{{ keycloak_config_path_to_properties }}"

View File

@@ -6,7 +6,7 @@
- iptables - iptables
- name: "Configure firewall ports for {{ keycloak.service_name }}" - name: "Configure firewall ports for {{ keycloak.service_name }}"
become: true become: "{{ keycloak_iptables_require_privilege_escalation }}"
ansible.builtin.iptables: ansible.builtin.iptables:
destination_port: "{{ item }}" destination_port: "{{ item }}"
action: "insert" action: "insert"

View File

@@ -3,7 +3,7 @@
ansible.builtin.stat: ansible.builtin.stat:
path: "{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_dir }}" path: "{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_dir }}"
register: dest_path register: dest_path
become: true become: "{{ keycloak_jdbc_driver_require_privilege_escalation }}"
- name: "Set up module dir for JDBC Driver {{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}" - name: "Set up module dir for JDBC Driver {{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}"
ansible.builtin.file: ansible.builtin.file:
@@ -13,7 +13,7 @@
owner: "{{ keycloak_service_user }}" owner: "{{ keycloak_service_user }}"
group: "{{ keycloak_service_group }}" group: "{{ keycloak_service_group }}"
mode: '0750' mode: '0750'
become: true become: "{{ keycloak_jdbc_driver_require_privilege_escalation }}"
when: when:
- not dest_path.stat.exists - not dest_path.stat.exists
- name: "Verify valid parameters for download credentials when specified" - name: "Verify valid parameters for download credentials when specified"
@@ -34,7 +34,7 @@
url_password: "{{ keycloak_jdbc_download_pass | default(omit) }}" url_password: "{{ keycloak_jdbc_download_pass | default(omit) }}"
validate_certs: "{{ keycloak_jdbc_download_validate_certs | default(omit) }}" validate_certs: "{{ keycloak_jdbc_download_validate_certs | default(omit) }}"
mode: '0640' mode: '0640'
become: true become: "{{ keycloak_jdbc_driver_require_privilege_escalation }}"
- name: "Deploy module.xml for JDBC Driver" - name: "Deploy module.xml for JDBC Driver"
ansible.builtin.template: ansible.builtin.template:
@@ -43,4 +43,4 @@
group: "{{ keycloak_service_group }}" group: "{{ keycloak_service_group }}"
owner: "{{ keycloak_service_user }}" owner: "{{ keycloak_service_user }}"
mode: '0640' mode: '0640'
become: true become: "{{ keycloak_jdbc_driver_require_privilege_escalation }}"

View File

@@ -1,22 +1,38 @@
--- ---
# tasks file for keycloak # tasks file for keycloak
- name: Check prerequisites - name: Check prerequisites
ansible.builtin.include_tasks: prereqs.yml ansible.builtin.include_tasks:
file: prereqs.yml
apply:
tags:
- prereqs
tags: tags:
- prereqs - prereqs
- name: Distro specific tasks - name: Distro specific tasks
ansible.builtin.include_tasks: "{{ ansible_os_family | lower }}.yml" ansible.builtin.include_tasks:
file: "{{ ansible_os_family | lower }}.yml"
apply:
tags:
- unbound
tags: tags:
- unbound - unbound
- name: Include install tasks - name: Include install tasks
ansible.builtin.include_tasks: install.yml ansible.builtin.include_tasks:
file: install.yml
apply:
tags:
- install
tags: tags:
- install - install
- name: Include systemd tasks - name: Include systemd tasks
ansible.builtin.include_tasks: systemd.yml ansible.builtin.include_tasks:
file: systemd.yml
apply:
tags:
- systemd
tags: tags:
- systemd - systemd
@@ -35,7 +51,7 @@
state: link state: link
src: "{{ keycloak_jboss_home }}/standalone/log" src: "{{ keycloak_jboss_home }}/standalone/log"
dest: "{{ keycloak_log_target }}" dest: "{{ keycloak_log_target }}"
become: true become: "{{ keycloak_require_privilege_escalation }}"
- name: Set admin credentials and restart if not already created - name: Set admin credentials and restart if not already created
block: block:
@@ -59,7 +75,7 @@
- "-u{{ keycloak_admin_user }}" - "-u{{ keycloak_admin_user }}"
- "-p{{ keycloak_admin_password }}" - "-p{{ keycloak_admin_password }}"
changed_when: true changed_when: true
become: true become: "{{ keycloak_require_privilege_escalation }}"
- name: "Restart {{ keycloak.service_name }}" - name: "Restart {{ keycloak.service_name }}"
ansible.builtin.include_tasks: tasks/restart_keycloak.yml ansible.builtin.include_tasks: tasks/restart_keycloak.yml
- name: "Wait until {{ keycloak.service_name }} becomes active {{ keycloak.health_url }}" - name: "Wait until {{ keycloak.service_name }} becomes active {{ keycloak.health_url }}"

View File

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

View File

@@ -5,7 +5,7 @@
enabled: true enabled: true
state: restarted state: restarted
daemon_reload: true daemon_reload: true
become: true become: "{{ keycloak_restart_require_privilege_escalation }}"
delegate_to: "{{ ansible_play_hosts | first }}" delegate_to: "{{ ansible_play_hosts | first }}"
run_once: true run_once: true
@@ -24,5 +24,5 @@
name: keycloak name: keycloak
enabled: true enabled: true
state: restarted state: restarted
become: true become: "{{ keycloak_restart_require_privilege_escalation }}"
when: inventory_hostname != ansible_play_hosts | first when: inventory_hostname != ansible_play_hosts | first

View File

@@ -12,7 +12,7 @@
path: "{{ patch_archive }}" path: "{{ patch_archive }}"
register: patch_archive_path register: patch_archive_path
when: sso_patch_version is defined when: sso_patch_version is defined
become: true become: "{{ keycloak_rhsso_patch_require_privilege_escalation }}"
- name: Perform patch download from RHN via JBossNetwork API - name: Perform patch download from RHN via JBossNetwork API
delegate_to: localhost delegate_to: localhost
@@ -86,7 +86,7 @@
ansible.builtin.stat: ansible.builtin.stat:
path: "{{ patch_archive }}" path: "{{ patch_archive }}"
register: patch_archive_path register: patch_archive_path
become: true become: "{{ keycloak_rhsso_patch_require_privilege_escalation }}"
## copy and unpack ## copy and unpack
- name: Copy patch archive to target nodes - name: Copy patch archive to target nodes
@@ -101,7 +101,7 @@
- not patch_archive_path.stat.exists - not patch_archive_path.stat.exists
- local_archive_path.stat is defined - local_archive_path.stat is defined
- local_archive_path.stat.exists - local_archive_path.stat.exists
become: true become: "{{ keycloak_rhsso_patch_require_privilege_escalation }}"
- name: "Check installed patches" - name: "Check installed patches"
ansible.builtin.include_tasks: rhsso_cli.yml ansible.builtin.include_tasks: rhsso_cli.yml
@@ -109,7 +109,7 @@
cli_query: "patch info" cli_query: "patch info"
args: args:
apply: apply:
become: true become: "{{ keycloak_rhsso_patch_require_privilege_escalation }}"
become_user: "{{ keycloak_service_user }}" become_user: "{{ keycloak_service_user }}"
- name: "Perform patching" - name: "Perform patching"
@@ -124,7 +124,7 @@
cli_query: "patch apply {{ patch_archive }}" cli_query: "patch apply {{ patch_archive }}"
args: args:
apply: apply:
become: true become: "{{ keycloak_rhsso_patch_require_privilege_escalation }}"
become_user: "{{ keycloak_service_user }}" become_user: "{{ keycloak_service_user }}"
- name: "Restart server to ensure patch content is running" - name: "Restart server to ensure patch content is running"
@@ -135,7 +135,7 @@
- cli_result.rc == 0 - cli_result.rc == 0
args: args:
apply: apply:
become: true become: "{{ keycloak_rhsso_patch_require_privilege_escalation }}"
become_user: "{{ keycloak_service_user }}" become_user: "{{ keycloak_service_user }}"
- name: "Wait until {{ keycloak.service_name }} becomes active {{ keycloak.health_url }}" - name: "Wait until {{ keycloak.service_name }} becomes active {{ keycloak.health_url }}"
@@ -152,7 +152,7 @@
cli_query: "patch info" cli_query: "patch info"
args: args:
apply: apply:
become: true become: "{{ keycloak_rhsso_patch_require_privilege_escalation }}"
become_user: "{{ keycloak_service_user }}" become_user: "{{ keycloak_service_user }}"
- name: "Verify installed patch version" - name: "Verify installed patch version"

View File

@@ -5,7 +5,7 @@
enabled: true enabled: true
state: started state: started
daemon_reload: true daemon_reload: true
become: true become: "{{ keycloak_start_require_privilege_escalation }}"
- name: "Wait until {{ keycloak.service_name }} becomes active {{ keycloak.health_url }}" - name: "Wait until {{ keycloak.service_name }} becomes active {{ keycloak.health_url }}"
ansible.builtin.uri: ansible.builtin.uri:

View File

@@ -4,4 +4,4 @@
name: keycloak name: keycloak
enabled: true enabled: true
state: stopped state: stopped
become: true become: "{{ keycloak_stop_require_privilege_escalation }}"

View File

@@ -1,6 +1,6 @@
--- ---
- name: "Configure {{ keycloak.service_name }} service script wrapper" - name: "Configure {{ keycloak.service_name }} service script wrapper"
become: true become: "{{ keycloak_systemd_require_privilege_escalation }}"
ansible.builtin.template: ansible.builtin.template:
src: keycloak-service.sh.j2 src: keycloak-service.sh.j2
dest: "{{ keycloak_dest }}/keycloak-service.sh" dest: "{{ keycloak_dest }}/keycloak-service.sh"
@@ -11,7 +11,7 @@
- restart keycloak - restart keycloak
- name: "Configure sysconfig file for {{ keycloak.service_name }} service" - name: "Configure sysconfig file for {{ keycloak.service_name }} service"
become: true become: "{{ keycloak_systemd_require_privilege_escalation }}"
ansible.builtin.template: ansible.builtin.template:
src: keycloak-sysconfig.j2 src: keycloak-sysconfig.j2
dest: "{{ keycloak_sysconf_file }}" dest: "{{ keycloak_sysconf_file }}"
@@ -28,7 +28,7 @@
owner: root owner: root
group: root group: root
mode: '0644' mode: '0644'
become: true become: "{{ keycloak_systemd_require_privilege_escalation }}"
register: systemdunit register: systemdunit
notify: notify:
- restart keycloak - restart keycloak

View File

@@ -33,7 +33,7 @@ Role Defaults
| Variable | Description | Default | | Variable | Description | Default |
|:---------|:------------|:--------| |:---------|:------------|:--------|
|`keycloak_quarkus_version`| keycloak.org package version | `26.0.7` | |`keycloak_quarkus_version`| keycloak.org package version | `26.4.7` |
|`keycloak_quarkus_offline_install` | Perform an offline install | `False`| |`keycloak_quarkus_offline_install` | Perform an offline install | `False`|
|`keycloak_quarkus_dest`| Installation root path | `/opt/keycloak` | |`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_url` | Download URL for keycloak | `https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}` |
@@ -60,13 +60,13 @@ Role Defaults
|`keycloak_quarkus_java_heap_opts`| Heap memory JVM setting | `-Xms1024m -Xmx2048m` | |`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_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_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 | `[]` | |`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_frontend_url`| Deprecated, use `keycloak_quarkus_hostname` instead. | |
|`keycloak_quarkus_admin_url`| Deprecated, use `keycloak_quarkus_hostname_admin` 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_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_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/master/.well-known/openid-configuration` | |`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/master/.well-known/openid-configuration` |
|`keycloak_quarkus_proxy_headers`| Parse reverse proxy headers (`forwarded` or `xforwarded`) | `""` | |`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_keystore_password` is not empty | `{{ keycloak.home }}/conf/conf_store.p12` if `keycloak_quarkus_keystore_password != ''`, else `''` | |`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_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_firewalld` | Ensure firewalld is running and configure keycloak ports | `False` |
|`keycloak_quarkus_configure_iptables` | Ensure iptables is configured for keycloak ports | `False` | |`keycloak_quarkus_configure_iptables` | Ensure iptables is configured for keycloak ports | `False` |
@@ -77,8 +77,9 @@ Role Defaults
| Variable | Description | Default | | Variable | Description | Default |
|:---------|:------------|:--------| |:---------|:------------|:--------|
|`keycloak_quarkus_ha_enabled`| Enable auto configuration for database backend, clustering and remote caches on infinispan | `False` | |`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 | `TCPPING` | |`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_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_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` | 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_port_number`| Which port the systemd unit should wait for | `{{ keycloak_quarkus_https_port }}` |
@@ -97,7 +98,7 @@ Role Defaults
| Variable | Description | Default | | 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`| 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_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_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_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. | | |`keycloak_quarkus_hostname_strict_backchannel`| Deprecated, use (the inverted!)`keycloak_quarkus_hostname_backchannel_dynamic` instead. | |
@@ -131,6 +132,17 @@ Role Defaults
|`keycloak_quarkus_http_enabled`| Enable listener on HTTP port | `True` | |`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 #### Database configuration
| Variable | Description | Default | | Variable | Description | Default |
@@ -146,11 +158,14 @@ Role Defaults
| Variable | Description | Default | | 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_username` | Username for connecting to infinispan | `supervisor` |
|`keycloak_quarkus_cache_remote_password` | Password for connecting to infinispan | `supervisor` | |`keycloak_quarkus_cache_remote_password` | Password for connecting to infinispan | `supervisor` |
|`keycloak_quarkus_cache_remote_host` | host name/port for connecting to infinispan, eg. host1:11222;host2:11222 | `localhost:11222` | |`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_sasl_mechanism` | Infinispan auth mechanism | `SCRAM-SHA-512` |
|`keycloak_quarkus_cache_remote_tls_enabled` | Whether infinispan uses TLS connection | `false` | |`keycloak_quarkus_cache_remote_tls_enabled` | Whether infinispan uses TLS connection | `false` |
|`keycloak_quarkus_cache_embedded_properties` | Embedded cache properties | `` |
#### Logging configuration #### Logging configuration
@@ -163,7 +178,7 @@ Role Defaults
|`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_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_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_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_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` | |`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` |
@@ -179,8 +194,8 @@ Role Defaults
|`keycloak_quarkus_config_dir` | Path for configuration | `{{ keycloak_quarkus_home }}/conf` | |`keycloak_quarkus_config_dir` | Path for configuration | `{{ keycloak_quarkus_home }}/conf` |
|`keycloak_quarkus_master_realm` | Name for rest authentication realm | `master` | |`keycloak_quarkus_master_realm` | Name for rest authentication realm | `master` |
|`keycloak_auth_client` | Authentication client for configuration REST calls | `admin-cli` | |`keycloak_auth_client` | Authentication client for configuration REST calls | `admin-cli` |
|`keycloak_force_install` | Remove pre-existing versions of service | `False` | |`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 | `edge` | |`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_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_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_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` |
@@ -218,6 +233,7 @@ keycloak_quarkus_providers:
restart: true # optional, whether to rebuild config and restart the service after deploying, default true 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 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 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 maven: # optional, for download using maven
repository_url: https://maven.pkg.github.com/OWNER/REPOSITORY # optional, maven repo url repository_url: https://maven.pkg.github.com/OWNER/REPOSITORY # optional, maven repo url
group_id: my.group # optional, maven group id group_id: my.group # optional, maven group id
@@ -228,6 +244,10 @@ keycloak_quarkus_providers:
properties: # optional, list of key-values properties: # optional, list of key-values
- key: default-connection-pool-size - key: default-connection-pool-size
value: 10 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: the definition above will generate the following build command:
@@ -247,8 +267,8 @@ Provider definition:
```yaml ```yaml
keycloak_quarkus_policies: keycloak_quarkus_policies:
- name: xato-net-10-million-passwords.txt # required, resulting file name - name: john-the-ripper.txt # required, resulting file name
url: https://github.com/danielmiessler/SecLists/raw/master/Passwords/xato-net-10-million-passwords.txt # required, url for download 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`] type: password-blacklists # optional, defaults to `password-blacklists`; supported values: [`password-blacklists`]
``` ```

View File

@@ -1,10 +1,14 @@
--- ---
### Configuration specific to keycloak ### Configuration specific to keycloak
keycloak_quarkus_version: 26.0.8 keycloak_quarkus_version: 26.4.7
keycloak_quarkus_archive: "keycloak-{{ keycloak_quarkus_version }}.zip" 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_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 }}" 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 # whether to install from local archive
keycloak_quarkus_offline_install: false keycloak_quarkus_offline_install: false
@@ -39,12 +43,20 @@ keycloak_quarkus_http_port: 8080
keycloak_quarkus_https_port: 8443 keycloak_quarkus_https_port: 8443
keycloak_quarkus_http_management_port: 9000 keycloak_quarkus_http_management_port: 9000
keycloak_quarkus_jgroups_port: 7800 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_heap_opts: "-Xms1024m -Xmx2048m"
keycloak_quarkus_java_jvm_opts: "-XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 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 -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 -Djava.security.egd=file:/dev/urandom -XX:+UseParallelGC -XX:GCTimeRatio=4
-XX:AdaptiveSizePolicyWeight=90 -XX:FlightRecorderOptions=stackdepth=512" -XX:AdaptiveSizePolicyWeight=90 -XX:FlightRecorderOptions=stackdepth=512
keycloak_quarkus_java_opts: "{{ keycloak_quarkus_java_heap_opts + ' ' + keycloak_quarkus_java_jvm_opts }}" 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: [] keycloak_quarkus_additional_env_vars: []
### TLS/HTTPS configuration ### TLS/HTTPS configuration
@@ -69,7 +81,7 @@ keycloak_quarkus_config_key_store_password: ''
### Enable configuration for database backend, clustering and remote caches on infinispan ### Enable configuration for database backend, clustering and remote caches on infinispan
keycloak_quarkus_ha_enabled: false keycloak_quarkus_ha_enabled: false
keycloak_quarkus_ha_discovery: "TCPPING" keycloak_quarkus_ha_discovery: "JDBCPING"
### Enable database configuration, must be enabled when HA is configured ### Enable database configuration, must be enabled when HA is configured
keycloak_quarkus_db_enabled: "{{ keycloak_quarkus_ha_enabled }}" keycloak_quarkus_db_enabled: "{{ keycloak_quarkus_ha_enabled }}"
keycloak_quarkus_systemd_wait_for_port: "{{ keycloak_quarkus_ha_enabled }}" keycloak_quarkus_systemd_wait_for_port: "{{ keycloak_quarkus_ha_enabled }}"
@@ -80,7 +92,7 @@ keycloak_quarkus_systemd_wait_for_delay: 10
### keycloak frontend url ### keycloak frontend url
keycloak_quarkus_hostname: keycloak_quarkus_hostname:
keycloak_quarkus_hostname_admin: keycloak_quarkus_hostname_admin: ""
### Set the path relative to / for serving resources. The path must start with a / ### Set the path relative to / for serving resources. The path must start with a /
### (set to `/auth` for retrocompatibility with pre-quarkus releases) ### (set to `/auth` for retrocompatibility with pre-quarkus releases)
@@ -97,7 +109,7 @@ keycloak_quarkus_hostname_backchannel_dynamic: false
keycloak_quarkus_proxy_headers: "" keycloak_quarkus_proxy_headers: ""
# deprecated: proxy address forwarding mode if the server is behind a reverse proxy. [none, edge, reencrypt, passthrough] # deprecated: proxy address forwarding mode if the server is behind a reverse proxy. [none, edge, reencrypt, passthrough]
keycloak_quarkus_proxy_mode: edge keycloak_quarkus_proxy_mode: none
# disable xa transactions # disable xa transactions
keycloak_quarkus_transaction_xa_enabled: true keycloak_quarkus_transaction_xa_enabled: true
@@ -109,10 +121,22 @@ keycloak_quarkus_spi_sticky_session_encoder_infinispan_should_attach_route: true
keycloak_quarkus_metrics_enabled: false keycloak_quarkus_metrics_enabled: false
keycloak_quarkus_health_enabled: true 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) ### 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_username: supervisor
keycloak_quarkus_cache_remote_password: supervisor keycloak_quarkus_cache_remote_password: supervisor
keycloak_quarkus_cache_remote_host: "localhost:11222" keycloak_quarkus_cache_remote_host: localhost
keycloak_quarkus_cache_remote_port: 11222
keycloak_quarkus_cache_remote_tls_enabled: false keycloak_quarkus_cache_remote_tls_enabled: false
keycloak_quarkus_cache_remote_sasl_mechanism: SCRAM-SHA-512 keycloak_quarkus_cache_remote_sasl_mechanism: SCRAM-SHA-512
@@ -124,19 +148,19 @@ keycloak_quarkus_db_user: keycloak-user
keycloak_quarkus_db_pass: keycloak-pass keycloak_quarkus_db_pass: keycloak-pass
keycloak_quarkus_db_url: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_db_engine].url }}" 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 }}" keycloak_quarkus_db_driver_version: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_db_engine].version }}"
# override the variables above, following defaults show minimum supported versions # override the variables above, following defaults show recommended version as per
# https://access.redhat.com/articles/7033107
keycloak_quarkus_default_jdbc: keycloak_quarkus_default_jdbc:
postgres: postgres:
url: 'jdbc:postgresql://localhost:5432/keycloak' url: 'jdbc:postgresql://localhost:5432/keycloak'
version: 9.4.1212 version: 42.7.7
mariadb: mariadb:
url: 'jdbc:mariadb://localhost:3306/keycloak' url: 'jdbc:mariadb://localhost:3306/keycloak'
version: 2.7.4 version: 3.5.2
mssql: mssql:
url: 'jdbc:sqlserver://localhost:1433;databaseName=keycloak;' url: 'jdbc:sqlserver://localhost:1433;databaseName=keycloak;'
version: 12.8.1 version: 13.2.0
driver_jar_url: "https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/12.8.1.jre11/mssql-jdbc-12.8.1.jre11.jar" driver_jar_url: "https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/13.2.0.jre11/mssql-jdbc-13.2.0.jre11.jar"
# cf. https://docs.redhat.com/en/documentation/red_hat_build_of_keycloak/26.0/html-single/server_configuration_guide/index#db-installing-the-microsoft-sql-server-driver
### logging configuration ### logging configuration
keycloak_quarkus_log: file keycloak_quarkus_log: file
keycloak_quarkus_log_level: info keycloak_quarkus_log_level: info
@@ -163,3 +187,5 @@ keycloak_quarkus_restart_health_check: true
keycloak_quarkus_restart_health_check_delay: 10 keycloak_quarkus_restart_health_check_delay: 10
keycloak_quarkus_restart_health_check_retries: 25 keycloak_quarkus_restart_health_check_retries: 25
keycloak_quarkus_restart_pause: 15 keycloak_quarkus_restart_pause: 15
keycloak_quarkus_force_install: false

View File

@@ -2,7 +2,7 @@ argument_specs:
main: main:
options: options:
keycloak_quarkus_version: keycloak_quarkus_version:
default: "26.0.8" default: "26.4.7"
description: "keycloak.org package version" description: "keycloak.org package version"
type: "str" type: "str"
keycloak_quarkus_archive: keycloak_quarkus_archive:
@@ -72,6 +72,10 @@ argument_specs:
default: "admin" default: "admin"
description: "Administration user account, only for bootstrapping" description: "Administration user account, only for bootstrapping"
type: "str" type: "str"
keycloak_quarkus_force_install:
default: false
description: "Remove pre-existing versions of service"
type: "bool"
keycloak_quarkus_bootstrap_admin_password: keycloak_quarkus_bootstrap_admin_password:
required: true required: true
description: "Password of admin account, only for bootstrapping" description: "Password of admin account, only for bootstrapping"
@@ -179,7 +183,7 @@ argument_specs:
type: "str" type: "str"
keycloak_quarkus_config_key_store_file: keycloak_quarkus_config_key_store_file:
default: "{{ keycloak.home }}/conf/conf_store.p12" default: "{{ keycloak.home }}/conf/conf_store.p12"
description: "Path to the configuration key store; only used if `keycloak_quarkus_keystore_password` is not empty" description: "Path to the configuration key store; only used if `keycloak_quarkus_config_key_store_password` is not empty"
type: "str" type: "str"
keycloak_quarkus_config_key_store_password: keycloak_quarkus_config_key_store_password:
default: "" default: ""
@@ -196,9 +200,25 @@ argument_specs:
description: "Port of the management interface. Relevant only when something is exposed on the management interface - see the guide for details." description: "Port of the management interface. Relevant only when something is exposed on the management interface - see the guide for details."
type: "int" type: "int"
keycloak_quarkus_jgroups_port: keycloak_quarkus_jgroups_port:
description: 'jgroups bind port'
default: 7800 default: 7800
description: "jgroups cluster tcp port"
type: "int" 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: keycloak_quarkus_java_heap_opts:
default: "-Xms1024m -Xmx2048m" default: "-Xms1024m -Xmx2048m"
description: "Heap memory JVM setting" description: "Heap memory JVM setting"
@@ -211,7 +231,7 @@ argument_specs:
description: "Other JVM settings" description: "Other JVM settings"
type: "str" type: "str"
keycloak_quarkus_java_opts: keycloak_quarkus_java_opts:
default: "{{ keycloak_quarkus_java_heap_opts + ' ' + keycloak_quarkus_java_jvm_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" description: "JVM arguments, by default heap_opts + jvm_opts, if overriden it takes precedence over them"
type: "str" type: "str"
keycloak_quarkus_additional_env_vars: keycloak_quarkus_additional_env_vars:
@@ -259,6 +279,10 @@ argument_specs:
default: true default: true
description: "If the server should expose health check endpoints on the management interface" description: "If the server should expose health check endpoints on the management interface"
type: "bool" type: "bool"
keycloak_quarkus_cache_remote:
description: "Whether to connect to remote cache infinispan server"
default: false
type: 'bool'
keycloak_quarkus_cache_remote_username: keycloak_quarkus_cache_remote_username:
default: "supervisor" default: "supervisor"
description: "Username for connecting to infinispan" description: "Username for connecting to infinispan"
@@ -268,8 +292,12 @@ argument_specs:
description: "Password for connecting to infinispan" description: "Password for connecting to infinispan"
type: "str" type: "str"
keycloak_quarkus_cache_remote_host: keycloak_quarkus_cache_remote_host:
default: "localhost:11222" default: "localhost"
description: "host name/port for connecting to infinispan, eg. host1:11222;host2:11222" description: "Hostname for connecting to infinispan"
type: "str"
keycloak_quarkus_cache_remote_port:
default: "11222"
description: "Port for connecting to infinispan"
type: "str" type: "str"
keycloak_quarkus_cache_remote_sasl_mechanism: keycloak_quarkus_cache_remote_sasl_mechanism:
default: "SCRAM-SHA-512" default: "SCRAM-SHA-512"
@@ -336,9 +364,9 @@ argument_specs:
Set the log file handler rotation file suffix. When used, the file will be rotated based on its suffix. Note: If the suffix ends 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. with .zip or .gz, the rotation file will also be compressed.
keycloak_quarkus_proxy_mode: keycloak_quarkus_proxy_mode:
default: 'edge' default: 'none'
type: "str" type: "str"
description: "The proxy address forwarding mode if the server is behind a reverse proxy. Set to 'none' if not using a proxy" 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: keycloak_quarkus_proxy_headers:
default: "" default: ""
type: "str" type: "str"
@@ -468,10 +496,30 @@ argument_specs:
description: "Path local to controller for offline/download of install archives" description: "Path local to controller for offline/download of install archives"
default: "{{ lookup('env', 'PWD') }}" default: "{{ lookup('env', 'PWD') }}"
type: "str" 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: downstream:
options: options:
rhbk_version: rhbk_version:
default: "26.0.11" default: "26.4.7"
description: "Red Hat Build of Keycloak version" description: "Red Hat Build of Keycloak version"
type: "str" type: "str"
rhbk_archive: rhbk_archive:

View File

@@ -1,6 +1,6 @@
--- ---
- name: Save ansible custom facts - name: Save ansible custom facts
become: true become: "{{ keycloak_quarkus_bootstrapped_require_privilege_escalation }}"
ansible.builtin.template: ansible.builtin.template:
src: keycloak.fact.j2 src: keycloak.fact.j2
dest: /etc/ansible/facts.d/keycloak.fact dest: /etc/ansible/facts.d/keycloak.fact

View File

@@ -6,7 +6,7 @@
value: "{{ keycloak_quarkus_db_pass }}" value: "{{ keycloak_quarkus_db_pass }}"
- name: "Initialize empty configuration key store" - name: "Initialize empty configuration key store"
become: true become: "{{ keycloak_quarkus_config_store_require_privilege_escalation }}"
# keytool doesn't allow creating an empty key store, so this is a hacky way around it # 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 ansible.builtin.shell: | # noqa blocked_modules shell is necessary here
set -o nounset # abort on unbound variable set -o nounset # abort on unbound variable
@@ -38,7 +38,7 @@
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 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: "{{ store_items }}" loop: "{{ store_items }}"
no_log: true no_log: true
become: true become: "{{ keycloak_quarkus_config_store_require_privilege_escalation }}"
changed_when: true changed_when: true
notify: notify:
- restart keycloak - restart keycloak
@@ -49,4 +49,4 @@
owner: "{{ keycloak.service_user }}" owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}" group: "{{ keycloak.service_group }}"
mode: '0400' mode: '0400'
become: true become: "{{ keycloak_quarkus_config_store_require_privilege_escalation }}"

View File

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

View File

@@ -13,7 +13,7 @@
when: ansible_facts.os_family == "RedHat" when: ansible_facts.os_family == "RedHat"
- name: "Install packages: {{ packages_to_install }}" - name: "Install packages: {{ packages_to_install }}"
become: true become: "{{ keycloak_quarkus_fastpackages_require_privilege_escalation }}"
ansible.builtin.dnf: ansible.builtin.dnf:
name: "{{ packages_to_install }}" name: "{{ packages_to_install }}"
state: present state: present
@@ -22,7 +22,7 @@
- ansible_facts.os_family == "RedHat" - ansible_facts.os_family == "RedHat"
- name: "Install packages: {{ packages_list }}" - name: "Install packages: {{ packages_list }}"
become: true become: "{{ keycloak_quarkus_fastpackages_require_privilege_escalation }}"
ansible.builtin.package: ansible.builtin.package:
name: "{{ packages_list }}" name: "{{ packages_list }}"
state: present state: present

View File

@@ -6,14 +6,14 @@
- firewalld - firewalld
- name: Enable and start the firewalld service - name: Enable and start the firewalld service
become: true become: "{{ keycloak_quarkus_firewalld_require_privilege_escalation }}"
ansible.builtin.systemd: ansible.builtin.systemd:
name: firewalld name: firewalld
enabled: true enabled: true
state: started state: started
- name: "Configure firewall for {{ keycloak.service_name }} http port" - name: "Configure firewall for {{ keycloak.service_name }} http port"
become: true become: "{{ keycloak_quarkus_firewalld_require_privilege_escalation }}"
ansible.posix.firewalld: ansible.posix.firewalld:
port: "{{ item }}" port: "{{ item }}"
permanent: true permanent: true
@@ -24,7 +24,7 @@
when: keycloak_quarkus_http_enabled | bool when: keycloak_quarkus_http_enabled | bool
- name: "Configure firewall for {{ keycloak.service_name }} ports" - name: "Configure firewall for {{ keycloak.service_name }} ports"
become: true become: "{{ keycloak_quarkus_firewalld_require_privilege_escalation }}"
ansible.posix.firewalld: ansible.posix.firewalld:
port: "{{ item }}" port: "{{ item }}"
permanent: true permanent: true

View File

@@ -12,13 +12,34 @@
quiet: true quiet: true
- name: Check for an existing deployment - name: Check for an existing deployment
become: true become: "{{ keycloak_quarkus_install_require_privilege_escalation }}"
ansible.builtin.stat: ansible.builtin.stat:
path: "{{ keycloak.home }}" path: "{{ keycloak.home }}"
register: existing_deploy register: existing_deploy
- name: Stop and restart if existing deployment exists and install forced
when: existing_deploy.stat.exists and keycloak_quarkus_force_install | bool
block:
- name: "Stop the old {{ keycloak.service_name }} service"
become: "{{ keycloak_quarkus_install_require_privilege_escalation }}"
failed_when: false
ansible.builtin.systemd:
name: keycloak
state: stopped
- name: "Remove the old {{ keycloak.service_name }} deployment"
become: "{{ keycloak_quarkus_install_require_privilege_escalation }}"
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 }}"
ansible.builtin.stat:
path: "{{ keycloak_quarkus_home }}"
register: existing_deploy
- name: "Create {{ keycloak.service_name }} service user/group" - name: "Create {{ keycloak.service_name }} service user/group"
become: true become: "{{ keycloak_quarkus_install_require_privilege_escalation }}"
ansible.builtin.user: ansible.builtin.user:
name: "{{ keycloak.service_user }}" name: "{{ keycloak.service_user }}"
home: /opt/keycloak home: /opt/keycloak
@@ -26,7 +47,7 @@
create_home: false create_home: false
- name: "Create {{ keycloak.service_name }} install location" - name: "Create {{ keycloak.service_name }} install location"
become: true become: "{{ keycloak_quarkus_install_require_privilege_escalation }}"
ansible.builtin.file: ansible.builtin.file:
dest: "{{ keycloak_quarkus_dest }}" dest: "{{ keycloak_quarkus_dest }}"
state: directory state: directory
@@ -35,7 +56,7 @@
mode: '0750' mode: '0750'
- name: Create directory for ansible custom facts - name: Create directory for ansible custom facts
become: true become: "{{ keycloak_quarkus_install_require_privilege_escalation }}"
ansible.builtin.file: ansible.builtin.file:
state: directory state: directory
recurse: true recurse: true
@@ -47,7 +68,7 @@
archive: "{{ keycloak_quarkus_dest }}/{{ keycloak.bundle }}" archive: "{{ keycloak_quarkus_dest }}/{{ keycloak.bundle }}"
- name: Check download archive path - name: Check download archive path
become: true become: "{{ keycloak_quarkus_install_require_privilege_escalation }}"
ansible.builtin.stat: ansible.builtin.stat:
path: "{{ archive }}" path: "{{ archive }}"
register: archive_path register: archive_path
@@ -58,6 +79,8 @@
url: "{{ keycloak_quarkus_download_url }}" url: "{{ keycloak_quarkus_download_url }}"
dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}" dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}"
mode: '0640' mode: '0640'
url_username: "{{ keycloak_quarkus_binary_download_user | default(omit) }}"
url_password: "{{ keycloak_quarkus_binary_download_pass | default(omit) }}"
delegate_to: localhost delegate_to: localhost
become: false become: false
run_once: true run_once: true
@@ -77,6 +100,7 @@
- not archive_path.stat.exists - not archive_path.stat.exists
- rhbk_enable is defined and rhbk_enable - rhbk_enable is defined and rhbk_enable
- not keycloak.offline_install - not keycloak.offline_install
- keycloak_quarkus_alternate_download_url is undefined
block: block:
- name: Retrieve product download using JBoss Network API - name: Retrieve product download using JBoss Network API
middleware_automation.common.product_search: middleware_automation.common.product_search:
@@ -148,13 +172,13 @@
- not archive_path.stat.exists - not archive_path.stat.exists
- local_archive_path.stat is defined - local_archive_path.stat is defined
- local_archive_path.stat.exists - local_archive_path.stat.exists
become: true become: "{{ keycloak_quarkus_install_require_privilege_escalation }}"
- name: "Check target directory: {{ keycloak.home }}/bin/" - name: "Check target directory: {{ keycloak.home }}/bin/"
ansible.builtin.stat: ansible.builtin.stat:
path: "{{ keycloak.home }}/bin/" path: "{{ keycloak.home }}/bin/"
register: path_to_workdir register: path_to_workdir
become: true become: "{{ keycloak_quarkus_install_require_privilege_escalation }}"
- name: "Extract Keycloak archive on target" # noqa no-handler need to run this here - name: "Extract Keycloak archive on target" # noqa no-handler need to run this here
ansible.builtin.unarchive: ansible.builtin.unarchive:
@@ -164,7 +188,7 @@
creates: "{{ keycloak.home }}/bin/" creates: "{{ keycloak.home }}/bin/"
owner: "{{ keycloak.service_user }}" owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}" group: "{{ keycloak.service_group }}"
become: true become: "{{ keycloak_quarkus_install_require_privilege_escalation }}"
when: when:
- (not path_to_workdir.stat.exists) or new_version_downloaded.changed - (not path_to_workdir.stat.exists) or new_version_downloaded.changed
notify: notify:
@@ -183,7 +207,7 @@
owner: "{{ keycloak.service_user }}" owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}" group: "{{ keycloak.service_group }}"
mode: '0640' mode: '0640'
become: true become: "{{ keycloak_quarkus_install_require_privilege_escalation }}"
when: when:
- keycloak_quarkus_https_key_file_enabled is defined and keycloak_quarkus_https_key_file_enabled - 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_file_copy_enabled is defined and keycloak_quarkus_key_file_copy_enabled
@@ -196,7 +220,7 @@
owner: "{{ keycloak.service_user }}" owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}" group: "{{ keycloak.service_group }}"
mode: '0644' mode: '0644'
become: true become: "{{ keycloak_quarkus_install_require_privilege_escalation }}"
when: when:
- keycloak_quarkus_https_key_file_enabled is defined and keycloak_quarkus_https_key_file_enabled - 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_copy_enabled is defined and keycloak_quarkus_cert_file_copy_enabled
@@ -215,7 +239,8 @@
owner: "{{ keycloak.service_user }}" owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}" group: "{{ keycloak.service_group }}"
mode: '0640' mode: '0640'
become: true checksum: "{{ item.checksum | default(omit) }}"
become: "{{ keycloak_quarkus_install_require_privilege_escalation }}"
loop: "{{ keycloak_quarkus_providers }}" loop: "{{ keycloak_quarkus_providers }}"
when: item.url is defined and item.url | length > 0 when: item.url is defined and item.url | length > 0
notify: "{{ ['invalidate keycloak theme cache', 'rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}" notify: "{{ ['invalidate keycloak theme cache', 'rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}"
@@ -235,7 +260,6 @@
loop: "{{ keycloak_quarkus_providers }}" loop: "{{ keycloak_quarkus_providers }}"
when: item.maven is defined when: item.maven is defined
no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}" 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 maven providers" - name: "Copy maven providers"
ansible.builtin.copy: ansible.builtin.copy:
@@ -244,10 +268,12 @@
owner: "{{ keycloak.service_user }}" owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}" group: "{{ keycloak.service_group }}"
mode: '0640' mode: '0640'
become: true checksum: "{{ item.checksum | default(omit) }}"
become: "{{ keycloak_quarkus_install_require_privilege_escalation }}"
loop: "{{ keycloak_quarkus_providers }}" loop: "{{ keycloak_quarkus_providers }}"
when: item.maven is defined when: item.maven is defined
no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}" 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" - name: "Copy local providers"
ansible.builtin.copy: ansible.builtin.copy:
@@ -256,7 +282,8 @@
owner: "{{ keycloak.service_user }}" owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}" group: "{{ keycloak.service_group }}"
mode: '0640' mode: '0640'
become: true remote_src: "{{ item.remote | default(false) }}"
become: "{{ keycloak_quarkus_install_require_privilege_escalation }}"
loop: "{{ keycloak_quarkus_providers }}" loop: "{{ keycloak_quarkus_providers }}"
when: item.local_path is defined when: item.local_path is defined
notify: "{{ ['invalidate keycloak theme cache', 'rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}" notify: "{{ ['invalidate keycloak theme cache', 'rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}"
@@ -268,7 +295,7 @@
owner: "{{ keycloak.service_user }}" owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}" group: "{{ keycloak.service_group }}"
mode: '0750' mode: '0750'
become: true become: "{{ keycloak_quarkus_install_require_privilege_escalation }}"
loop: "{{ keycloak_quarkus_supported_policy_types }}" loop: "{{ keycloak_quarkus_supported_policy_types }}"
- name: "Install custom policies" - name: "Install custom policies"
@@ -278,7 +305,7 @@
owner: "{{ keycloak.service_user }}" owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}" group: "{{ keycloak.service_group }}"
mode: '0640' mode: '0640'
become: true become: "{{ keycloak_quarkus_install_require_privilege_escalation }}"
loop: "{{ keycloak_quarkus_policies }}" loop: "{{ keycloak_quarkus_policies }}"
when: item.url is defined and item.url | length > 0 when: item.url is defined and item.url | length > 0
notify: "restart keycloak" notify: "restart keycloak"

View File

@@ -8,4 +8,4 @@
ansible.builtin.file: ansible.builtin.file:
path: "{{ keycloak.home }}/data/tmp/kc-gzip-cache" path: "{{ keycloak.home }}/data/tmp/kc-gzip-cache"
state: absent state: absent
become: true become: "{{ keycloak_quarkus_invalidate_theme_cache_require_privilege_escalation }}"

View File

@@ -6,7 +6,7 @@
- iptables - iptables
- name: "Configure firewall ports for {{ keycloak.service_name }}" - name: "Configure firewall ports for {{ keycloak.service_name }}"
become: true become: "{{ keycloak_quarkus_iptables_require_privilege_escalation }}"
ansible.builtin.iptables: ansible.builtin.iptables:
destination_port: "{{ item }}" destination_port: "{{ item }}"
action: "insert" action: "insert"

View File

@@ -17,6 +17,6 @@
url_password: "{{ keycloak_quarkus_jdbc_download_pass | default(omit) }}" url_password: "{{ keycloak_quarkus_jdbc_download_pass | default(omit) }}"
validate_certs: "{{ keycloak_quarkus_jdbc_download_validate_certs | default(omit) }}" validate_certs: "{{ keycloak_quarkus_jdbc_download_validate_certs | default(omit) }}"
mode: '0640' mode: '0640'
become: true become: "{{ keycloak_quarkus_jdbc_driver_require_privilege_escalation }}"
notify: notify:
- restart keycloak - restart keycloak

View File

@@ -1,34 +1,58 @@
--- ---
# tasks file for keycloak # tasks file for keycloak
- name: Check prerequisites - name: Check prerequisites
ansible.builtin.include_tasks: prereqs.yml ansible.builtin.include_tasks:
file: prereqs.yml
apply:
tags:
- prereqs
tags: tags:
- prereqs - prereqs
- always - always
- name: Check for deprecations - name: Check for deprecations
ansible.builtin.include_tasks: deprecations.yml ansible.builtin.include_tasks:
file: deprecations.yml
apply:
tags:
- always
tags: tags:
- always - always
- name: Distro specific tasks - name: Distro specific tasks
ansible.builtin.include_tasks: "{{ ansible_os_family | lower }}.yml" ansible.builtin.include_tasks:
file: "{{ ansible_os_family | lower }}.yml"
apply:
tags:
- unbound
tags: tags:
- unbound - unbound
- name: Include install tasks - name: Include install tasks
ansible.builtin.include_tasks: install.yml ansible.builtin.include_tasks:
file: install.yml
apply:
tags:
- install
tags: tags:
- install - install
- name: Include systemd tasks - name: Include systemd tasks
ansible.builtin.include_tasks: systemd.yml ansible.builtin.include_tasks:
file: systemd.yml
apply:
tags:
- systemd
tags: tags:
- systemd - systemd
- name: Include configuration key store tasks - name: Include configuration key store tasks
ansible.builtin.include_tasks:
file: config_store.yml
apply:
tags:
- install
when: keycloak.config_key_store_enabled when: keycloak.config_key_store_enabled
ansible.builtin.include_tasks: config_store.yml
tags: tags:
- install - install
@@ -39,13 +63,18 @@
{ {
"name": item, "name": item,
"address": 'jgroups-' + item, "address": 'jgroups-' + item,
"inventory_host": hostvars[item].ansible_default_ipv4.address | default(item) + '[' + (keycloak_quarkus_jgroups_port | string) + ']', "inventory_host": hostvars[item].keycloak_quarkus_jgroups_ip | default(item) + '[' + (keycloak_quarkus_jgroups_port | string) + ']',
"value": hostvars[item].ansible_default_ipv4.address | default(item) "value": hostvars[item].keycloak_quarkus_jgroups_ip | default(item)
} }
] }} ] }}
loop: "{{ ansible_play_batch }}" loop: "{{ ansible_play_batch }}"
when: keycloak_quarkus_ha_enabled and keycloak_quarkus_ha_discovery == 'TCPPING' 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" - name: "Configure config files for keycloak service"
ansible.builtin.template: ansible.builtin.template:
src: "{{ item }}.j2" src: "{{ item }}.j2"
@@ -53,11 +82,8 @@
owner: "{{ keycloak.service_user }}" owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}" group: "{{ keycloak.service_group }}"
mode: '0640' mode: '0640'
become: true become: "{{ keycloak_quarkus_require_privilege_escalation }}"
loop: loop: "{{ keycloak_quarkus_config_files }}"
- keycloak.conf
- quarkus.properties
- cache-ispn.xml
notify: notify:
- rebuild keycloak config - rebuild keycloak config
- restart keycloak - restart keycloak
@@ -69,7 +95,16 @@
owner: "{{ keycloak.service_user }}" owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}" group: "{{ keycloak.service_group }}"
mode: '0775' mode: '0775'
become: true become: "{{ keycloak_quarkus_require_privilege_escalation }}"
- 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 }}"
- name: Flush pending handlers - name: Flush pending handlers
ansible.builtin.meta: flush_handlers ansible.builtin.meta: flush_handlers
@@ -83,7 +118,7 @@
src: "{{ keycloak.log.file | dirname }}" src: "{{ keycloak.log.file | dirname }}"
dest: "{{ keycloak_quarkus_log_target }}" dest: "{{ keycloak_quarkus_log_target }}"
force: true force: true
become: true become: "{{ keycloak_quarkus_require_privilege_escalation }}"
- name: Check service status - name: Check service status
ansible.builtin.systemd_service: ansible.builtin.systemd_service:

View File

@@ -3,5 +3,5 @@
- name: "Rebuild {{ keycloak.service_name }} config" - name: "Rebuild {{ keycloak.service_name }} config"
ansible.builtin.shell: | # noqa blocked_modules shell is necessary here 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 " env -i bash -c "set -a ; source {{ keycloak_quarkus_sysconf_file }} ; {{ keycloak.home }}/bin/kc.sh build "
become: true become: "{{ keycloak_quarkus_rebuild_config_require_privilege_escalation }}"
changed_when: true changed_when: true

View File

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

View File

@@ -5,7 +5,7 @@
enabled: true enabled: true
state: restarted state: restarted
daemon_reload: true daemon_reload: true
become: true become: "{{ keycloak_quarkus_restart_require_privilege_escalation }}"
- name: "Wait until {{ keycloak.service_name }} service becomes active {{ keycloak.health_url }}" - name: "Wait until {{ keycloak.service_name }} service becomes active {{ keycloak.health_url }}"
ansible.builtin.uri: ansible.builtin.uri:

View File

@@ -2,7 +2,7 @@
- name: "Restart services in serial, with optional healtch check (keycloak_quarkus_restart_health_check)" - name: "Restart services in serial, with optional healtch check (keycloak_quarkus_restart_health_check)"
throttle: 1 throttle: 1
block: block:
- name: "Restart and enable {{ keycloak.service_name }} service on {{ item }}" - name: "Restart and enable {{ keycloak.service_name }} service"
ansible.builtin.include_tasks: ansible.builtin.include_tasks:
file: restart.yml file: restart.yml
apply: apply:

View File

@@ -16,5 +16,5 @@
enabled: true enabled: true
state: restarted state: restarted
daemon_reload: true daemon_reload: true
become: true become: "{{ keycloak_quarkus_restart_require_privilege_escalation }}"
when: inventory_hostname != ansible_play_hosts | first when: inventory_hostname != ansible_play_hosts | first

View File

@@ -5,7 +5,7 @@
enabled: true enabled: true
state: started state: started
daemon_reload: true daemon_reload: true
become: true become: "{{ keycloak_quarkus_start_require_privilege_escalation }}"
- name: "Wait until {{ keycloak.service_name }} becomes active {{ keycloak.health_url }}" - name: "Wait until {{ keycloak.service_name }} becomes active {{ keycloak.health_url }}"
ansible.builtin.uri: ansible.builtin.uri:

View File

@@ -1,6 +1,6 @@
--- ---
- name: "Configure sysconfig file for {{ keycloak.service_name }} service" - name: "Configure sysconfig file for {{ keycloak.service_name }} service"
become: true become: "{{ keycloak_quarkus_systemd_require_privilege_escalation }}"
ansible.builtin.template: ansible.builtin.template:
src: keycloak-sysconfig.j2 src: keycloak-sysconfig.j2
dest: "{{ keycloak_quarkus_sysconf_file }}" dest: "{{ keycloak_quarkus_sysconf_file }}"
@@ -20,7 +20,7 @@
owner: root owner: root
group: root group: root
mode: '0644' mode: '0644'
become: true become: "{{ keycloak_quarkus_systemd_require_privilege_escalation }}"
register: systemdunit register: systemdunit
notify: notify:
- rebuild keycloak config - rebuild keycloak config

View File

@@ -22,7 +22,9 @@
xmlns="urn:infinispan:config:15.0"> xmlns="urn:infinispan:config:15.0">
{% set stack_expression='' %} {% set stack_expression='' %}
{% if keycloak_quarkus_ha_enabled and keycloak_quarkus_ha_discovery == 'TCPPING' %} {% 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"' %} {% set stack_expression='stack="tcpping"' %}
<jgroups> <jgroups>
<stack name="tcpping" extends="tcp"> <stack name="tcpping" extends="tcp">
@@ -35,6 +37,10 @@
/> />
</stack> </stack>
</jgroups> </jgroups>
{% elif keycloak_quarkus_ha_discovery == 'JDBCPING' %}
{% set stack_expression='stack="JDBC_PING2"' %}
{% endif %}
{% endif %}
{% endif %} {% endif %}
<cache-container name="keycloak"> <cache-container name="keycloak">
@@ -93,6 +99,14 @@
<expiration max-idle="3600000"/> <expiration max-idle="3600000"/>
<memory max-count="1000"/> <memory max-count="1000"/>
</local-cache> </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"> <distributed-cache name="actionTokens" owners="2">
<encoding> <encoding>
<key media-type="application/x-java-object"/> <key media-type="application/x-java-object"/>

View File

@@ -58,10 +58,17 @@ hostname-backchannel-dynamic={{ keycloak_quarkus_hostname_backchannel_dynamic |
# Cluster # Cluster
{% if keycloak_quarkus_ha_enabled %} {% if keycloak_quarkus_ha_enabled %}
cache=ispn cache=ispn
{% if keycloak_quarkus_cache_managed_infinispan_config %}
cache-config-file=cache-ispn.xml cache-config-file=cache-ispn.xml
{% if keycloak_quarkus_ha_enabled and keycloak_quarkus_ha_discovery == 'TCPPING' %}
# cache-stack=tcp # configured directly in `cache-ispn.xml`
{% endif %} {% 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 %} {% endif %}
{% if keycloak_quarkus_proxy_headers | length > 0 %} {% if keycloak_quarkus_proxy_headers | length > 0 %}

View File

@@ -1,11 +1,11 @@
{{ ansible_managed | comment }} {{ ansible_managed | comment }}
{% if keycloak_quarkus_ha_enabled %} {% if keycloak_quarkus_ha_enabled %}
{% if keycloak_quarkus_version.split('.')[0] | int < 22 %} {% if keycloak_quarkus_version.split('.')[0] | int < 22 %}
quarkus.infinispan-client.server-list={{ keycloak_quarkus_cache_remote_host }} 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-username={{ keycloak_quarkus_cache_remote_username }}
quarkus.infinispan-client.auth-password={{ keycloak_quarkus_cache_remote_password }} quarkus.infinispan-client.auth-password={{ keycloak_quarkus_cache_remote_password }}
{% else %} {% else %}
quarkus.infinispan-client.hosts={{ keycloak_quarkus_cache_remote_host }} 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.username={{ keycloak_quarkus_cache_remote_username }}
quarkus.infinispan-client.password={{ keycloak_quarkus_cache_remote_password }} quarkus.infinispan-client.password={{ keycloak_quarkus_cache_remote_password }}
{% endif %} {% endif %}

View File

@@ -1,5 +1,5 @@
--- ---
keycloak_quarkus_varjvm_package: "{{ keycloak_quarkus_jvm_package | default('openjdk-17-jdk-headless') }}" keycloak_quarkus_varjvm_package: "{{ keycloak_quarkus_jvm_package | default('openjdk-21-jdk-headless') }}"
keycloak_quarkus_prereq_package_list: keycloak_quarkus_prereq_package_list:
- "{{ keycloak_quarkus_varjvm_package }}" - "{{ keycloak_quarkus_varjvm_package }}"
- bash - bash

View File

@@ -12,7 +12,7 @@ Role Defaults
|:---------|:------------|:--------| |:---------|:------------|:--------|
|`keycloak_admin_user`| Administration console user account | `admin` | |`keycloak_admin_user`| Administration console user account | `admin` |
|`keycloak_host`| hostname | `localhost` | |`keycloak_host`| hostname | `localhost` |
|`keycloak_context`| Context path for rest calls | `/auth` | |`keycloak_context`| Context path for rest calls (set to `/auth` for legacy WildFly-based Keycloak) | `` |
|`keycloak_http_port`| HTTP port | `8080` | |`keycloak_http_port`| HTTP port | `8080` |
|`keycloak_https_port`| TLS HTTP port | `8443` | |`keycloak_https_port`| TLS HTTP port | `8443` |
|`keycloak_auth_realm`| Name of the main authentication realm | `master` | |`keycloak_auth_realm`| Name of the main authentication realm | `master` |
@@ -44,7 +44,7 @@ The following variables are available for creating clients:
|`keycloak_client_users` | List of user/role mappings for a client | `[]` | |`keycloak_client_users` | List of user/role mappings for a client | `[]` |
The following variable are available for creating user federation: The following variables are available for creating user federation:
| Variable | Description | Default | | Variable | Description | Default |
|:---------|:------------|:---------| |:---------|:------------|:---------|
@@ -74,6 +74,7 @@ Refer to [docs](https://docs.ansible.com/ansible/latest/collections/community/ge
- name: <name of the client> - name: <name of the client>
id: <id of the client> id: <id of the client>
client_id: <id of the client> client_id: <id of the client>
secret: <secret of the client (Optional)>
roles: <keycloak_client_default_roles> roles: <keycloak_client_default_roles>
realm: <name of the realm that contains the client> realm: <name of the realm that contains the client>
public_client: <true for public, false for confidential> public_client: <true for public, false for confidential>
@@ -106,6 +107,20 @@ Refer to [docs](https://docs.ansible.com/ansible/latest/collections/community/ge
For a comprehensive example, refer to the [playbook](../../playbooks/keycloak_realm.yml). For a comprehensive example, refer to the [playbook](../../playbooks/keycloak_realm.yml).
Related Modules
---------------
For features not covered by this role, the collection provides dedicated modules:
| Module | What It Manages |
|:-------|:----------------|
| `keycloak_client_scope` | Client scopes and protocol mappers — see [example playbook](../../playbooks/keycloak_client_scope.yml) |
| `keycloak_authentication_flow` | Authentication flows and execution steps — see [example playbook](../../playbooks/keycloak_authentication_flow.yml) |
| `keycloak_client` | Clients (also used internally by this role) |
| `keycloak_role` | Realm and client roles |
| `keycloak_user_federation` | User federations such as LDAP (also used internally by this role) |
Example Playbook Example Playbook
---------------- ----------------
@@ -126,6 +141,47 @@ The following is an example playbook that makes use of the role to create a real
keycloak_clients: [...] keycloak_clients: [...]
``` ```
The following example uses the `keycloak_client_scope` module to create a client scope with protocol mappers:
```yaml
- name: Create client scope
middleware_automation.keycloak.keycloak_client_scope:
auth_keycloak_url: http://localhost:8080
auth_realm: master
auth_username: admin
auth_password: changeme
realm: TestRealm
name: my-scope
protocol_mappers:
- name: email
protocolMapper: oidc-usermodel-attribute-mapper
config:
user.attribute: email
claim.name: email
id.token.claim: "true"
access.token.claim: "true"
state: present
```
The following example uses the `keycloak_authentication_flow` module to create a custom authentication flow:
```yaml
- name: Create authentication flow
middleware_automation.keycloak.keycloak_authentication_flow:
auth_keycloak_url: http://localhost:8080
auth_realm: master
auth_username: admin
auth_password: changeme
realm: TestRealm
alias: my-browser-flow
executions:
- provider_id: auth-cookie
requirement: ALTERNATIVE
- provider_id: auth-password
requirement: REQUIRED
state: present
```
License License
------- -------

View File

@@ -9,7 +9,7 @@ keycloak_management_http_port: 9990
keycloak_admin_user: admin keycloak_admin_user: admin
keycloak_auth_realm: master keycloak_auth_realm: master
keycloak_auth_client: admin-cli keycloak_auth_client: admin-cli
keycloak_context: /auth keycloak_context: ''
# administrator console password, this is a required variable # administrator console password, this is a required variable
keycloak_admin_password: '' keycloak_admin_password: ''

View File

@@ -8,8 +8,8 @@ argument_specs:
type: "str" type: "str"
keycloak_context: keycloak_context:
# line 5 of keycloak_realm/defaults/main.yml # line 5 of keycloak_realm/defaults/main.yml
default: "/auth" default: ""
description: "Context path for rest calls" description: "Context path for rest calls (was /auth for legacy WildFly-based Keycloak, empty for Quarkus-based Keycloak/RHBK)"
type: "str" type: "str"
keycloak_http_port: keycloak_http_port:
# line 4 of keycloak_realm/defaults/main.yml # line 4 of keycloak_realm/defaults/main.yml

View File

@@ -46,7 +46,7 @@
name: "{{ item.name }}" name: "{{ item.name }}"
state: present state: present
provider_id: "{{ item.provider_id }}" provider_id: "{{ item.provider_id }}"
provider_type: "{{ item.provider_type | default(org.keycloak.storage.UserStorageProvider) }}" provider_type: "{{ item.provider_type | default('org.keycloak.storage.UserStorageProvider') }}"
config: "{{ item.config }}" config: "{{ item.config }}"
mappers: "{{ item.mappers | default(omit) }}" mappers: "{{ item.mappers | default(omit) }}"
no_log: "{{ keycloak_no_log | default('True') }}" no_log: "{{ keycloak_no_log | default('True') }}"
@@ -76,6 +76,7 @@
default_roles: "{{ item.roles | default(omit) }}" default_roles: "{{ item.roles | default(omit) }}"
client_id: "{{ item.client_id | default(omit) }}" client_id: "{{ item.client_id | default(omit) }}"
id: "{{ item.id | default(omit) }}" id: "{{ item.id | default(omit) }}"
secret: "{{ item.secret | default(omit) }}"
name: "{{ item.name | default(omit) }}" name: "{{ item.name | default(omit) }}"
description: "{{ item.description | default(omit) }}" description: "{{ item.description | default(omit) }}"
root_url: "{{ item.root_url | default('') }}" root_url: "{{ item.root_url | default('') }}"

View File

@@ -2,7 +2,7 @@
middleware_automation.keycloak.keycloak_role: middleware_automation.keycloak.keycloak_role:
name: "{{ item }}" name: "{{ item }}"
realm: "{{ client.realm | default(keycloak_realm) }}" realm: "{{ client.realm | default(keycloak_realm) }}"
client_id: "{{ client.name }}" client_id: "{{ client.client_id }}"
auth_client_id: "{{ keycloak_auth_client }}" auth_client_id: "{{ keycloak_auth_client }}"
auth_keycloak_url: "{{ keycloak_url }}{{ keycloak_context }}" auth_keycloak_url: "{{ keycloak_url }}{{ keycloak_context }}"
auth_realm: "{{ keycloak_auth_realm }}" auth_realm: "{{ keycloak_auth_realm }}"