Compare commits

...

210 Commits
1.0.4 ... 1.2.8

Author SHA1 Message Date
github-actions
6330f08b28 Update changelog for release 1.2.8
Signed-off-by: github-actions <ggraziol@redhat.com>
2023-08-28 15:55:52 +00:00
Guido Grazioli
5c8d7d9554 ci: update release workflow 2023-08-28 17:45:52 +02:00
Guido Grazioli
2513ac2c43 Merge pull request #107 from Footur/keycloak-update-22.0.1
Update Keycloak to version 22.0.1
2023-08-28 08:59:53 +02:00
footur
6e6bf2ff71 Fix JRE version in README 2023-08-27 21:57:25 +02:00
Guido Grazioli
11621516e3 update workflows 2023-08-25 11:40:27 +02:00
footur
7c05ee5239 Update Keycloak to version 22.0.1 2023-08-25 11:38:45 +02:00
Guido Grazioli
5251826477 ci: update workflows 2023-08-24 13:57:38 +02:00
Guido Grazioli
0783000849 ci: update workflows 2023-08-24 13:53:22 +02:00
Guido Grazioli
ca2dbe78c2 ci: update workflows 2023-08-24 13:46:50 +02:00
Guido Grazioli
52d9286ea3 ci: update workflows 2023-08-24 13:20:49 +02:00
Guido Grazioli
345c50fb85 Merge pull request #105 from JoelKle/JoelKle-patch-1
Update bindep.txt package python3-devel to support RHEL9
2023-08-08 15:30:30 +02:00
Joel
db0aafd465 Update bindep.txt to support RHEL9
On RHEL9 the rpm package `python39-devel` doesn't exists. The real name is `python3-devel`.
2023-08-08 11:05:25 +02:00
Guido Grazioli
b950cdb8b4 Merge pull request #103 from guidograzioli/quarkus_java_17
keycloak_quarkus: set openjdk 17 as default
2023-07-31 10:48:26 +02:00
Guido Grazioli
5b01123846 fix verify for molecule default scenario 2023-07-31 10:39:47 +02:00
Guido Grazioli
84d6e7baca set java-17 for keycloak_quarkus 2023-07-31 10:29:28 +02:00
Guido Grazioli
ea735ea79e Merge pull request #100 from Footur/keycloak-update-22.0.0
Update keycloak_quarkus to Keycloak version 22.0.0
2023-07-31 09:50:38 +02:00
Guido Grazioli
9db1cbd564 Merge pull request #91 from schmaxit/main
Undefine `keycloak_db_valid_conn_sql` default
2023-07-31 09:22:01 +02:00
Guido Grazioli
7933592725 Revert README.md 2023-07-31 09:19:47 +02:00
Guido Grazioli
3170af8b2b Merge pull request #102 from guidograzioli/bugzilla_2224411
fix_java_11_tzdata
2023-07-31 09:17:34 +02:00
Guido Grazioli
f400a5bbf8 fix_java_11_tzdata 2023-07-31 09:01:54 +02:00
Guido Grazioli
5385fbb8e9 ci: update molecule 2023-07-31 08:40:17 +02:00
Guido Grazioli
7fea211639 ci: update molecule 2023-07-31 08:38:36 +02:00
Guido Grazioli
8738240a24 docs: add missing param in defaults comment 2023-07-28 09:57:37 +02:00
footur
f195d164d1 Enable Ansible verbosity in the CI test 2023-07-14 13:21:27 +02:00
footur
7c4d420fea Update Keycloak to version 22.0.0 2023-07-14 11:36:54 +02:00
Massimo Schiavon
d45071bf58 Merge branch 'ansible-middleware:main' into main 2023-07-03 09:54:47 +02:00
Guido Grazioli
10876ba615 Merge pull request #99 from Footur/update-keycloak
Update the Keycloakx version in the README
2023-06-23 15:20:36 +02:00
Guido Grazioli
f3815403c8 Merge pull request #98 from world-direct/fix/missing_if
Fix #97 - proper checks for keycloak_jgroups_subnet
2023-06-23 15:18:20 +02:00
Footur
18d686b43a Merge branch 'ansible-middleware:main' into update-keycloak 2023-06-23 12:36:16 +02:00
footur
26a9249d07 Update the Keycloakx version in the README 2023-06-23 12:32:35 +02:00
Helmut Wolf
fae3079751 Fix #97 - proper checks for keycloak_jgroups_subnet 2023-06-23 11:40:15 +02:00
Guido Grazioli
a82e654cc4 Bump to 1.2.8 2023-06-19 17:26:15 +02:00
github-actions
cebec9c717 Update changelog for release 1.2.7 2023-06-19 15:23:06 +00:00
Guido Grazioli
ad59cd8cb3 Merge pull request #95 from guidograzioli/aap_11169
add certified collection notice
2023-06-19 17:05:12 +02:00
Guido Grazioli
926353f395 add certified collection notice 2023-06-19 16:41:35 +02:00
Guido Grazioli
fed86ac0c3 Merge pull request #92 from Footur/update-keycloak
Update keycloakx to v21.1.1
2023-06-19 11:15:16 +02:00
footur
5f1f8b5762 [CI] Use ansible-lint in v6.17.0 2023-06-17 13:16:10 +02:00
Footur
bab3069712 Merge branch 'ansible-middleware:main' into update-keycloak 2023-06-16 10:20:56 +02:00
footur
fc6e00974d Define the varbosity of Ansible in Molecule 2023-06-16 10:19:31 +02:00
footur
83525dbed0 Update the Keycloakx version in Molecule 2023-06-16 10:15:59 +02:00
Guido Grazioli
7ec695ee15 Fix wrong task message 2023-06-10 19:27:48 +02:00
Guido Grazioli
14e7b402b7 fix typo in templates 2023-06-10 18:37:58 +02:00
Guido Grazioli
832432b86c Merge pull request #93 from guidograzioli/override_jgroups_subnet_match
Allow to override jgroups subnet
2023-06-10 16:47:36 +02:00
Guido Grazioli
8f697f6a53 Bump to 1.2.7 2023-06-10 16:45:13 +02:00
Guido Grazioli
1dd579a6d1 Allow to override jgroups subnet 2023-06-10 16:31:19 +02:00
footur
3340428194 Remove the "--auto-build" flag – it's deprecated
Signed-off-by: footur <3769085+Footur@users.noreply.github.com>
2023-06-10 15:18:31 +02:00
footur
18e60daa93 Update Keycloakx to v21.1.1
Signed-off-by: footur <3769085+Footur@users.noreply.github.com>
2023-06-10 15:16:58 +02:00
Massimo Schiavon
874215a592 remove empty string default for keycloak_db_valid_conn_sql
rely on defaults set in keycloak_jdbc dict
2023-06-09 10:51:13 +02:00
github-actions
97bea7ba39 Update changelog for release 1.2.6 2023-06-07 12:29:15 +00:00
Guido Grazioli
e99a0db174 Add missing type conversion in templates 2023-06-07 12:25:58 +02:00
Guido Grazioli
3b03c54fed Merge pull request #90 from guidograzioli/background-validation-millis
handle WFLYCTL0117 when validation_millis is 0
2023-06-07 11:56:55 +02:00
Guido Grazioli
ced4ce7828 handle WFLYCTL0117 when validation_millis is 0 2023-06-07 11:56:12 +02:00
Guido Grazioli
6986190159 Bumo to v1.2.6 2023-06-01 10:27:46 +02:00
Guido Grazioli
db480d0bc9 Merge pull request #88 from world-direct/feature/improve_service_restart_behavior
Improve service restart behavior configuration
2023-06-01 10:18:26 +02:00
Helmut Wolf
bc4cb5c52a Introduce keycloak_service_restart_always alongside keycloak_service_restart_on_failure 2023-05-31 20:29:24 +02:00
Guido Grazioli
8f042d3e29 Merge pull request #89 from schmaxit/main
Change xa_datasource_class value for mariadb jdbc configuration
2023-05-31 17:24:53 +02:00
Guido Grazioli
24eaacc1ac Merge pull request #87 from world-direct/feature/profiles
Keycloak: add feature enabling/disabling
2023-05-31 17:19:08 +02:00
Helmut Wolf
623db426e0 Keycloak: add feature enabling/disabling 2023-05-31 16:41:57 +02:00
Massimo Schiavon
b77c166945 change xa_datasource_class for mariadb jdbc configuration 2023-05-31 11:12:24 +02:00
github-actions
b7eef6a720 Update changelog for release 1.2.5 2023-05-26 21:00:15 +00:00
Guido Grazioli
203e6c06ac Merge pull request #86 from guidograzioli/admin_url
Allow to configure administration endpoint URL
2023-05-26 19:46:59 +02:00
Guido Grazioli
aaae1d1129 Allow to configure admin_url 2023-05-26 16:31:13 +02:00
Guido Grazioli
cca20a067d Merge pull request #85 from guidograzioli/datasource_validation
Add configuration for database connection pool validation
2023-05-26 16:09:51 +02:00
Guido Grazioli
2be35f9a67 typo in readme 2023-05-26 14:28:52 +02:00
Guido Grazioli
19f1750a33 Add db pool validation configuration 2023-05-25 11:47:19 +02:00
Guido Grazioli
c3d8bbc94e Merge pull request #84 from guidograzioli/hostname_spi
Allow to force backend URLs to frontend URLs
2023-05-25 11:34:37 +02:00
Guido Grazioli
c4b4be3c3b add variable for force_frontend_url 2023-05-25 11:10:18 +02:00
Guido Grazioli
98e1633c43 ci: new linter rules take 2 2023-05-22 16:24:28 +02:00
Guido Grazioli
fd375a141d ci: update linter settings, fix new linter issues 2023-05-22 16:12:25 +02:00
Guido Grazioli
0cf7b3ac49 Merge pull request #81 from world-direct/fix/80
Close #80 - introduce systemd restart behavior
2023-05-22 15:41:30 +02:00
Helmut Wolf
370d424b24 Close #80 - introduce systemd restart behavior 2023-05-22 11:30:11 +02:00
Guido Grazioli
01fd2cc4fd Bump to 1.2.5 2023-05-09 16:44:16 +02:00
github-actions
7471e07921 Update changelog for release 1.2.4 2023-05-09 13:49:15 +00:00
Guido Grazioli
e8e0f6718b Merge pull request #78 from world-direct/fix/74
Close #74 - add `sqlserver` support to keycloak role
2023-05-09 15:31:01 +02:00
Guido Grazioli
e4811221be ci: fix release wf, bump to 1.2.4 2023-05-09 15:25:41 +02:00
Guido Grazioli
6cb4aac556 Merge pull request #77 from world-direct/fix/76
Close #76 - Keycloak role: fix deprecation warning for `ipaddr`
2023-05-09 15:14:07 +02:00
Helmut Wolf
aad373a8e9 Close #74 - add sqlserver support to keycloak role 2023-05-09 13:14:42 +02:00
Helmut Wolf
fd0a4e4492 Close #76 - Keycloak role: fix deprecation warning for ipaddr 2023-05-09 11:45:25 +02:00
Guido Grazioli
706677910b ci: update apt before installing hub 2023-05-05 11:07:42 +02:00
Guido Grazioli
a3bffe9401 Bump to 1.2.3 2023-05-03 15:23:46 +02:00
Guido Grazioli
f566917bc2 ci: rename galaxy tag 2023-05-03 08:54:20 +02:00
Guido Grazioli
44ad3b8e6d add galaxy tag 2023-05-02 18:05:12 +02:00
Guido Grazioli
1a450ea1d7 ci: add galaxy tags 2023-05-02 17:00:26 +02:00
Guido Grazioli
b0a01a8e46 Merge pull request #73 from jonathanspw/main
add configurability for XA transactions
2023-04-24 16:48:20 +02:00
Jonathan Wright
020bc86955 document keycloak_quarkus_transaction_xa_enabled 2023-04-24 08:52:36 -05:00
Jonathan Wright
d72d46c945 fix typo 2023-04-24 08:50:16 -05:00
Jonathan Wright
c7d2bdcee3 add configurability for XA transactions 2023-04-21 15:12:59 -05:00
Guido Grazioli
43d978370d bump to 1.2.2 2023-04-14 15:50:48 +02:00
Guido Grazioli
3d37def38d Merge pull request #71 from guidograzioli/downstream_offline_patching_fix
Fix undefined facts when offline patching sso
2023-04-14 15:31:13 +02:00
Guido Grazioli
8d16e241c1 fix undefined facts when offline patching sso 2023-04-14 14:58:40 +02:00
Guido Grazioli
6ac0c18842 fix: drop xml element not available in 7.6 2023-04-12 11:12:32 +02:00
Guido Grazioli
6334daf244 ci: fix typo and indent for TCPPING discovery 2023-04-12 10:59:21 +02:00
github-actions
87ad97d57f Update changelog for release 1.2.1 2023-04-11 07:07:07 +00:00
Guido Grazioli
242b1cea0a version bump to 1.2.1 2023-04-01 12:20:29 +02:00
Guido Grazioli
c0b72b6890 Merge pull request #69 from guidograzioli/keycloak_realm_attrs
Pass attributes to realm clients
2023-04-01 12:20:06 +02:00
Guido Grazioli
1cfa229a5f Pass attributes to realm clients 2023-04-01 11:45:40 +02:00
Guido Grazioli
f013a99832 Merge pull request #68 from guidograzioli/ha_internal_infinispan
Allow to setup keycloak HA cluster without remote cache store
2023-04-01 10:34:00 +02:00
Guido Grazioli
6bfe046f5e fix templates path 2023-04-01 10:13:01 +02:00
Guido Grazioli
526f64e5eb standalone ha without remote store 2023-04-01 10:06:03 +02:00
Guido Grazioli
a2c17f545e docs: downstream hide build status 2023-03-29 10:32:14 +02:00
Guido Grazioli
40c29d07b8 ci: update main README.md 2023-03-24 15:19:28 +01:00
Guido Grazioli
91a18bf571 ci: downstream test offline/online handling 2023-03-17 11:43:34 +01:00
Guido Grazioli
ecb6cbb9bf ci: downstream test asset management 2023-03-17 11:25:40 +01:00
github-actions
05dccdaf3b Update changelog for release 1.2.0 2023-03-16 14:21:04 +00:00
Guido Grazioli
398c3c478e docs: use role, not name, in sample keycloak_realm playbook 2023-03-15 18:05:01 +01:00
Guido Grazioli
ed24ca637a docs: update sample keycloak_realm playbook 2023-03-15 17:56:08 +01:00
Guido Grazioli
23ce09d595 sso: remove conditional on apply_patches 2023-03-15 17:23:04 +01:00
Guido Grazioli
59b69a6592 Downstream patching: add missing becomes 2023-03-10 15:38:56 +01:00
Guido Grazioli
bf89b1895a minor fixes to downstream rhn install/patch download 2023-03-10 15:22:15 +01:00
Guido Grazioli
2ce7104077 ci: docs need ansible-core 2.14 2023-03-10 14:34:12 +01:00
Guido Grazioli
d438648e39 Merge pull request #64 from guidograzioli/runtimes_common_patching
Switch to middleware_automation.common for rh-sso patching
2023-03-10 12:23:34 +01:00
Guido Grazioli
49566455d6 add missing file 2023-03-10 11:59:14 +01:00
Guido Grazioli
1f2a88982d linter 2023-03-10 11:54:37 +01:00
Guido Grazioli
a554736246 Use middleware_automation.common for rh-sso patching 2023-03-10 11:52:02 +01:00
Guido Grazioli
7bbe5ae386 Merge pull request #63 from guidograzioli/runtimes_common
Switch middleware_automation.redhat_csp_download for middleware_automation.common
2023-03-08 17:57:34 +01:00
Guido Grazioli
7c9cc7ce36 remove non-printing chars from arg_specs 2023-03-08 17:40:03 +01:00
Guido Grazioli
ce18c91b67 revert downstream playbook rename 2023-03-08 17:32:32 +01:00
Guido Grazioli
d15324c1c8 fix indent typo 2023-03-08 17:00:38 +01:00
Guido Grazioli
527d3eb264 Fix typo 2023-03-08 16:02:54 +01:00
Guido Grazioli
ccf773057b Replace main download 2023-03-08 15:58:25 +01:00
Guido Grazioli
e530ccdc31 Replace metadata 2023-03-08 15:58:09 +01:00
Guido Grazioli
6852871aeb Merge pull request #62 from guidograzioli/jgroups
Allow to configure TCPPING for discovery
2023-03-08 15:30:31 +01:00
Guido Grazioli
68bcff36f6 only try to create cluster node list when tcpping is selected 2023-03-08 14:59:55 +01:00
Guido Grazioli
a7c9304c68 fix typo 2023-03-08 11:06:01 +01:00
Guido Grazioli
6e9a17bbf5 initial tcpping support 2023-03-08 09:23:34 +01:00
Guido Grazioli
0052025917 Merge pull request #61 from guidograzioli/drop_community_general
Drop community.general from dependencies
2023-03-08 08:51:31 +01:00
Guido Grazioli
7050dafcbd add doc_fragments 2023-03-07 18:01:02 +01:00
Guido Grazioli
65e4b3b813 add hardforked modules 2023-03-07 17:56:05 +01:00
Guido Grazioli
00ae087732 drop community.general from tasks/meta 2023-03-07 17:07:27 +01:00
Guido Grazioli
e15ebd3233 Merge pull request #60 from guidograzioli/modcluster_proxy_list
Provide config for multiple modcluster proxies
2023-03-07 15:51:55 +01:00
Guido Grazioli
d27d15efa9 modcluster proxies string wants space-separated list 2023-03-07 14:16:44 +01:00
Guido Grazioli
41eed509ea add modcluster testing to default scenario 2023-03-07 12:51:16 +01:00
Guido Grazioli
0d2624cfff linter: role arg_specs dont support removed_in_version and removed_from_collection 2023-03-07 12:30:29 +01:00
Guido Grazioli
f980d4d1e1 Provide config for multiple modcluster proxies 2023-03-07 12:21:50 +01:00
Guido Grazioli
2959cb3cf0 Bump to 1.2.0 2023-03-07 08:10:42 +01:00
github-actions
821c256f04 Update changelog for release 1.1.1 2023-03-07 07:05:59 +00:00
Guido Grazioli
7abdd83ea4 ci: linter 2023-03-03 10:54:50 +01:00
Guido Grazioli
007094ab5b Merge pull request #53 from bbarun/patch-1
Update keycloak.conf.j2
2023-03-03 10:41:58 +01:00
Božo Barun
5cdaa7aabb Update keycloak.conf.j2
The documentation states that the path is relative to the /conf directory, which is true. So if one changes the contents of this file it would have no effect because the path would be non-existent. To make things worse, there would be no obvious error stating this and the configuration would be loaded from the defaults.
2023-01-26 15:27:57 +01:00
Guido Grazioli
808b944f19 Bump to v1.1.1 2023-01-09 09:46:47 +01:00
github-actions
ae95d69435 Update changelog for release 1.1.0 2023-01-09 08:36:24 +00:00
Guido Grazioli
73d7a46307 Merge pull request #51 from ansible-middleware/tweak_start_retries_and_delay
Variables to override service start retries and delay
2023-01-02 07:46:05 +01:00
Romain Pelisse
9196b234b6 Add missing 'java' keywords to galaxy.yml 2022-12-29 12:40:21 +01:00
Romain Pelisse
46d5af90ba Allow overrides for service start retries and delay 2022-12-29 12:40:21 +01:00
Guido Grazioli
8ed82d937c Merge pull request #50 from ansible-middleware/mol_one_req
molecule: centralize requirements
2022-12-21 07:07:13 +01:00
Guido Grazioli
842cb7bf23 molecule: update overridden template 2022-12-19 23:32:35 +01:00
Guido Grazioli
cd1c2ae3e7 keycloak: switch http-invoker from security-realm to http-authentication-factory 2022-12-19 23:09:43 +01:00
Guido Grazioli
2a219222bc Add missing ejb application-security-domain 2022-12-19 22:23:45 +01:00
Guido Grazioli
ab3247fc3b docs: update resources 2022-12-19 12:08:05 +01:00
Romain Pelisse
320f167bea molecule: centralize requirements 2022-12-19 08:56:25 +01:00
Guido Grazioli
5563317718 ci: update overridexml molecule override template 2022-12-16 16:28:11 +01:00
Guido Grazioli
e209507a3f Merge pull request #46 from guidograzioli/keycloak_19
Update keycloak to 18.0.2 - sso to 7.6.1
2022-12-16 12:51:39 +01:00
Guido Grazioli
b9560458d8 Merge branch 'main' into keycloak_19 2022-12-15 15:50:29 +01:00
Guido Grazioli
d7829f77df update sso version in molecule tests 2022-12-15 15:47:12 +01:00
Guido Grazioli
e17fda2da9 Merge pull request #47 from ansible-middleware/no_log
allows user to switch no_log to True for debugging purpose
2022-12-15 15:33:10 +01:00
Guido Grazioli
34cab23abe update configuration templates 2022-12-15 15:32:33 +01:00
Guido Grazioli
7c8db06378 set keycloak to 18.0.2, sso to 7.6.1 2022-12-15 14:38:04 +01:00
Romain Pelisse
699ab1a5c4 allows user to switch no_log to True for debugging purpose 2022-12-15 14:36:31 +01:00
Guido Grazioli
007464d6b3 Update keycloak to 19.0.3 2022-12-13 15:53:53 +01:00
Guido Grazioli
2054082b40 get rid of rhn_ids dict 2022-12-13 15:31:08 +01:00
Guido Grazioli
9157f83f96 Merge pull request #45 from generalpax/patch-1
keycloak_quarkus: variable to enable development mode
2022-12-13 15:23:46 +01:00
Guido Grazioli
6e4b224fe1 molecule: quarkus update verify urls 2022-12-13 15:07:11 +01:00
Guido Grazioli
2594fd9186 update molecule scenario 2022-12-13 14:48:12 +01:00
Guido Grazioli
853d4ab96b rebase and update documentation 2022-12-13 14:28:17 +01:00
Katzy
2df5fd22cf Update main.yml 2022-12-13 14:25:52 +01:00
Katzy
422986a8fd Enable this role to start keycloak in dev mode 2022-12-13 14:25:52 +01:00
Katzy
ad4d5dae68 Update keycloak.service.j2 2022-12-13 14:25:52 +01:00
Katzy
e624870e38 Fix hardcoded health_url to parameterized 2022-12-13 14:25:52 +01:00
Guido Grazioli
63dc369148 Add missing deps declarations 2022-12-13 12:31:03 +01:00
Guido Grazioli
f4fe9df97c ci: add noqa to commands 2022-12-13 11:49:36 +01:00
Guido Grazioli
b3b016bf3b Drop deprecated builtin.command arg 2022-12-13 11:39:57 +01:00
Guido Grazioli
3d0cfc9e42 ci: add bogus lint rule to warnings 2022-12-13 11:28:56 +01:00
Guido Grazioli
89fa82eddb ci: add new docs reqs 2022-12-13 11:27:18 +01:00
Guido Grazioli
c0d4c6211e ci: drop --docker from ansible-test sanity 2022-12-13 11:24:28 +01:00
Guido Grazioli
bdc1ad8b51 Add validation of realm client and id 2022-12-13 11:20:51 +01:00
Guido Grazioli
db111aaf3a add fqcn to firewalld tasks 2022-12-13 10:50:53 +01:00
Guido Grazioli
ab1c06a2e8 ci: set new ansible rule as warning 2022-10-19 17:27:00 +02:00
Guido Grazioli
c8a2283cc6 Merge pull request #44 from kabroxiko/patch-1
keycloak_quarkus: fix /var/log/keycloak symlink to keycloak log directory
2022-10-19 17:25:10 +02:00
kabroxiko
bea5062287 fix: Wrong file location
Fix:

Wrong quarkus log link and folder creation.

Replace:
keycloak -> /opt/keycloak/keycloak-19.0.2//opt/keycloak/keycloak-19.0.2/data/log

For:
keycloak -> /opt/keycloak/keycloak-19.0.2/data/log
2022-10-08 21:05:49 -03:00
Guido Grazioli
65da436d74 restart handler: orchestrate 2022-09-28 16:48:40 +02:00
Guido Grazioli
be582171ce restart handler: orchestrate 2022-09-28 16:30:26 +02:00
Guido Grazioli
a7fbce2990 New variable for binding of management ports 2022-09-28 15:33:30 +02:00
Guido Grazioli
dfc1912a99 jdbc_driver: switch from uri to get_url 2022-09-27 15:36:14 +02:00
Guido Grazioli
ec47a1c5a8 downstream: correctly interpolate defaults 2022-09-19 22:41:18 +02:00
Guido Grazioli
52e38f7398 Bump to 1.1.0 2022-09-19 22:10:03 +02:00
Guido Grazioli
69a8860551 Break dependency on wildfly/eap 2022-09-19 22:07:23 +02:00
Guido Grazioli
9f8b1c6d76 Merge pull request #42 from guidograzioli/downstream_rename
Rename variables from `infinispan_` prefix to `keycloak_infinispan_`
2022-09-19 17:38:02 +02:00
Guido Grazioli
5e89139870 Downstream variables 2022-09-19 17:11:56 +02:00
Guido Grazioli
38b5a02e95 Rename infinispan_ vars to keycloak_infinispan_, prepare downstrea 2022-09-19 15:42:01 +02:00
Guido Grazioli
9b2ea35184 ci: linter, workflows, galaxy, molecule 2022-09-19 15:41:12 +02:00
Romain Pelisse
ded44b084d quarkus scenario skip part if hera is used 2022-08-03 09:15:44 +02:00
github-actions
1b1127ed91 Update changelog for release 1.0.7 2022-07-06 12:10:08 +00:00
Guido Grazioli
9252433cc8 Merge pull request #39 from xabarin-forks/use_absolute_path_for_certs
keycloak_quarkus: use absolute path for certificate files
2022-07-05 14:06:02 +02:00
Xabier Davila
adb0a4da45 Fix molecule tests 2022-07-04 08:31:10 +02:00
Xabier Davila
41caa49cfc Use absolute path for certificate files 2022-07-01 10:31:18 +02:00
Guido Grazioli
d47e045f5e Merge pull request #38 from xabarin-forks/use_sudo_for_tasks_that_need_it
Use become for tasks that will otherwise fail
2022-06-30 16:43:11 +02:00
Xabier Davila
4baa61e0cf Use sudo for tasks that will otherwise fail 2022-06-30 16:15:48 +02:00
Guido Grazioli
065fb53eb2 Bump to 1.0.7 2022-06-01 17:33:29 +02:00
github-actions
713437343d Update changelog for release 1.0.6 2022-06-01 13:12:11 +00:00
Guido Grazioli
46f445560b Bump to 1.0.6 2022-05-31 19:10:20 +02:00
Guido Grazioli
76cbb4c676 keycloak_quarkus: add https to molecule test setup (#36)
* keycloak_quarkus: add https to molecule test setup

* move converge pre_tasks to prepare phase

* Update zipfile unarchive to cater for existing certs
2022-05-31 12:07:18 -05:00
Guido Grazioli
469036e9e7 keycloak_quarkus: set logfile path correctly under keycloak home (#35) 2022-05-31 03:47:29 -05:00
Guido Grazioli
8454f5c341 keycloak_quarkus: add selected java to PATH in systemd unit (#34) 2022-05-31 03:43:55 -05:00
github-actions
d5a63f55f9 Update changelog for release 1.0.5 2022-05-25 15:37:36 +00:00
Guido Grazioli
09a34567a7 Bump to 1.0.5, add EE setup ref 2022-05-19 09:38:47 +02:00
Harsha Cherukuri
bc63cbf649 Merge pull request #32 from guidograzioli/config_options
Update config options: keycloak and quarkus
2022-05-18 13:53:26 -04:00
Guido Grazioli
281767f505 add missing variable spec 2022-05-18 10:05:34 +02:00
Guido Grazioli
8bede6791e update 18.0.0, add JAVA_HOME check, runas systemd unit 2022-05-18 09:29:28 +02:00
Guido Grazioli
0ddbc66448 Add keycloak X playbook, update roles README 2022-05-17 19:30:13 +02:00
Guido Grazioli
31420fc24c update config options: keycloak and quarkus 2022-05-17 18:59:10 +02:00
91 changed files with 8657 additions and 961 deletions

View File

@@ -5,6 +5,8 @@ exclude_paths:
- molecule/
- .ansible-lint
- .yamllint
- meta/
- playbooks/roles/
rulesdir:
- ../../ansible-lint-custom-rules/rules/
@@ -19,11 +21,21 @@ warn_list:
- experimental
- ignore-errors
- no-handler
- fqcn-builtins
- no-log-password
- jinja[spacing]
- jinja[invalid]
- meta-no-tags
- name[casing]
- fqcn[action]
- schema[meta]
- var-naming[no-role-prefix]
- key-order[task]
- blocked_modules
skip_list:
- vars_should_not_be_used
- file_is_small_enough
- name[template]
use_default_rules: true
parseable: true

View File

@@ -1,51 +1,18 @@
---
name: CI
"on":
on:
push:
branches:
- main
pull_request:
schedule:
- cron: '0 6 * * *'
jobs:
ci:
runs-on: ubuntu-latest
strategy:
matrix:
python_version: ["3.9"]
steps:
- name: Check out code
uses: actions/checkout@v2
with:
path: ansible_collections/middleware_automation/keycloak
- name: Set up Python ${{ matrix.python_version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python_version }}
- name: Install yamllint, ansible and molecule
run: |
python -m pip install --upgrade pip
pip install yamllint 'molecule[docker]~=3.5.2' ansible-core flake8 ansible-lint voluptuous
pip install -r ansible_collections/middleware_automation/keycloak/requirements.txt
- name: Install ansible-lint custom rules
uses: actions/checkout@v2
with:
repository: ansible-middleware/ansible-lint-custom-rules
path: ansible_collections/ansible-lint-custom-rules/
- name: Create default collection path
run: |
mkdir -p /home/runner/.ansible/collections/ansible_collections
- name: Run sanity tests
run: ansible-test sanity --docker -v --color --python ${{ matrix.python_version }} --exclude changelogs/fragments/.gitignore
working-directory: ./ansible_collections/middleware_automation/keycloak
- name: Run molecule test
run: molecule test --all
working-directory: ./ansible_collections/middleware_automation/keycloak
env:
PY_COLORS: '1'
ANSIBLE_FORCE_COLOR: '1'
uses: ansible-middleware/github-actions/.github/workflows/ci.yml@main
secrets: inherit
with:
fqcn: 'middleware_automation/keycloak'
molecule_tests: >-
[ "default", "quarkus", "overridexml" ]

View File

@@ -8,54 +8,10 @@ on:
- "[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
env:
COLORTERM: 'yes'
TERM: 'xterm-256color'
PYTEST_ADDOPTS: '--color=yes'
jobs:
docs:
runs-on: ubuntu-latest
if: github.repository == 'ansible-middleware/keycloak'
permissions:
actions: write
checks: write
contents: write
deployments: write
packages: write
pages: write
steps:
- name: Check out code
uses: actions/checkout@v2
with:
path: ansible_collections/middleware_automation/keycloak
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install doc dependencies
run: |
python -m pip install --upgrade pip
pip install -r ansible_collections/middleware_automation/keycloak/docs/requirements.txt
pip install -r ansible_collections/middleware_automation/keycloak/requirements.txt
sudo apt install -y sed hub
- name: Create default collection path
run: |
mkdir -p /home/runner/.ansible/collections/ansible_collections
- name: Create changelog and documentation
uses: ansible-middleware/collection-docs-action@main
with:
collection_fqcn: middleware_automation.keycloak
collection_repo: ansible-middleware/keycloak
dependencies: false
commit_changelog: false
commit_ghpages: true
changelog_release: false
generate_docs: true
path: ansible_collections/middleware_automation/keycloak
token: ${{ secrets.GITHUB_TOKEN }}
uses: ansible-middleware/github-actions/.github/workflows/docs.yml@main
secrets: inherit
with:
fqcn: 'middleware_automation/keycloak'
collection_fqcn: 'middleware_automation.keycloak'

View File

@@ -5,87 +5,10 @@ on:
jobs:
release:
runs-on: ubuntu-latest
if: github.repository == 'ansible-middleware/keycloak'
permissions:
actions: write
checks: write
contents: write
deployments: write
packages: write
pages: write
outputs:
tag_version: ${{ steps.get_version.outputs.TAG_VERSION }}
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0
token: ${{ secrets.TRIGGERING_PAT }}
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: "3.x"
- name: Get current version
id: get_version
run: echo "::set-output name=TAG_VERSION::$(grep version galaxy.yml | awk -F'"' '{ print $2 }')"
- name: Check if tag exists
id: check_tag
run: echo "::set-output name=TAG_EXISTS::$(git tag | grep ${{ steps.get_version.outputs.TAG_VERSION }})"
- name: Fail if tag exists
if: ${{ steps.get_version.outputs.TAG_VERSION == steps.check_tag.outputs.TAG_EXISTS }}
uses: actions/github-script@v3
with:
script: |
core.setFailed('Release tag already exists')
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install ansible-core antsibull
sudo apt install -y sed hub
- name: Build collection
run: |
ansible-galaxy collection build .
- name: Create changelog and documentation
uses: ansible-middleware/collection-docs-action@main
with:
collection_fqcn: middleware_automation.keycloak
collection_repo: ansible-middleware/keycloak
dependencies: false
commit_changelog: true
commit_ghpages: false
changelog_release: true
generate_docs: false
token: ${{ secrets.GITHUB_TOKEN }}
- name: Publish collection
env:
ANSIBLE_GALAXY_API_KEY: ${{ secrets.ANSIBLE_GALAXY_API_KEY }}
run: |
ansible-galaxy collection publish *.tar.gz --api-key $ANSIBLE_GALAXY_API_KEY
- name: Create release tag
run: |
git config user.name github-actions
git config user.email github-actions@github.com
git tag -a ${{ steps.get_version.outputs.TAG_VERSION }} -m "Release v${{ steps.get_version.outputs.TAG_VERSION }}" || true
git push origin --tags
- name: Publish Release
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.get_version.outputs.TAG_VERSION }}
files: "*.tar.gz"
body_path: gh-release.md
uses: ansible-middleware/github-actions/.github/workflows/release.yml@main
secrets: inherit
with:
collection_fqcn: 'middleware_automation.keycloak'
dispatch:
needs: release

3
.gitignore vendored
View File

@@ -2,6 +2,8 @@
*.zip
.tmp
.cache
.vscode/
__pycache__/
docs/plugins/
docs/roles/
docs/_build/
@@ -9,3 +11,4 @@ docs/_build/
.mypy_cache/
*.retry
changelogs/.plugin-cache.yaml
*.pem

View File

@@ -6,6 +6,160 @@ middleware_automation.keycloak Release Notes
This changelog describes changes after version 0.2.6.
v1.2.8
======
Minor Changes
-------------
- keycloak_quarkus: set openjdk 17 as default `#103 <https://github.com/ansible-middleware/keycloak/pull/103>`_
- keycloak_quarkus: update to version 22.0.1 `#107 <https://github.com/ansible-middleware/keycloak/pull/107>`_
Bugfixes
--------
- Fix incorrect checks for ``keycloak_jgroups_subnet`` `#98 <https://github.com/ansible-middleware/keycloak/pull/98>`_
- Undefine ``keycloak_db_valid_conn_sql`` default `#91 <https://github.com/ansible-middleware/keycloak/pull/91>`_
- Update bindep.txt package python3-devel to support RHEL9 `#105 <https://github.com/ansible-middleware/keycloak/pull/105>`_
v1.2.7
======
Minor Changes
-------------
- Allow to override jgroups subnet `#93 <https://github.com/ansible-middleware/keycloak/pull/93>`_
- keycloak-quarkus: update keycloakx to v21.1.1 `#92 <https://github.com/ansible-middleware/keycloak/pull/92>`_
v1.2.6
======
Minor Changes
-------------
- Add profile features enabling/disabling `#87 <https://github.com/ansible-middleware/keycloak/pull/87>`_
- Improve service restart behavior configuration `#88 <https://github.com/ansible-middleware/keycloak/pull/88>`_
- Update default xa_datasource_class value for mariadb jdbc configuration `#89 <https://github.com/ansible-middleware/keycloak/pull/89>`_
Bugfixes
--------
- Handle WFLYCTL0117 when background validation millis is 0 `#90 <https://github.com/ansible-middleware/keycloak/pull/90>`_
v1.2.5
======
Minor Changes
-------------
- Add configuration for database connection pool validation `#85 <https://github.com/ansible-middleware/keycloak/pull/85>`_
- Allow to configure administration endpoint URL `#86 <https://github.com/ansible-middleware/keycloak/pull/86>`_
- Allow to force backend URLs to frontend URLs `#84 <https://github.com/ansible-middleware/keycloak/pull/84>`_
- Introduce systemd unit restart behavior `#81 <https://github.com/ansible-middleware/keycloak/pull/81>`_
v1.2.4
======
Minor Changes
-------------
- Add ``sqlserver`` to keycloak role jdbc configurations `#78 <https://github.com/ansible-middleware/keycloak/pull/78>`_
- Add configurability for XA transactions `#73 <https://github.com/ansible-middleware/keycloak/pull/73>`_
Bugfixes
--------
- Fix deprecation warning for ``ipaddr`` `#77 <https://github.com/ansible-middleware/keycloak/pull/77>`_
- Fix undefined facts when offline patching sso `#71 <https://github.com/ansible-middleware/keycloak/pull/71>`_
v1.2.1
======
Minor Changes
-------------
- Allow to setup keycloak HA cluster without remote cache store `#68 <https://github.com/ansible-middleware/keycloak/pull/68>`_
Bugfixes
--------
- Pass attributes to realm clients `#69 <https://github.com/ansible-middleware/keycloak/pull/69>`_
v1.2.0
======
Major Changes
-------------
- Provide config for multiple modcluster proxies `#60 <https://github.com/ansible-middleware/keycloak/pull/60>`_
Minor Changes
-------------
- Allow to configure TCPPING for cluster discovery `#62 <https://github.com/ansible-middleware/keycloak/pull/62>`_
- Drop community.general from dependencies `#61 <https://github.com/ansible-middleware/keycloak/pull/61>`_
- Switch middleware_automation.redhat_csp_download for middleware_automation.common `#63 <https://github.com/ansible-middleware/keycloak/pull/63>`_
- Switch to middleware_automation.common for rh-sso patching `#64 <https://github.com/ansible-middleware/keycloak/pull/64>`_
v1.1.1
======
Bugfixes
--------
- keycloak-quarkus: fix ``cache-config-file`` path in keycloak.conf.j2 template `#53 <https://github.com/ansible-middleware/keycloak/pull/53>`_
v1.1.0
======
Minor Changes
-------------
- Update keycloak to 18.0.2 - sso to 7.6.1 `#46 <https://github.com/ansible-middleware/keycloak/pull/46>`_
- Variable ``keycloak_no_log`` controls ansible ``no_log`` parameter (for debugging purposes) `#47 <https://github.com/ansible-middleware/keycloak/pull/47>`_
- Variables to override service start retries and delay `#51 <https://github.com/ansible-middleware/keycloak/pull/51>`_
- keycloak_quarkus: variable to enable development mode `#45 <https://github.com/ansible-middleware/keycloak/pull/45>`_
Breaking Changes / Porting Guide
--------------------------------
- Rename variables from ``infinispan_`` prefix to ``keycloak_infinispan_`` `#42 <https://github.com/ansible-middleware/keycloak/pull/42>`_
Bugfixes
--------
- keycloak_quarkus: fix /var/log/keycloak symlink to keycloak log directory `#44 <https://github.com/ansible-middleware/keycloak/pull/44>`_
v1.0.7
======
Breaking Changes / Porting Guide
--------------------------------
- keycloak_quarkus: use absolute path for certificate files `#39 <https://github.com/ansible-middleware/keycloak/pull/39>`_
Bugfixes
--------
- keycloak_quarkus: use become for tasks that will otherwise fail `#38 <https://github.com/ansible-middleware/keycloak/pull/38>`_
v1.0.6
======
Bugfixes
--------
- keycloak_quarkus: add selected java to PATH in systemd unit `#34 <https://github.com/ansible-middleware/keycloak/pull/34>`_
- keycloak_quarkus: set logfile path correctly under keycloak home `#35 <https://github.com/ansible-middleware/keycloak/pull/35>`_
v1.0.5
======
Minor Changes
-------------
- Update config options: keycloak and quarkus `#32 <https://github.com/ansible-middleware/keycloak/pull/32>`_
v1.0.4
======

View File

@@ -1,8 +1,11 @@
# Ansible Collection - middleware_automation.keycloak
<!--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)
> **_NOTE:_ If you are Red Hat customer, install `redhat.sso` from [Automation Hub](https://console.redhat.com/ansible/ansible-dashboard) as the certified version of this collection.**
<!--end build_status -->
Collection to install and configure [Keycloak](https://www.keycloak.org/) or [Red Hat Single Sign-On](https://access.redhat.com/products/red-hat-single-sign-on).
<!--start requires_ansible-->
@@ -16,12 +19,15 @@ Plugins and modules within a collection may be tested with only specific Ansible
## Installation
<!--start galaxy_download -->
### Installing the Collection from Ansible Galaxy
Before using the collection, you need to install it with the Ansible Galaxy CLI:
ansible-galaxy collection install middleware_automation.keycloak
<!--end galaxy_download -->
You can also include it in a `requirements.yml` file and install it via `ansible-galaxy collection install -r requirements.yml`, using the format:
```yaml
@@ -51,66 +57,30 @@ A requirement file is provided to install:
### Install Playbook
* [`playbooks/keycloak.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak.yml) installs the upstream(Keycloak) based on the defined variables.
* [`playbooks/rhsso.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/rhsso.yml) installs Red Hat Single Sign-On(RHSSO) based on defined variables.
* [`playbooks/keycloak.yml`](https://github.com/ansible-middleware/keycloak/blob/main/playbooks/keycloak.yml) installs based on the defined variables (using most defaults).
Both playbooks include the `keycloak` role, with different settings, as described in the following sections.
For full service configuration details, refer to the [keycloak role README](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak/README.md).
### Choosing between upstream project (Keycloak) and Red Hat Single Sign-On (RHSSO)
#### Install from controller node (offline)
The general flag `keycloak_rhsso_enable` controls what to install between upstream (Keycloak, when `False`) or Red Hat Single Sign-On (when `True`).
The default value for the flag if `True` when Red Hat Network credentials are defined, `False` otherwise.
#### Install upstream (Keycloak) from keycloak releases
This is the default approach when RHN credentials are not defined. Keycloak is downloaded from keycloak builds (hosted on github.com) locally, and distributed to target nodes.
#### Install RHSSO from the Red Hat Customer Support Portal
Define the credentials as follows, and the default behaviour is to download a fresh archive of RHSSO on the controller node, then distribute to target nodes.
```yaml
rhn_username: '<customer_portal_username>'
rhn_password: '<customer_portal_password>'
# (keycloak_rhsso_enable defaults to True)
```
#### Install from controller node (local source)
Making the keycloak zip archive (or the RHSSO zip archive), available to the playbook repository root directory, and setting `keycloak_offline_install` to `True`, allows to skip
the download tasks. The local path for the archive matches the downloaded archive path, so it is also used as a cache when multiple hosts are provisioned in a cluster.
Making the keycloak zip archive available to the playbook working directory, and setting `keycloak_offline_install` to `True`, allows to skip
the download tasks. The local path for the archive does match the downloaded archive path, so that it is also used as a cache when multiple hosts are provisioned in a cluster.
```yaml
keycloak_offline_install: True
```
And depending on `keycloak_rhsso_enable`:
* `True`: install RHSSO using file rh-sso-x.y.z-server-dist.zip
* `False`: install keycloak using file keycloak-x.y.zip
<!--start rhn_credentials -->
<!--end rhn_credentials -->
#### Install from alternate sources (like corporate Nexus, artifactory, proxy, etc)
For RHSSO:
```yaml
keycloak_rhsso_enable: True
keycloak_rhsso_download_url: "https://<internal-nexus.private.net>/<path>/<to>/rh-sso-x.y.z-server-dist.zip"
```
For keycloak:
```yaml
keycloak_rhsso_enable: False
keycloak_download_url: "https://<internal-nexus.private.net>/<path>/<to>/keycloak-x.y.zip"
```
It is possible to perform downloads from alternate sources, using the `keycloak_download_url` variable; make sure the final downloaded filename matches with the source filename (ie. keycloak-legacy-x.y.zip or rh-sso-x.y.z-server-dist.zip).
### Example installation command
@@ -129,6 +99,8 @@ ansible-playbook -i <ansible_hosts> -e @rhn-creds.yml playbooks/keycloak.yml -e
localhost ansible_connection=local
```
Note: when deploying clustered configurations, all hosts belonging to the cluster must be present in ansible_play_batch; ie. they must be targeted by the same ansible-playbook execution.
## Configuration
@@ -157,9 +129,10 @@ 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).
## Support
Keycloak collection v1.0.0 is a Beta release and for [Technical Preview](https://access.redhat.com/support/offerings/techpreview). If you have any issues or questions related to collection, please don't hesitate to contact us on Ansible-middleware-core@redhat.com or open an issue on https://github.com/ansible-middleware/keycloak/issues
<!--start support -->
<!--end support -->
## License

8
bindep.txt Normal file
View File

@@ -0,0 +1,8 @@
python3-devel [compile platform:rpm]
python39-devel [compile platform:centos-8 platform:rhel-8]
git-lfs [platform:rpm]
python3-netaddr [platform:rpm]
python3-lxml [platform:rpm]
python3-jmespath [platform:rpm]
python3-requests [platform:rpm]

View File

@@ -60,3 +60,229 @@ releases:
release_date: '2022-05-09'
1.0.4:
release_date: '2022-05-11'
1.0.5:
changes:
minor_changes:
- 'Update config options: keycloak and quarkus `#32 <https://github.com/ansible-middleware/keycloak/pull/32>`_
'
fragments:
- 32.yaml
release_date: '2022-05-25'
1.0.6:
changes:
bugfixes:
- 'keycloak_quarkus: add selected java to PATH in systemd unit `#34 <https://github.com/ansible-middleware/keycloak/pull/34>`_
'
- 'keycloak_quarkus: set logfile path correctly under keycloak home `#35 <https://github.com/ansible-middleware/keycloak/pull/35>`_
'
fragments:
- 34.yaml
- 35.yaml
release_date: '2022-06-01'
1.0.7:
changes:
breaking_changes:
- 'keycloak_quarkus: use absolute path for certificate files `#39 <https://github.com/ansible-middleware/keycloak/pull/39>`_
'
bugfixes:
- 'keycloak_quarkus: use become for tasks that will otherwise fail `#38 <https://github.com/ansible-middleware/keycloak/pull/38>`_
'
fragments:
- 38.yaml
- 39.yaml
release_date: '2022-07-06'
1.1.0:
changes:
breaking_changes:
- 'Rename variables from ``infinispan_`` prefix to ``keycloak_infinispan_``
`#42 <https://github.com/ansible-middleware/keycloak/pull/42>`_
'
bugfixes:
- 'keycloak_quarkus: fix /var/log/keycloak symlink to keycloak log directory
`#44 <https://github.com/ansible-middleware/keycloak/pull/44>`_
'
minor_changes:
- 'Update keycloak to 18.0.2 - sso to 7.6.1 `#46 <https://github.com/ansible-middleware/keycloak/pull/46>`_
'
- 'Variable ``keycloak_no_log`` controls ansible ``no_log`` parameter (for debugging
purposes) `#47 <https://github.com/ansible-middleware/keycloak/pull/47>`_
'
- 'Variables to override service start retries and delay `#51 <https://github.com/ansible-middleware/keycloak/pull/51>`_
'
- 'keycloak_quarkus: variable to enable development mode `#45 <https://github.com/ansible-middleware/keycloak/pull/45>`_
'
fragments:
- 42.yaml
- 44.yaml
- 45.yaml
- 46.yaml
- 47.yaml
- 51.yaml
release_date: '2023-01-09'
1.1.1:
changes:
bugfixes:
- 'keycloak-quarkus: fix ``cache-config-file`` path in keycloak.conf.j2 template
`#53 <https://github.com/ansible-middleware/keycloak/pull/53>`_
'
fragments:
- 53.yaml
release_date: '2023-03-07'
1.2.0:
changes:
major_changes:
- 'Provide config for multiple modcluster proxies `#60 <https://github.com/ansible-middleware/keycloak/pull/60>`_
'
minor_changes:
- 'Allow to configure TCPPING for cluster discovery `#62 <https://github.com/ansible-middleware/keycloak/pull/62>`_
'
- 'Drop community.general from dependencies `#61 <https://github.com/ansible-middleware/keycloak/pull/61>`_
'
- 'Switch middleware_automation.redhat_csp_download for middleware_automation.common
`#63 <https://github.com/ansible-middleware/keycloak/pull/63>`_
'
- 'Switch to middleware_automation.common for rh-sso patching `#64 <https://github.com/ansible-middleware/keycloak/pull/64>`_
'
fragments:
- 60.yaml
- 61.yaml
- 62.yaml
- 63.yaml
- 64.yaml
release_date: '2023-03-16'
1.2.1:
changes:
bugfixes:
- 'Pass attributes to realm clients `#69 <https://github.com/ansible-middleware/keycloak/pull/69>`_
'
minor_changes:
- 'Allow to setup keycloak HA cluster without remote cache store `#68 <https://github.com/ansible-middleware/keycloak/pull/68>`_
'
fragments:
- 68.yaml
- 69.yaml
release_date: '2023-04-11'
1.2.4:
changes:
bugfixes:
- 'Fix deprecation warning for ``ipaddr`` `#77 <https://github.com/ansible-middleware/keycloak/pull/77>`_
'
- 'Fix undefined facts when offline patching sso `#71 <https://github.com/ansible-middleware/keycloak/pull/71>`_
'
minor_changes:
- 'Add ``sqlserver`` to keycloak role jdbc configurations `#78 <https://github.com/ansible-middleware/keycloak/pull/78>`_
'
- 'Add configurability for XA transactions `#73 <https://github.com/ansible-middleware/keycloak/pull/73>`_
'
fragments:
- 71.yaml
- 73.yaml
- 77.yaml
- 78.yaml
release_date: '2023-05-09'
1.2.5:
changes:
minor_changes:
- 'Add configuration for database connection pool validation `#85 <https://github.com/ansible-middleware/keycloak/pull/85>`_
'
- 'Allow to configure administration endpoint URL `#86 <https://github.com/ansible-middleware/keycloak/pull/86>`_
'
- 'Allow to force backend URLs to frontend URLs `#84 <https://github.com/ansible-middleware/keycloak/pull/84>`_
'
- 'Introduce systemd unit restart behavior `#81 <https://github.com/ansible-middleware/keycloak/pull/81>`_
'
fragments:
- 81.yaml
- 84.yaml
- 85.yaml
- 86.yaml
release_date: '2023-05-26'
1.2.6:
changes:
bugfixes:
- 'Handle WFLYCTL0117 when background validation millis is 0 `#90 <https://github.com/ansible-middleware/keycloak/pull/90>`_
'
minor_changes:
- 'Add profile features enabling/disabling `#87 <https://github.com/ansible-middleware/keycloak/pull/87>`_
'
- 'Improve service restart behavior configuration `#88 <https://github.com/ansible-middleware/keycloak/pull/88>`_
'
- 'Update default xa_datasource_class value for mariadb jdbc configuration `#89
<https://github.com/ansible-middleware/keycloak/pull/89>`_
'
fragments:
- 87.yaml
- 88.yaml
- 89.yaml
- 90.yaml
release_date: '2023-06-07'
1.2.7:
changes:
minor_changes:
- 'Allow to override jgroups subnet `#93 <https://github.com/ansible-middleware/keycloak/pull/93>`_
'
- 'keycloak-quarkus: update keycloakx to v21.1.1 `#92 <https://github.com/ansible-middleware/keycloak/pull/92>`_
'
fragments:
- 92.yaml
- 93.yaml
release_date: '2023-06-19'
1.2.8:
changes:
bugfixes:
- 'Fix incorrect checks for ``keycloak_jgroups_subnet`` `#98 <https://github.com/ansible-middleware/keycloak/pull/98>`_
'
- 'Undefine ``keycloak_db_valid_conn_sql`` default `#91 <https://github.com/ansible-middleware/keycloak/pull/91>`_
'
- 'Update bindep.txt package python3-devel to support RHEL9 `#105 <https://github.com/ansible-middleware/keycloak/pull/105>`_
'
minor_changes:
- 'keycloak_quarkus: set openjdk 17 as default `#103 <https://github.com/ansible-middleware/keycloak/pull/103>`_
'
- 'keycloak_quarkus: update to version 22.0.1 `#107 <https://github.com/ansible-middleware/keycloak/pull/107>`_
'
fragments:
- 103.yaml
- 105.yaml
- 107.yaml
- 91.yaml
- 98.yaml
release_date: '2023-08-28'

View File

@@ -21,6 +21,19 @@
<div class="wy-side-nav-search" >
<a href="#" class="icon icon-home"> Keycloak Ansible Collection</a>
</div>
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
<p class="caption" role="heading"><span class="caption-text">Middleware Automation</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/infinispan/">Infinispan / Red Hat Data Grid</a></li>
<li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/keycloak/">Keycloak / Red Hat Single Sign-On</a></li>
<li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/wildfly/">Wildfly / Red Hat JBoss EAP</a></li>
<li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/jws/">Tomcat / Red Hat JWS</a></li>
<li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/amq/">ActiveMQ / Red Hat AMQ Broker</a></li>
<li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/amq_streams/">Kafka / Red Hat AMQ Streams</a></li>
<li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/redhat-csp-download/">Red Hat CSP Download</a></li>
<li class="toctree-l1"><a class="reference internal" href="https://ansible-middleware.github.io/ansible_collections_jcliff/">JCliff</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">

View File

@@ -43,6 +43,7 @@ extensions = [
'myst_parser',
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
'sphinx_antsibull_ext',
'ansible_basic_sphinx_ext',
]
@@ -71,7 +72,7 @@ language = None
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '.tmp']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = 'ansible'
highlight_language = 'YAML+Jinja'

View File

@@ -25,8 +25,15 @@ Welcome to Keycloak Collection documentation
Changelog <CHANGELOG>
Indices and tables
==================
.. toctree::
:maxdepth: 2
:caption: Middleware collections
* :ref:`genindex`
* :ref:`search`
Infinispan / Red Hat Data Grid <https://ansible-middleware.github.io/infinispan/>
Keycloak / Red Hat Single Sign-On <https://ansible-middleware.github.io/keycloak/>
Wildfly / Red Hat JBoss EAP <https://ansible-middleware.github.io/wildfly/>
Tomcat / Red Hat JWS <https://ansible-middleware.github.io/jws/>
ActiveMQ / Red Hat AMQ Broker <https://ansible-middleware.github.io/amq/>
Kafka / Red Hat AMQ Streams <https://ansible-middleware.github.io/amq_streams/>
Red Hat CSP Download <https://ansible-middleware.github.io/redhat-csp-download/>
JCliff <https://ansible-middleware.github.io/ansible_collections_jcliff/>

View File

@@ -1,5 +1,8 @@
antsibull>=0.17.0
ansible-base>=2.10.12
antsibull-docs
antsibull-changelog
ansible-core>=2.14.1
ansible-pygments
sphinx-rtd-theme
git+https://github.com/felixfontein/ansible-basic-sphinx-ext
myst-parser

View File

@@ -46,4 +46,3 @@ EOF
# run the playbook
ansible-playbook -i inventory playbooks/keycloak.yml
```

View File

@@ -1,7 +1,7 @@
---
namespace: middleware_automation
name: keycloak
version: "1.0.4"
version: "1.2.8"
readme: README.md
authors:
- Romain Pelisse <rpelisse@redhat.com>
@@ -20,15 +20,27 @@ tags:
- security
- infrastructure
- authentication
- java
- runtimes
- middleware
- a4mw
dependencies:
"middleware_automation.redhat_csp_download": ">=1.2.1"
"middleware_automation.wildfly": ">=1.0.0"
"middleware_automation.common": ">=1.1.0"
"ansible.posix": ">=1.4.0"
repository: https://github.com/ansible-middleware/keycloak
documentation: https://ansible-middleware.github.io/keycloak
homepage: https://github.com/ansible-middleware/keycloak
issues: https://github.com/ansible-middleware/keycloak/issues
build_ignore:
- molecule
- .gitignore
- .github
- .ansible-lint
- .yamllint
- '*.tar.gz'
- '*.zip'
- molecule
- changelogs
- docs/_gh_include
- docs/conf.py
- docs/roles.rst.template
- docs/requirements.yml

View File

@@ -0,0 +1,11 @@
---
version: 1
build_arg_defaults:
EE_BASE_IMAGE: 'quay.io/ansible/ansible-runner:stable-2.12-devel'
dependencies:
galaxy: requirements.yml
python: requirements.txt
system: bindep.txt
additional_build_steps:
append:
- RUN alternatives --set python /usr/bin/python3

View File

@@ -4,6 +4,12 @@
vars:
keycloak_admin_password: "remembertochangeme"
keycloak_jvm_package: java-11-openjdk-headless
keycloak_modcluster_enabled: True
keycloak_modcluster_urls:
- host: myhost1
port: 16667
- host: myhost2
port: 16668
roles:
- role: keycloak
tasks:
@@ -39,3 +45,16 @@
web_origins: "{{ keycloak_client_web_origins }}"
users: "{{ keycloak_client_users }}"
client_id: TestClient
attributes:
post.logout.redirect.uris: '/public/logout'
pre_tasks:
- name: "Retrieve assets server from env"
ansible.builtin.set_fact:
assets_server: "{{ lookup('env','MIDDLEWARE_DOWNLOAD_RELEASE_SERVER_URL') }}"
- name: "Set offline when assets server from env is defined"
ansible.builtin.set_fact:
sso_offline_install: True
when:
- assets_server is defined
- assets_server | length > 0

View File

@@ -1,12 +1,6 @@
---
dependency:
name: shell
command: ansible-galaxy collection install -r molecule/default/requirements.yml -p $HOME/.ansible/collections --force-with-deps
driver:
name: docker
lint: |
ansible-lint --version
ansible-lint -v
platforms:
- name: instance
image: registry.access.redhat.com/ubi8/ubi-init:latest
@@ -16,7 +10,7 @@ platforms:
port_bindings:
- "8080/tcp"
- "8443/tcp"
- "8009/tcp"
- "8009/tcp"
provisioner:
name: ansible
config_options:
@@ -33,16 +27,14 @@ provisioner:
localhost:
ansible_python_interpreter: "{{ ansible_playbook_python }}"
env:
ANSIBLE_FORCE_COLOR: "true"
ANSIBLE_FORCE_COLOR: "true"
ANSIBLE_VERBOSITY: 3
verifier:
name: ansible
scenario:
test_sequence:
- dependency
- lint
- cleanup
- destroy
- syntax
- create
- prepare
- converge

View File

@@ -2,13 +2,19 @@
- name: Prepare
hosts: all
tasks:
- name: Disable beta repos
ansible.builtin.command: yum config-manager --disable '*beta*'
ignore_errors: yes
- name: Install sudo
ansible.builtin.yum:
name:
- sudo
- java-1.8.0-openjdk
state: present
- name: Prepare
hosts: all
tasks:
- name: "Run preparation common to all scenario"
ansible.builtin.include_tasks: ../prepare.yml
vars:
assets:
- "{{ assets_server }}/sso/7.6.0/rh-sso-7.6.0-server-dist.zip"
- "{{ assets_server }}/sso/7.6.1/rh-sso-7.6.1-patch.zip"

View File

@@ -1,10 +0,0 @@
---
collections:
- name: middleware_automation.redhat_csp_download
version: ">=1.2.1"
- name: middleware_automation.wildfly
version: ">=0.0.5"
- name: community.general
- name: community.docker
version: ">=1.9.1"

View File

@@ -4,7 +4,7 @@
vars:
keycloak_admin_password: "remembertochangeme"
keycloak_jvm_package: java-11-openjdk-headless
keycloak_port: http://localhost:8080
keycloak_uri: http://localhost:8080
keycloak_management_port: http://localhost:9990
tasks:
- name: Populate service facts
@@ -14,16 +14,44 @@
that:
- ansible_facts.services["keycloak.service"]["state"] == "running"
- ansible_facts.services["keycloak.service"]["status"] == "enabled"
- name: Verify we are running on requested jvm
shell: |
ps -ef | grep /usr/lib/jvm/java-11 | grep -v grep
- name: Verify we are running on requested jvm # noqa blocked_modules command-instead-of-module
ansible.builtin.shell: |
set -o pipefail
ps -ef | grep '/etc/alternatives/jre_11/' | grep -v grep
args:
executable: /bin/bash
changed_when: no
- name: Verify token api call
ansible.builtin.uri:
url: "{{ keycloak_port }}/auth/realms/master/protocol/openid-connect/token"
url: "{{ keycloak_uri }}/auth/realms/master/protocol/openid-connect/token"
method: POST
body: "client_id=admin-cli&username=admin&password={{ keycloak_admin_password }}&grant_type=password"
validate_certs: no
register: keycloak_auth_response
until: keycloak_auth_response.status == 200
retries: 2
delay: 2
delay: 2
- name: Fetch openid-connect config
ansible.builtin.uri:
url: "{{ keycloak_uri }}/auth/realms/TestRealm/.well-known/openid-configuration"
method: GET
validate_certs: no
status_code: 200
register: keycloak_openid_config
- name: Verify expected config
ansible.builtin.assert:
that:
- keycloak_openid_config.json.registration_endpoint == 'http://localhost:8080/auth/realms/TestRealm/clients-registrations/openid-connect'
- name: Get test realm clients
ansible.builtin.uri:
url: "{{ keycloak_uri }}/auth/admin/realms/TestRealm/clients"
method: GET
validate_certs: no
status_code: 200
headers:
Authorization: "Bearer {{ keycloak_auth_response.json.access_token }}"
register: keycloak_query_clients
- name: Verify expected config
ansible.builtin.assert:
that:
- (keycloak_query_clients.json | selectattr('clientId','equalto','TestClient') | first)["attributes"]["post.logout.redirect.uris"] == '/public/logout'

View File

@@ -41,3 +41,14 @@
web_origins: "{{ keycloak_client_web_origins }}"
users: "{{ keycloak_client_users }}"
client_id: TestClient
pre_tasks:
- name: "Retrieve assets server from env"
ansible.builtin.set_fact:
assets_server: "{{ lookup('env','MIDDLEWARE_DOWNLOAD_RELEASE_SERVER_URL') }}"
- name: "Set offline when assets server from env is defined"
ansible.builtin.set_fact:
sso_offline_install: True
when:
- assets_server is defined
- assets_server | length > 0

View File

@@ -1,12 +1,6 @@
---
dependency:
name: shell
command: ansible-galaxy collection install -r molecule/default/requirements.yml -p $HOME/.ansible/collections --force-with-deps
driver:
name: docker
lint: |
ansible-lint --version
ansible-lint -v
platforms:
- name: instance
image: registry.access.redhat.com/ubi8/ubi-init:latest
@@ -16,7 +10,7 @@ platforms:
port_bindings:
- "8080/tcp"
- "8443/tcp"
- "8009/tcp"
- "8009/tcp"
provisioner:
name: ansible
config_options:
@@ -33,16 +27,13 @@ provisioner:
localhost:
ansible_python_interpreter: "{{ ansible_playbook_python }}"
env:
ANSIBLE_FORCE_COLOR: "true"
ANSIBLE_FORCE_COLOR: "true"
verifier:
name: ansible
scenario:
test_sequence:
- dependency
- lint
- cleanup
- destroy
- syntax
- create
- prepare
- converge

View File

@@ -2,11 +2,8 @@
- name: Prepare
hosts: all
tasks:
- name: Disable beta repos
ansible.builtin.command: yum config-manager --disable '*beta*'
ignore_errors: yes
- name: Install sudo
ansible.builtin.yum:
name: sudo
state: present
- name: "Run preparation common to all scenario"
ansible.builtin.include_tasks: ../prepare.yml
vars:
assets:
- "{{ assets_server }}/sso/7.6.0/rh-sso-7.6.0-server-dist.zip"

View File

@@ -1,10 +0,0 @@
---
collections:
- name: middleware_automation.redhat_csp_download
version: ">=1.2.1"
- name: middleware_automation.wildfly
version: ">=0.0.5"
- name: community.general
- name: community.docker
version: ">=1.9.1"

View File

@@ -15,7 +15,6 @@
<extension module="org.jboss.as.modcluster"/>
<extension module="org.jboss.as.naming"/>
<extension module="org.jboss.as.remoting"/>
<extension module="org.jboss.as.security"/>
<extension module="org.jboss.as.transactions"/>
<extension module="org.jboss.as.weld"/>
<extension module="org.keycloak.keycloak-server-subsystem"/>
@@ -30,31 +29,6 @@
<extension module="org.wildfly.extension.undertow"/>
</extensions>
<management>
<security-realms>
<security-realm name="ManagementRealm">
<authentication>
<local default-user="$local" skip-group-loading="true"/>
<properties path="mgmt-users.properties" relative-to="jboss.server.config.dir"/>
</authentication>
<authorization map-groups-to-roles="false">
<properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
</authorization>
</security-realm>
<security-realm name="ApplicationRealm">
<server-identities>
<ssl>
<keystore path="application.keystore" relative-to="jboss.server.config.dir" keystore-password="password" alias="server" key-password="password" generate-self-signed-certificate-host="localhost"/>
</ssl>
</server-identities>
<authentication>
<local default-user="$local" allowed-users="*" skip-group-loading="true"/>
<properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
</authentication>
<authorization>
<properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
</authorization>
</security-realm>
</security-realms>
<audit-log>
<formatters>
<json-formatter name="json-formatter"/>
@@ -69,7 +43,7 @@
</logger>
</audit-log>
<management-interfaces>
<http-interface security-realm="ManagementRealm">
<http-interface http-authentication-factory="management-http-authentication">
<http-upgrade enabled="true"/>
<socket-binding http="management-http"/>
</http-interface>
@@ -205,6 +179,9 @@
</thread-pool>
</thread-pools>
<default-security-domain value="other"/>
<application-security-domains>
<application-security-domain name="other" security-domain="ApplicationDomain"/>
</application-security-domains>
<default-missing-method-permissions-deny-access value="true"/>
<statistics enabled="${wildfly.ejb3.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
<log-system-exceptions value="true"/>
@@ -278,6 +255,13 @@
</mechanism>
</mechanism-configuration>
</http-authentication-factory>
<http-authentication-factory name="application-http-authentication" security-domain="ApplicationDomain" http-server-mechanism-factory="global">
<mechanism-configuration>
<mechanism mechanism-name="BASIC">
<mechanism-realm realm-name="ApplicationRealm"/>
</mechanism>
</mechanism-configuration>
</http-authentication-factory>
<provider-http-server-mechanism-factory name="global"/>
</http>
<sasl>
@@ -513,41 +497,9 @@
<remote-naming/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:remoting:4.0">
<http-connector name="http-remoting-connector" connector-ref="default" security-realm="ApplicationRealm"/>
<http-connector name="http-remoting-connector" connector-ref="default" sasl-authentication-factory="application-sasl-authentication"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:request-controller:1.0"/>
<subsystem xmlns="urn:jboss:domain:security:2.0">
<security-domains>
<security-domain name="other" cache-type="default">
<authentication>
<login-module code="Remoting" flag="optional">
<module-option name="password-stacking" value="useFirstPass"/>
</login-module>
<login-module code="RealmDirect" flag="required">
<module-option name="password-stacking" value="useFirstPass"/>
</login-module>
</authentication>
</security-domain>
<security-domain name="jboss-web-policy" cache-type="default">
<authorization>
<policy-module code="Delegating" flag="required"/>
</authorization>
</security-domain>
<security-domain name="jaspitest" cache-type="default">
<authentication-jaspi>
<login-module-stack name="dummy">
<login-module code="Dummy" flag="optional"/>
</login-module-stack>
<auth-module code="Dummy"/>
</authentication-jaspi>
</security-domain>
<security-domain name="jboss-ejb-policy" cache-type="default">
<authorization>
<policy-module code="Delegating" flag="required"/>
</authorization>
</security-domain>
</security-domains>
</subsystem>
<subsystem xmlns="urn:jboss:domain:security-manager:1.0">
<deployment-permissions>
<maximum-set>
@@ -571,7 +523,7 @@
<http-listener name="default" socket-binding="http"/>
<host name="default-host" alias="localhost">
<location name="/" handler="welcome-content"/>
<http-invoker security-realm="ApplicationRealm"/>
<http-invoker http-authentication-factory="application-http-authentication"/>
</host>
</server>
<servlet-container name="default">

35
molecule/prepare.yml Normal file
View File

@@ -0,0 +1,35 @@
---
- name: Display Ansible version
ansible.builtin.debug:
msg: "Ansible version is {{ ansible_version.full }}"
- name: Install sudo
ansible.builtin.yum:
name:
- sudo
- iproute
state: present
- name: "Retrieve assets server from env"
ansible.builtin.set_fact:
assets_server: "{{ lookup('env','MIDDLEWARE_DOWNLOAD_RELEASE_SERVER_URL') }}"
- name: "Set offline when assets server from env is defined"
ansible.builtin.set_fact:
sso_offline_install: True
when:
- assets_server is defined
- assets_server | length > 0
- name: "Download and deploy zips from {{ assets_server }}"
ansible.builtin.get_url:
url: "{{ asset }}"
dest: "{{ lookup('env', 'PWD') }}"
validate_certs: no
delegate_to: localhost
loop: "{{ assets }}"
loop_control:
loop_var: asset
when:
- assets_server is defined
- assets_server | length > 0

View File

@@ -5,6 +5,12 @@
keycloak_quarkus_admin_pass: "remembertochangeme"
keycloak_admin_password: "remembertochangeme"
keycloak_realm: TestRealm
keycloak_quarkus_host: instance
keycloak_quarkus_http_relative_path: ''
keycloak_quarkus_log: file
keycloak_quarkus_https_enabled: True
keycloak_quarkus_key_file: "{{ keycloak.home }}/conf/key.pem"
keycloak_quarkus_cert_file: "{{ keycloak.home }}/conf/cert.pem"
roles:
- role: keycloak_quarkus
- role: keycloak_realm

View File

@@ -1,12 +1,6 @@
---
dependency:
name: shell
command: ansible-galaxy collection install -r molecule/default/requirements.yml -p $HOME/.ansible/collections --force-with-deps
driver:
name: docker
lint: |
ansible-lint --version
ansible-lint -v
platforms:
- name: instance
image: registry.access.redhat.com/ubi8/ubi-init:latest
@@ -16,7 +10,9 @@ platforms:
port_bindings:
- "8080/tcp"
- "8443/tcp"
- "8009/tcp"
- "8009/tcp"
published_ports:
- 0.0.0.0:8443:8443/tcp
provisioner:
name: ansible
config_options:
@@ -33,16 +29,13 @@ provisioner:
localhost:
ansible_python_interpreter: "{{ ansible_playbook_python }}"
env:
ANSIBLE_FORCE_COLOR: "true"
ANSIBLE_FORCE_COLOR: "true"
verifier:
name: ansible
scenario:
test_sequence:
- dependency
- lint
- cleanup
- destroy
- syntax
- create
- prepare
- converge

View File

@@ -2,11 +2,42 @@
- name: Prepare
hosts: all
tasks:
- name: Disable beta repos
ansible.builtin.command: yum config-manager --disable '*beta*'
ignore_errors: yes
- name: Install sudo
ansible.builtin.yum:
name: sudo
state: present
- name: "Display hera_home if defined."
ansible.builtin.set_fact:
hera_home: "{{ lookup('env', 'HERA_HOME') }}"
- 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'
delegate_to: localhost
changed_when: False
- name: Set /etc/hosts
ansible.builtin.lineinfile:
dest: /etc/hosts
line: "127.0.0.1 instance"
state: present
delegate_to: localhost
become: yes
when:
- hera_home is defined
- hera_home | length == 0
- name: Create conf directory # risky-file-permissions in test user account does not exist yet
ansible.builtin.file:
state: directory
path: /opt/keycloak/keycloak-22.0.1/conf/
mode: 0755
- name: Copy certificates
ansible.builtin.copy:
src: "{{ item }}"
dest: "/opt/keycloak/keycloak-22.0.1/conf/{{ item }}"
mode: 0444
loop:
- cert.pem
- key.pem

View File

@@ -1,10 +0,0 @@
---
collections:
- name: middleware_automation.redhat_csp_download
version: ">=1.2.1"
- name: middleware_automation.wildfly
version: ">=0.0.5"
- name: community.general
- name: community.docker
version: ">=1.9.1"

View File

@@ -4,8 +4,36 @@
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"
- name: Set internal envvar
ansible.builtin.set_fact:
hera_home: "{{ lookup('env', 'HERA_HOME') }}"
- name: Verify openid config
block:
- name: Fetch openID config # noqa blocked_modules command-instead-of-module
ansible.builtin.shell: |
set -o pipefail
curl https://instance:8443/realms/master/.well-known/openid-configuration -k | jq .
args:
executable: /bin/bash
delegate_to: localhost
register: openid_config
changed_when: False
- name: Verify endpoint URLs
ansible.builtin.assert:
that:
- (openid_config.stdout | from_json)["backchannel_authentication_endpoint"] == 'https://instance/realms/master/protocol/openid-connect/ext/ciba/auth'
- (openid_config.stdout | from_json)['issuer'] == 'https://instance/realms/master'
- (openid_config.stdout | from_json)['authorization_endpoint'] == 'https://instance/realms/master/protocol/openid-connect/auth'
- (openid_config.stdout | from_json)['token_endpoint'] == 'https://instance/realms/master/protocol/openid-connect/token'
delegate_to: localhost
when:
- hera_home is defined
- hera_home | length == 0

View File

@@ -0,0 +1,8 @@
---
collections:
- name: middleware_automation.common
- name: community.general
- name: ansible.posix
- name: community.docker
version: ">=1.9.1"

View File

@@ -3,7 +3,5 @@
hosts: all
vars:
keycloak_admin_password: "remembertochangeme"
collections:
- middleware_automation.keycloak
roles:
- keycloak
- middleware_automation.keycloak.keycloak

View File

@@ -0,0 +1,68 @@
---
- name: Playbook for Keycloak Hosts
hosts: all
tasks:
- name: Keycloak Realm Role
ansible.builtin.include_role:
name: keycloak_realm
vars:
keycloak_admin_password: "remembertochangeme"
keycloak_realm: TestRealm
keycloak_user_federation:
- realm: TestRealm
name: my-ldap
provider_id: ldap
provider_type: org.keycloak.storage.UserStorageProvider
config:
priority: '0'
enabled: true
cachePolicy: DEFAULT
batchSizeForSync: '1000'
editMode: READ_ONLY
importEnabled: true
syncRegistrations: false
vendor: other
usernameLDAPAttribute: uid
rdnLDAPAttribute: uid
uuidLDAPAttribute: entryUUID
userObjectClasses: inetOrgPerson, organizationalPerson
connectionUrl: ldaps://ldap.example.com:636
usersDn: ou=Users,dc=example,dc=com
authType: simple
bindDn: cn=directory reader
bindCredential: password
searchScope: '1'
validatePasswordPolicy: false
trustEmail: false
useTruststoreSpi: ldapsOnly
connectionPooling: true
pagination: true
allowKerberosAuthentication: false
debug: false
useKerberosForPasswordAuthentication: false
mappers:
- name: "full name"
providerId: "full-name-ldap-mapper"
providerType: "org.keycloak.storage.ldap.mappers.LDAPStorageMapper"
config:
ldap.full.name.attribute: cn
read.only: true
write.only: false
keycloak_clients:
- name: TestClient1
client_id: TestClient1
roles:
- TestClient1Admin
- TestClient1User
realm: "{{ keycloak_realm }}"
public_client: True
web_origins:
- http://testclient1origin/application
- http://testclient1origin/other
users:
- username: TestUser
password: password
client_roles:
- client: TestClient1
role: TestClient1User
realm: "{{ keycloak_realm }}"

View File

@@ -0,0 +1,13 @@
---
- name: Playbook for Keycloak X Hosts
hosts: all
vars:
keycloak_admin_password: "remembertochangeme"
keycloak_quarkus_host: localhost:8443
keycloak_quarkus_http_relative_path: ''
keycloak_quarkus_log: file
keycloak_quarkus_https_enabled: True
keycloak_quarkus_key_file: conf/key.pem
keycloak_quarkus_cert_file: conf/cert.pem
roles:
- middleware_automation.keycloak.keycloak_quarkus

View File

@@ -1,67 +1,26 @@
---
- name: Playbook for Keycloak Hosts
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_user_federation:
- realm: TestRealm
name: my-ldap
provider_id: ldap
provider_type: org.keycloak.storage.UserStorageProvider
config:
priority: '0'
enabled: true
cachePolicy: DEFAULT
batchSizeForSync: '1000'
editMode: READ_ONLY
importEnabled: true
syncRegistrations: false
vendor: other
usernameLDAPAttribute: uid
rdnLDAPAttribute: uid
uuidLDAPAttribute: entryUUID
userObjectClasses: inetOrgPerson, organizationalPerson
connectionUrl: ldaps://ldap.example.com:636
usersDn: ou=Users,dc=example,dc=com
authType: simple
bindDn: cn=directory reader
bindCredential: password
searchScope: '1'
validatePasswordPolicy: false
trustEmail: false
useTruststoreSpi: ldapsOnly
connectionPooling: true
pagination: true
allowKerberosAuthentication: false
debug: false
useKerberosForPasswordAuthentication: false
mappers:
- name: "full name"
providerId: "full-name-ldap-mapper"
providerType: "org.keycloak.storage.ldap.mappers.LDAPStorageMapper"
config:
ldap.full.name.attribute: cn
read.only: true
write.only: false
keycloak_clients:
- name: TestClient1
roles:
- TestClient1Admin
- TestClient1User
realm: "{{ keycloak_realm }}"
public_client: True
web_origins:
- http://testclient1origin/application
- http://testclient1origin/other
users:
- username: TestUser
password: password
client_roles:
- client: TestClient1
role: TestClient1User
realm: "{{ keycloak_realm }}"
vars:
keycloak_admin_password: "remembertochangeme"
keycloak_clients:
- name: TestClient1
client_id: TestClient1
roles:
- TestClient1Admin
- TestClient1User
realm: TestRealm
public_client: True
web_origins:
- http://testclient1origin/application
- http://testclient1origin/other
users:
- username: TestUser
password: password
client_roles:
- client: TestClient1
role: TestClient1User
realm: TestRealm
roles:
- role: middleware_automation.keycloak.keycloak_realm
keycloak_realm: TestRealm

View File

@@ -1,12 +1,8 @@
---
- name: Playbook for Keycloak Hosts
hosts: keycloak
- name: Playbook for Red Hat SSO Hosts
hosts: sso
vars:
keycloak_admin_password: "remembertochangeme"
keycloak_rhsso_enable: True
collections:
- middleware_automation.redhat_csp_download
- middleware_automation.keycloak
sso_enable: True
roles:
- middleware_automation.redhat_csp_download.redhat_csp_download
- middleware_automation.keycloak.keycloak

View File

@@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see COPYING 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
class ModuleDocFragment(object):
# Standard documentation fragment
DOCUMENTATION = r'''
options: {}
attributes:
check_mode:
description: Can run in C(check_mode) and return changed status prediction without modifying target.
diff_mode:
description: Will return details on what has changed (or possibly needs changing in C(check_mode)), when in diff mode.
'''
PLATFORM = r'''
options: {}
attributes:
platform:
description: Target OS/families that can be operated against.
support: N/A
'''
# Should be used together with the standard fragment
INFO_MODULE = r'''
options: {}
attributes:
check_mode:
support: full
details:
- This action does not modify state.
diff_mode:
support: N/A
details:
- This action does not modify state.
'''
CONN = r'''
options: {}
attributes:
become:
description: Is usable alongside C(become) keywords.
connection:
description: Uses the target's configured connection information to execute code on it.
delegation:
description: Can be used in conjunction with C(delegate_to) and related keywords.
'''
FACTS = r'''
options: {}
attributes:
facts:
description: Action returns an C(ansible_facts) dictionary that will update existing host facts.
'''
# Should be used together with the standard fragment and the FACTS fragment
FACTS_MODULE = r'''
options: {}
attributes:
check_mode:
support: full
details:
- This action does not modify state.
diff_mode:
support: N/A
details:
- This action does not modify state.
facts:
support: full
'''
FILES = r'''
options: {}
attributes:
safe_file_operations:
description: Uses Ansible's strict file operation functions to ensure proper permissions and avoid data corruption.
'''
FLOW = r'''
options: {}
attributes:
action:
description: Indicates this has a corresponding action plugin so some parts of the options can be executed on the controller.
async:
description: Supports being used with the C(async) keyword.
'''

View File

@@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Eike Frost <ei@kefro.st>
# 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
class ModuleDocFragment(object):
# Standard documentation fragment
DOCUMENTATION = r'''
options:
auth_keycloak_url:
description:
- URL to the Keycloak instance.
type: str
required: true
aliases:
- url
auth_client_id:
description:
- OpenID Connect I(client_id) to authenticate to the API with.
type: str
default: admin-cli
auth_realm:
description:
- Keycloak realm name to authenticate to for API access.
type: str
auth_client_secret:
description:
- Client Secret to use in conjunction with I(auth_client_id) (if required).
type: str
auth_username:
description:
- Username to authenticate for API access with.
type: str
aliases:
- username
auth_password:
description:
- Password to authenticate for API access with.
type: str
aliases:
- password
token:
description:
- Authentication token for Keycloak API.
type: str
version_added: 3.0.0
validate_certs:
description:
- Verify TLS certificates (do not disable this in production).
type: bool
default: true
connection_timeout:
description:
- Controls the HTTP connections timeout period (in seconds) to Keycloak API.
type: int
default: 10
version_added: 4.5.0
http_agent:
description:
- Configures the HTTP User-Agent header.
type: str
default: Ansible
version_added: 5.4.0
'''

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,984 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Eike Frost <ei@kefro.st>
# 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
short_description: Allows administration of Keycloak clients via Keycloak API
description:
- This module allows the administration of Keycloak clients 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.
- The names of module options are snake_cased versions of the camelCase ones found in the
Keycloak API and its documentation at U(https://www.keycloak.org/docs-api/8.0/rest-api/index.html).
Aliases are provided so camelCased versions can be used as well.
- The Keycloak API does not always sanity check inputs e.g. you can set
SAML-specific settings on an OpenID Connect client for instance and vice versa. Be careful.
If you do not specify a setting, usually a sensible default is chosen.
attributes:
check_mode:
support: full
diff_mode:
support: full
options:
state:
description:
- State of the client
- On C(present), the client will be created (or updated if it exists already).
- On C(absent), the client will be removed if it exists
choices: ['present', 'absent']
default: 'present'
type: str
realm:
description:
- The realm to create the client in.
type: str
default: master
client_id:
description:
- Client id of client to be worked on. This is usually an alphanumeric name chosen by
you. Either this or I(id) is required. If you specify both, I(id) takes precedence.
This is 'clientId' in the Keycloak REST API.
aliases:
- clientId
type: str
id:
description:
- Id of client to be worked on. This is usually an UUID. Either this or I(client_id)
is required. If you specify both, this takes precedence.
type: str
name:
description:
- Name of the client (this is not the same as I(client_id)).
type: str
description:
description:
- Description of the client in Keycloak.
type: str
root_url:
description:
- Root URL appended to relative URLs for this client.
This is 'rootUrl' in the Keycloak REST API.
aliases:
- rootUrl
type: str
admin_url:
description:
- URL to the admin interface of the client.
This is 'adminUrl' in the Keycloak REST API.
aliases:
- adminUrl
type: str
base_url:
description:
- Default URL to use when the auth server needs to redirect or link back to the client
This is 'baseUrl' in the Keycloak REST API.
aliases:
- baseUrl
type: str
enabled:
description:
- Is this client enabled or not?
type: bool
client_authenticator_type:
description:
- How do clients authenticate with the auth server? Either C(client-secret) or
C(client-jwt) can be chosen. When using C(client-secret), the module parameter
I(secret) can set it, while for C(client-jwt), you can use the keys C(use.jwks.url),
C(jwks.url), and C(jwt.credential.certificate) in the I(attributes) module parameter
to configure its behavior.
This is 'clientAuthenticatorType' in the Keycloak REST API.
choices: ['client-secret', 'client-jwt']
aliases:
- clientAuthenticatorType
type: str
secret:
description:
- When using I(client_authenticator_type) C(client-secret) (the default), you can
specify a secret here (otherwise one will be generated if it does not exit). If
changing this secret, the module will not register a change currently (but the
changed secret will be saved).
type: str
registration_access_token:
description:
- The registration access token provides access for clients to the client registration
service.
This is 'registrationAccessToken' in the Keycloak REST API.
aliases:
- registrationAccessToken
type: str
default_roles:
description:
- list of default roles for this client. If the client roles referenced do not exist
yet, they will be created.
This is 'defaultRoles' in the Keycloak REST API.
aliases:
- defaultRoles
type: list
elements: str
redirect_uris:
description:
- Acceptable redirect URIs for this client.
This is 'redirectUris' in the Keycloak REST API.
aliases:
- redirectUris
type: list
elements: str
web_origins:
description:
- List of allowed CORS origins.
This is 'webOrigins' in the Keycloak REST API.
aliases:
- webOrigins
type: list
elements: str
not_before:
description:
- Revoke any tokens issued before this date for this client (this is a UNIX timestamp).
This is 'notBefore' in the Keycloak REST API.
type: int
aliases:
- notBefore
bearer_only:
description:
- The access type of this client is bearer-only.
This is 'bearerOnly' in the Keycloak REST API.
aliases:
- bearerOnly
type: bool
consent_required:
description:
- If enabled, users have to consent to client access.
This is 'consentRequired' in the Keycloak REST API.
aliases:
- consentRequired
type: bool
standard_flow_enabled:
description:
- Enable standard flow for this client or not (OpenID connect).
This is 'standardFlowEnabled' in the Keycloak REST API.
aliases:
- standardFlowEnabled
type: bool
implicit_flow_enabled:
description:
- Enable implicit flow for this client or not (OpenID connect).
This is 'implicitFlowEnabled' in the Keycloak REST API.
aliases:
- implicitFlowEnabled
type: bool
direct_access_grants_enabled:
description:
- Are direct access grants enabled for this client or not (OpenID connect).
This is 'directAccessGrantsEnabled' in the Keycloak REST API.
aliases:
- directAccessGrantsEnabled
type: bool
service_accounts_enabled:
description:
- Are service accounts enabled for this client or not (OpenID connect).
This is 'serviceAccountsEnabled' in the Keycloak REST API.
aliases:
- serviceAccountsEnabled
type: bool
authorization_services_enabled:
description:
- Are authorization services enabled for this client or not (OpenID connect).
This is 'authorizationServicesEnabled' in the Keycloak REST API.
aliases:
- authorizationServicesEnabled
type: bool
public_client:
description:
- Is the access type for this client public or not.
This is 'publicClient' in the Keycloak REST API.
aliases:
- publicClient
type: bool
frontchannel_logout:
description:
- Is frontchannel logout enabled for this client or not.
This is 'frontchannelLogout' in the Keycloak REST API.
aliases:
- frontchannelLogout
type: bool
protocol:
description:
- Type of client (either C(openid-connect) or C(saml).
type: str
choices: ['openid-connect', 'saml']
full_scope_allowed:
description:
- Is the "Full Scope Allowed" feature set for this client or not.
This is 'fullScopeAllowed' in the Keycloak REST API.
aliases:
- fullScopeAllowed
type: bool
node_re_registration_timeout:
description:
- Cluster node re-registration timeout for this client.
This is 'nodeReRegistrationTimeout' in the Keycloak REST API.
type: int
aliases:
- nodeReRegistrationTimeout
registered_nodes:
description:
- dict of registered cluster nodes (with C(nodename) as the key and last registration
time as the value).
This is 'registeredNodes' in the Keycloak REST API.
type: dict
aliases:
- registeredNodes
client_template:
description:
- Client template to use for this client. If it does not exist this field will silently
be dropped.
This is 'clientTemplate' in the Keycloak REST API.
type: str
aliases:
- clientTemplate
use_template_config:
description:
- Whether or not to use configuration from the I(client_template).
This is 'useTemplateConfig' in the Keycloak REST API.
aliases:
- useTemplateConfig
type: bool
use_template_scope:
description:
- Whether or not to use scope configuration from the I(client_template).
This is 'useTemplateScope' in the Keycloak REST API.
aliases:
- useTemplateScope
type: bool
use_template_mappers:
description:
- Whether or not to use mapper configuration from the I(client_template).
This is 'useTemplateMappers' in the Keycloak REST API.
aliases:
- useTemplateMappers
type: bool
always_display_in_console:
description:
- Whether or not to display this client in account console, even if the
user does not have an active session.
aliases:
- alwaysDisplayInConsole
type: bool
version_added: 4.7.0
surrogate_auth_required:
description:
- Whether or not surrogate auth is required.
This is 'surrogateAuthRequired' in the Keycloak REST API.
aliases:
- surrogateAuthRequired
type: bool
authorization_settings:
description:
- a data structure defining the authorization settings for this client. For reference,
please see the Keycloak API docs at U(https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_resourceserverrepresentation).
This is 'authorizationSettings' in the Keycloak REST API.
type: dict
aliases:
- authorizationSettings
authentication_flow_binding_overrides:
description:
- Override realm authentication flow bindings.
type: dict
aliases:
- authenticationFlowBindingOverrides
version_added: 3.4.0
default_client_scopes:
description:
- List of default client scopes.
aliases:
- defaultClientScopes
type: list
elements: str
version_added: 4.7.0
optional_client_scopes:
description:
- List of optional client scopes.
aliases:
- optionalClientScopes
type: list
elements: str
version_added: 4.7.0
protocol_mappers:
description:
- a list of dicts defining protocol mappers for this client.
This is 'protocolMappers' in the Keycloak REST API.
aliases:
- protocolMappers
type: list
elements: dict
suboptions:
consentRequired:
description:
- Specifies whether a user needs to provide consent to a client for this mapper to be active.
type: bool
consentText:
description:
- The human-readable name of the consent the user is presented to accept.
type: str
id:
description:
- Usually a UUID specifying the internal ID of this protocol mapper instance.
type: str
name:
description:
- The name of this protocol mapper.
type: str
protocol:
description:
- This is either C(openid-connect) or C(saml), this specifies for which protocol this protocol mapper.
is active.
choices: ['openid-connect', 'saml']
type: str
protocolMapper:
description:
- The Keycloak-internal name of the type of this protocol-mapper. While an exhaustive list is
impossible to provide since this may be extended through SPIs by the user of Keycloak,
by default Keycloak as of 3.4 ships with at least
- C(docker-v2-allow-all-mapper)
- C(oidc-address-mapper)
- C(oidc-full-name-mapper)
- C(oidc-group-membership-mapper)
- C(oidc-hardcoded-claim-mapper)
- C(oidc-hardcoded-role-mapper)
- C(oidc-role-name-mapper)
- C(oidc-script-based-protocol-mapper)
- C(oidc-sha256-pairwise-sub-mapper)
- C(oidc-usermodel-attribute-mapper)
- C(oidc-usermodel-client-role-mapper)
- C(oidc-usermodel-property-mapper)
- C(oidc-usermodel-realm-role-mapper)
- C(oidc-usersessionmodel-note-mapper)
- C(saml-group-membership-mapper)
- C(saml-hardcode-attribute-mapper)
- C(saml-hardcode-role-mapper)
- C(saml-role-list-mapper)
- C(saml-role-name-mapper)
- C(saml-user-attribute-mapper)
- C(saml-user-property-mapper)
- C(saml-user-session-note-mapper)
- An exhaustive list of available mappers on your installation can be obtained on
the admin console by going to Server Info -> Providers and looking under
'protocol-mapper'.
type: str
config:
description:
- Dict specifying the configuration options for the protocol mapper; the
contents differ depending on the value of I(protocolMapper) and are not documented
other than by the source of the mappers and its parent class(es). An example is given
below. It is easiest to obtain valid config values by dumping an already-existing
protocol mapper configuration through check-mode in the I(existing) field.
type: dict
attributes:
description:
- A dict of further attributes for this client. This can contain various configuration
settings; an example is given in the examples section. While an exhaustive list of
permissible options is not available; possible options as of Keycloak 3.4 are listed below. The Keycloak
API does not validate whether a given option is appropriate for the protocol used; if specified
anyway, Keycloak will simply not use it.
type: dict
suboptions:
saml.authnstatement:
description:
- For SAML clients, boolean specifying whether or not a statement containing method and timestamp
should be included in the login response.
saml.client.signature:
description:
- For SAML clients, boolean specifying whether a client signature is required and validated.
saml.encrypt:
description:
- Boolean specifying whether SAML assertions should be encrypted with the client's public key.
saml.force.post.binding:
description:
- For SAML clients, boolean specifying whether always to use POST binding for responses.
saml.onetimeuse.condition:
description:
- For SAML clients, boolean specifying whether a OneTimeUse condition should be included in login responses.
saml.server.signature:
description:
- Boolean specifying whether SAML documents should be signed by the realm.
saml.server.signature.keyinfo.ext:
description:
- For SAML clients, boolean specifying whether REDIRECT signing key lookup should be optimized through inclusion
of the signing key id in the SAML Extensions element.
saml.signature.algorithm:
description:
- Signature algorithm used to sign SAML documents. One of C(RSA_SHA256), C(RSA_SHA1), C(RSA_SHA512), or C(DSA_SHA1).
saml.signing.certificate:
description:
- SAML signing key certificate, base64-encoded.
saml.signing.private.key:
description:
- SAML signing key private key, base64-encoded.
saml_assertion_consumer_url_post:
description:
- SAML POST Binding URL for the client's assertion consumer service (login responses).
saml_assertion_consumer_url_redirect:
description:
- SAML Redirect Binding URL for the client's assertion consumer service (login responses).
saml_force_name_id_format:
description:
- For SAML clients, Boolean specifying whether to ignore requested NameID subject format and using the configured one instead.
saml_name_id_format:
description:
- For SAML clients, the NameID format to use (one of C(username), C(email), C(transient), or C(persistent))
saml_signature_canonicalization_method:
description:
- SAML signature canonicalization method. This is one of four values, namely
C(http://www.w3.org/2001/10/xml-exc-c14n#) for EXCLUSIVE,
C(http://www.w3.org/2001/10/xml-exc-c14n#WithComments) for EXCLUSIVE_WITH_COMMENTS,
C(http://www.w3.org/TR/2001/REC-xml-c14n-20010315) for INCLUSIVE, and
C(http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments) for INCLUSIVE_WITH_COMMENTS.
saml_single_logout_service_url_post:
description:
- SAML POST binding url for the client's single logout service.
saml_single_logout_service_url_redirect:
description:
- SAML redirect binding url for the client's single logout service.
user.info.response.signature.alg:
description:
- For OpenID-Connect clients, JWA algorithm for signed UserInfo-endpoint responses. One of C(RS256) or C(unsigned).
request.object.signature.alg:
description:
- For OpenID-Connect clients, JWA algorithm which the client needs to use when sending
OIDC request object. One of C(any), C(none), C(RS256).
use.jwks.url:
description:
- For OpenID-Connect clients, boolean specifying whether to use a JWKS URL to obtain client
public keys.
jwks.url:
description:
- For OpenID-Connect clients, URL where client keys in JWK are stored.
jwt.credential.certificate:
description:
- For OpenID-Connect clients, client certificate for validating JWT issued by
client and signed by its key, base64-encoded.
extends_documentation_fragment:
- middleware_automation.keycloak.keycloak
- middleware_automation.keycloak.attributes
author:
- Eike Frost (@eikef)
'''
EXAMPLES = '''
- name: Create or update Keycloak client (minimal example), authentication with credentials
middleware_automation.keycloak.keycloak_client:
auth_keycloak_url: https://auth.example.com/auth
auth_realm: master
auth_username: USERNAME
auth_password: PASSWORD
client_id: test
state: present
delegate_to: localhost
- name: Create or update Keycloak client (minimal example), authentication with token
middleware_automation.keycloak.keycloak_client:
auth_client_id: admin-cli
auth_keycloak_url: https://auth.example.com/auth
auth_realm: master
token: TOKEN
client_id: test
state: present
delegate_to: localhost
- name: Delete a Keycloak client
middleware_automation.keycloak.keycloak_client:
auth_client_id: admin-cli
auth_keycloak_url: https://auth.example.com/auth
auth_realm: master
auth_username: USERNAME
auth_password: PASSWORD
client_id: test
state: absent
delegate_to: localhost
- name: Create or update a Keycloak client (with all the bells and whistles)
middleware_automation.keycloak.keycloak_client:
auth_client_id: admin-cli
auth_keycloak_url: https://auth.example.com/auth
auth_realm: master
auth_username: USERNAME
auth_password: PASSWORD
state: present
realm: master
client_id: test
id: d8b127a3-31f6-44c8-a7e4-4ab9a3e78d95
name: this_is_a_test
description: Description of this wonderful client
root_url: https://www.example.com/
admin_url: https://www.example.com/admin_url
base_url: basepath
enabled: true
client_authenticator_type: client-secret
secret: REALLYWELLKEPTSECRET
redirect_uris:
- https://www.example.com/*
- http://localhost:8888/
web_origins:
- https://www.example.com/*
not_before: 1507825725
bearer_only: false
consent_required: false
standard_flow_enabled: true
implicit_flow_enabled: false
direct_access_grants_enabled: false
service_accounts_enabled: false
authorization_services_enabled: false
public_client: false
frontchannel_logout: false
protocol: openid-connect
full_scope_allowed: false
node_re_registration_timeout: -1
client_template: test
use_template_config: false
use_template_scope: false
use_template_mappers: false
always_display_in_console: true
registered_nodes:
node01.example.com: 1507828202
registration_access_token: eyJWT_TOKEN
surrogate_auth_required: false
default_roles:
- test01
- test02
authentication_flow_binding_overrides:
browser: 4c90336b-bf1d-4b87-916d-3677ba4e5fbb
protocol_mappers:
- config:
access.token.claim: true
claim.name: "family_name"
id.token.claim: true
jsonType.label: String
user.attribute: lastName
userinfo.token.claim: true
consentRequired: true
consentText: "${familyName}"
name: family name
protocol: openid-connect
protocolMapper: oidc-usermodel-property-mapper
- config:
attribute.name: Role
attribute.nameformat: Basic
single: false
consentRequired: false
name: role list
protocol: saml
protocolMapper: saml-role-list-mapper
attributes:
saml.authnstatement: true
saml.client.signature: true
saml.force.post.binding: true
saml.server.signature: true
saml.signature.algorithm: RSA_SHA256
saml.signing.certificate: CERTIFICATEHERE
saml.signing.private.key: PRIVATEKEYHERE
saml_force_name_id_format: false
saml_name_id_format: username
saml_signature_canonicalization_method: "http://www.w3.org/2001/10/xml-exc-c14n#"
user.info.response.signature.alg: RS256
request.object.signature.alg: RS256
use.jwks.url: true
jwks.url: JWKS_URL_FOR_CLIENT_AUTH_JWT
jwt.credential.certificate: JWT_CREDENTIAL_CERTIFICATE_FOR_CLIENT_AUTH
delegate_to: localhost
'''
RETURN = '''
msg:
description: Message as to what action was taken.
returned: always
type: str
sample: "Client testclient has been updated"
proposed:
description: Representation of proposed client.
returned: always
type: dict
sample: {
clientId: "test"
}
existing:
description: Representation of existing client (sample is truncated).
returned: always
type: dict
sample: {
"adminUrl": "http://www.example.com/admin_url",
"attributes": {
"request.object.signature.alg": "RS256",
}
}
end_state:
description: Representation of client after module execution (sample is truncated).
returned: on success
type: dict
sample: {
"adminUrl": "http://www.example.com/admin_url",
"attributes": {
"request.object.signature.alg": "RS256",
}
}
'''
from ansible_collections.middleware_automation.keycloak.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, camel, \
keycloak_argument_spec, get_token, KeycloakError
from ansible.module_utils.basic import AnsibleModule
import copy
def normalise_cr(clientrep, remove_ids=False):
""" Re-sorts any properties where the order so that diff's is minimised, and adds default values where appropriate so that the
the change detection is more effective.
:param clientrep: the clientrep dict to be sanitized
:param remove_ids: If set to true, then the unique ID's of objects is removed to make the diff and checks for changed
not alert when the ID's of objects are not usually known, (e.g. for protocol_mappers)
:return: normalised clientrep dict
"""
# Avoid the dict passed in to be modified
clientrep = clientrep.copy()
if 'attributes' in clientrep:
clientrep['attributes'] = list(sorted(clientrep['attributes']))
if 'redirectUris' in clientrep:
clientrep['redirectUris'] = list(sorted(clientrep['redirectUris']))
if 'protocolMappers' in clientrep:
clientrep['protocolMappers'] = sorted(clientrep['protocolMappers'], key=lambda x: (x.get('name'), x.get('protocol'), x.get('protocolMapper')))
for mapper in clientrep['protocolMappers']:
if remove_ids:
mapper.pop('id', None)
# Set to a default value.
mapper['consentRequired'] = mapper.get('consentRequired', False)
return clientrep
def sanitize_cr(clientrep):
""" Removes probably sensitive details from a client representation.
:param clientrep: the clientrep dict to be sanitized
:return: sanitized clientrep dict
"""
result = copy.deepcopy(clientrep)
if 'secret' in result:
result['secret'] = 'no_log'
if 'attributes' in result:
if 'saml.signing.private.key' in result['attributes']:
result['attributes']['saml.signing.private.key'] = 'no_log'
return normalise_cr(result)
def main():
"""
Module execution
:return:
"""
argument_spec = keycloak_argument_spec()
protmapper_spec = dict(
consentRequired=dict(type='bool'),
consentText=dict(type='str'),
id=dict(type='str'),
name=dict(type='str'),
protocol=dict(type='str', choices=['openid-connect', 'saml']),
protocolMapper=dict(type='str'),
config=dict(type='dict'),
)
meta_args = dict(
state=dict(default='present', choices=['present', 'absent']),
realm=dict(type='str', default='master'),
id=dict(type='str'),
client_id=dict(type='str', aliases=['clientId']),
name=dict(type='str'),
description=dict(type='str'),
root_url=dict(type='str', aliases=['rootUrl']),
admin_url=dict(type='str', aliases=['adminUrl']),
base_url=dict(type='str', aliases=['baseUrl']),
surrogate_auth_required=dict(type='bool', aliases=['surrogateAuthRequired']),
enabled=dict(type='bool'),
client_authenticator_type=dict(type='str', choices=['client-secret', 'client-jwt'], aliases=['clientAuthenticatorType']),
secret=dict(type='str', no_log=True),
registration_access_token=dict(type='str', aliases=['registrationAccessToken'], no_log=True),
default_roles=dict(type='list', elements='str', aliases=['defaultRoles']),
redirect_uris=dict(type='list', elements='str', aliases=['redirectUris']),
web_origins=dict(type='list', elements='str', aliases=['webOrigins']),
not_before=dict(type='int', aliases=['notBefore']),
bearer_only=dict(type='bool', aliases=['bearerOnly']),
consent_required=dict(type='bool', aliases=['consentRequired']),
standard_flow_enabled=dict(type='bool', aliases=['standardFlowEnabled']),
implicit_flow_enabled=dict(type='bool', aliases=['implicitFlowEnabled']),
direct_access_grants_enabled=dict(type='bool', aliases=['directAccessGrantsEnabled']),
service_accounts_enabled=dict(type='bool', aliases=['serviceAccountsEnabled']),
authorization_services_enabled=dict(type='bool', aliases=['authorizationServicesEnabled']),
public_client=dict(type='bool', aliases=['publicClient']),
frontchannel_logout=dict(type='bool', aliases=['frontchannelLogout']),
protocol=dict(type='str', choices=['openid-connect', 'saml']),
attributes=dict(type='dict'),
full_scope_allowed=dict(type='bool', aliases=['fullScopeAllowed']),
node_re_registration_timeout=dict(type='int', aliases=['nodeReRegistrationTimeout']),
registered_nodes=dict(type='dict', aliases=['registeredNodes']),
client_template=dict(type='str', aliases=['clientTemplate']),
use_template_config=dict(type='bool', aliases=['useTemplateConfig']),
use_template_scope=dict(type='bool', aliases=['useTemplateScope']),
use_template_mappers=dict(type='bool', aliases=['useTemplateMappers']),
always_display_in_console=dict(type='bool', aliases=['alwaysDisplayInConsole']),
authentication_flow_binding_overrides=dict(type='dict', aliases=['authenticationFlowBindingOverrides']),
protocol_mappers=dict(type='list', elements='dict', options=protmapper_spec, aliases=['protocolMappers']),
authorization_settings=dict(type='dict', aliases=['authorizationSettings']),
default_client_scopes=dict(type='list', elements='str', aliases=['defaultClientScopes']),
optional_client_scopes=dict(type='list', elements='str', aliases=['optionalClientScopes']),
)
argument_spec.update(meta_args)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True,
required_one_of=([['client_id', 'id'],
['token', 'auth_realm', 'auth_username', 'auth_password']]),
required_together=([['auth_realm', 'auth_username', 'auth_password']]))
result = dict(changed=False, msg='', diff={}, proposed={}, existing={}, end_state={})
# Obtain access token, initialize API
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')
cid = module.params.get('id')
state = module.params.get('state')
# Filter and map the parameters names that apply to the client
client_params = [x for x in module.params
if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm'] and
module.params.get(x) is not None]
# See if it already exists in Keycloak
if cid is None:
before_client = kc.get_client_by_clientid(module.params.get('client_id'), realm=realm)
if before_client is not None:
cid = before_client['id']
else:
before_client = kc.get_client_by_id(cid, realm=realm)
if before_client is None:
before_client = {}
# Build a proposed changeset from parameters given to this module
changeset = {}
for client_param in client_params:
new_param_value = module.params.get(client_param)
# some lists in the Keycloak API are sorted, some are not.
if isinstance(new_param_value, list):
if client_param in ['attributes']:
try:
new_param_value = sorted(new_param_value)
except TypeError:
pass
# Unfortunately, the ansible argument spec checker introduces variables with null values when
# they are not specified
if client_param == 'protocol_mappers':
new_param_value = [dict((k, v) for k, v in x.items() if x[k] is not None) for x in new_param_value]
changeset[camel(client_param)] = new_param_value
# Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis)
desired_client = before_client.copy()
desired_client.update(changeset)
result['proposed'] = sanitize_cr(changeset)
result['existing'] = sanitize_cr(before_client)
# Cater for when it doesn't exist (an empty dict)
if not before_client:
if state == 'absent':
# Do nothing and exit
if module._diff:
result['diff'] = dict(before='', after='')
result['changed'] = False
result['end_state'] = {}
result['msg'] = 'Client does not exist; doing nothing.'
module.exit_json(**result)
# Process a creation
result['changed'] = True
if 'clientId' not in desired_client:
module.fail_json(msg='client_id needs to be specified when creating a new client')
if module._diff:
result['diff'] = dict(before='', after=sanitize_cr(desired_client))
if module.check_mode:
module.exit_json(**result)
# create it
kc.create_client(desired_client, realm=realm)
after_client = kc.get_client_by_clientid(desired_client['clientId'], realm=realm)
result['end_state'] = sanitize_cr(after_client)
result['msg'] = 'Client %s has been created.' % desired_client['clientId']
module.exit_json(**result)
else:
if state == 'present':
# Process an update
result['changed'] = True
if module.check_mode:
# We can only compare the current client with the proposed updates we have
before_norm = normalise_cr(before_client, remove_ids=True)
desired_norm = normalise_cr(desired_client, remove_ids=True)
if module._diff:
result['diff'] = dict(before=sanitize_cr(before_norm),
after=sanitize_cr(desired_norm))
result['changed'] = (before_norm != desired_norm)
module.exit_json(**result)
# do the update
kc.update_client(cid, desired_client, realm=realm)
after_client = kc.get_client_by_id(cid, realm=realm)
if before_client == after_client:
result['changed'] = False
if module._diff:
result['diff'] = dict(before=sanitize_cr(before_client),
after=sanitize_cr(after_client))
result['end_state'] = sanitize_cr(after_client)
result['msg'] = 'Client %s has been updated.' % desired_client['clientId']
module.exit_json(**result)
else:
# Process a deletion (because state was not 'present')
result['changed'] = True
if module._diff:
result['diff'] = dict(before=sanitize_cr(before_client), after='')
if module.check_mode:
module.exit_json(**result)
# delete it
kc.delete_client(cid, realm=realm)
result['proposed'] = {}
result['end_state'] = {}
result['msg'] = 'Client %s has been deleted.' % before_client['clientId']
module.exit_json(**result)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,374 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Adam Goossens <adam.goossens@gmail.com>
# 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_role
short_description: Allows administration of Keycloak roles via Keycloak API
version_added: 3.4.0
description:
- This module allows you to add, remove or modify Keycloak roles 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.
- The names of module options are snake_cased versions of the camelCase ones found in the
Keycloak API and its documentation at U(https://www.keycloak.org/docs-api/8.0/rest-api/index.html).
- Attributes are multi-valued in the Keycloak API. All attributes are lists of individual values and will
be returned that way by this module. You may pass single values for attributes when calling the module,
and this will be translated into a list suitable for the API.
attributes:
check_mode:
support: full
diff_mode:
support: full
options:
state:
description:
- State of the role.
- On C(present), the role will be created if it does not yet exist, or updated with the parameters you provide.
- On C(absent), the role will be removed if it exists.
default: 'present'
type: str
choices:
- present
- absent
name:
type: str
required: true
description:
- Name of the role.
- This parameter is required.
description:
type: str
description:
- The role description.
realm:
type: str
description:
- The Keycloak realm under which this role resides.
default: 'master'
client_id:
type: str
description:
- If the role is a client role, the client id under which it resides.
- If this parameter is absent, the role is considered a realm role.
attributes:
type: dict
description:
- A dict of key/value pairs to set as custom attributes for the role.
- Values may be single values (e.g. a string) or a list of strings.
extends_documentation_fragment:
- middleware_automation.keycloak.keycloak
- middleware_automation.keycloak.attributes
author:
- Laurent Paumier (@laurpaum)
'''
EXAMPLES = '''
- name: Create a Keycloak realm role, authentication with credentials
middleware_automation.keycloak.keycloak_role:
name: my-new-kc-role
realm: MyCustomRealm
state: present
auth_client_id: admin-cli
auth_keycloak_url: https://auth.example.com/auth
auth_realm: master
auth_username: USERNAME
auth_password: PASSWORD
delegate_to: localhost
- name: Create a Keycloak realm role, authentication with token
middleware_automation.keycloak.keycloak_role:
name: my-new-kc-role
realm: MyCustomRealm
state: present
auth_client_id: admin-cli
auth_keycloak_url: https://auth.example.com/auth
token: TOKEN
delegate_to: localhost
- name: Create a Keycloak client role
middleware_automation.keycloak.keycloak_role:
name: my-new-kc-role
realm: MyCustomRealm
client_id: MyClient
state: present
auth_client_id: admin-cli
auth_keycloak_url: https://auth.example.com/auth
auth_realm: master
auth_username: USERNAME
auth_password: PASSWORD
delegate_to: localhost
- name: Delete a Keycloak role
middleware_automation.keycloak.keycloak_role:
name: my-role-for-deletion
state: absent
auth_client_id: admin-cli
auth_keycloak_url: https://auth.example.com/auth
auth_realm: master
auth_username: USERNAME
auth_password: PASSWORD
delegate_to: localhost
- name: Create a keycloak role with some custom attributes
middleware_automation.keycloak.keycloak_role:
auth_client_id: admin-cli
auth_keycloak_url: https://auth.example.com/auth
auth_realm: master
auth_username: USERNAME
auth_password: PASSWORD
name: my-new-role
attributes:
attrib1: value1
attrib2: value2
attrib3:
- with
- numerous
- individual
- list
- items
delegate_to: localhost
'''
RETURN = '''
msg:
description: Message as to what action was taken.
returned: always
type: str
sample: "Role myrole has been updated"
proposed:
description: Representation of proposed role.
returned: always
type: dict
sample: {
"description": "My updated test description"
}
existing:
description: Representation of existing role.
returned: always
type: dict
sample: {
"attributes": {},
"clientRole": true,
"composite": false,
"containerId": "9f03eb61-a826-4771-a9fd-930e06d2d36a",
"description": "My client test role",
"id": "561703dd-0f38-45ff-9a5a-0c978f794547",
"name": "myrole"
}
end_state:
description: Representation of role after module execution (sample is truncated).
returned: on success
type: dict
sample: {
"attributes": {},
"clientRole": true,
"composite": false,
"containerId": "9f03eb61-a826-4771-a9fd-930e06d2d36a",
"description": "My updated client test role",
"id": "561703dd-0f38-45ff-9a5a-0c978f794547",
"name": "myrole"
}
'''
from ansible_collections.middleware_automation.keycloak.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, camel, \
keycloak_argument_spec, get_token, KeycloakError
from ansible.module_utils.basic import AnsibleModule
def main():
"""
Module execution
:return:
"""
argument_spec = keycloak_argument_spec()
meta_args = dict(
state=dict(type='str', default='present', choices=['present', 'absent']),
name=dict(type='str', required=True),
description=dict(type='str'),
realm=dict(type='str', default='master'),
client_id=dict(type='str'),
attributes=dict(type='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={}, proposed={}, existing={}, end_state={})
# Obtain access token, initialize API
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')
clientid = module.params.get('client_id')
name = module.params.get('name')
state = module.params.get('state')
# attributes in Keycloak have their values returned as lists
# via the API. attributes is a dict, so we'll transparently convert
# the values to lists.
if module.params.get('attributes') is not None:
for key, val in module.params['attributes'].items():
module.params['attributes'][key] = [val] if not isinstance(val, list) else val
# Filter and map the parameters names that apply to the role
role_params = [x for x in module.params
if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm', 'client_id', 'composites'] and
module.params.get(x) is not None]
# See if it already exists in Keycloak
if clientid is None:
before_role = kc.get_realm_role(name, realm)
else:
before_role = kc.get_client_role(name, clientid, realm)
if before_role is None:
before_role = {}
# Build a proposed changeset from parameters given to this module
changeset = {}
for param in role_params:
new_param_value = module.params.get(param)
old_value = before_role[param] if param in before_role else None
if new_param_value != old_value:
changeset[camel(param)] = new_param_value
# Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis)
desired_role = before_role.copy()
desired_role.update(changeset)
result['proposed'] = changeset
result['existing'] = before_role
# Cater for when it doesn't exist (an empty dict)
if not before_role:
if state == 'absent':
# Do nothing and exit
if module._diff:
result['diff'] = dict(before='', after='')
result['changed'] = False
result['end_state'] = {}
result['msg'] = 'Role does not exist, doing nothing.'
module.exit_json(**result)
# Process a creation
result['changed'] = True
if name is None:
module.fail_json(msg='name must be specified when creating a new role')
if module._diff:
result['diff'] = dict(before='', after=desired_role)
if module.check_mode:
module.exit_json(**result)
# create it
if clientid is None:
kc.create_realm_role(desired_role, realm)
after_role = kc.get_realm_role(name, realm)
else:
kc.create_client_role(desired_role, clientid, realm)
after_role = kc.get_client_role(name, clientid, realm)
result['end_state'] = after_role
result['msg'] = 'Role {name} has been created'.format(name=name)
module.exit_json(**result)
else:
if state == 'present':
# Process an update
# no changes
if desired_role == before_role:
result['changed'] = False
result['end_state'] = desired_role
result['msg'] = "No changes required to role {name}.".format(name=name)
module.exit_json(**result)
# doing an update
result['changed'] = True
if module._diff:
result['diff'] = dict(before=before_role, after=desired_role)
if module.check_mode:
module.exit_json(**result)
# do the update
if clientid is None:
kc.update_realm_role(desired_role, realm)
after_role = kc.get_realm_role(name, realm)
else:
kc.update_client_role(desired_role, clientid, realm)
after_role = kc.get_client_role(name, clientid, realm)
result['end_state'] = after_role
result['msg'] = "Role {name} has been updated".format(name=name)
module.exit_json(**result)
else:
# Process a deletion (because state was not 'present')
result['changed'] = True
if module._diff:
result['diff'] = dict(before=before_role, after='')
if module.check_mode:
module.exit_json(**result)
# delete it
if clientid is None:
kc.delete_realm_role(name, realm)
else:
kc.delete_client_role(name, clientid, realm)
result['end_state'] = {}
result['msg'] = "Role {name} has been deleted".format(name=name)
module.exit_json(**result)
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,4 @@
---
collections:
- name: middleware_automation.redhat_csp_download
version: ">=1.2.1"
- name: middleware_automation.wildfly
version: ">=0.0.5"
- name: community.general
- name: middleware_automation.common
- name: ansible.posix

View File

@@ -19,8 +19,12 @@ Dependencies
The roles depends on:
* the `redhat_csp_download` role from [middleware_automation.redhat_csp_download](https://github.com/ansible-middleware/redhat-csp-download) collection if Red Hat Single Sign-on zip have to be downloaded from RHN.
* the `wildfly_driver` role from [middleware_automation.wildfly](https://github.com/ansible-middleware/wildfly) collection
* [middleware_automation.common](https://github.com/ansible-middleware/common)
* [ansible-posix](https://docs.ansible.com/ansible/latest/collections/ansible/posix/index.html)
To install all the dependencies via galaxy:
ansible-galaxy collection install -r requirements.yml
Versions
@@ -28,7 +32,8 @@ Versions
| RH-SSO VERSION | Release Date | Keycloak Version | EAP Version | Notes |
|:---------------|:------------------|:-----------------|:------------|:----------------|
|`7.5.0 GA` |September 20, 2021 |`15.0.2` | `7.4.0` |[Release Notes](https://access.redhat.com/documentation/en-us/red_hat_single_sign-on/7.5/html/release_notes/index)|
|`7.5.0 GA` |September 20, 2021 |`15.0.2` | `7.4.6` |[Release Notes](https://access.redhat.com/documentation/en-us/red_hat_single_sign-on/7.5/html/release_notes/index)|
|`7.6.0 GA` |June 30, 2022 |`18.0.3` | `7.4.6` |[Release Notes](https://access.redhat.com/documentation/en-us/red_hat_single_sign-on/7.6/html-single/release_notes/index)|
Patching
@@ -38,8 +43,8 @@ When variable `keycloak_rhsso_apply_patches` is `True` (default: `False`), the r
| RH-SSO VERSION | Release Date | RH-SSO LATEST CP | Notes |
|:---------------|:------------------|:-----------------|:----------------|
|`7.5.0 GA` |January 20, 2022 |`7.5.1 GA` |[Release Notes](https://access.redhat.com/articles/6646321)|
|`7.5.0 GA` |January 20, 2022 |`7.5.3 GA` |[Release Notes](https://access.redhat.com/articles/6646321)|
|`7.6.0 GA` |November 11, 2022 |`7.6.1 GA` |[Release Notes](https://access.redhat.com/articles/6982711)|
Role Defaults
@@ -50,9 +55,12 @@ Role Defaults
| Variable | Description | Default |
|:---------|:------------|:---------|
|`keycloak_ha_enabled`| Enable auto configuration for database backend, clustering and remote caches on infinispan | `False` |
|`keycloak_ha_discovery`| Discovery protocol for HA cluster members | `JDBC_PING` if keycloak_db_enabled else `TCPPING` |
|`keycloak_db_enabled`| Enable auto configuration for database backend | `True` if `keycloak_ha_enabled` is True, else `False` |
|`keycloak_remote_cache_enabled`| Enable remote cache store when in clustered ha configurations | `True` if `keycloak_ha_enabled` else `False` |
|`keycloak_admin_user`| Administration console user account | `admin` |
|`keycloak_bind_address`| Address for binding service ports | `0.0.0.0` |
|`keycloak_management_port_bind_address`| Address for binding management ports | `127.0.0.1` |
|`keycloak_host`| hostname | `localhost` |
|`keycloak_http_port`| HTTP port | `8080` |
|`keycloak_https_port`| TLS HTTP port | `8443` |
@@ -64,7 +72,13 @@ Role Defaults
|`keycloak_config_standalone_xml`| filename for configuration | `keycloak.xml` |
|`keycloak_service_user`| posix account username | `keycloak` |
|`keycloak_service_group`| posix account group | `keycloak` |
|`keycloak_service_restart_always`| systemd restart always behavior activation | `False`
|`keycloak_service_restart_on_failure`| systemd restart on-failure behavior activation | `False`
|`keycloak_service_startlimitintervalsec`| systemd StartLimitIntervalSec | `300` |
|`keycloak_service_startlimitburst`| systemd StartLimitBurst | `5` |
|`keycloak_service_restartsec`| systemd RestartSec | `10s` |
|`keycloak_service_pidfile`| pid file path for service | `/run/keycloak.pid` |
|`keycloak_features` | List of `name`/`status` pairs of features (also known as profiles on RH-SSO) to `enable` or `disable`, example: `[ { name: 'docker', status: 'enabled' } ]` | `[]`
|`keycloak_jvm_package`| RHEL java package runtime | `java-1.8.0-openjdk-headless` |
|`keycloak_java_home`| JAVA_HOME of installed JRE, leave empty for using specified keycloak_jvm_package RPM path | `None` |
|`keycloak_java_opts`| Additional JVM options | `-Xms1024m -Xmx2048m` |
@@ -74,16 +88,11 @@ Role Defaults
| Variable | Description | Default |
|:---------|:------------|:---------|
|`keycloak_rhsso_enable`| Enable Red Hat Single Sign-on installation | `False` |
|`keycloak_offline_install` | perform an offline install | `False`|
|`keycloak_download_url`| Download URL for keycloak | `https://github.com/keycloak/keycloak/releases/download/<version>/<archive>`|
|`keycloak_rhsso_download_url`| Download URL for RHSSO | `https://access.redhat.com/jbossnetwork/restricted/softwareDownload.html?softwareId=<productID>`|
|`keycloak_version`| keycloak.org package version | `15.0.2` |
|`keycloak_rhsso_version`| RHSSO version | `7.5.0` |
|`keycloak_rhsso_apply_patches`| Install RHSSO more recent cumulative patch | `False` |
|`keycloak_download_url`| Download URL for keycloak | `https://github.com/keycloak/keycloak/releases/download/<version>/<archive>`|
|`keycloak_version`| keycloak.org package version | `18.0.2` |
|`keycloak_dest`| Installation root path | `/opt/keycloak` |
|`keycloak_download_url` | Download URL for keycloak | `https://github.com/keycloak/keycloak/releases/download/{{ keycloak_version }}/{{ keycloak_archive }}` |
|`keycloak_rhn_url` | Base download URI for customer portal | `https://access.redhat.com/jbossnetwork/restricted/softwareDownload.html?softwareId=` |
|`keycloak_configure_firewalld` | Ensure firewalld is running and configure keycloak ports | `False` |
@@ -91,13 +100,10 @@ Role Defaults
| Variable | Description | Default |
|:---------|:------------|:--------|
|`keycloak_archive` | keycloak install archive filename | `keycloak-{{ keycloak_version }}.zip` |
|`keycloak_archive` | keycloak install archive filename | `keycloak-legacy-{{ keycloak_version }}.zip` |
|`keycloak_download_url_9x` | Download URL for keycloak (deprecated) | `https://downloads.jboss.org/keycloak/{{ keycloak_version }}/{{ keycloak_archive }}` |
|`keycloak_installdir` | Installation path | `{{ keycloak_dest }}/keycloak-{{ keycloak_version }}` |
|`keycloak_rhsso_archive` | Red Hat SSO install archive filename | `rh-sso-{{ keycloak_rhsso_version }}-server-dist.zip` |
|`keycloak_rhsso_installdir`| Installation path for Red Hat SSO | `{{ keycloak_dest }}/rh-sso-{{ keycloak_rhsso_version | regex_replace('^([0-9])\.([0-9]*).*', '\1.\2') }}` |
|`keycloak_rhsso_download_url`| Full download URI for Red Hat SSO | `{{ keycloak_rhn_url }}{{ rhsso_rhn_id }}` |
|`keycloak_jboss_home` | Installation work directory | `{{ keycloak_rhsso_installdir if keycloak_rhsso_enable else keycloak_installdir }}` |
|`keycloak_jboss_home` | Installation work directory | `{{ keycloak_rhsso_installdir }}` |
|`keycloak_config_dir` | Path for configuration | `{{ keycloak_jboss_home }}/standalone/configuration` |
|`keycloak_config_path_to_standalone_xml` | Custom path for configuration | `{{ keycloak_jboss_home }}/standalone/configuration/{{ keycloak_config_standalone_xml }}` |
|`keycloak_config_override_template` | Path to custom template for standalone.xml configuration | `''` |
@@ -106,7 +112,12 @@ Role Defaults
|`keycloak_force_install` | Remove pre-existing versions of service | `False` |
|`keycloak_url` | URL for configuration rest calls | `http://{{ keycloak_host }}:{{ keycloak_http_port }}` |
|`keycloak_management_url` | URL for management console rest calls | `http://{{ keycloak_host }}:{{ keycloak_management_http_port }}` |
|`rhsso_rhn_id` | Customer Portal product ID for Red Hat SSO | `{{ rhsso_rhn_ids[keycloak_rhsso_version].id }}` |
|`keycloak_frontend_url_force` | Force backend requests to use the frontend URL | `False` |
|`keycloak_db_background_validation` | Enable background validation of database connection | `False` |
|`keycloak_db_background_validation_millis`| How frequenly the connection pool is validated in the background | `10000` if background validation enabled |
|`keycloak_db_background_validate_on_match` | Enable validate on match for database connections | `False` |
|`keycloak_frontend_url` | frontend URL for keycloak endpoint | `http://localhost:8080/auth/` |
Role Variables
@@ -117,25 +128,28 @@ The following are a set of _required_ variables for the role:
| Variable | Description |
|:---------|:------------|
|`keycloak_admin_password`| Password for the administration console user account (minimum 12 characters) |
|`keycloak_frontend_url` | frontend URL for keycloak endpoint | `http://localhost:8080/auth` |
|`keycloak_frontend_url` | frontend URL for keycloak endpoint | `http://localhost:8080/auth/` |
The following variables are _required_ only when `keycloak_ha_enabled` is True:
The following parameters are _required_ only when `keycloak_ha_enabled` is True:
| Variable | Description | Default |
|:---------|:------------|:---------|
|`keycloak_modcluster_url` | URL for the modcluster reverse proxy | `localhost` |
|`keycloak_jdbc_engine` | backend database engine when db is enabled: [ postgres, mariadb ] | `postgres` |
|`infinispan_url` | URL for the infinispan remote-cache server | `localhost:11122` |
|`infinispan_user` | username for connecting to infinispan | `supervisor` |
|`infinispan_pass` | password for connecting to infinispan | `supervisor` |
|`infinispan_sasl_mechanism`| Authentication type | `SCRAM-SHA-512` |
|`infinispan_use_ssl`| Enable hotrod TLS communication | `False` |
|`infinispan_trust_store_path`| Path to truststore with infinispan server certificate | `/etc/pki/java/cacerts` |
|`infinispan_trust_store_password`| Password for opening truststore | `changeit` |
|:---------|:------------|:--------|
|`keycloak_modcluster_enabled`| Enable configuration for modcluster subsystem | `True` if `keycloak_ha_enabled` is True, else `False` |
|`keycloak_modcluster_url` | _deprecated_ Host for the modcluster reverse proxy | `localhost` |
|`keycloak_modcluster_port` | _deprecated_ Port for the modcluster reverse proxy | `6666` |
|`keycloak_modcluster_urls` | List of {host,port} dicts for the modcluster reverse proxies | `[ { localhost:6666 } ]` |
|`keycloak_jdbc_engine` | backend database engine when db is enabled: [ postgres, mariadb, sqlserver ] | `postgres` |
|`keycloak_infinispan_url` | URL for the infinispan remote-cache server | `localhost:11122` |
|`keycloak_infinispan_user` | username for connecting to infinispan | `supervisor` |
|`keycloak_infinispan_pass` | password for connecting to infinispan | `supervisor` |
|`keycloak_infinispan_sasl_mechanism`| Authentication type | `SCRAM-SHA-512` |
|`keycloak_infinispan_use_ssl`| Enable hotrod TLS communication | `False` |
|`keycloak_infinispan_trust_store_path`| Path to truststore with infinispan server certificate | `/etc/pki/java/cacerts` |
|`keycloak_infinispan_trust_store_password`| Password for opening truststore | `changeit` |
The following variables are _required_ only when `keycloak_db_enabled` is True:
The following parameters are _required_ only when `keycloak_db_enabled` is True:
| Variable | Description | Default |
|:---------|:------------|:---------|
@@ -145,12 +159,17 @@ The following variables are _required_ only when `keycloak_db_enabled` is True:
|`keycloak_db_pass` | password for connecting to postgres | `keycloak-pass` |
Example Playbooks
The following variables are _optional_:
| Variable | Description |
|:---------|:------------|
|`keycloak_db_valid_conn_sql` | Override the default database connection validation query sql |
|`keycloak_admin_url` | Override the default administration endpoint URL |
|`keycloak_jgroups_subnet`| Override the subnet match for jgroups cluster formation; if not defined, it will be inferred from local machine route configuration |
Example Playbook
-----------------
_NOTE_: use ansible vaults or other security systems for storing credentials.
* The following is an example playbook that makes use of the role to install keycloak from remote:
```yaml
@@ -158,33 +177,10 @@ _NOTE_: use ansible vaults or other security systems for storing credentials.
- hosts: ...
vars:
keycloak_admin_password: "remembertochangeme"
collections:
- middleware_automation.keycloak
roles:
- middleware_automation.keycloak.keycloak
```
* The following is an example playbook that makes use of the role to install Red Hat Single Sign-On from RHN:
```yaml
---
- name: Playbook for RHSSO
hosts: keycloak
collections:
- middleware_automation.redhat_csp_download
roles:
- redhat_csp_download
tasks:
- name: Keycloak Role
include_role:
name: keycloak
vars:
keycloak_admin_password: "remembertochangeme"
keycloak_rhsso_enable: True
rhn_username: '<customer portal username>'
rhn_password: '<customer portal password>'
```
* The following example playbook makes use of the role to install keycloak from the controller node:
@@ -203,45 +199,6 @@ _NOTE_: use ansible vaults or other security systems for storing credentials.
# This should be the filename of keycloak archive on Ansible node: keycloak-16.1.0.zip
```
* This playbook installs Red Hat Single Sign-On from an alternate url:
```yaml
---
- hosts: keycloak
collections:
- middleware_automation.keycloak
tasks:
- name: Keycloak Role
include_role:
name: keycloak
vars:
keycloak_admin_password: "remembertochangeme"
keycloak_rhsso_enable: True
keycloak_rhsso_download_url: "<REPLACE with download url>"
# This should be the full of remote source rhsso zip file and can contain basic authentication credentials
```
* The following is an example playbook that makes use of the role to install Red Hat Single Sign-On offline from the controller node, and apply latest cumulative patch:
```yaml
---
- hosts: keycloak
collections:
- middleware_automation.keycloak
tasks:
- name: Keycloak Role
include_role:
name: keycloak
vars:
keycloak_admin_password: "remembertochangeme"
keycloak_rhsso_enable: True
keycloak_offline_install: True
keycloak_rhsso_apply_patches: True
# This should be the filename of rhsso zip file on Ansible node: rh-sso-7.5-server-dist.zip
```
License
-------

View File

@@ -1,37 +1,35 @@
---
### Configuration specific to keycloak
keycloak_version: 15.0.2
keycloak_archive: "keycloak-{{ keycloak_version }}.zip"
keycloak_download_url: "https://github.com/keycloak/keycloak/releases/download/{{ keycloak_version }}/{{ keycloak_archive }}"
keycloak_version: 18.0.2
keycloak_archive: "keycloak-legacy-{{ keycloak_version }}.zip"
keycloak_download_url: "https://github.com/keycloak/keycloak/releases/download/{{ keycloak_version }}/{{ keycloak_archive }}"
keycloak_download_url_9x: "https://downloads.jboss.org/keycloak/{{ keycloak_version }}/{{ keycloak_archive }}"
keycloak_installdir: "{{ keycloak_dest }}/keycloak-{{ keycloak_version }}"
### Configuration specific to Red Hat Single Sign-On
keycloak_rhsso_version: 7.5.0
rhsso_rhn_id: "{{ rhsso_rhn_ids[keycloak_rhsso_version].id }}"
keycloak_rhsso_archive: "rh-sso-{{ keycloak_rhsso_version }}-server-dist.zip"
keycloak_rhsso_installdir: "{{ keycloak_dest }}/rh-sso-{{ keycloak_rhsso_version | regex_replace('^([0-9])\\.([0-9]*).*', '\\1.\\2') }}"
keycloak_rhn_url: 'https://access.redhat.com/jbossnetwork/restricted/softwareDownload.html?softwareId='
keycloak_rhsso_download_url: "{{ keycloak_rhn_url }}{{ rhsso_rhn_id }}"
keycloak_rhsso_apply_patches: False
### keycloak/rhsso choice: by default install rhsso if rhn credentials are defined
keycloak_rhsso_enable: "{{ True if rhsso_rhn_id is defined and rhn_username is defined and rhn_password is defined else False }}"
# whether to install from local archive; filename must be keycloak_archive or keycloak_rhsso_archive depending on keycloak_rhsso_enable
keycloak_offline_install: False
### Install location and service settings
keycloak_jvm_package: java-1.8.0-openjdk-headless
keycloak_java_home:
keycloak_dest: /opt/keycloak
keycloak_jboss_home: "{{ keycloak_rhsso_installdir if keycloak_rhsso_enable else keycloak_installdir }}"
keycloak_jboss_home: "{{ keycloak_installdir }}"
keycloak_config_dir: "{{ keycloak_jboss_home }}/standalone/configuration"
keycloak_config_standalone_xml: "keycloak.xml"
keycloak_config_path_to_standalone_xml: "{{ keycloak_jboss_home }}/standalone/configuration/{{ keycloak_config_standalone_xml }}"
keycloak_config_override_template: ''
keycloak_config_path_to_properties: "{{ keycloak_jboss_home }}/standalone/configuration/profile.properties"
keycloak_service_user: keycloak
keycloak_service_group: keycloak
keycloak_service_pidfile: "/run/keycloak.pid"
keycloak_service_name: keycloak
keycloak_service_desc: Keycloak
keycloak_service_start_delay: 10
keycloak_service_start_retries: 25
keycloak_service_restart_always: False
keycloak_service_restart_on_failure: False
keycloak_service_startlimitintervalsec: "300"
keycloak_service_startlimitburst: "5"
keycloak_service_restartsec: "10s"
keycloak_configure_firewalld: False
### administrator console password
@@ -44,15 +42,22 @@ keycloak_http_port: 8080
keycloak_https_port: 8443
keycloak_ajp_port: 8009
keycloak_jgroups_port: 7600
keycloak_jgroups_subnet:
keycloak_management_port_bind_address: 127.0.0.1
keycloak_management_http_port: 9990
keycloak_management_https_port: 9993
keycloak_java_opts: "-Xms1024m -Xmx2048m"
keycloak_prefer_ipv4: True
keycloak_features: []
### Enable configuration for database backend, clustering and remote caches on infinispan
keycloak_ha_enabled: False
### Enable database configuration, must be enabled when HA is configured
keycloak_db_enabled: "{{ True if keycloak_ha_enabled else False }}"
### Discovery protocol for ha cluster members, valus [ 'JDBC_PING', 'TCPPING' ]
keycloak_ha_discovery: "{{ 'JDBC_PING' if keycloak_db_enabled else 'TCPPING' }}"
### Remote cache store on infinispan cluster
keycloak_remote_cache_enabled: "{{ True if keycloak_ha_enabled else False }}"
### Keycloak administration console user
keycloak_admin_user: admin
@@ -61,27 +66,38 @@ keycloak_auth_client: admin-cli
keycloak_force_install: False
### mod_cluster reverse proxy
### mod_cluster reverse proxy list
keycloak_modcluster_enabled: "{{ True if keycloak_ha_enabled else False }}"
keycloak_modcluster_url: localhost
keycloak_modcluster_port: 6666
keycloak_modcluster_urls:
- host: "{{ keycloak_modcluster_url }}"
port: "{{ keycloak_modcluster_port }}"
### keycloak frontend url
keycloak_frontend_url: http://localhost:8080/auth
keycloak_frontend_url: http://localhost:8080/auth/
keycloak_frontend_url_force: False
keycloak_admin_url:
### infinispan remote caches access (hotrod)
infinispan_user: supervisor
infinispan_pass: supervisor
infinispan_url: localhost
infinispan_sasl_mechanism: SCRAM-SHA-512
infinispan_use_ssl: False
keycloak_infinispan_user: supervisor
keycloak_infinispan_pass: supervisor
keycloak_infinispan_url: localhost
keycloak_infinispan_sasl_mechanism: SCRAM-SHA-512
keycloak_infinispan_use_ssl: False
# if ssl is enabled, import ispn server certificate here
infinispan_trust_store_path: /etc/pki/java/cacerts
infinispan_trust_store_password: changeit
keycloak_infinispan_trust_store_path: /etc/pki/java/cacerts
keycloak_infinispan_trust_store_password: changeit
### database backend engine: values [ 'postgres', 'mariadb' ]
### database backend engine: values [ 'postgres', 'mariadb', 'sqlserver' ]
keycloak_jdbc_engine: postgres
### database backend credentials
keycloak_db_user: keycloak-user
keycloak_db_pass: keycloak-pass
## connection validation
keycloak_db_background_validation: False
keycloak_db_background_validation_millis: "{{ 10000 if keycloak_db_background_validation else 0 }}"
keycloak_db_background_validate_on_match: False
keycloak_jdbc_url: "{{ keycloak_default_jdbc[keycloak_jdbc_engine].url }}"
keycloak_jdbc_driver_version: "{{ keycloak_default_jdbc[keycloak_jdbc_engine].version }}"
# override the variables above, following defaults show minimum supported versions
@@ -92,3 +108,8 @@ keycloak_default_jdbc:
mariadb:
url: 'jdbc:mariadb://localhost:3306/keycloak'
version: 2.7.4
sqlserver:
url: 'jdbc:sqlserver://localhost:1433;databaseName=keycloak;'
version: 12.2.0
# role specific vars
keycloak_no_log: True

View File

@@ -3,12 +3,12 @@ argument_specs:
options:
keycloak_version:
# line 3 of keycloak/defaults/main.yml
default: "15.0.2"
default: "18.0.2"
description: "keycloak.org package version"
type: "str"
keycloak_archive:
# line 4 of keycloak/defaults/main.yml
default: "keycloak-{{ keycloak_version }}.zip"
default: "keycloak-legacy-{{ keycloak_version }}.zip"
description: "keycloak install archive filename"
type: "str"
keycloak_configure_firewalld:
@@ -31,46 +31,6 @@ argument_specs:
default: "{{ keycloak_dest }}/keycloak-{{ keycloak_version }}"
description: "Installation path"
type: "str"
keycloak_rhsso_version:
# line 10 of keycloak/defaults/main.yml
default: "7.5.0"
description: "Red Hat Single Sign-On version"
type: "str"
rhsso_rhn_id:
# line 11 of keycloak/defaults/main.yml
default: "{{ rhsso_rhn_ids[keycloak_rhsso_version].id }}"
description: "Customer Portal product ID for Red Hat SSO"
type: "str"
keycloak_rhsso_archive:
# line 12 of keycloak/defaults/main.yml
default: "rh-sso-{{ keycloak_rhsso_version }}-server-dist.zip"
description: "ed Hat SSO install archive filename"
type: "str"
keycloak_rhsso_apply_patches:
# line 16 of keycloak/defaults/main.yml
default: false
description: "Install RHSSO more recent cumulative patch"
type: "bool"
keycloak_rhsso_installdir:
# line 13 of keycloak/defaults/main.yml
default: "{{ keycloak_dest }}/rh-sso-{{ keycloak_rhsso_version | regex_replace('^([0-9])\\.([0-9]*).*', '\\1.\\2') }}"
description: "Installation path for Red Hat SSO"
type: "str"
keycloak_rhn_url:
# line 14 of keycloak/defaults/main.yml
default: "https://access.redhat.com/jbossnetwork/restricted/softwareDownload.html?softwareId="
description: "Base download URI for customer portal"
type: "str"
keycloak_rhsso_download_url:
# line 15 of keycloak/defaults/main.yml
default: "{{ keycloak_rhn_url }}{{ rhsso_rhn_id }}"
description: "Full download URI for Red Hat SSO"
type: "str"
keycloak_rhsso_enable:
# line 18 of keycloak/defaults/main.yml
default: "{{ True if rhsso_rhn_id is defined and rhn_username is defined and rhn_password is defined else False }}"
description: "Enable Red Hat Single Sign-on installation"
type: "str"
keycloak_offline_install:
# line 20 of keycloak/defaults/main.yml
default: false
@@ -91,7 +51,7 @@ argument_specs:
type: "str"
keycloak_jboss_home:
# line 25 of keycloak/defaults/main.yml
default: "{{ keycloak_rhsso_installdir if keycloak_rhsso_enable else keycloak_installdir }}"
default: "{{ keycloak_installdir }}"
description: "Installation work directory"
type: "str"
keycloak_config_dir:
@@ -129,11 +89,20 @@ argument_specs:
default: "/run/keycloak.pid"
description: "PID file path for service"
type: "str"
keycloak_features:
# line 17 of keycloak/defaults/main.yml
default: "[]"
description: "List of `name`/`status` pairs of features (also known as profiles on RH-SSO) to `enable` or `disable`, example: `[ { name: 'docker', status: 'enabled' } ]`"
type: "list"
keycloak_bind_address:
# line 34 of keycloak/defaults/main.yml
default: "0.0.0.0"
description: "Address for binding service ports"
type: "str"
keycloak_management_port_bind_address:
default: "127.0.0.1"
description: "Address for binding the management ports"
type: "str"
keycloak_host:
# line 35 of keycloak/defaults/main.yml
default: "localhost"
@@ -184,11 +153,15 @@ argument_specs:
default: false
description: "Enable auto configuration for database backend, clustering and remote caches on infinispan"
type: "bool"
keycloak_ha_discovery:
default: "{{ 'JDBC_PING' if keycloak_db_enabled else 'TCPPING' }}"
description: "Discovery protocol for HA cluster members"
type: "str"
keycloak_db_enabled:
# line 48 of keycloak/defaults/main.yml
default: "{{ True if keycloak_ha_enabled else False }}"
description: "Enable auto configuration for database backend"
type: "str"
type: "bool"
keycloak_admin_user:
# line 51 of keycloak/defaults/main.yml
default: "admin"
@@ -209,47 +182,63 @@ argument_specs:
default: false
description: "Remove pre-existing versions of service"
type: "bool"
keycloak_modcluster_enabled:
default: "{{ True if keycloak_ha_enabled else False }}"
description: "Enable configuration for modcluster subsystem"
type: "bool"
keycloak_modcluster_url:
# line 58 of keycloak/defaults/main.yml
default: "localhost"
description: "URL for the modcluster reverse proxy"
type: "str"
keycloak_modcluster_port:
default: 6666
description: "Port for the modcluster reverse proxy"
type: "int"
keycloak_modcluster_urls:
default: "[ { host: 'localhost', port: 6666 } ]"
description: "List of modproxy node URLs in the format { host, port } for the modcluster reverse proxy"
type: "list"
keycloak_frontend_url:
# line 59 of keycloak/defaults/main.yml
default: "http://localhost"
description: "Frontend URL for keycloak endpoints when a reverse proxy is used"
type: "str"
infinispan_user:
keycloak_frontend_url_force:
default: False
description: "Force backend requests to use the frontend URL"
type: "bool"
keycloak_infinispan_user:
# line 62 of keycloak/defaults/main.yml
default: "supervisor"
description: "Username for connecting to infinispan"
type: "str"
infinispan_pass:
keycloak_infinispan_pass:
# line 63 of keycloak/defaults/main.yml
default: "supervisor"
description: "Password for connecting to infinispan"
type: "str"
infinispan_url:
keycloak_infinispan_url:
# line 64 of keycloak/defaults/main.yml
default: "localhost"
description: "URL for the infinispan remote-cache server"
type: "str"
infinispan_sasl_mechanism:
keycloak_infinispan_sasl_mechanism:
# line 65 of keycloak/defaults/main.yml
default: "SCRAM-SHA-512"
description: "Authentication type to infinispan server"
type: "str"
infinispan_use_ssl:
keycloak_infinispan_use_ssl:
# line 66 of keycloak/defaults/main.yml
default: false
description: "Enable hotrod client TLS communication"
type: "bool"
infinispan_trust_store_path:
keycloak_infinispan_trust_store_path:
# line 68 of keycloak/defaults/main.yml
default: "/etc/pki/java/cacerts"
description: "TODO document argument"
type: "str"
infinispan_trust_store_password:
keycloak_infinispan_trust_store_password:
# line 69 of keycloak/defaults/main.yml
default: "changeit"
description: "Path to truststore containing infinispan server certificate"
@@ -257,7 +246,7 @@ argument_specs:
keycloak_jdbc_engine:
# line 72 of keycloak/defaults/main.yml
default: "postgres"
description: "Backend database flavour when db is enabled: [ postgres, mariadb ]"
description: "Backend database flavour when db is enabled: [ postgres, mariadb, sqlserver ]"
type: "str"
keycloak_db_user:
# line 74 of keycloak/defaults/main.yml
@@ -294,3 +283,121 @@ argument_specs:
default: "http://{{ keycloak_host }}:{{ keycloak_management_http_port }}"
description: "URL for management console rest calls"
type: "str"
keycloak_service_name:
default: "keycloak"
description: "systemd service name for keycloak"
type: "str"
keycloak_service_desc:
default: "Keycloak"
description: "systemd description for keycloak"
type: "str"
keycloak_service_start_delay:
default: "10"
description: "Expected delay in ms before the service is expected to be available after start."
type: "int"
keycloak_service_start_retries:
default: "25"
description: "How many time should Ansible retry to connect to the service after it was started, before failing."
type: "int"
keycloak_service_restart_always:
default: false
description: "systemd restart always behavior activation for keycloak"
type: "bool"
keycloak_service_restart_on_failure:
default: false
description: "systemd restart on-failure behavior activation for keycloak"
type: "bool"
keycloak_service_startlimitintervalsec:
default: 300
description: "systemd StartLimitIntervalSec for keycloak"
type: "int"
keycloak_service_startlimitburst:
default: 5
description: "systemd StartLimitBurst for keycloak"
type: "int"
keycloak_service_restartsec:
default: "5s"
description: "systemd RestartSec for keycloak"
type: "str"
keycloak_no_log:
default: true
type: "bool"
description: "Changes default behavior for no_log for debugging purpose, do not change for production system."
keycloak_remote_cache_enabled:
default: "{{ True if keycloak_ha_enabled else False }}"
description: "Enable remote cache store when in clustered ha configurations"
type: "bool"
keycloak_db_background_validation:
default: False
description: "Enable background validation of database connection"
type: "bool"
keycloak_db_background_validation_millis:
default: "{{ 10000 if keycloak_db_background_validation else 0 }}"
description: "How frequenly the connection pool is validated in the background"
type: 'int'
keycloak_db_background_validate_on_match:
default: False
description: "Enable validate on match for database connections"
type: "bool"
keycloak_db_valid_conn_sql:
required: False
description: "Override the default database connection validation query sql"
type: "str"
keycloak_admin_url:
required: False
description: "Override the default administration endpoint URL"
type: "str"
keycloak_jgroups_subnet:
required: False
description: "Override the subnet match for jgroups cluster formation; if not defined, it will be inferred from local machine route configuration"
type: "str"
downstream:
options:
sso_version:
default: "7.6.0"
description: "Red Hat Single Sign-On version"
type: "str"
sso_archive:
default: "rh-sso-{{ sso_version }}-server-dist.zip"
description: "Red Hat SSO install archive filename"
type: "str"
sso_dest:
default: "/opt/sso"
description: "Root installation directory"
type: "str"
sso_installdir:
default: "{{ sso_dest }}/rh-sso-{{ sso_version.split('.')[0] }}.{{ sso_version.split('.')[1] }}"
description: "Installation path for Red Hat SSO"
type: "str"
sso_apply_patches:
default: False
description: "Install Red Hat SSO most recent cumulative patch"
type: "bool"
sso_enable:
default: True
description: "Enable Red Hat Single Sign-on installation"
type: "str"
sso_offline_install:
default: False
description: "Perform an offline install"
type: "bool"
sso_service_name:
default: "sso"
description: "systemd service name for Single Sign-On"
type: "str"
sso_service_desc:
default: "Red Hat Single Sign-On"
description: "systemd description for Red Hat Single Sign-On"
type: "str"
sso_patch_version:
required: False
description: "Red Hat Single Sign-On latest cumulative patch version to apply; defaults to latest version when sso_apply_patches is True"
type: "str"
sso_patch_bundle:
default: "rh-sso-{{ sso_patch_version | default('[0-9]+[.][0-9]+[.][0-9]+') }}-patch.zip"
description: "Red Hat SSO patch archive filename"
type: "str"
sso_product_category:
default: "core.service.rhsso"
description: "JBossNetwork API category for Single Sign-On"
type: "str"

View File

@@ -1,7 +1,7 @@
---
collections:
- middleware_automation.redhat_csp_download
- middleware_automation.wildfly
- middleware_automation.common
- ansible.posix
galaxy_info:
role_name: keycloak

View File

@@ -1,10 +1,8 @@
---
- name: Check packages to be installed
block:
- name: "Check if packages are already installed"
- name: "Check if packages are already installed" # noqa command-instead-of-module this runs faster
ansible.builtin.command: "rpm -q {{ packages_list | join(' ') }}"
args:
warn: no
register: rpm_info
changed_when: rpm_info.failed
@@ -19,4 +17,4 @@
ansible.builtin.yum:
name: "{{ packages_to_install }}"
state: present
when: packages_to_install | default([]) | length > 0
when: packages_to_install | default([]) | length > 0

View File

@@ -14,7 +14,7 @@
- name: "Configure firewall for {{ keycloak.service_name }} ports"
become: yes
firewalld:
ansible.posix.firewalld:
port: "{{ item }}"
permanent: true
state: enabled

View File

@@ -17,6 +17,7 @@
register: existing_deploy
- name: Stop and restart if existing deployment exists and install forced
when: existing_deploy.stat.exists and keycloak_force_install | bool
block:
- name: "Stop the old {{ keycloak.service_name }} service"
become: yes
@@ -29,7 +30,6 @@
ansible.builtin.file:
path: "{{ keycloak_jboss_home }}"
state: absent
when: existing_deploy.stat.exists and keycloak_force_install|bool
- name: Check for an existing deployment after possible forced removal
become: yes
@@ -77,28 +77,51 @@
dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}"
mode: 0644
delegate_to: localhost
run_once: yes
when:
- archive_path is defined
- archive_path.stat is defined
- not archive_path.stat.exists
- not keycloak_rhsso_enable
- not sso_enable is defined or not sso_enable
- not keycloak_offline_install
- name: Perform download from RHN
middleware_automation.redhat_csp_download.redhat_csp_download:
url: "{{ keycloak_rhsso_download_url }}"
dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}"
username: "{{ rhn_username }}"
password: "{{ rhn_password }}"
no_log: "{{ omit_rhn_output | default(true) }}"
- name: Perform download from RHN using JBoss Network API
delegate_to: localhost
run_once: yes
when:
- archive_path is defined
- archive_path.stat is defined
- not archive_path.stat.exists
- keycloak_rhsso_enable
- sso_enable is defined and sso_enable
- not keycloak_offline_install
- keycloak_rhn_url in keycloak_rhsso_download_url
block:
- name: Retrieve product download using JBoss Network API
middleware_automation.common.product_search:
client_id: "{{ rhn_username }}"
client_secret: "{{ rhn_password }}"
product_type: DISTRIBUTION
product_version: "{{ sso_version.split('.')[:2] | join('.') }}"
product_category: "{{ sso_product_category }}"
register: rhn_products
no_log: "{{ omit_rhn_output | default(true) }}"
delegate_to: localhost
run_once: yes
- name: Determine install zipfile from search results
ansible.builtin.set_fact:
rhn_filtered_products: "{{ rhn_products.results | selectattr('file_path', 'match', '[^/]*/' + sso_archive + '$') }}"
delegate_to: localhost
run_once: yes
- name: Download Red Hat Single Sign-On
middleware_automation.common.product_download: # noqa risky-file-permissions delegated, uses controller host user
client_id: "{{ rhn_username }}"
client_secret: "{{ rhn_password }}"
product_id: "{{ (rhn_filtered_products | first).id }}"
dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}"
no_log: "{{ omit_rhn_output | default(true) }}"
delegate_to: localhost
run_once: yes
- name: Download rhsso archive from alternate location
ansible.builtin.get_url: # noqa risky-file-permissions delegated, uses controller host user
@@ -106,13 +129,14 @@
dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}"
mode: 0644
delegate_to: localhost
run_once: yes
when:
- archive_path is defined
- archive_path.stat is defined
- not archive_path.stat.exists
- keycloak_rhsso_enable
- sso_enable is defined and sso_enable
- not keycloak_offline_install
- not keycloak_rhn_url in keycloak_rhsso_download_url
- keycloak_rhsso_download_url is defined
- name: Check downloaded archive
ansible.builtin.stat:
@@ -141,7 +165,7 @@
register: path_to_workdir
become: yes
- name: "Extract {{ 'Red Hat Single Sign-On' if keycloak_rhsso_enable else 'Keycloak' }} archive on target"
- name: "Extract {{ keycloak_service_desc }} archive on target"
ansible.builtin.unarchive:
remote_src: yes
src: "{{ archive }}"
@@ -172,31 +196,65 @@
# driver and configuration
- name: "Install {{ keycloak_jdbc_engine }} driver"
ansible.builtin.include_role:
name: middleware_automation.wildfly.wildfly_driver
vars:
wildfly_user: "{{ keycloak_service_user }}"
jdbc_driver_module_dir: "{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_dir }}"
jdbc_driver_version: "{{ keycloak_jdbc[keycloak_jdbc_engine].driver_version }}"
jdbc_driver_jar_filename: "{{ keycloak_jdbc[keycloak_jdbc_engine].driver_jar_filename }}"
jdbc_driver_jar_url: "{{ keycloak_jdbc[keycloak_jdbc_engine].driver_jar_url }}"
jdbc_driver_jar_installation_path: "{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_dir }}/{{ keycloak_jdbc[keycloak_jdbc_engine].driver_jar_filename }}"
jdbc_driver_module_name: "{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}"
ansible.builtin.include_tasks: jdbc_driver.yml
when: keycloak_jdbc[keycloak_jdbc_engine].enabled
- name: "Deploy {{ keycloak.service_name }} config to {{ keycloak_config_path_to_standalone_xml }} from {{ keycloak.config_template_source }}"
- name: "Deploy custom {{ keycloak.service_name }} config to {{ keycloak_config_path_to_standalone_xml }} from {{ keycloak_config_override_template }}"
become: yes
ansible.builtin.template:
src: "templates/{{ keycloak.config_template_source }}"
src: "templates/{{ keycloak_config_override_template }}"
dest: "{{ keycloak_config_path_to_standalone_xml }}"
owner: "{{ keycloak_service_user }}"
group: "{{ keycloak_service_group }}"
mode: 0640
notify:
- restart keycloak
when: not keycloak_remotecache.enabled or keycloak_config_override_template|length > 0
when: keycloak_config_override_template | length > 0
- name: "Deploy {{ keycloak.service_name }} config with remote cache store to {{ keycloak_config_path_to_standalone_xml }}"
- name: "Deploy standalone {{ keycloak.service_name }} config to {{ keycloak_config_path_to_standalone_xml }}"
become: yes
ansible.builtin.template:
src: templates/standalone.xml.j2
dest: "{{ keycloak_config_path_to_standalone_xml }}"
owner: "{{ keycloak_service_user }}"
group: "{{ keycloak_service_group }}"
mode: 0640
notify:
- restart keycloak
when:
- not keycloak_ha_enabled
- keycloak_config_override_template | length == 0
- name: Create tcpping cluster node list
ansible.builtin.set_fact:
keycloak_cluster_nodes: >
{{ keycloak_cluster_nodes | default([]) + [
{
"name": item,
"address": 'jgroups-' + item,
"inventory_host": hostvars[item].ansible_default_ipv4.address | default(item) + '[' + (keycloak_jgroups_port | string) + ']',
"value": hostvars[item].ansible_default_ipv4.address | default(item)
}
] }}
loop: "{{ ansible_play_batch }}"
when: keycloak_ha_enabled and keycloak_ha_discovery == 'TCPPING'
- name: "Deploy HA {{ keycloak.service_name }} config to {{ keycloak_config_path_to_standalone_xml }}"
become: yes
ansible.builtin.template:
src: templates/standalone-ha.xml.j2
dest: "{{ keycloak_config_path_to_standalone_xml }}"
owner: "{{ keycloak_service_user }}"
group: "{{ keycloak_service_group }}"
mode: 0640
notify:
- restart keycloak
when:
- keycloak_ha_enabled
- not keycloak_remote_cache_enabled
- 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 }}"
become: yes
ansible.builtin.template:
src: templates/standalone-infinispan.xml.j2
@@ -206,4 +264,19 @@
mode: 0640
notify:
- restart keycloak
when: keycloak_remotecache.enabled
when:
- keycloak_ha_enabled
- keycloak_remote_cache_enabled
- keycloak_config_override_template | length == 0
- name: "Deploy profile.properties file to {{ keycloak_config_path_to_properties }}"
become: yes
ansible.builtin.template:
src: keycloak-profile.properties.j2
dest: "{{ keycloak_config_path_to_properties }}"
owner: "{{ keycloak_service_user }}"
group: "{{ keycloak_service_group }}"
mode: 0640
notify:
- restart keycloak
when: keycloak_features | length > 0

View File

@@ -0,0 +1,36 @@
---
- name: "Check module directory: {{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_dir }}"
ansible.builtin.stat:
path: "{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_dir }}"
register: dest_path
become: yes
- name: "Set up module dir for JDBC Driver {{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}"
ansible.builtin.file:
path: "{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_dir }}"
state: directory
recurse: yes
owner: "{{ keycloak_service_user }}"
group: "{{ keycloak_service_group }}"
mode: 0750
become: yes
when:
- not dest_path.stat.exists
- name: "Retrieve JDBC Driver from {{ keycloak_jdbc[keycloak_jdbc_engine].driver_jar_url }}"
ansible.builtin.get_url:
url: "{{ keycloak_jdbc[keycloak_jdbc_engine].driver_jar_url }}"
dest: "{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_dir }}/{{ keycloak_jdbc[keycloak_jdbc_engine].driver_jar_filename }}"
group: "{{ keycloak_service_group }}"
owner: "{{ keycloak_service_user }}"
mode: 0640
become: yes
- name: "Deploy module.xml for JDBC Driver"
ansible.builtin.template:
src: "templates/jdbc_driver_module.xml.j2"
dest: "{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_dir }}/module.xml"
group: "{{ keycloak_service_group }}"
owner: "{{ keycloak_service_user }}"
mode: 0640
become: yes

View File

@@ -1,6 +1,5 @@
---
# tasks file for keycloak
- name: Check prerequisites
ansible.builtin.include_tasks: prereqs.yml
tags:
@@ -24,7 +23,9 @@
- name: Include patch install tasks
ansible.builtin.include_tasks: rhsso_patch.yml
when: keycloak_rhsso_apply_patches and keycloak_rhsso_enable
when:
- sso_apply_patches is defined and sso_apply_patches
- sso_enable is defined and sso_enable
tags:
- install
- patch
@@ -34,6 +35,7 @@
state: link
src: "{{ keycloak_jboss_home }}/standalone/log"
dest: /var/log/keycloak
become: yes
- name: Set admin credentials and restart if not already created
block:

View File

@@ -18,16 +18,16 @@
- name: Validate credentials
ansible.builtin.assert:
that:
- (rhn_username is defined and keycloak_rhsso_enable) or not keycloak_rhsso_enable or keycloak_offline_install
- (rhn_password is defined and keycloak_rhsso_enable) or not keycloak_rhsso_enable or keycloak_offline_install
- (rhn_username is defined and sso_enable is defined and sso_enable) or not sso_enable is defined or not sso_enable or keycloak_offline_install
- (rhn_password is defined and sso_enable is defined and sso_enable) or not sso_enable is defined or not sso_enable or keycloak_offline_install
quiet: True
fail_msg: "Cannot install Red Hat SSO without RHN credentials. Check rhn_username and rhn_password are defined"
success_msg: "{{ 'Installing Red Hat Single Sign-On' if keycloak_rhsso_enable else 'Installing keycloak.org' }}"
success_msg: "Installing {{ keycloak_service_desc }}"
- name: Validate persistence configuration
ansible.builtin.assert:
that:
- keycloak_jdbc_engine is defined and keycloak_jdbc_engine in [ 'postgres', 'mariadb' ]
- keycloak_jdbc_engine is defined and keycloak_jdbc_engine in [ 'postgres', 'mariadb', 'sqlserver' ]
- keycloak_jdbc_url | length > 0
- keycloak_db_user | length > 0
- keycloak_db_pass | length > 0
@@ -43,4 +43,5 @@
- "{{ keycloak_jvm_package }}"
- unzip
- procps-ng
- initscripts
- initscripts
- tzdata-java

View File

@@ -5,3 +5,23 @@
enabled: yes
state: restarted
become: yes
delegate_to: "{{ ansible_play_hosts | first }}"
run_once: True
- name: "Wait until {{ keycloak.service_name }} becomes active {{ keycloak.health_url }}"
ansible.builtin.uri:
url: "{{ keycloak.health_url }}"
register: keycloak_status
until: keycloak_status.status == 200
delegate_to: "{{ ansible_play_hosts | first }}"
run_once: True
retries: "{{ keycloak_service_start_retries }}"
delay: "{{ keycloak_service_start_delay }}"
- name: "Restart and enable {{ keycloak.service_name }} service"
ansible.builtin.systemd:
name: keycloak
enabled: yes
state: restarted
become: yes
when: inventory_hostname != ansible_play_hosts | first

View File

@@ -2,32 +2,94 @@
## check remote patch archive
- name: Set download patch archive path
ansible.builtin.set_fact:
patch_archive: "{{ keycloak_dest }}/{{ keycloak.patch_bundle }}"
patch_archive: "{{ keycloak_dest }}/{{ sso_patch_bundle }}"
patch_bundle: "{{ sso_patch_bundle }}"
patch_version: "{{ sso_patch_version }}"
when: sso_patch_version is defined
- name: Check download patch archive path
ansible.builtin.stat:
path: "{{ patch_archive }}"
register: patch_archive_path
when: sso_patch_version is defined
become: yes
- name: Perform download from RHN
middleware_automation.redhat_csp_download.redhat_csp_download:
url: "{{ keycloak_rhn_url }}{{ rhsso_rhn_ids[keycloak_rhsso_version].latest_cp.id }}"
dest: "{{ local_path.stat.path }}/{{ keycloak.patch_bundle }}"
username: "{{ rhn_username }}"
password: "{{ rhn_password }}"
no_log: "{{ omit_rhn_output | default(true) }}"
- name: Perform patch download from RHN via JBossNetwork API
delegate_to: localhost
run_once: yes
when:
- patch_archive_path is defined
- patch_archive_path.stat is defined
- not patch_archive_path.stat.exists
- keycloak_rhsso_enable
- sso_enable is defined and sso_enable
- not keycloak_offline_install
- sso_apply_patches
block:
- name: Retrieve product download using JBossNetwork API
middleware_automation.common.product_search:
client_id: "{{ rhn_username }}"
client_secret: "{{ rhn_password }}"
product_type: BUGFIX
product_version: "{{ sso_version.split('.')[:2] | join('.') }}"
product_category: "{{ sso_product_category }}"
register: rhn_products
no_log: "{{ omit_rhn_output | default(true) }}"
delegate_to: localhost
run_once: yes
- name: Determine patch versions list
ansible.builtin.set_fact:
filtered_versions: "{{ rhn_products.results | map(attribute='file_path') | select('match', '^[^/]*/rh-sso-.*[0-9]*[.][0-9]*[.][0-9]*.*$') | map('regex_replace','[^/]*/rh-sso-([0-9]*[.][0-9]*[.][0-9]*)-.*','\\1' ) | list | unique }}"
when: sso_patch_version is not defined or sso_patch_version | length == 0
delegate_to: localhost
run_once: yes
- name: Determine latest version
ansible.builtin.set_fact:
sso_latest_version: "{{ filtered_versions | middleware_automation.common.version_sort | last }}"
when: sso_patch_version is not defined or sso_patch_version | length == 0
delegate_to: localhost
run_once: yes
- name: Determine install zipfile from search results
ansible.builtin.set_fact:
rhn_filtered_products: "{{ rhn_products.results | selectattr('file_path', 'match', '[^/]*/rh-sso-' + sso_latest_version + '-patch.zip$') }}"
patch_bundle: "rh-sso-{{ sso_latest_version }}-patch.zip"
patch_version: "{{ sso_latest_version }}"
when: sso_patch_version is not defined or sso_patch_version | length == 0
delegate_to: localhost
run_once: yes
- name: "Determine selected patch from supplied version: {{ sso_patch_version }}"
ansible.builtin.set_fact:
rhn_filtered_products: "{{ rhn_products.results | selectattr('file_path', 'match', '[^/]*/' + sso_patch_bundle + '$') }}"
patch_bundle: "{{ sso_patch_bundle }}"
patch_version: "{{ sso_patch_version }}"
when: sso_patch_version is defined
delegate_to: localhost
run_once: yes
- name: Download Red Hat Single Sign-On patch
middleware_automation.common.product_download: # noqa risky-file-permissions delegated, uses controller host user
client_id: "{{ rhn_username }}"
client_secret: "{{ rhn_password }}"
product_id: "{{ (rhn_filtered_products | first).id }}"
dest: "{{ local_path.stat.path }}/{{ patch_bundle }}"
no_log: "{{ omit_rhn_output | default(true) }}"
delegate_to: localhost
run_once: yes
- name: Set download patch archive path
ansible.builtin.set_fact:
patch_archive: "{{ keycloak_dest }}/{{ patch_bundle }}"
- name: Check download patch archive path
ansible.builtin.stat:
path: "{{ patch_archive }}"
register: patch_archive_path
become: yes
## copy and unpack
- name: Copy patch archive to target nodes
ansible.builtin.copy:
src: "{{ local_path.stat.path }}/{{ keycloak.patch_bundle }}"
src: "{{ local_path.stat.path }}/{{ patch_bundle }}"
dest: "{{ patch_archive }}"
owner: "{{ keycloak_service_user }}"
group: "{{ keycloak_service_group }}"
@@ -43,17 +105,25 @@
ansible.builtin.include_tasks: rhsso_cli.yml
vars:
query: "patch info"
args:
apply:
become: yes
become_user: "{{ keycloak_service_user }}"
- name: "Perform patching"
when:
when:
- cli_result is defined
- cli_result.stdout is defined
- rhsso_rhn_ids[keycloak_rhsso_version].latest_cp.v not in cli_result.stdout
- patch_version not in cli_result.stdout
block:
- name: "Apply patch {{ rhsso_rhn_ids[keycloak_rhsso_version].latest_cp.v }} to server"
- name: "Apply patch {{ patch_version }} to server"
ansible.builtin.include_tasks: rhsso_cli.yml
vars:
query: "patch apply {{ patch_archive }}"
args:
apply:
become: yes
become_user: "{{ keycloak_service_user }}"
- name: "Restart server to ensure patch content is running"
ansible.builtin.include_tasks: rhsso_cli.yml
@@ -61,6 +131,10 @@
query: "shutdown --restart"
when:
- cli_result.rc == 0
args:
apply:
become: yes
become_user: "{{ keycloak_service_user }}"
- name: "Wait until {{ keycloak.service_name }} becomes active {{ keycloak.health_url }}"
ansible.builtin.uri:
@@ -74,14 +148,22 @@
ansible.builtin.include_tasks: rhsso_cli.yml
vars:
query: "patch info"
args:
apply:
become: yes
become_user: "{{ keycloak_service_user }}"
- name: "Verify installed patch version"
ansible.builtin.assert:
that:
- rhsso_rhn_ids[keycloak_rhsso_version].latest_cp.v not in cli_result.stdout
- patch_version not in cli_result.stdout
fail_msg: "Patch installation failed"
success_msg: "Patch installation successful"
- name: "Skipping patch"
ansible.builtin.debug:
msg: "Latest cumulative patch {{ rhsso_rhn_ids[keycloak_rhsso_version].latest_cp.v }} already installed, skipping patch installation."
msg: "Cumulative patch {{ patch_version }} already installed, skipping patch installation."
when:
- cli_result is defined
- cli_result.stdout is defined
- patch_version in cli_result.stdout

View File

@@ -11,5 +11,5 @@
url: "{{ keycloak.health_url }}"
register: keycloak_status
until: keycloak_status.status == 200
retries: 25
delay: 10
retries: "{{ keycloak_service_start_retries }}"
delay: "{{ keycloak_service_start_delay }}"

View File

@@ -10,14 +10,9 @@
notify:
- restart keycloak
- name: Determine JAVA_HOME for selected JVM RPM # noqa blocked_modules
ansible.builtin.shell: |
set -o pipefail
rpm -ql {{ keycloak_jvm_package }} | grep -Po '/usr/lib/jvm/.*(?=/bin/java$)'
args:
executable: /bin/bash
changed_when: False
register: rpm_java_home
- name: Determine JAVA_HOME for selected JVM RPM
ansible.builtin.set_fact:
rpm_java_home: "/etc/alternatives/jre_{{ keycloak_jvm_package | regex_search('(?<=java-)[0-9.]+') }}"
- name: "Configure sysconfig file for {{ keycloak.service_name }} service"
become: yes
@@ -28,7 +23,7 @@
group: root
mode: 0644
vars:
keycloak_rpm_java_home: "{{ rpm_java_home.stdout }}"
keycloak_rpm_java_home: "{{ rpm_java_home }}"
notify:
- restart keycloak

View File

@@ -0,0 +1,761 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- {{ ansible_managed }} -->
<server xmlns="urn:jboss:domain:16.0">
<extensions>
<extension module="org.jboss.as.clustering.infinispan"/>
<extension module="org.jboss.as.clustering.jgroups"/>
<extension module="org.jboss.as.connector"/>
<extension module="org.jboss.as.deployment-scanner"/>
<extension module="org.jboss.as.ee"/>
<extension module="org.jboss.as.ejb3"/>
<extension module="org.jboss.as.jaxrs"/>
<extension module="org.jboss.as.jmx"/>
<extension module="org.jboss.as.jpa"/>
<extension module="org.jboss.as.logging"/>
<extension module="org.jboss.as.mail"/>
<extension module="org.jboss.as.modcluster"/>
<extension module="org.jboss.as.naming"/>
<extension module="org.jboss.as.remoting"/>
<extension module="org.jboss.as.security"/>
<extension module="org.jboss.as.transactions"/>
<extension module="org.jboss.as.weld"/>
<extension module="org.keycloak.keycloak-server-subsystem"/>
<extension module="org.wildfly.extension.bean-validation"/>
<extension module="org.wildfly.extension.core-management"/>
<extension module="org.wildfly.extension.elytron"/>
<extension module="org.wildfly.extension.health"/>
<extension module="org.wildfly.extension.io"/>
<extension module="org.wildfly.extension.metrics"/>
<extension module="org.wildfly.extension.request-controller"/>
<extension module="org.wildfly.extension.security.manager"/>
<extension module="org.wildfly.extension.undertow"/>
</extensions>
<management>
<security-realms>
<security-realm name="ManagementRealm">
<authentication>
<local default-user="$local" skip-group-loading="true"/>
<properties path="mgmt-users.properties" relative-to="jboss.server.config.dir"/>
</authentication>
<authorization map-groups-to-roles="false">
<properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
</authorization>
</security-realm>
<security-realm name="ApplicationRealm">
<server-identities>
<ssl>
<keystore path="application.keystore" relative-to="jboss.server.config.dir" keystore-password="password" alias="server" key-password="password" generate-self-signed-certificate-host="localhost"/>
</ssl>
</server-identities>
<authentication>
<local default-user="$local" allowed-users="*" skip-group-loading="true"/>
<properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
</authentication>
<authorization>
<properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
</authorization>
</security-realm>
</security-realms>
<audit-log>
<formatters>
<json-formatter name="json-formatter"/>
</formatters>
<handlers>
<file-handler name="file" formatter="json-formatter" path="audit-log.log" relative-to="jboss.server.data.dir"/>
</handlers>
<logger log-boot="true" log-read-only="false" enabled="false">
<handlers>
<handler name="file"/>
</handlers>
</logger>
</audit-log>
<management-interfaces>
<http-interface security-realm="ManagementRealm">
<http-upgrade enabled="true"/>
<socket-binding http="management-http"/>
</http-interface>
</management-interfaces>
<access-control provider="simple">
<role-mapping>
<role name="SuperUser">
<include>
<user name="$local"/>
</include>
</role>
</role-mapping>
</access-control>
</management>
<profile>
<subsystem xmlns="urn:jboss:domain:logging:8.0">
<console-handler name="CONSOLE">
<level name="INFO"/>
<formatter>
<named-formatter name="COLOR-PATTERN"/>
</formatter>
</console-handler>
<periodic-rotating-file-handler name="FILE" autoflush="true">
<formatter>
<named-formatter name="PATTERN"/>
</formatter>
<file relative-to="jboss.server.log.dir" path="server.log"/>
<suffix value=".yyyy-MM-dd"/>
<append value="true"/>
</periodic-rotating-file-handler>
<logger category="com.arjuna">
<level name="WARN"/>
</logger>
<logger category="io.jaegertracing.Configuration">
<level name="WARN"/>
</logger>
<logger category="org.jboss.as.config">
<level name="DEBUG"/>
</logger>
<logger category="sun.rmi">
<level name="WARN"/>
</logger>
<logger category="org.keycloak.cluster.infinispan">
<level name="DEBUG"/>
</logger>
<logger category="org.keycloak.connections.infinispan">
<level name="DEBUG"/>
</logger>
<logger category="org.keycloak.models.cache.infinispan">
<level name="DEBUG"/>
</logger>
<logger category="org.keycloak.models.sessions.infinispan">
<level name="DEBUG"/>
</logger>
<root-logger>
<level name="INFO"/>
<handlers>
<handler name="CONSOLE"/>
<handler name="FILE"/>
</handlers>
</root-logger>
<formatter name="PATTERN">
<pattern-formatter pattern="%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
</formatter>
<formatter name="COLOR-PATTERN">
<pattern-formatter pattern="%K{level}%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
</formatter>
</subsystem>
<subsystem xmlns="urn:jboss:domain:bean-validation:1.0"/>
<subsystem xmlns="urn:jboss:domain:core-management:1.0"/>
<subsystem xmlns="urn:jboss:domain:datasources:6.0">
<datasources>
<datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
<connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url>
<driver>h2</driver>
<security>
<user-name>sa</user-name>
<password>sa</password>
</security>
</datasource>
<datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
{% if keycloak_jdbc[keycloak_jdbc_engine].enabled %}
<connection-url>{{ keycloak_jdbc[keycloak_jdbc_engine].connection_url }}</connection-url>
<driver>{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}</driver>
<pool>
<max-pool-size>20</max-pool-size>
</pool>
<security>
<user-name>{{ keycloak_jdbc[keycloak_jdbc_engine].db_user }}</user-name>
<password>{{ keycloak_jdbc[keycloak_jdbc_engine].db_password }}</password>
</security>
{% else %}
<connection-url>jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE</connection-url>
<driver>h2</driver>
<security>
<user-name>sa</user-name>
<password>sa</password>
</security>
{% endif %}
</datasource>
<drivers>
{% if keycloak_jdbc[keycloak_jdbc_engine].enabled %}
<driver name="{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}" module="{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}">
<driver-class>{{ keycloak_jdbc[keycloak_jdbc_engine].driver_class }}</driver-class>
<xa-datasource-class>{{ keycloak_jdbc[keycloak_jdbc_engine].xa_datasource_class }}</xa-datasource-class>
</driver>
{% endif %}
<driver name="h2" module="com.h2database.h2">
<xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
</driver>
</drivers>
</datasources>
</subsystem>
<subsystem xmlns="urn:jboss:domain:deployment-scanner:2.0">
<deployment-scanner path="deployments" relative-to="jboss.server.base.dir" scan-interval="5000" runtime-failure-causes-rollback="${jboss.deployment.scanner.rollback.on.failure:false}"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:ee:6.0">
<spec-descriptor-property-replacement>false</spec-descriptor-property-replacement>
<concurrent>
<context-services>
<context-service name="default" jndi-name="java:jboss/ee/concurrency/context/default" use-transaction-setup-provider="true"/>
</context-services>
<managed-thread-factories>
<managed-thread-factory name="default" jndi-name="java:jboss/ee/concurrency/factory/default" context-service="default"/>
</managed-thread-factories>
<managed-executor-services>
<managed-executor-service name="default" jndi-name="java:jboss/ee/concurrency/executor/default" context-service="default" hung-task-termination-period="0" hung-task-threshold="60000" keepalive-time="5000"/>
</managed-executor-services>
<managed-scheduled-executor-services>
<managed-scheduled-executor-service name="default" jndi-name="java:jboss/ee/concurrency/scheduler/default" context-service="default" hung-task-termination-period="0" hung-task-threshold="60000" keepalive-time="3000"/>
</managed-scheduled-executor-services>
</concurrent>
<default-bindings context-service="java:jboss/ee/concurrency/context/default" datasource="java:jboss/datasources/ExampleDS" managed-executor-service="java:jboss/ee/concurrency/executor/default" managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default" managed-thread-factory="java:jboss/ee/concurrency/factory/default"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:ejb3:9.0">
<session-bean>
<stateless>
<bean-instance-pool-ref pool-name="slsb-strict-max-pool"/>
</stateless>
<stateful default-access-timeout="5000" cache-ref="distributable" passivation-disabled-cache-ref="simple"/>
<singleton default-access-timeout="5000"/>
</session-bean>
<pools>
<bean-instance-pools>
<strict-max-pool name="mdb-strict-max-pool" derive-size="from-cpu-count" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
<strict-max-pool name="slsb-strict-max-pool" derive-size="from-worker-pools" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
</bean-instance-pools>
</pools>
<caches>
<cache name="simple"/>
<cache name="distributable" passivation-store-ref="infinispan" aliases="passivating clustered"/>
</caches>
<passivation-stores>
<passivation-store name="infinispan" cache-container="ejb" max-size="10000"/>
</passivation-stores>
<async thread-pool-name="default"/>
<timer-service thread-pool-name="default" default-data-store="default-file-store">
<data-stores>
<file-data-store name="default-file-store" path="timer-service-data" relative-to="jboss.server.data.dir"/>
</data-stores>
</timer-service>
<remote cluster="ejb" connectors="http-remoting-connector" thread-pool-name="default">
<channel-creation-options>
<option name="MAX_OUTBOUND_MESSAGES" value="1234" type="remoting"/>
</channel-creation-options>
</remote>
<thread-pools>
<thread-pool name="default">
<max-threads count="10"/>
<keepalive-time time="60" unit="seconds"/>
</thread-pool>
</thread-pools>
<default-security-domain value="other"/>
<default-missing-method-permissions-deny-access value="true"/>
<statistics enabled="${wildfly.ejb3.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
<log-system-exceptions value="true"/>
</subsystem>
<subsystem xmlns="urn:wildfly:elytron:13.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
<providers>
<aggregate-providers name="combined-providers">
<providers name="elytron"/>
<providers name="openssl"/>
</aggregate-providers>
<provider-loader name="elytron" module="org.wildfly.security.elytron"/>
<provider-loader name="openssl" module="org.wildfly.openssl"/>
</providers>
<audit-logging>
<file-audit-log name="local-audit" path="audit.log" relative-to="jboss.server.log.dir" format="JSON"/>
</audit-logging>
<security-domains>
<security-domain name="ApplicationDomain" default-realm="ApplicationRealm" permission-mapper="default-permission-mapper">
<realm name="ApplicationRealm" role-decoder="groups-to-roles"/>
<realm name="local"/>
</security-domain>
<security-domain name="ManagementDomain" default-realm="ManagementRealm" permission-mapper="default-permission-mapper">
<realm name="ManagementRealm" role-decoder="groups-to-roles"/>
<realm name="local" role-mapper="super-user-mapper"/>
</security-domain>
</security-domains>
<security-realms>
<identity-realm name="local" identity="$local"/>
<properties-realm name="ApplicationRealm">
<users-properties path="application-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ApplicationRealm"/>
<groups-properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
</properties-realm>
<properties-realm name="ManagementRealm">
<users-properties path="mgmt-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ManagementRealm"/>
<groups-properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
</properties-realm>
</security-realms>
<mappers>
<simple-permission-mapper name="default-permission-mapper" mapping-mode="first">
<permission-mapping>
<principal name="anonymous"/>
<permission-set name="default-permissions"/>
</permission-mapping>
<permission-mapping match-all="true">
<permission-set name="login-permission"/>
<permission-set name="default-permissions"/>
</permission-mapping>
</simple-permission-mapper>
<constant-realm-mapper name="local" realm-name="local"/>
<simple-role-decoder name="groups-to-roles" attribute="groups"/>
<constant-role-mapper name="super-user-mapper">
<role name="SuperUser"/>
</constant-role-mapper>
</mappers>
<permission-sets>
<permission-set name="login-permission">
<permission class-name="org.wildfly.security.auth.permission.LoginPermission"/>
</permission-set>
<permission-set name="default-permissions">
<permission class-name="org.wildfly.extension.batch.jberet.deployment.BatchPermission" module="org.wildfly.extension.batch.jberet" target-name="*"/>
<permission class-name="org.wildfly.transaction.client.RemoteTransactionPermission" module="org.wildfly.transaction.client"/>
<permission class-name="org.jboss.ejb.client.RemoteEJBPermission" module="org.jboss.ejb-client"/>
<permission class-name="org.jboss.ejb.client.RemoteEJBPermission" module="org.jboss.ejb-client"/>
</permission-set>
</permission-sets>
<http>
<http-authentication-factory name="management-http-authentication" security-domain="ManagementDomain" http-server-mechanism-factory="global">
<mechanism-configuration>
<mechanism mechanism-name="DIGEST">
<mechanism-realm realm-name="ManagementRealm"/>
</mechanism>
</mechanism-configuration>
</http-authentication-factory>
<provider-http-server-mechanism-factory name="global"/>
</http>
<sasl>
<sasl-authentication-factory name="application-sasl-authentication" sasl-server-factory="configured" security-domain="ApplicationDomain">
<mechanism-configuration>
<mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
<mechanism mechanism-name="DIGEST-MD5">
<mechanism-realm realm-name="ApplicationRealm"/>
</mechanism>
</mechanism-configuration>
</sasl-authentication-factory>
<sasl-authentication-factory name="management-sasl-authentication" sasl-server-factory="configured" security-domain="ManagementDomain">
<mechanism-configuration>
<mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
<mechanism mechanism-name="DIGEST-MD5">
<mechanism-realm realm-name="ManagementRealm"/>
</mechanism>
</mechanism-configuration>
</sasl-authentication-factory>
<configurable-sasl-server-factory name="configured" sasl-server-factory="elytron">
<properties>
<property name="wildfly.sasl.local-user.default-user" value="$local"/>
</properties>
</configurable-sasl-server-factory>
<mechanism-provider-filtering-sasl-server-factory name="elytron" sasl-server-factory="global">
<filters>
<filter provider-name="WildFlyElytron"/>
</filters>
</mechanism-provider-filtering-sasl-server-factory>
<provider-sasl-server-factory name="global"/>
</sasl>
<tls>
<key-stores>
<key-store name="applicationKS">
<credential-reference clear-text="password"/>
<implementation type="JKS"/>
<file path="application.keystore" relative-to="jboss.server.config.dir"/>
</key-store>
</key-stores>
<key-managers>
<key-manager name="applicationKM" key-store="applicationKS" generate-self-signed-certificate-host="localhost">
<credential-reference clear-text="password"/>
</key-manager>
</key-managers>
<server-ssl-contexts>
<server-ssl-context name="applicationSSC" key-manager="applicationKM"/>
</server-ssl-contexts>
</tls>
</subsystem>
<subsystem xmlns="urn:wildfly:health:1.0" security-enabled="false"/>
<subsystem xmlns="urn:jboss:domain:infinispan:12.0">
<cache-container name="ejb" default-cache="dist" aliases="sfsb" modules="org.wildfly.clustering.ejb.infinispan">
<transport lock-timeout="60000"/>
<distributed-cache name="dist">
<locking isolation="REPEATABLE_READ"/>
<transaction mode="BATCH"/>
<file-store/>
</distributed-cache>
</cache-container>
<cache-container name="keycloak" modules="org.keycloak.keycloak-model-infinispan">
<transport lock-timeout="60000"/>
<local-cache name="realms">
<heap-memory size="10000"/>
</local-cache>
<local-cache name="users">
<heap-memory size="10000"/>
</local-cache>
{% for cachename in [ "sessions", "offlineSessions", "clientSessions", "offlineClientSessions", "loginFailures", "actionTokens", "authenticationSessions" ] %}
<distributed-cache name="{{ cachename }}">
<remote-store cache="{{ cachename }}"
remote-servers="remote-cache"
passivation="false"
fetch-state="false"
purge="false"
preload="false"
shared="true">
<property name="rawValues">true</property>
<property name="marshaller">org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory</property>
<property name="remoteStoreSecurityEnabled">false</property>
<property name="infinispan.client.hotrod.auth_username">{{ keycloak_remotecache.username }}</property>
<property name="infinispan.client.hotrod.auth_password">{{ keycloak_remotecache.password }}</property>
<property name="infinispan.client.hotrod.auth_realm">{{ keycloak_remotecache.realm | default('default') }}</property>
<property name="infinispan.client.hotrod.auth_server_name">{{ keycloak_remotecache.server_name }}</property>
<property name="infinispan.client.hotrod.sasl_mechanism">{{ keycloak_remotecache.sasl_mechanism }}</property>
<property name="infinispan.client.hotrod.use_ssl">{{ keycloak_remotecache.use_ssl }}</property>
<property name="infinispan.client.hotrod.trust_store_file_name">{{ keycloak_remotecache.trust_store_path }}</property>
<property name="infinispan.client.hotrod.trust_store_type">JKS</property>
<property name="infinispan.client.hotrod.trust_store_password">{{ keycloak_remotecache.trust_store_password }}</property>
<property name="infinispan.client.hotrod.client_intelligence">TOPOLOGY_AWARE</property>
</remote-store>
</distributed-cache>
{% endfor %}
<replicated-cache name="work">
<remote-store cache="work"
remote-servers="remote-cache"
passivation="false"
fetch-state="false"
purge="false"
preload="false"
shared="true">
<property name="rawValues">true</property>
<property name="marshaller">org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory</property>
<property name="remoteStoreSecurityEnabled">false</property>
<property name="infinispan.client.hotrod.auth_username">{{ keycloak_remotecache.username }}</property>
<property name="infinispan.client.hotrod.auth_password">{{ keycloak_remotecache.password }}</property>
<property name="infinispan.client.hotrod.auth_realm">{{ keycloak_remotecache.realm | default('default') }}</property>
<property name="infinispan.client.hotrod.auth_server_name">{{ keycloak_remotecache.server_name }}</property>
<property name="infinispan.client.hotrod.sasl_mechanism">{{ keycloak_remotecache.sasl_mechanism }}</property>
<property name="infinispan.client.hotrod.use_ssl">{{ keycloak_remotecache.use_ssl }}</property>
<property name="infinispan.client.hotrod.trust_store_file_name">{{ keycloak_remotecache.trust_store_path }}</property>
<property name="infinispan.client.hotrod.trust_store_type">JKS</property>
<property name="infinispan.client.hotrod.trust_store_password">{{ keycloak_remotecache.trust_store_password }}</property>
<property name="infinispan.client.hotrod.client_intelligence">TOPOLOGY_AWARE</property>
</remote-store>
</replicated-cache>
<local-cache name="authorization">
<heap-memory size="10000"/>
</local-cache>
<local-cache name="keys">
<heap-memory size="1000"/>
<expiration max-idle="3600000"/>
</local-cache>
</cache-container>
<cache-container name="server" default-cache="default" aliases="singleton cluster" modules="org.wildfly.clustering.server">
<transport lock-timeout="60000"/>
<replicated-cache name="default">
<transaction mode="BATCH"/>
</replicated-cache>
</cache-container>
<cache-container name="web" default-cache="dist" modules="org.wildfly.clustering.web.infinispan">
<transport lock-timeout="60000"/>
<replicated-cache name="sso">
<locking isolation="REPEATABLE_READ"/>
<transaction mode="BATCH"/>
</replicated-cache>
<distributed-cache name="dist">
<locking isolation="REPEATABLE_READ"/>
<transaction mode="BATCH"/>
<file-store/>
</distributed-cache>
<distributed-cache name="routing"/>
</cache-container>
<cache-container name="hibernate" modules="org.infinispan.hibernate-cache">
<transport lock-timeout="60000"/>
<local-cache name="local-query">
<heap-memory size="10000"/>
<expiration max-idle="100000"/>
</local-cache>
<invalidation-cache name="entity">
<transaction mode="NON_XA"/>
<heap-memory size="10000"/>
<expiration max-idle="100000"/>
</invalidation-cache>
<replicated-cache name="timestamps"/>
</cache-container>
</subsystem>
<subsystem xmlns="urn:jboss:domain:io:3.0">
<worker name="default"/>
<buffer-pool name="default"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:jaxrs:2.0"/>
<subsystem xmlns="urn:jboss:domain:jca:5.0">
<archive-validation enabled="true" fail-on-error="true" fail-on-warn="false"/>
<bean-validation enabled="true"/>
<default-workmanager>
<short-running-threads>
<core-threads count="50"/>
<queue-length count="50"/>
<max-threads count="50"/>
<keepalive-time time="10" unit="seconds"/>
</short-running-threads>
<long-running-threads>
<core-threads count="50"/>
<queue-length count="50"/>
<max-threads count="50"/>
<keepalive-time time="10" unit="seconds"/>
</long-running-threads>
</default-workmanager>
<cached-connection-manager/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:jgroups:8.0">
<channels default="ee">
<channel name="ee" stack="tcp" cluster="ejb"/>
</channels>
<stacks>
<stack name="tcp">
<transport site="${jboss.node.name}" type="TCP" socket-binding="jgroups-tcp"/>
{% if keycloak_jdbc[keycloak_jdbc_engine].enabled %}
<protocol type="JDBC_PING">
<property name="datasource_jndi_name">java:jboss/datasources/KeycloakDS</property>
<property name="initialize_sql">{{ keycloak_jdbc[keycloak_jdbc_engine].initialize_db }}</property>
<property name="insert_single_sql">INSERT INTO JGROUPSPING (own_addr, cluster_name, ping_data) values (?, ?, ?)</property>
<property name="delete_single_sql">DELETE FROM JGROUPSPING WHERE own_addr=? AND cluster_name=?</property>
<property name="select_all_pingdata_sql">SELECT ping_data FROM JGROUPSPING WHERE cluster_name=?</property>
</protocol>
{% endif %}
<protocol type="MERGE3"/>
<protocol type="FD_SOCK"/>
<protocol type="FD_ALL"/>
<protocol type="VERIFY_SUSPECT"/>
<protocol type="pbcast.NAKACK2"/>
<protocol type="UNICAST3"/>
<protocol type="pbcast.STABLE"/>
<protocol type="pbcast.GMS">
<property name="join_timeout">30000</property>
</protocol>
<protocol type="MFC"/>
<protocol type="FRAG2"/>
</stack>
</stacks>
</subsystem>
<subsystem xmlns="urn:jboss:domain:jmx:1.3">
<expose-resolved-model/>
<expose-expression-model/>
<remoting-connector/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:jpa:1.1">
<jpa default-extended-persistence-inheritance="DEEP"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
<web-context>auth</web-context>
<providers>
<provider>
classpath:${jboss.home.dir}/providers/*
</provider>
</providers>
<master-realm-name>master</master-realm-name>
<scheduled-task-interval>900</scheduled-task-interval>
<theme>
<staticMaxAge>2592000</staticMaxAge>
<cacheThemes>true</cacheThemes>
<cacheTemplates>true</cacheTemplates>
<dir>${jboss.home.dir}/themes</dir>
</theme>
{% if keycloak_ha_enabled %}
<spi name="dblock">
<provider name="jpa" enabled="true">
<properties>
<property name="lockWaitTimeout" value="900"/>
</properties>
</provider>
</spi>
{% endif %}
<spi name="eventsStore">
<provider name="jpa" enabled="true">
<properties>
<property name="exclude-events" value="[&quot;REFRESH_TOKEN&quot;]"/>
</properties>
</provider>
</spi>
<spi name="userCache">
<provider name="default" enabled="true"/>
</spi>
<spi name="userSessionPersister">
<default-provider>jpa</default-provider>
</spi>
<spi name="timer">
<default-provider>basic</default-provider>
</spi>
<spi name="connectionsHttpClient">
<provider name="default" enabled="true"/>
</spi>
<spi name="connectionsJpa">
<provider name="default" enabled="true">
<properties>
<property name="dataSource" value="java:jboss/datasources/KeycloakDS"/>
<property name="initializeEmpty" value="true"/>
<property name="migrationStrategy" value="update"/>
<property name="migrationExport" value="${jboss.home.dir}/keycloak-database-update.sql"/>
</properties>
</provider>
</spi>
<spi name="realmCache">
<provider name="default" enabled="true"/>
</spi>
<spi name="connectionsInfinispan">
<default-provider>default</default-provider>
<provider name="default" enabled="true">
<properties>
<property name="cacheContainer" value="java:jboss/infinispan/container/keycloak"/>
</properties>
</provider>
</spi>
<spi name="jta-lookup">
<default-provider>${keycloak.jta.lookup.provider:jboss}</default-provider>
<provider name="jboss" enabled="true"/>
</spi>
<spi name="publicKeyStorage">
<provider name="infinispan" enabled="true">
<properties>
<property name="minTimeBetweenRequests" value="10"/>
</properties>
</provider>
</spi>
<spi name="x509cert-lookup">
<default-provider>${keycloak.x509cert.lookup.provider:default}</default-provider>
<provider name="default" enabled="true"/>
</spi>
<spi name="hostname">
<default-provider>default</default-provider>
<provider name="default" enabled="true">
<properties>
<property name="frontendUrl" value="{{ keycloak_modcluster.frontend_url }}"/>
<property name="forceBackendUrlToFrontendUrl" value="true"/>
</properties>
</provider>
</spi>
</subsystem>
<subsystem xmlns="urn:jboss:domain:mail:4.0">
<mail-session name="default" jndi-name="java:jboss/mail/Default">
<smtp-server outbound-socket-binding-ref="mail-smtp"/>
</mail-session>
</subsystem>
<subsystem xmlns="urn:wildfly:metrics:1.0" security-enabled="false" exposed-subsystems="*" prefix="${wildfly.metrics.prefix:jboss}"/>
{% if keycloak_modcluster.enabled %}
<subsystem xmlns="urn:jboss:domain:modcluster:5.0">
<proxy name="default" advertise="false" listener="ajp" proxies="proxy1">
<dynamic-load-provider>
<load-metric type="cpu"/>
</dynamic-load-provider>
</proxy>
</subsystem>
{% endif %}
<subsystem xmlns="urn:jboss:domain:naming:2.0">
<remote-naming/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:remoting:4.0">
<http-connector name="http-remoting-connector" connector-ref="default" security-realm="ApplicationRealm"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:request-controller:1.0"/>
<subsystem xmlns="urn:jboss:domain:security:2.0">
<security-domains>
<security-domain name="other" cache-type="default">
<authentication>
<login-module code="Remoting" flag="optional">
<module-option name="password-stacking" value="useFirstPass"/>
</login-module>
<login-module code="RealmDirect" flag="required">
<module-option name="password-stacking" value="useFirstPass"/>
</login-module>
</authentication>
</security-domain>
<security-domain name="jboss-web-policy" cache-type="default">
<authorization>
<policy-module code="Delegating" flag="required"/>
</authorization>
</security-domain>
<security-domain name="jaspitest" cache-type="default">
<authentication-jaspi>
<login-module-stack name="dummy">
<login-module code="Dummy" flag="optional"/>
</login-module-stack>
<auth-module code="Dummy"/>
</authentication-jaspi>
</security-domain>
<security-domain name="jboss-ejb-policy" cache-type="default">
<authorization>
<policy-module code="Delegating" flag="required"/>
</authorization>
</security-domain>
</security-domains>
</subsystem>
<subsystem xmlns="urn:jboss:domain:security-manager:1.0">
<deployment-permissions>
<maximum-set>
<permission class="java.security.AllPermission"/>
</maximum-set>
</deployment-permissions>
</subsystem>
<subsystem xmlns="urn:jboss:domain:transactions:6.0">
<core-environment node-identifier="{{ inventory_hostname | default('${jboss.tx.node.id:1}') }}">
<process-id>
<uuid/>
</process-id>
</core-environment>
<recovery-environment socket-binding="txn-recovery-environment" status-socket-binding="txn-status-manager"/>
<coordinator-environment statistics-enabled="${wildfly.transactions.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
<object-store path="tx-object-store" relative-to="jboss.server.data.dir"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:undertow:12.0" default-server="default-server" default-virtual-host="default-host" default-servlet-container="default" default-security-domain="other" statistics-enabled="${wildfly.undertow.statistics-enabled:${wildfly.statistics-enabled:false}}">
<buffer-cache name="default"/>
<server name="default-server">
<ajp-listener name="ajp" socket-binding="ajp"/>
<http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true" proxy-address-forwarding="true"/>
<https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/>
<host name="default-host" alias="localhost">
<location name="/" handler="welcome-content"/>
<http-invoker security-realm="ApplicationRealm"/>
<filter-ref name="proxy-peer"/>
</host>
</server>
<servlet-container name="default">
<jsp-config/>
<websockets/>
</servlet-container>
<handlers>
<file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
</handlers>
<filters>
<filter name="proxy-peer" module="io.undertow.core"
class-name="io.undertow.server.handlers.ProxyPeerAddressHandler"/>
</filters>
</subsystem>
<subsystem xmlns="urn:jboss:domain:weld:4.0"/>
</profile>
<interfaces>
<interface name="management">
<inet-address value="{{ keycloak_management_port_bind_address }}"/>
</interface>
<interface name="jgroups">
{% if ansible_default_ipv4 is defined %}
<subnet-match value="{{ (ansible_default_ipv4.network + '/' + ansible_default_ipv4.netmask) | ansible.utils.ipaddr('net') }}"/>
{% else %}
<any-address />
{% endif %}
</interface>
<interface name="public">
<inet-address value="{{ keycloak_bind_address }}"/>
</interface>
</interfaces>
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
<socket-binding name="ajp" port="{{ keycloak_ajp_port }}"/>
<socket-binding name="http" port="{{ keycloak_http_port }}"/>
<socket-binding name="https" port="{{ keycloak_https_port }}"/>
<socket-binding name="management-http" interface="management" port="{{ keycloak_management_http_port }}"/>
<socket-binding name="management-https" interface="management" port="{{ keycloak_management_https_port }}"/>
<socket-binding name="jgroups-tcp" interface="jgroups" port="{{ keycloak_jgroups_port }}"/>
<socket-binding name="txn-recovery-environment" port="4712"/>
<socket-binding name="txn-status-manager" port="4713"/>
<outbound-socket-binding name="mail-smtp">
<remote-destination host="${jboss.mail.server.host:localhost}" port="${jboss.mail.server.port:25}"/>
</outbound-socket-binding>
{% if keycloak_modcluster.enabled %}
<outbound-socket-binding name="proxy1">
<remote-destination host="{{ keycloak_modcluster.reverse_proxy_url | default('localhost') }}" port="6666"/>
</outbound-socket-binding>
{% endif %}
<outbound-socket-binding name="remote-cache">
<remote-destination host="{{ keycloak_remotecache.server_name | default('localhost') }}" port="${remote.cache.port:11222}"/>
</outbound-socket-binding>
</socket-binding-group>
</server>

View File

@@ -0,0 +1,658 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- {{ ansible_managed }} -->
<server xmlns="urn:jboss:domain:16.0">
<extensions>
<extension module="org.jboss.as.clustering.infinispan"/>
<extension module="org.jboss.as.connector"/>
<extension module="org.jboss.as.deployment-scanner"/>
<extension module="org.jboss.as.ee"/>
<extension module="org.jboss.as.ejb3"/>
<extension module="org.jboss.as.jaxrs"/>
<extension module="org.jboss.as.jmx"/>
<extension module="org.jboss.as.jpa"/>
<extension module="org.jboss.as.logging"/>
<extension module="org.jboss.as.mail"/>
<extension module="org.jboss.as.modcluster"/>
<extension module="org.jboss.as.naming"/>
<extension module="org.jboss.as.remoting"/>
<extension module="org.jboss.as.security"/>
<extension module="org.jboss.as.transactions"/>
<extension module="org.jboss.as.weld"/>
<extension module="org.keycloak.keycloak-server-subsystem"/>
<extension module="org.wildfly.extension.bean-validation"/>
<extension module="org.wildfly.extension.core-management"/>
<extension module="org.wildfly.extension.elytron"/>
<extension module="org.wildfly.extension.health"/>
<extension module="org.wildfly.extension.io"/>
<extension module="org.wildfly.extension.metrics"/>
<extension module="org.wildfly.extension.request-controller"/>
<extension module="org.wildfly.extension.security.manager"/>
<extension module="org.wildfly.extension.undertow"/>
</extensions>
<management>
<security-realms>
<security-realm name="ManagementRealm">
<authentication>
<local default-user="$local" skip-group-loading="true"/>
<properties path="mgmt-users.properties" relative-to="jboss.server.config.dir"/>
</authentication>
<authorization map-groups-to-roles="false">
<properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
</authorization>
</security-realm>
<security-realm name="ApplicationRealm">
<server-identities>
<ssl>
<keystore path="application.keystore" relative-to="jboss.server.config.dir" keystore-password="password" alias="server" key-password="password" generate-self-signed-certificate-host="localhost"/>
</ssl>
</server-identities>
<authentication>
<local default-user="$local" allowed-users="*" skip-group-loading="true"/>
<properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
</authentication>
<authorization>
<properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
</authorization>
</security-realm>
</security-realms>
<audit-log>
<formatters>
<json-formatter name="json-formatter"/>
</formatters>
<handlers>
<file-handler name="file" formatter="json-formatter" path="audit-log.log" relative-to="jboss.server.data.dir"/>
</handlers>
<logger log-boot="true" log-read-only="false" enabled="false">
<handlers>
<handler name="file"/>
</handlers>
</logger>
</audit-log>
<management-interfaces>
<http-interface security-realm="ManagementRealm">
<http-upgrade enabled="true"/>
<socket-binding http="management-http"/>
</http-interface>
</management-interfaces>
<access-control provider="simple">
<role-mapping>
<role name="SuperUser">
<include>
<user name="$local"/>
</include>
</role>
</role-mapping>
</access-control>
</management>
<profile>
<subsystem xmlns="urn:jboss:domain:logging:8.0">
<console-handler name="CONSOLE">
<level name="INFO"/>
<formatter>
<named-formatter name="COLOR-PATTERN"/>
</formatter>
</console-handler>
<periodic-rotating-file-handler name="FILE" autoflush="true">
<formatter>
<named-formatter name="PATTERN"/>
</formatter>
<file relative-to="jboss.server.log.dir" path="server.log"/>
<suffix value=".yyyy-MM-dd"/>
<append value="true"/>
</periodic-rotating-file-handler>
<logger category="com.arjuna">
<level name="WARN"/>
</logger>
<logger category="io.jaegertracing.Configuration">
<level name="WARN"/>
</logger>
<logger category="org.jboss.as.config">
<level name="DEBUG"/>
</logger>
<logger category="sun.rmi">
<level name="WARN"/>
</logger>
<root-logger>
<level name="INFO"/>
<handlers>
<handler name="CONSOLE"/>
<handler name="FILE"/>
</handlers>
</root-logger>
<formatter name="PATTERN">
<pattern-formatter pattern="%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
</formatter>
<formatter name="COLOR-PATTERN">
<pattern-formatter pattern="%K{level}%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
</formatter>
</subsystem>
<subsystem xmlns="urn:jboss:domain:bean-validation:1.0"/>
<subsystem xmlns="urn:jboss:domain:core-management:1.0"/>
<subsystem xmlns="urn:jboss:domain:datasources:6.0">
<datasources>
<datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
<connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url>
<driver>h2</driver>
<security>
<user-name>sa</user-name>
<password>sa</password>
</security>
</datasource>
<datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
{% if keycloak_jdbc[keycloak_jdbc_engine].enabled %}
<connection-url>{{ keycloak_jdbc[keycloak_jdbc_engine].connection_url }}</connection-url>
<driver>{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}</driver>
<pool>
<max-pool-size>20</max-pool-size>
</pool>
<security>
<user-name>{{ keycloak_jdbc[keycloak_jdbc_engine].db_user }}</user-name>
<password>{{ keycloak_jdbc[keycloak_jdbc_engine].db_password }}</password>
</security>
{% else %}
<connection-url>jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE</connection-url>
<driver>h2</driver>
<security>
<user-name>sa</user-name>
<password>sa</password>
</security>
{% endif %}
</datasource>
<drivers>
{% if keycloak_jdbc[keycloak_jdbc_engine].enabled %}
<driver name="{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}" module="{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}">
<driver-class>{{ keycloak_jdbc[keycloak_jdbc_engine].driver_class }}</driver-class>
<xa-datasource-class>{{ keycloak_jdbc[keycloak_jdbc_engine].xa_datasource_class }}</xa-datasource-class>
</driver>
{% endif %}
<driver name="h2" module="com.h2database.h2">
<xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
</driver>
</drivers>
</datasources>
</subsystem>
<subsystem xmlns="urn:jboss:domain:deployment-scanner:2.0">
<deployment-scanner path="deployments" relative-to="jboss.server.base.dir" scan-interval="5000" runtime-failure-causes-rollback="${jboss.deployment.scanner.rollback.on.failure:false}"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:ee:6.0">
<spec-descriptor-property-replacement>false</spec-descriptor-property-replacement>
<concurrent>
<context-services>
<context-service name="default" jndi-name="java:jboss/ee/concurrency/context/default" use-transaction-setup-provider="true"/>
</context-services>
<managed-thread-factories>
<managed-thread-factory name="default" jndi-name="java:jboss/ee/concurrency/factory/default" context-service="default"/>
</managed-thread-factories>
<managed-executor-services>
<managed-executor-service name="default" jndi-name="java:jboss/ee/concurrency/executor/default" context-service="default" hung-task-termination-period="0" hung-task-threshold="60000" keepalive-time="5000"/>
</managed-executor-services>
<managed-scheduled-executor-services>
<managed-scheduled-executor-service name="default" jndi-name="java:jboss/ee/concurrency/scheduler/default" context-service="default" hung-task-termination-period="0" hung-task-threshold="60000" keepalive-time="3000"/>
</managed-scheduled-executor-services>
</concurrent>
<default-bindings context-service="java:jboss/ee/concurrency/context/default" datasource="java:jboss/datasources/ExampleDS" managed-executor-service="java:jboss/ee/concurrency/executor/default" managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default" managed-thread-factory="java:jboss/ee/concurrency/factory/default"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:ejb3:9.0">
<session-bean>
<stateless>
<bean-instance-pool-ref pool-name="slsb-strict-max-pool"/>
</stateless>
<stateful default-access-timeout="5000" cache-ref="simple" passivation-disabled-cache-ref="simple"/>
<singleton default-access-timeout="5000"/>
</session-bean>
<pools>
<bean-instance-pools>
<strict-max-pool name="mdb-strict-max-pool" derive-size="from-cpu-count" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
<strict-max-pool name="slsb-strict-max-pool" derive-size="from-worker-pools" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
</bean-instance-pools>
</pools>
<caches>
<cache name="simple"/>
<cache name="distributable" passivation-store-ref="infinispan" aliases="passivating clustered"/>
</caches>
<passivation-stores>
<passivation-store name="infinispan" cache-container="ejb" max-size="10000"/>
</passivation-stores>
<async thread-pool-name="default"/>
<timer-service thread-pool-name="default" default-data-store="default-file-store">
<data-stores>
<file-data-store name="default-file-store" path="timer-service-data" relative-to="jboss.server.data.dir"/>
</data-stores>
</timer-service>
<remote cluster="ejb" connectors="http-remoting-connector" thread-pool-name="default">
<channel-creation-options>
<option name="MAX_OUTBOUND_MESSAGES" value="1234" type="remoting"/>
</channel-creation-options>
</remote>
<thread-pools>
<thread-pool name="default">
<max-threads count="10"/>
<keepalive-time time="60" unit="seconds"/>
</thread-pool>
</thread-pools>
<default-security-domain value="other"/>
<default-missing-method-permissions-deny-access value="true"/>
<statistics enabled="${wildfly.ejb3.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
<log-system-exceptions value="true"/>
</subsystem>
<subsystem xmlns="urn:wildfly:elytron:13.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
<providers>
<aggregate-providers name="combined-providers">
<providers name="elytron"/>
<providers name="openssl"/>
</aggregate-providers>
<provider-loader name="elytron" module="org.wildfly.security.elytron"/>
<provider-loader name="openssl" module="org.wildfly.openssl"/>
</providers>
<audit-logging>
<file-audit-log name="local-audit" path="audit.log" relative-to="jboss.server.log.dir" format="JSON"/>
</audit-logging>
<security-domains>
<security-domain name="ApplicationDomain" default-realm="ApplicationRealm" permission-mapper="default-permission-mapper">
<realm name="ApplicationRealm" role-decoder="groups-to-roles"/>
<realm name="local"/>
</security-domain>
<security-domain name="ManagementDomain" default-realm="ManagementRealm" permission-mapper="default-permission-mapper">
<realm name="ManagementRealm" role-decoder="groups-to-roles"/>
<realm name="local" role-mapper="super-user-mapper"/>
</security-domain>
</security-domains>
<security-realms>
<identity-realm name="local" identity="$local"/>
<properties-realm name="ApplicationRealm">
<users-properties path="application-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ApplicationRealm"/>
<groups-properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
</properties-realm>
<properties-realm name="ManagementRealm">
<users-properties path="mgmt-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ManagementRealm"/>
<groups-properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
</properties-realm>
</security-realms>
<mappers>
<simple-permission-mapper name="default-permission-mapper" mapping-mode="first">
<permission-mapping>
<principal name="anonymous"/>
<permission-set name="default-permissions"/>
</permission-mapping>
<permission-mapping match-all="true">
<permission-set name="login-permission"/>
<permission-set name="default-permissions"/>
</permission-mapping>
</simple-permission-mapper>
<constant-realm-mapper name="local" realm-name="local"/>
<simple-role-decoder name="groups-to-roles" attribute="groups"/>
<constant-role-mapper name="super-user-mapper">
<role name="SuperUser"/>
</constant-role-mapper>
</mappers>
<permission-sets>
<permission-set name="login-permission">
<permission class-name="org.wildfly.security.auth.permission.LoginPermission"/>
</permission-set>
<permission-set name="default-permissions">
<permission class-name="org.wildfly.extension.batch.jberet.deployment.BatchPermission" module="org.wildfly.extension.batch.jberet" target-name="*"/>
<permission class-name="org.wildfly.transaction.client.RemoteTransactionPermission" module="org.wildfly.transaction.client"/>
<permission class-name="org.jboss.ejb.client.RemoteEJBPermission" module="org.jboss.ejb-client"/>
<permission class-name="org.jboss.ejb.client.RemoteEJBPermission" module="org.jboss.ejb-client"/>
</permission-set>
</permission-sets>
<http>
<http-authentication-factory name="management-http-authentication" security-domain="ManagementDomain" http-server-mechanism-factory="global">
<mechanism-configuration>
<mechanism mechanism-name="DIGEST">
<mechanism-realm realm-name="ManagementRealm"/>
</mechanism>
</mechanism-configuration>
</http-authentication-factory>
<provider-http-server-mechanism-factory name="global"/>
</http>
<sasl>
<sasl-authentication-factory name="application-sasl-authentication" sasl-server-factory="configured" security-domain="ApplicationDomain">
<mechanism-configuration>
<mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
<mechanism mechanism-name="DIGEST-MD5">
<mechanism-realm realm-name="ApplicationRealm"/>
</mechanism>
</mechanism-configuration>
</sasl-authentication-factory>
<sasl-authentication-factory name="management-sasl-authentication" sasl-server-factory="configured" security-domain="ManagementDomain">
<mechanism-configuration>
<mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
<mechanism mechanism-name="DIGEST-MD5">
<mechanism-realm realm-name="ManagementRealm"/>
</mechanism>
</mechanism-configuration>
</sasl-authentication-factory>
<configurable-sasl-server-factory name="configured" sasl-server-factory="elytron">
<properties>
<property name="wildfly.sasl.local-user.default-user" value="$local"/>
</properties>
</configurable-sasl-server-factory>
<mechanism-provider-filtering-sasl-server-factory name="elytron" sasl-server-factory="global">
<filters>
<filter provider-name="WildFlyElytron"/>
</filters>
</mechanism-provider-filtering-sasl-server-factory>
<provider-sasl-server-factory name="global"/>
</sasl>
<tls>
<key-stores>
<key-store name="applicationKS">
<credential-reference clear-text="password"/>
<implementation type="JKS"/>
<file path="application.keystore" relative-to="jboss.server.config.dir"/>
</key-store>
</key-stores>
<key-managers>
<key-manager name="applicationKM" key-store="applicationKS" generate-self-signed-certificate-host="localhost">
<credential-reference clear-text="password"/>
</key-manager>
</key-managers>
<server-ssl-contexts>
<server-ssl-context name="applicationSSC" key-manager="applicationKM"/>
</server-ssl-contexts>
</tls>
</subsystem>
<subsystem xmlns="urn:wildfly:health:1.0" security-enabled="false"/>
<subsystem xmlns="urn:jboss:domain:infinispan:12.0">
<cache-container name="ejb" default-cache="passivation" aliases="sfsb" modules="org.wildfly.clustering.ejb.infinispan">
<local-cache name="passivation">
<locking isolation="REPEATABLE_READ"/>
<transaction mode="BATCH"/>
<file-store passivation="true" purge="false"/>
</local-cache>
</cache-container>
<cache-container name="keycloak" modules="org.keycloak.keycloak-model-infinispan">
<local-cache name="realms">
<heap-memory size="10000"/>
</local-cache>
<local-cache name="users">
<heap-memory size="10000"/>
</local-cache>
<local-cache name="sessions"/>
<local-cache name="authenticationSessions"/>
<local-cache name="offlineSessions"/>
<local-cache name="clientSessions"/>
<local-cache name="offlineClientSessions"/>
<local-cache name="loginFailures"/>
<local-cache name="work"/>
<local-cache name="authorization">
<heap-memory size="10000"/>
</local-cache>
<local-cache name="keys">
<heap-memory size="1000"/>
<expiration max-idle="3600000"/>
</local-cache>
<local-cache name="actionTokens">
<heap-memory size="-1"/>
<expiration interval="300000" max-idle="-1"/>
</local-cache>
</cache-container>
<cache-container name="server" default-cache="default" modules="org.wildfly.clustering.server">
<local-cache name="default">
<transaction mode="BATCH"/>
</local-cache>
</cache-container>
<cache-container name="web" default-cache="passivation" modules="org.wildfly.clustering.web.infinispan">
<local-cache name="passivation">
<locking isolation="REPEATABLE_READ"/>
<transaction mode="BATCH"/>
<file-store passivation="true" purge="false"/>
</local-cache>
<local-cache name="sso">
<locking isolation="REPEATABLE_READ"/>
<transaction mode="BATCH"/>
</local-cache>
<local-cache name="routing"/>
</cache-container>
<cache-container name="hibernate" modules="org.infinispan.hibernate-cache">
<local-cache name="entity">
<heap-memory size="10000"/>
<expiration max-idle="100000"/>
</local-cache>
<local-cache name="local-query">
<heap-memory size="10000"/>
<expiration max-idle="100000"/>
</local-cache>
<local-cache name="timestamps"/>
</cache-container>
</subsystem>
<subsystem xmlns="urn:jboss:domain:io:3.0">
<worker name="default"/>
<buffer-pool name="default"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:jaxrs:2.0"/>
<subsystem xmlns="urn:jboss:domain:jca:5.0">
<archive-validation enabled="true" fail-on-error="true" fail-on-warn="false"/>
<bean-validation enabled="true"/>
<default-workmanager>
<short-running-threads>
<core-threads count="50"/>
<queue-length count="50"/>
<max-threads count="50"/>
<keepalive-time time="10" unit="seconds"/>
</short-running-threads>
<long-running-threads>
<core-threads count="50"/>
<queue-length count="50"/>
<max-threads count="50"/>
<keepalive-time time="10" unit="seconds"/>
</long-running-threads>
</default-workmanager>
<cached-connection-manager/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:jmx:1.3">
<expose-resolved-model/>
<expose-expression-model/>
<remoting-connector/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:jpa:1.1">
<jpa default-extended-persistence-inheritance="DEEP"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
<web-context>auth</web-context>
<providers>
<provider>
classpath:${jboss.home.dir}/providers/*
</provider>
</providers>
<master-realm-name>master</master-realm-name>
<scheduled-task-interval>900</scheduled-task-interval>
<theme>
<staticMaxAge>2592000</staticMaxAge>
<cacheThemes>true</cacheThemes>
<cacheTemplates>true</cacheTemplates>
<dir>${jboss.home.dir}/themes</dir>
</theme>
{% if keycloak_ha_enabled %}
<spi name="dblock">
<provider name="jpa" enabled="true">
<properties>
<property name="lockWaitTimeout" value="900"/>
</properties>
</provider>
</spi>
{% endif %}
<spi name="eventsStore">
<provider name="jpa" enabled="true">
<properties>
<property name="exclude-events" value="[&quot;REFRESH_TOKEN&quot;]"/>
</properties>
</provider>
</spi>
<spi name="userCache">
<provider name="default" enabled="true"/>
</spi>
<spi name="userSessionPersister">
<default-provider>jpa</default-provider>
</spi>
<spi name="timer">
<default-provider>basic</default-provider>
</spi>
<spi name="connectionsHttpClient">
<provider name="default" enabled="true"/>
</spi>
<spi name="connectionsJpa">
<provider name="default" enabled="true">
<properties>
<property name="dataSource" value="java:jboss/datasources/KeycloakDS"/>
<property name="initializeEmpty" value="true"/>
<property name="migrationStrategy" value="update"/>
<property name="migrationExport" value="${jboss.home.dir}/keycloak-database-update.sql"/>
</properties>
</provider>
</spi>
<spi name="realmCache">
<provider name="default" enabled="true"/>
</spi>
<spi name="connectionsInfinispan">
<default-provider>default</default-provider>
<provider name="default" enabled="true">
<properties>
<property name="cacheContainer" value="java:jboss/infinispan/container/keycloak"/>
</properties>
</provider>
</spi>
<spi name="jta-lookup">
<default-provider>${keycloak.jta.lookup.provider:jboss}</default-provider>
<provider name="jboss" enabled="true"/>
</spi>
<spi name="publicKeyStorage">
<provider name="infinispan" enabled="true">
<properties>
<property name="minTimeBetweenRequests" value="10"/>
</properties>
</provider>
</spi>
<spi name="x509cert-lookup">
<default-provider>${keycloak.x509cert.lookup.provider:default}</default-provider>
<provider name="default" enabled="true"/>
</spi>
<spi name="hostname">
<default-provider>default</default-provider>
<provider name="default" enabled="true">
<properties>
<property name="frontendUrl" value="{{ keycloak_modcluster.frontend_url }}"/>
<property name="forceBackendUrlToFrontendUrl" value="true"/>
</properties>
</provider>
</spi>
</subsystem>
<subsystem xmlns="urn:jboss:domain:mail:4.0">
<mail-session name="default" jndi-name="java:jboss/mail/Default">
<smtp-server outbound-socket-binding-ref="mail-smtp"/>
</mail-session>
</subsystem>
<subsystem xmlns="urn:wildfly:metrics:1.0" security-enabled="false" exposed-subsystems="*" prefix="${wildfly.metrics.prefix:jboss}"/>
{% if keycloak_modcluster.enabled %}
<subsystem xmlns="urn:jboss:domain:modcluster:5.0">
<proxy name="default" advertise="false" listener="ajp" proxies="proxy1">
<dynamic-load-provider>
<load-metric type="cpu"/>
</dynamic-load-provider>
</proxy>
</subsystem>
{% endif %}
<subsystem xmlns="urn:jboss:domain:naming:2.0">
<remote-naming/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:remoting:4.0">
<http-connector name="http-remoting-connector" connector-ref="default" security-realm="ApplicationRealm"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:request-controller:1.0"/>
<subsystem xmlns="urn:jboss:domain:security:2.0">
<security-domains>
<security-domain name="other" cache-type="default">
<authentication>
<login-module code="Remoting" flag="optional">
<module-option name="password-stacking" value="useFirstPass"/>
</login-module>
<login-module code="RealmDirect" flag="required">
<module-option name="password-stacking" value="useFirstPass"/>
</login-module>
</authentication>
</security-domain>
<security-domain name="jboss-web-policy" cache-type="default">
<authorization>
<policy-module code="Delegating" flag="required"/>
</authorization>
</security-domain>
<security-domain name="jaspitest" cache-type="default">
<authentication-jaspi>
<login-module-stack name="dummy">
<login-module code="Dummy" flag="optional"/>
</login-module-stack>
<auth-module code="Dummy"/>
</authentication-jaspi>
</security-domain>
<security-domain name="jboss-ejb-policy" cache-type="default">
<authorization>
<policy-module code="Delegating" flag="required"/>
</authorization>
</security-domain>
</security-domains>
</subsystem>
<subsystem xmlns="urn:jboss:domain:security-manager:1.0">
<deployment-permissions>
<maximum-set>
<permission class="java.security.AllPermission"/>
</maximum-set>
</deployment-permissions>
</subsystem>
<subsystem xmlns="urn:jboss:domain:transactions:6.0">
<core-environment node-identifier="${jboss.tx.node.id:1}">
<process-id>
<uuid/>
</process-id>
</core-environment>
<recovery-environment socket-binding="txn-recovery-environment" status-socket-binding="txn-status-manager"/>
<coordinator-environment statistics-enabled="${wildfly.transactions.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
<object-store path="tx-object-store" relative-to="jboss.server.data.dir"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:undertow:12.0" default-server="default-server" default-virtual-host="default-host" default-servlet-container="default" default-security-domain="other" statistics-enabled="${wildfly.undertow.statistics-enabled:${wildfly.statistics-enabled:false}}">
<buffer-cache name="default"/>
<server name="default-server">
<ajp-listener name="ajp" socket-binding="ajp"/>
<http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/>
<https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/>
<host name="default-host" alias="localhost">
<location name="/" handler="welcome-content"/>
<http-invoker security-realm="ApplicationRealm"/>
</host>
</server>
<servlet-container name="default">
<jsp-config/>
<websockets/>
</servlet-container>
<handlers>
<file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
</handlers>
</subsystem>
<subsystem xmlns="urn:jboss:domain:weld:4.0"/>
</profile>
<interfaces>
<interface name="management">
<inet-address value="{{ keycloak_management_port_bind_address }}"/>
</interface>
<interface name="public">
<inet-address value="{{ keycloak_bind_address }}"/>
</interface>
</interfaces>
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
<socket-binding name="ajp" port="{{ keycloak_ajp_port }}"/>
<socket-binding name="http" port="{{ keycloak_http_port }}"/>
<socket-binding name="https" port="{{ keycloak_https_port }}"/>
<socket-binding name="management-http" interface="management" port="{{ keycloak_management_http_port }}"/>
<socket-binding name="management-https" interface="management" port="{{ keycloak_management_https_port }}"/>
<socket-binding name="txn-recovery-environment" port="4712"/>
<socket-binding name="txn-status-manager" port="4713"/>
<outbound-socket-binding name="mail-smtp">
<remote-destination host="${jboss.mail.server.host:localhost}" port="${jboss.mail.server.port:25}"/>
</outbound-socket-binding>
{% if keycloak_modcluster.enabled %}
<outbound-socket-binding name="proxy1">
<remote-destination host="{{ keycloak_modcluster.reverse_proxy_url | default('localhost') }}" port="6666"/>
</outbound-socket-binding>
{% endif %}
</socket-binding-group>
</server>

View File

@@ -725,7 +725,7 @@
</interface>
<interface name="jgroups">
{% if ansible_default_ipv4 is defined %}
<subnet-match value="{{ (ansible_default_ipv4.network + '/' + ansible_default_ipv4.netmask) | ipaddr('net') }}"/>
<subnet-match value="{{ (ansible_default_ipv4.network + '/' + ansible_default_ipv4.netmask) | ansible.utils.ipaddr('net') }}"/>
{% else %}
<any-address />
{% endif %}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.0" name="{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}">
<resources>
<resource-root path="{{ keycloak_jdbc[keycloak_jdbc_engine].driver_jar_filename }}"/>
</resources>
<dependencies>
<module name="javax.api"/>
<module name="javax.transaction.api"/>
</dependencies>
</module>

View File

@@ -0,0 +1,3 @@
{% for feature in keycloak.features %}
feature.{{ feature.name }}={{ feature.status | default('enabled') }}
{% endfor %}

View File

@@ -2,6 +2,9 @@
[Unit]
Description={{ keycloak.service_name }} Server
After=network.target
StartLimitIntervalSec={{ keycloak_service_startlimitintervalsec }}
StartLimitBurst={{ keycloak_service_startlimitburst }}
[Service]
Type=forking
@@ -12,6 +15,12 @@ ExecStop={{ keycloak_dest }}/keycloak-service.sh stop
TimeoutStartSec=30
TimeoutStopSec=30
LimitNOFILE=102642
{% if keycloak_service_restart_always %}
Restart=always
{% elif keycloak_service_restart_on_failure %}
Restart=on-failure
{% endif %}
RestartSec={{ keycloak_service_restartsec }}
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,707 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- {{ ansible_managed }} -->
<server xmlns="urn:jboss:domain:16.0">
<extensions>
<extension module="org.jboss.as.clustering.infinispan"/>
<extension module="org.jboss.as.clustering.jgroups"/>
<extension module="org.jboss.as.connector"/>
<extension module="org.jboss.as.deployment-scanner"/>
<extension module="org.jboss.as.ee"/>
<extension module="org.jboss.as.ejb3"/>
<extension module="org.jboss.as.jaxrs"/>
<extension module="org.jboss.as.jmx"/>
<extension module="org.jboss.as.jpa"/>
<extension module="org.jboss.as.logging"/>
<extension module="org.jboss.as.mail"/>
<extension module="org.jboss.as.modcluster"/>
<extension module="org.jboss.as.naming"/>
<extension module="org.jboss.as.remoting"/>
<extension module="org.jboss.as.transactions"/>
<extension module="org.jboss.as.weld"/>
<extension module="org.keycloak.keycloak-server-subsystem"/>
<extension module="org.wildfly.extension.bean-validation"/>
<extension module="org.wildfly.extension.core-management"/>
<extension module="org.wildfly.extension.elytron"/>
<extension module="org.wildfly.extension.health"/>
<extension module="org.wildfly.extension.io"/>
<extension module="org.wildfly.extension.metrics"/>
<extension module="org.wildfly.extension.request-controller"/>
<extension module="org.wildfly.extension.security.manager"/>
<extension module="org.wildfly.extension.undertow"/>
</extensions>
<management>
<audit-log>
<formatters>
<json-formatter name="json-formatter"/>
</formatters>
<handlers>
<file-handler name="file" formatter="json-formatter" path="audit-log.log" relative-to="jboss.server.data.dir"/>
</handlers>
<logger log-boot="true" log-read-only="false" enabled="false">
<handlers>
<handler name="file"/>
</handlers>
</logger>
</audit-log>
<management-interfaces>
<http-interface http-authentication-factory="management-http-authentication">
<http-upgrade enabled="true" sasl-authentication-factory="management-sasl-authentication"/>
<socket-binding http="management-http"/>
</http-interface>
</management-interfaces>
<access-control provider="simple">
<role-mapping>
<role name="SuperUser">
<include>
<user name="$local"/>
</include>
</role>
</role-mapping>
</access-control>
</management>
<profile>
<subsystem xmlns="urn:jboss:domain:logging:8.0">
<console-handler name="CONSOLE">
<level name="INFO"/>
<formatter>
<named-formatter name="COLOR-PATTERN"/>
</formatter>
</console-handler>
<periodic-rotating-file-handler name="FILE" autoflush="true">
<formatter>
<named-formatter name="PATTERN"/>
</formatter>
<file relative-to="jboss.server.log.dir" path="server.log"/>
<suffix value=".yyyy-MM-dd"/>
<append value="true"/>
</periodic-rotating-file-handler>
<logger category="com.arjuna">
<level name="WARN"/>
</logger>
<logger category="io.jaegertracing.Configuration">
<level name="WARN"/>
</logger>
<logger category="org.jboss.as.config">
<level name="DEBUG"/>
</logger>
<logger category="sun.rmi">
<level name="WARN"/>
</logger>
<logger category="org.keycloak.cluster.infinispan">
<level name="INFO"/>
</logger>
<logger category="org.keycloak.connections.infinispan">
<level name="INFO"/>
</logger>
<logger category="org.keycloak.models.cache.infinispan">
<level name="INFO"/>
</logger>
<logger category="org.keycloak.models.sessions.infinispan">
<level name="INFO"/>
</logger>
<root-logger>
<level name="INFO"/>
<handlers>
<handler name="CONSOLE"/>
<handler name="FILE"/>
</handlers>
</root-logger>
<formatter name="PATTERN">
<pattern-formatter pattern="%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
</formatter>
<formatter name="COLOR-PATTERN">
<pattern-formatter pattern="%K{level}%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
</formatter>
</subsystem>
<subsystem xmlns="urn:jboss:domain:bean-validation:1.0"/>
<subsystem xmlns="urn:jboss:domain:core-management:1.0"/>
<subsystem xmlns="urn:jboss:domain:datasources:6.0">
<datasources>
<datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
<connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url>
<driver>h2</driver>
<security>
<user-name>sa</user-name>
<password>sa</password>
</security>
</datasource>
<datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
{% if keycloak_jdbc[keycloak_jdbc_engine].enabled %}
<connection-url>{{ keycloak_jdbc[keycloak_jdbc_engine].connection_url }}</connection-url>
<driver>{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}</driver>
<pool>
<max-pool-size>20</max-pool-size>
</pool>
<security>
<user-name>{{ keycloak_jdbc[keycloak_jdbc_engine].db_user }}</user-name>
<password>{{ keycloak_jdbc[keycloak_jdbc_engine].db_password }}</password>
</security>
<validation>
<check-valid-connection-sql>{{ keycloak_jdbc[keycloak_jdbc_engine].validate_query }}</check-valid-connection-sql>
<validate-on-match>{{ keycloak_db_background_validate_on_match }}</validate-on-match>
{% if keycloak_db_background_validation_millis | int > 0 or keycloak_db_background_validation %}
<background-validation>{{ keycloak_db_background_validation }}</background-validation>
<background-validation-millis>{{ keycloak_db_background_validation_millis }}</background-validation-millis>
{% endif %}
</validation>
{% else %}
<connection-url>jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE</connection-url>
<driver>h2</driver>
<security>
<user-name>sa</user-name>
<password>sa</password>
</security>
{% endif %}
</datasource>
<drivers>
{% if keycloak_jdbc[keycloak_jdbc_engine].enabled %}
<driver name="{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}" module="{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}">
<driver-class>{{ keycloak_jdbc[keycloak_jdbc_engine].driver_class }}</driver-class>
<xa-datasource-class>{{ keycloak_jdbc[keycloak_jdbc_engine].xa_datasource_class }}</xa-datasource-class>
</driver>
{% endif %}
<driver name="h2" module="com.h2database.h2">
<xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
</driver>
</drivers>
</datasources>
</subsystem>
<subsystem xmlns="urn:jboss:domain:deployment-scanner:2.0">
<deployment-scanner path="deployments" relative-to="jboss.server.base.dir" scan-interval="5000" runtime-failure-causes-rollback="${jboss.deployment.scanner.rollback.on.failure:false}"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:ee:6.0">
<spec-descriptor-property-replacement>false</spec-descriptor-property-replacement>
<concurrent>
<context-services>
<context-service name="default" jndi-name="java:jboss/ee/concurrency/context/default" use-transaction-setup-provider="true"/>
</context-services>
<managed-thread-factories>
<managed-thread-factory name="default" jndi-name="java:jboss/ee/concurrency/factory/default" context-service="default"/>
</managed-thread-factories>
<managed-executor-services>
<managed-executor-service name="default" jndi-name="java:jboss/ee/concurrency/executor/default" context-service="default" hung-task-termination-period="0" hung-task-threshold="60000" keepalive-time="5000"/>
</managed-executor-services>
<managed-scheduled-executor-services>
<managed-scheduled-executor-service name="default" jndi-name="java:jboss/ee/concurrency/scheduler/default" context-service="default" hung-task-termination-period="0" hung-task-threshold="60000" keepalive-time="3000"/>
</managed-scheduled-executor-services>
</concurrent>
<default-bindings context-service="java:jboss/ee/concurrency/context/default" datasource="java:jboss/datasources/ExampleDS" managed-executor-service="java:jboss/ee/concurrency/executor/default" managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default" managed-thread-factory="java:jboss/ee/concurrency/factory/default"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:ejb3:9.0">
<session-bean>
<stateless>
<bean-instance-pool-ref pool-name="slsb-strict-max-pool"/>
</stateless>
<stateful default-access-timeout="5000" cache-ref="distributable" passivation-disabled-cache-ref="simple"/>
<singleton default-access-timeout="5000"/>
</session-bean>
<pools>
<bean-instance-pools>
<strict-max-pool name="mdb-strict-max-pool" derive-size="from-cpu-count" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
<strict-max-pool name="slsb-strict-max-pool" derive-size="from-worker-pools" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
</bean-instance-pools>
</pools>
<caches>
<cache name="simple"/>
<cache name="distributable" passivation-store-ref="infinispan" aliases="passivating clustered"/>
</caches>
<passivation-stores>
<passivation-store name="infinispan" cache-container="ejb" max-size="10000"/>
</passivation-stores>
<async thread-pool-name="default"/>
<timer-service thread-pool-name="default" default-data-store="default-file-store">
<data-stores>
<file-data-store name="default-file-store" path="timer-service-data" relative-to="jboss.server.data.dir"/>
</data-stores>
</timer-service>
<remote cluster="ejb" connectors="http-remoting-connector" thread-pool-name="default">
<channel-creation-options>
<option name="MAX_OUTBOUND_MESSAGES" value="1234" type="remoting"/>
</channel-creation-options>
</remote>
<thread-pools>
<thread-pool name="default">
<max-threads count="10"/>
<keepalive-time time="60" unit="seconds"/>
</thread-pool>
</thread-pools>
<default-security-domain value="other"/>
<application-security-domains>
<application-security-domain name="other" security-domain="ApplicationDomain"/>
</application-security-domains>
<default-missing-method-permissions-deny-access value="true"/>
<statistics enabled="${wildfly.ejb3.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
<log-system-exceptions value="true"/>
</subsystem>
<subsystem xmlns="urn:wildfly:elytron:13.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
<providers>
<aggregate-providers name="combined-providers">
<providers name="elytron"/>
<providers name="openssl"/>
</aggregate-providers>
<provider-loader name="elytron" module="org.wildfly.security.elytron"/>
<provider-loader name="openssl" module="org.wildfly.openssl"/>
</providers>
<audit-logging>
<file-audit-log name="local-audit" path="audit.log" relative-to="jboss.server.log.dir" format="JSON"/>
</audit-logging>
<security-domains>
<security-domain name="ApplicationDomain" default-realm="ApplicationRealm" permission-mapper="default-permission-mapper">
<realm name="ApplicationRealm" role-decoder="groups-to-roles"/>
<realm name="local"/>
</security-domain>
<security-domain name="ManagementDomain" default-realm="ManagementRealm" permission-mapper="default-permission-mapper">
<realm name="ManagementRealm" role-decoder="groups-to-roles"/>
<realm name="local" role-mapper="super-user-mapper"/>
</security-domain>
</security-domains>
<security-realms>
<identity-realm name="local" identity="$local"/>
<properties-realm name="ApplicationRealm">
<users-properties path="application-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ApplicationRealm"/>
<groups-properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
</properties-realm>
<properties-realm name="ManagementRealm">
<users-properties path="mgmt-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ManagementRealm"/>
<groups-properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
</properties-realm>
</security-realms>
<mappers>
<simple-permission-mapper name="default-permission-mapper" mapping-mode="first">
<permission-mapping>
<principal name="anonymous"/>
<permission-set name="default-permissions"/>
</permission-mapping>
<permission-mapping match-all="true">
<permission-set name="login-permission"/>
<permission-set name="default-permissions"/>
</permission-mapping>
</simple-permission-mapper>
<constant-realm-mapper name="local" realm-name="local"/>
<simple-role-decoder name="groups-to-roles" attribute="groups"/>
<constant-role-mapper name="super-user-mapper">
<role name="SuperUser"/>
</constant-role-mapper>
</mappers>
<permission-sets>
<permission-set name="login-permission">
<permission class-name="org.wildfly.security.auth.permission.LoginPermission"/>
</permission-set>
<permission-set name="default-permissions">
<permission class-name="org.wildfly.extension.batch.jberet.deployment.BatchPermission" module="org.wildfly.extension.batch.jberet" target-name="*"/>
<permission class-name="org.wildfly.transaction.client.RemoteTransactionPermission" module="org.wildfly.transaction.client"/>
<permission class-name="org.jboss.ejb.client.RemoteEJBPermission" module="org.jboss.ejb-client"/>
<permission class-name="org.jboss.ejb.client.RemoteEJBPermission" module="org.jboss.ejb-client"/>
</permission-set>
</permission-sets>
<http>
<http-authentication-factory name="management-http-authentication" security-domain="ManagementDomain" http-server-mechanism-factory="global">
<mechanism-configuration>
<mechanism mechanism-name="DIGEST">
<mechanism-realm realm-name="ManagementRealm"/>
</mechanism>
</mechanism-configuration>
</http-authentication-factory>
<http-authentication-factory name="application-http-authentication" security-domain="ApplicationDomain" http-server-mechanism-factory="global">
<mechanism-configuration>
<mechanism mechanism-name="BASIC">
<mechanism-realm realm-name="ApplicationRealm"/>
</mechanism>
</mechanism-configuration>
</http-authentication-factory>
<provider-http-server-mechanism-factory name="global"/>
</http>
<sasl>
<sasl-authentication-factory name="application-sasl-authentication" sasl-server-factory="configured" security-domain="ApplicationDomain">
<mechanism-configuration>
<mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
<mechanism mechanism-name="DIGEST-MD5">
<mechanism-realm realm-name="ApplicationRealm"/>
</mechanism>
</mechanism-configuration>
</sasl-authentication-factory>
<sasl-authentication-factory name="management-sasl-authentication" sasl-server-factory="configured" security-domain="ManagementDomain">
<mechanism-configuration>
<mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
<mechanism mechanism-name="DIGEST-MD5">
<mechanism-realm realm-name="ManagementRealm"/>
</mechanism>
</mechanism-configuration>
</sasl-authentication-factory>
<configurable-sasl-server-factory name="configured" sasl-server-factory="elytron">
<properties>
<property name="wildfly.sasl.local-user.default-user" value="$local"/>
</properties>
</configurable-sasl-server-factory>
<mechanism-provider-filtering-sasl-server-factory name="elytron" sasl-server-factory="global">
<filters>
<filter provider-name="WildFlyElytron"/>
</filters>
</mechanism-provider-filtering-sasl-server-factory>
<provider-sasl-server-factory name="global"/>
</sasl>
<tls>
<key-stores>
<key-store name="applicationKS">
<credential-reference clear-text="password"/>
<implementation type="JKS"/>
<file path="application.keystore" relative-to="jboss.server.config.dir"/>
</key-store>
</key-stores>
<key-managers>
<key-manager name="applicationKM" key-store="applicationKS" generate-self-signed-certificate-host="localhost">
<credential-reference clear-text="password"/>
</key-manager>
</key-managers>
<server-ssl-contexts>
<server-ssl-context name="applicationSSC" key-manager="applicationKM"/>
</server-ssl-contexts>
</tls>
</subsystem>
<subsystem xmlns="urn:wildfly:health:1.0" security-enabled="false"/>
<subsystem xmlns="urn:jboss:domain:infinispan:12.0">
<cache-container name="ejb" default-cache="dist" aliases="sfsb" modules="org.wildfly.clustering.ejb.infinispan">
<transport lock-timeout="60000"/>
<distributed-cache name="dist">
<locking isolation="REPEATABLE_READ"/>
<transaction mode="BATCH"/>
<file-store/>
</distributed-cache>
</cache-container>
<cache-container name="keycloak">
<transport lock-timeout="60000"/>
<local-cache name="realms">
<heap-memory size="10000"/>
</local-cache>
<local-cache name="users">
<heap-memory size="10000"/>
</local-cache>
<replicated-cache name="work"/>
<distributed-cache name="sessions" owners="2"/>
<distributed-cache name="authenticationSessions" owners="2"/>
<distributed-cache name="offlineSessions" owners="2"/>
<distributed-cache name="clientSessions" owners="2"/>
<distributed-cache name="offlineClientSessions" owners="2"/>
<distributed-cache name="loginFailures" owners="2"/>
<distributed-cache name="actionTokens" owners="2">
<expiration interval="300000" max-idle="-1"/>
</distributed-cache>
<local-cache name="authorization">
<heap-memory size="10000"/>
</local-cache>
<local-cache name="keys">
<heap-memory size="1000"/>
<expiration max-idle="3600000"/>
</local-cache>
</cache-container>
<cache-container name="server" default-cache="default" aliases="singleton cluster" modules="org.wildfly.clustering.server">
<transport lock-timeout="60000"/>
<replicated-cache name="default">
<transaction mode="BATCH"/>
</replicated-cache>
</cache-container>
<cache-container name="web" default-cache="dist" modules="org.wildfly.clustering.web.infinispan">
<transport lock-timeout="60000"/>
<replicated-cache name="sso">
<locking isolation="REPEATABLE_READ"/>
<transaction mode="BATCH"/>
</replicated-cache>
<distributed-cache name="dist">
<locking isolation="REPEATABLE_READ"/>
<transaction mode="BATCH"/>
<file-store/>
</distributed-cache>
<distributed-cache name="routing"/>
</cache-container>
<cache-container name="hibernate" modules="org.infinispan.hibernate-cache">
<transport lock-timeout="60000"/>
<local-cache name="local-query">
<heap-memory size="10000"/>
<expiration max-idle="100000"/>
</local-cache>
<invalidation-cache name="entity">
<transaction mode="NON_XA"/>
<heap-memory size="10000"/>
<expiration max-idle="100000"/>
</invalidation-cache>
<replicated-cache name="timestamps"/>
</cache-container>
</subsystem>
<subsystem xmlns="urn:jboss:domain:io:3.0">
<worker name="default"/>
<buffer-pool name="default"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:jaxrs:2.0"/>
<subsystem xmlns="urn:jboss:domain:jca:5.0">
<archive-validation enabled="true" fail-on-error="true" fail-on-warn="false"/>
<bean-validation enabled="true"/>
<default-workmanager>
<short-running-threads>
<core-threads count="50"/>
<queue-length count="50"/>
<max-threads count="50"/>
<keepalive-time time="10" unit="seconds"/>
</short-running-threads>
<long-running-threads>
<core-threads count="50"/>
<queue-length count="50"/>
<max-threads count="50"/>
<keepalive-time time="10" unit="seconds"/>
</long-running-threads>
</default-workmanager>
<cached-connection-manager/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:jgroups:8.0">
<channels default="ee">
<channel name="ee" stack="tcp" cluster="ejb"/>
</channels>
<stacks>
<stack name="tcp">
<transport site="${jboss.node.name}" type="TCP" socket-binding="jgroups-tcp"/>
{% if keycloak_ha_discovery == 'JDBC_PING' and keycloak_jdbc[keycloak_jdbc_engine].enabled %}
<protocol type="JDBC_PING">
<property name="datasource_jndi_name">java:jboss/datasources/KeycloakDS</property>
<property name="initialize_sql">{{ keycloak_jdbc[keycloak_jdbc_engine].initialize_db }}</property>
<property name="insert_single_sql">INSERT INTO JGROUPSPING (own_addr, cluster_name, ping_data) values (?, ?, ?)</property>
<property name="delete_single_sql">DELETE FROM JGROUPSPING WHERE own_addr=? AND cluster_name=?</property>
<property name="select_all_pingdata_sql">SELECT ping_data FROM JGROUPSPING WHERE cluster_name=?</property>
</protocol>
{% elif keycloak_ha_discovery == 'TCPPING' %}
<protocol type="TCPPING">
<property name="initial_hosts">{{ keycloak_cluster_nodes | map(attribute='inventory_host') | join (',') }}</property>
<property name="port_range">0</property>
<property name="timeout">3000</property>
<property name="num_initial_members">2</property>
</protocol>
{% endif %}
<protocol type="MERGE3"/>
<protocol type="FD_SOCK"/>
<protocol type="FD_ALL"/>
<protocol type="VERIFY_SUSPECT"/>
<protocol type="pbcast.NAKACK2"/>
<protocol type="UNICAST3"/>
<protocol type="pbcast.STABLE"/>
<protocol type="pbcast.GMS">
<property name="join_timeout">30000</property>
</protocol>
<protocol type="MFC"/>
<protocol type="FRAG2"/>
</stack>
</stacks>
</subsystem>
<subsystem xmlns="urn:jboss:domain:jmx:1.3">
<expose-resolved-model/>
<expose-expression-model/>
<remoting-connector/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:jpa:1.1">
<jpa default-extended-persistence-inheritance="DEEP"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
<web-context>auth</web-context>
<providers>
<provider>
classpath:${jboss.home.dir}/providers/*
</provider>
</providers>
<master-realm-name>master</master-realm-name>
<scheduled-task-interval>900</scheduled-task-interval>
<theme>
<staticMaxAge>2592000</staticMaxAge>
<cacheThemes>true</cacheThemes>
<cacheTemplates>true</cacheTemplates>
<dir>${jboss.home.dir}/themes</dir>
</theme>
{% if keycloak_ha_enabled %}
<spi name="dblock">
<provider name="jpa" enabled="true">
<properties>
<property name="lockWaitTimeout" value="900"/>
</properties>
</provider>
</spi>
{% endif %}
<spi name="eventsStore">
<provider name="jpa" enabled="true">
<properties>
<property name="exclude-events" value="[&quot;REFRESH_TOKEN&quot;]"/>
</properties>
</provider>
</spi>
<spi name="userCache">
<provider name="default" enabled="true"/>
</spi>
<spi name="userSessionPersister">
<default-provider>jpa</default-provider>
</spi>
<spi name="timer">
<default-provider>basic</default-provider>
</spi>
<spi name="connectionsHttpClient">
<provider name="default" enabled="true"/>
</spi>
<spi name="connectionsJpa">
<provider name="default" enabled="true">
<properties>
<property name="dataSource" value="java:jboss/datasources/KeycloakDS"/>
<property name="initializeEmpty" value="true"/>
<property name="migrationStrategy" value="update"/>
<property name="migrationExport" value="${jboss.home.dir}/keycloak-database-update.sql"/>
</properties>
</provider>
</spi>
<spi name="realmCache">
<provider name="default" enabled="true"/>
</spi>
<spi name="connectionsInfinispan">
<default-provider>default</default-provider>
<provider name="default" enabled="true">
<properties>
<property name="cacheContainer" value="java:jboss/infinispan/container/keycloak"/>
</properties>
</provider>
</spi>
<spi name="jta-lookup">
<default-provider>${keycloak.jta.lookup.provider:jboss}</default-provider>
<provider name="jboss" enabled="true"/>
</spi>
<spi name="publicKeyStorage">
<provider name="infinispan" enabled="true">
<properties>
<property name="minTimeBetweenRequests" value="10"/>
</properties>
</provider>
</spi>
<spi name="x509cert-lookup">
<default-provider>${keycloak.x509cert.lookup.provider:default}</default-provider>
<provider name="default" enabled="true"/>
</spi>
<spi name="hostname">
<default-provider>default</default-provider>
<provider name="default" enabled="true">
<properties>
<property name="frontendUrl" value="{{ keycloak_modcluster.frontend_url }}"/>
<property name="forceBackendUrlToFrontendUrl" value="{{ keycloak_modcluster.force_frontend_url }}"/>
{% if keycloak_modcluster.admin_url | length > 0 %}
<property name="adminUrl" value="{{ keycloak_modcluster.admin_url }}" />
{% endif %}
</properties>
</provider>
</spi>
</subsystem>
<subsystem xmlns="urn:jboss:domain:mail:4.0">
<mail-session name="default" jndi-name="java:jboss/mail/Default">
<smtp-server outbound-socket-binding-ref="mail-smtp"/>
</mail-session>
</subsystem>
<subsystem xmlns="urn:wildfly:metrics:1.0" security-enabled="false" exposed-subsystems="*" prefix="${wildfly.metrics.prefix:jboss}"/>
{% if keycloak_modcluster.enabled %}
<subsystem xmlns="urn:jboss:domain:modcluster:5.0">
<proxy name="default" advertise="false" listener="ajp" proxies="{{ ['proxy_'] | product(keycloak_modcluster.reverse_proxy_urls | map(attribute='host')) | map('join') | list | join(' ') }}">
<dynamic-load-provider>
<load-metric type="cpu"/>
</dynamic-load-provider>
</proxy>
</subsystem>
{% endif %}
<subsystem xmlns="urn:jboss:domain:naming:2.0">
<remote-naming/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:remoting:4.0">
<http-connector name="http-remoting-connector" connector-ref="default" sasl-authentication-factory="application-sasl-authentication"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:request-controller:1.0"/>
<subsystem xmlns="urn:jboss:domain:security-manager:1.0">
<deployment-permissions>
<maximum-set>
<permission class="java.security.AllPermission"/>
</maximum-set>
</deployment-permissions>
</subsystem>
<subsystem xmlns="urn:jboss:domain:transactions:6.0">
<core-environment node-identifier="{{ inventory_hostname | default('${jboss.tx.node.id:1}') }}">
<process-id>
<uuid/>
</process-id>
</core-environment>
<recovery-environment socket-binding="txn-recovery-environment" status-socket-binding="txn-status-manager"/>
<coordinator-environment statistics-enabled="${wildfly.transactions.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
<object-store path="tx-object-store" relative-to="jboss.server.data.dir"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:undertow:12.0" default-server="default-server" default-virtual-host="default-host" default-servlet-container="default" default-security-domain="other" statistics-enabled="${wildfly.undertow.statistics-enabled:${wildfly.statistics-enabled:false}}">
<buffer-cache name="default"/>
<server name="default-server">
<ajp-listener name="ajp" socket-binding="ajp"/>
<http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true" proxy-address-forwarding="true"/>
<https-listener name="https" socket-binding="https" ssl-context="applicationSSC" enable-http2="true"/>
<host name="default-host" alias="localhost">
<location name="/" handler="welcome-content"/>
<http-invoker http-authentication-factory="application-http-authentication"/>
<filter-ref name="proxy-peer"/>
</host>
</server>
<servlet-container name="default">
<jsp-config/>
<websockets/>
</servlet-container>
<handlers>
<file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
</handlers>
<application-security-domains>
<application-security-domain name="other" security-domain="ApplicationDomain"/>
</application-security-domains>
<filters>
<filter name="proxy-peer" module="io.undertow.core"
class-name="io.undertow.server.handlers.ProxyPeerAddressHandler"/>
</filters>
</subsystem>
<subsystem xmlns="urn:jboss:domain:weld:4.0"/>
</profile>
<interfaces>
<interface name="management">
<inet-address value="{{ keycloak_management_port_bind_address }}"/>
</interface>
<interface name="jgroups">
{% if keycloak_jgroups_subnet is defined and keycloak_jgroups_subnet is not none and keycloak_jgroups_subnet | string | length > 0 %}
<subnet-match value="{{ keycloak_jgroups_subnet | string }}"/>
{% elif ansible_default_ipv4 is defined and (ansible_default_ipv4.network + '/' + ansible_default_ipv4.netmask) | ansible.utils.ipaddr('net') | length > 0 %}
<subnet-match value="{{ (ansible_default_ipv4.network + '/' + ansible_default_ipv4.netmask) | ansible.utils.ipaddr('net') }}"/>
{% else %}
<any-address />
{% endif %}
</interface>
<interface name="public">
<inet-address value="{{ keycloak_bind_address }}"/>
</interface>
</interfaces>
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
<socket-binding name="ajp" port="{{ keycloak_ajp_port }}"/>
<socket-binding name="http" port="{{ keycloak_http_port }}"/>
<socket-binding name="https" port="{{ keycloak_https_port }}"/>
<socket-binding name="management-http" interface="management" port="{{ keycloak_management_http_port }}"/>
<socket-binding name="management-https" interface="management" port="{{ keycloak_management_https_port }}"/>
<socket-binding name="jgroups-tcp" interface="jgroups" port="{{ keycloak_jgroups_port }}"/>
<socket-binding name="txn-recovery-environment" port="4712"/>
<socket-binding name="txn-status-manager" port="4713"/>
<outbound-socket-binding name="mail-smtp">
<remote-destination host="${jboss.mail.server.host:localhost}" port="${jboss.mail.server.port:25}"/>
</outbound-socket-binding>
{% if keycloak_modcluster.enabled %}
{% for modcluster in keycloak_modcluster.reverse_proxy_urls %}
<outbound-socket-binding name="proxy_{{ modcluster.host }}">
<remote-destination host="{{ modcluster.host }}" port="{{ modcluster.port }}"/>
</outbound-socket-binding>
{% endfor %}
{% endif %}
{% if keycloak_ha_discovery == 'TCPPING' %}
{% for node in keycloak_cluster_nodes %}
<outbound-socket-binding name="jgroups_{{ node.address }}">
<remote-destination host="{{ node.value }}" port="{{ keycloak_jgroups_port }}"/>
</outbound-socket-binding>
{% endfor %}
{% endif %}
<outbound-socket-binding name="remote-cache">
<remote-destination host="{{ keycloak_remotecache.server_name | default('localhost') }}" port="${remote.cache.port:11222}"/>
</outbound-socket-binding>
</socket-binding-group>
</server>

View File

@@ -16,7 +16,6 @@
<extension module="org.jboss.as.modcluster"/>
<extension module="org.jboss.as.naming"/>
<extension module="org.jboss.as.remoting"/>
<extension module="org.jboss.as.security"/>
<extension module="org.jboss.as.transactions"/>
<extension module="org.jboss.as.weld"/>
<extension module="org.keycloak.keycloak-server-subsystem"/>
@@ -31,31 +30,6 @@
<extension module="org.wildfly.extension.undertow"/>
</extensions>
<management>
<security-realms>
<security-realm name="ManagementRealm">
<authentication>
<local default-user="$local" skip-group-loading="true"/>
<properties path="mgmt-users.properties" relative-to="jboss.server.config.dir"/>
</authentication>
<authorization map-groups-to-roles="false">
<properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
</authorization>
</security-realm>
<security-realm name="ApplicationRealm">
<server-identities>
<ssl>
<keystore path="application.keystore" relative-to="jboss.server.config.dir" keystore-password="password" alias="server" key-password="password" generate-self-signed-certificate-host="localhost"/>
</ssl>
</server-identities>
<authentication>
<local default-user="$local" allowed-users="*" skip-group-loading="true"/>
<properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
</authentication>
<authorization>
<properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
</authorization>
</security-realm>
</security-realms>
<audit-log>
<formatters>
<json-formatter name="json-formatter"/>
@@ -70,8 +44,8 @@
</logger>
</audit-log>
<management-interfaces>
<http-interface security-realm="ManagementRealm">
<http-upgrade enabled="true"/>
<http-interface http-authentication-factory="management-http-authentication">
<http-upgrade enabled="true" sasl-authentication-factory="management-sasl-authentication"/>
<socket-binding http="management-http"/>
</http-interface>
</management-interfaces>
@@ -162,6 +136,14 @@
<user-name>{{ keycloak_jdbc[keycloak_jdbc_engine].db_user }}</user-name>
<password>{{ keycloak_jdbc[keycloak_jdbc_engine].db_password }}</password>
</security>
<validation>
<check-valid-connection-sql>{{ keycloak_jdbc[keycloak_jdbc_engine].validate_query }}</check-valid-connection-sql>
<validate-on-match>{{ keycloak_db_background_validate_on_match }}</validate-on-match>
{% if keycloak_db_background_validation_millis | int > 0 or keycloak_db_background_validation %}
<background-validation>{{ keycloak_db_background_validation }}</background-validation>
<background-validation-millis>{{ keycloak_db_background_validation_millis }}</background-validation-millis>
{% endif %}
</validation>
{% else %}
<connection-url>jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE</connection-url>
<driver>h2</driver>
@@ -244,6 +226,9 @@
</thread-pool>
</thread-pools>
<default-security-domain value="other"/>
<application-security-domains>
<application-security-domain name="other" security-domain="ApplicationDomain"/>
</application-security-domains>
<default-missing-method-permissions-deny-access value="true"/>
<statistics enabled="${wildfly.ejb3.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
<log-system-exceptions value="true"/>
@@ -317,6 +302,13 @@
</mechanism>
</mechanism-configuration>
</http-authentication-factory>
<http-authentication-factory name="application-http-authentication" security-domain="ApplicationDomain" http-server-mechanism-factory="global">
<mechanism-configuration>
<mechanism mechanism-name="BASIC">
<mechanism-realm realm-name="ApplicationRealm"/>
</mechanism>
</mechanism-configuration>
</http-authentication-factory>
<provider-http-server-mechanism-factory name="global"/>
</http>
<sasl>
@@ -504,7 +496,7 @@
<stacks>
<stack name="tcp">
<transport site="${jboss.node.name}" type="TCP" socket-binding="jgroups-tcp"/>
{% if keycloak_jdbc[keycloak_jdbc_engine].enabled %}
{% if keycloak_ha_discovery == 'JDBC_PING' and keycloak_jdbc[keycloak_jdbc_engine].enabled %}
<protocol type="JDBC_PING">
<property name="datasource_jndi_name">java:jboss/datasources/KeycloakDS</property>
<property name="initialize_sql">{{ keycloak_jdbc[keycloak_jdbc_engine].initialize_db }}</property>
@@ -512,6 +504,13 @@
<property name="delete_single_sql">DELETE FROM JGROUPSPING WHERE own_addr=? AND cluster_name=?</property>
<property name="select_all_pingdata_sql">SELECT ping_data FROM JGROUPSPING WHERE cluster_name=?</property>
</protocol>
{% elif keycloak_ha_discovery == 'TCPPING' %}
<protocol type="TCPPING">
<property name="initial_hosts">{{ keycloak_cluster_nodes | map(attribute='inventory_host') | join (',') }}</property>
<property name="port_range">0</property>
<property name="timeout">3000</property>
<property name="num_initial_members">2</property>
</protocol>
{% endif %}
<protocol type="MERGE3"/>
<protocol type="FD_SOCK"/>
@@ -620,7 +619,10 @@
<provider name="default" enabled="true">
<properties>
<property name="frontendUrl" value="{{ keycloak_modcluster.frontend_url }}"/>
<property name="forceBackendUrlToFrontendUrl" value="true"/>
<property name="forceBackendUrlToFrontendUrl" value="{{ keycloak_modcluster.force_frontend_url }}"/>
{% if keycloak_modcluster.admin_url | length > 0 %}
<property name="adminUrl" value="{{ keycloak_modcluster.admin_url }}" />
{% endif %}
</properties>
</provider>
</spi>
@@ -631,54 +633,22 @@
</mail-session>
</subsystem>
<subsystem xmlns="urn:wildfly:metrics:1.0" security-enabled="false" exposed-subsystems="*" prefix="${wildfly.metrics.prefix:jboss}"/>
{% if keycloak_modcluster.enabled %}
{% if keycloak_modcluster.enabled %}
<subsystem xmlns="urn:jboss:domain:modcluster:5.0">
<proxy name="default" advertise="false" listener="ajp" proxies="proxy1">
<proxy name="default" advertise="false" listener="ajp" proxies="{{ ['proxy_'] | product(keycloak_modcluster.reverse_proxy_urls | map(attribute='host')) | map('join') | list | join(' ') }}">
<dynamic-load-provider>
<load-metric type="cpu"/>
</dynamic-load-provider>
</proxy>
</subsystem>
{% endif %}
{% endif %}
<subsystem xmlns="urn:jboss:domain:naming:2.0">
<remote-naming/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:remoting:4.0">
<http-connector name="http-remoting-connector" connector-ref="default" security-realm="ApplicationRealm"/>
<http-connector name="http-remoting-connector" connector-ref="default" sasl-authentication-factory="application-sasl-authentication"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:request-controller:1.0"/>
<subsystem xmlns="urn:jboss:domain:security:2.0">
<security-domains>
<security-domain name="other" cache-type="default">
<authentication>
<login-module code="Remoting" flag="optional">
<module-option name="password-stacking" value="useFirstPass"/>
</login-module>
<login-module code="RealmDirect" flag="required">
<module-option name="password-stacking" value="useFirstPass"/>
</login-module>
</authentication>
</security-domain>
<security-domain name="jboss-web-policy" cache-type="default">
<authorization>
<policy-module code="Delegating" flag="required"/>
</authorization>
</security-domain>
<security-domain name="jaspitest" cache-type="default">
<authentication-jaspi>
<login-module-stack name="dummy">
<login-module code="Dummy" flag="optional"/>
</login-module-stack>
<auth-module code="Dummy"/>
</authentication-jaspi>
</security-domain>
<security-domain name="jboss-ejb-policy" cache-type="default">
<authorization>
<policy-module code="Delegating" flag="required"/>
</authorization>
</security-domain>
</security-domains>
</subsystem>
<subsystem xmlns="urn:jboss:domain:security-manager:1.0">
<deployment-permissions>
<maximum-set>
@@ -701,10 +671,10 @@
<server name="default-server">
<ajp-listener name="ajp" socket-binding="ajp"/>
<http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true" proxy-address-forwarding="true"/>
<https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/>
<https-listener name="https" socket-binding="https" ssl-context="applicationSSC" enable-http2="true"/>
<host name="default-host" alias="localhost">
<location name="/" handler="welcome-content"/>
<http-invoker security-realm="ApplicationRealm"/>
<http-invoker http-authentication-factory="application-http-authentication"/>
<filter-ref name="proxy-peer"/>
</host>
</server>
@@ -715,6 +685,9 @@
<handlers>
<file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
</handlers>
<application-security-domains>
<application-security-domain name="other" security-domain="ApplicationDomain"/>
</application-security-domains>
<filters>
<filter name="proxy-peer" module="io.undertow.core"
class-name="io.undertow.server.handlers.ProxyPeerAddressHandler"/>
@@ -724,17 +697,19 @@
</profile>
<interfaces>
<interface name="management">
<inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
<inet-address value="{{ keycloak_management_port_bind_address }}"/>
</interface>
<interface name="jgroups">
{% if ansible_default_ipv4 is defined %}
<subnet-match value="{{ (ansible_default_ipv4.network + '/' + ansible_default_ipv4.netmask) | ipaddr('net') }}"/>
{% if keycloak_jgroups_subnet is defined and keycloak_jgroups_subnet is not none and keycloak_jgroups_subnet | string | length > 0 %}
<subnet-match value="{{ keycloak_jgroups_subnet | string }}"/>
{% elif ansible_default_ipv4 is defined and (ansible_default_ipv4.network + '/' + ansible_default_ipv4.netmask) | ansible.utils.ipaddr('net') | length > 0 %}
<subnet-match value="{{ (ansible_default_ipv4.network + '/' + ansible_default_ipv4.netmask) | ansible.utils.ipaddr('net') }}"/>
{% else %}
<any-address />
{% endif %}
</interface>
<interface name="public">
<inet-address value="${jboss.bind.address:127.0.0.1}"/>
<inet-address value="{{ keycloak_bind_address }}"/>
</interface>
</interfaces>
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
@@ -750,9 +725,18 @@
<remote-destination host="${jboss.mail.server.host:localhost}" port="${jboss.mail.server.port:25}"/>
</outbound-socket-binding>
{% if keycloak_modcluster.enabled %}
<outbound-socket-binding name="proxy1">
<remote-destination host="{{ keycloak_modcluster.reverse_proxy_url | default('localhost') }}" port="6666"/>
{% for modcluster in keycloak_modcluster.reverse_proxy_urls %}
<outbound-socket-binding name="proxy_{{ modcluster.host }}">
<remote-destination host="{{ modcluster.host }}" port="{{ modcluster.port }}"/>
</outbound-socket-binding>
{% endfor %}
{% endif %}
{% if keycloak_ha_discovery == 'TCPPING' %}
{% for node in keycloak_cluster_nodes %}
<outbound-socket-binding name="jgroups_{{ node.address }}">
<remote-destination host="{{ node.value }}" port="{{ keycloak_jgroups_port }}"/>
</outbound-socket-binding>
{% endfor %}
{% endif %}
<outbound-socket-binding name="remote-cache">
<remote-destination host="{{ keycloak_remotecache.server_name | default('localhost') }}" port="${remote.cache.port:11222}"/>

View File

@@ -15,7 +15,6 @@
<extension module="org.jboss.as.modcluster"/>
<extension module="org.jboss.as.naming"/>
<extension module="org.jboss.as.remoting"/>
<extension module="org.jboss.as.security"/>
<extension module="org.jboss.as.transactions"/>
<extension module="org.jboss.as.weld"/>
<extension module="org.keycloak.keycloak-server-subsystem"/>
@@ -30,31 +29,6 @@
<extension module="org.wildfly.extension.undertow"/>
</extensions>
<management>
<security-realms>
<security-realm name="ManagementRealm">
<authentication>
<local default-user="$local" skip-group-loading="true"/>
<properties path="mgmt-users.properties" relative-to="jboss.server.config.dir"/>
</authentication>
<authorization map-groups-to-roles="false">
<properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
</authorization>
</security-realm>
<security-realm name="ApplicationRealm">
<server-identities>
<ssl>
<keystore path="application.keystore" relative-to="jboss.server.config.dir" keystore-password="password" alias="server" key-password="password" generate-self-signed-certificate-host="localhost"/>
</ssl>
</server-identities>
<authentication>
<local default-user="$local" allowed-users="*" skip-group-loading="true"/>
<properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
</authentication>
<authorization>
<properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
</authorization>
</security-realm>
</security-realms>
<audit-log>
<formatters>
<json-formatter name="json-formatter"/>
@@ -69,8 +43,8 @@
</logger>
</audit-log>
<management-interfaces>
<http-interface security-realm="ManagementRealm">
<http-upgrade enabled="true"/>
<http-interface http-authentication-factory="management-http-authentication">
<http-upgrade enabled="true" sasl-authentication-factory="management-sasl-authentication"/>
<socket-binding http="management-http"/>
</http-interface>
</management-interfaces>
@@ -149,6 +123,14 @@
<user-name>{{ keycloak_jdbc[keycloak_jdbc_engine].db_user }}</user-name>
<password>{{ keycloak_jdbc[keycloak_jdbc_engine].db_password }}</password>
</security>
<validation>
<check-valid-connection-sql>{{ keycloak_jdbc[keycloak_jdbc_engine].validate_query }}</check-valid-connection-sql>
<validate-on-match>{{ keycloak_db_background_validate_on_match }}</validate-on-match>
{% if keycloak_db_background_validation_millis | int > 0 or keycloak_db_background_validation %}
<background-validation>{{ keycloak_db_background_validation }}</background-validation>
<background-validation-millis>{{ keycloak_db_background_validation_millis }}</background-validation-millis>
{% endif %}
</validation>
{% else %}
<connection-url>jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE</connection-url>
<driver>h2</driver>
@@ -231,6 +213,9 @@
</thread-pool>
</thread-pools>
<default-security-domain value="other"/>
<application-security-domains>
<application-security-domain name="other" security-domain="ApplicationDomain"/>
</application-security-domains>
<default-missing-method-permissions-deny-access value="true"/>
<statistics enabled="${wildfly.ejb3.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
<log-system-exceptions value="true"/>
@@ -304,6 +289,13 @@
</mechanism>
</mechanism-configuration>
</http-authentication-factory>
<http-authentication-factory name="application-http-authentication" security-domain="ApplicationDomain" http-server-mechanism-factory="global">
<mechanism-configuration>
<mechanism mechanism-name="BASIC">
<mechanism-realm realm-name="ApplicationRealm"/>
</mechanism>
</mechanism-configuration>
</http-authentication-factory>
<provider-http-server-mechanism-factory name="global"/>
</http>
<sasl>
@@ -533,7 +525,10 @@
<provider name="default" enabled="true">
<properties>
<property name="frontendUrl" value="{{ keycloak_modcluster.frontend_url }}"/>
<property name="forceBackendUrlToFrontendUrl" value="true"/>
<property name="forceBackendUrlToFrontendUrl" value="{{ keycloak_modcluster.force_frontend_url }}"/>
{% if keycloak_modcluster.admin_url | length > 0 %}
<property name="adminUrl" value="{{ keycloak_modcluster.admin_url }}" />
{% endif %}
</properties>
</provider>
</spi>
@@ -546,7 +541,7 @@
<subsystem xmlns="urn:wildfly:metrics:1.0" security-enabled="false" exposed-subsystems="*" prefix="${wildfly.metrics.prefix:jboss}"/>
{% if keycloak_modcluster.enabled %}
<subsystem xmlns="urn:jboss:domain:modcluster:5.0">
<proxy name="default" advertise="false" listener="ajp" proxies="proxy1">
<proxy name="default" advertise="false" listener="ajp" proxies="{{ ['proxy_'] | product(keycloak_modcluster.reverse_proxy_urls | map(attribute='host')) | map('join') | list | join(' ') }}">
<dynamic-load-provider>
<load-metric type="cpu"/>
</dynamic-load-provider>
@@ -557,41 +552,9 @@
<remote-naming/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:remoting:4.0">
<http-connector name="http-remoting-connector" connector-ref="default" security-realm="ApplicationRealm"/>
<http-connector name="http-remoting-connector" connector-ref="default" sasl-authentication-factory="application-sasl-authentication"/>
</subsystem>
<subsystem xmlns="urn:jboss:domain:request-controller:1.0"/>
<subsystem xmlns="urn:jboss:domain:security:2.0">
<security-domains>
<security-domain name="other" cache-type="default">
<authentication>
<login-module code="Remoting" flag="optional">
<module-option name="password-stacking" value="useFirstPass"/>
</login-module>
<login-module code="RealmDirect" flag="required">
<module-option name="password-stacking" value="useFirstPass"/>
</login-module>
</authentication>
</security-domain>
<security-domain name="jboss-web-policy" cache-type="default">
<authorization>
<policy-module code="Delegating" flag="required"/>
</authorization>
</security-domain>
<security-domain name="jaspitest" cache-type="default">
<authentication-jaspi>
<login-module-stack name="dummy">
<login-module code="Dummy" flag="optional"/>
</login-module-stack>
<auth-module code="Dummy"/>
</authentication-jaspi>
</security-domain>
<security-domain name="jboss-ejb-policy" cache-type="default">
<authorization>
<policy-module code="Delegating" flag="required"/>
</authorization>
</security-domain>
</security-domains>
</subsystem>
<subsystem xmlns="urn:jboss:domain:security-manager:1.0">
<deployment-permissions>
<maximum-set>
@@ -614,10 +577,10 @@
<server name="default-server">
<ajp-listener name="ajp" socket-binding="ajp"/>
<http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/>
<https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/>
<https-listener name="https" socket-binding="https" ssl-context="applicationSSC" enable-http2="true"/>
<host name="default-host" alias="localhost">
<location name="/" handler="welcome-content"/>
<http-invoker security-realm="ApplicationRealm"/>
<http-invoker http-authentication-factory="application-http-authentication"/>
</host>
</server>
<servlet-container name="default">
@@ -627,15 +590,18 @@
<handlers>
<file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
</handlers>
<application-security-domains>
<application-security-domain name="other" security-domain="ApplicationDomain"/>
</application-security-domains>
</subsystem>
<subsystem xmlns="urn:jboss:domain:weld:4.0"/>
</profile>
<interfaces>
<interface name="management">
<inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
<inet-address value="{{ keycloak_management_port_bind_address }}"/>
</interface>
<interface name="public">
<inet-address value="${jboss.bind.address:127.0.0.1}"/>
<inet-address value="{{ keycloak_bind_address }}"/>
</interface>
</interfaces>
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
@@ -649,10 +615,12 @@
<outbound-socket-binding name="mail-smtp">
<remote-destination host="${jboss.mail.server.host:localhost}" port="${jboss.mail.server.port:25}"/>
</outbound-socket-binding>
{% if keycloak_modcluster.enabled %}
<outbound-socket-binding name="proxy1">
<remote-destination host="{{ keycloak_modcluster.reverse_proxy_url | default('localhost') }}" port="6666"/>
{% if keycloak_modcluster.enabled %}
{% for modcluster in keycloak_modcluster.reverse_proxy_urls %}
<outbound-socket-binding name="proxy_{{ modcluster.host }}">
<remote-destination host="{{ modcluster.host }}" port="{{ modcluster.port }}"/>
</outbound-socket-binding>
{% endfor %}
{% endif %}
</socket-binding-group>
</server>

View File

@@ -1,11 +1,5 @@
---
# internal variables below
rhsso_rhn_ids:
'7.5.0': # noqa vars_in_vars_files_have_valid_names
id: '101971'
latest_cp:
id: '103836'
v: '7.5.1'
# locations
keycloak_url: "http://{{ keycloak_host }}:{{ keycloak_http_port }}"
@@ -15,12 +9,12 @@ keycloak_management_url: "http://{{ keycloak_host }}:{{ keycloak_management_http
keycloak:
home: "{{ keycloak_jboss_home }}"
config_dir: "{{ keycloak_config_dir }}"
bundle: "{{ keycloak_rhsso_archive if keycloak_rhsso_enable else keycloak_archive }}"
patch_bundle: "rh-sso-{{ rhsso_rhn_ids[keycloak_rhsso_version].latest_cp.v }}-patch.zip"
service_name: "{{ 'rhsso' if keycloak_rhsso_enable else 'keycloak' }}"
bundle: "{{ keycloak_archive }}"
service_name: "{{ keycloak_service_name }}"
health_url: "{{ keycloak_management_url }}/health"
cli_path: "{{ keycloak_jboss_home }}/bin/jboss-cli.sh"
config_template_source: "{{ keycloak_config_override_template if keycloak_config_override_template | length > 0 else 'standalone.xml.j2' }}"
config_template_source: "{{ keycloak_config_override_template if keycloak_config_override_template | length > 0 else 'standalone-ha.xml.j2' if keycloak_remote_cache_enabled else 'standalone.xml.j2' }}"
features: "{{ keycloak_features }}"
# database
keycloak_jdbc:
@@ -36,6 +30,7 @@ keycloak_jdbc:
connection_url: "{{ keycloak_jdbc_url }}"
db_user: "{{ keycloak_db_user }}"
db_password: "{{ keycloak_db_pass }}"
validate_query: "{{ keycloak_db_valid_conn_sql | default('select 1') }}"
initialize_db: >
CREATE TABLE IF NOT EXISTS JGROUPSPING (
own_addr varchar(200) NOT NULL,
@@ -46,15 +41,16 @@ keycloak_jdbc:
mariadb:
enabled: "{{ (keycloak_ha_enabled or keycloak_db_enabled) and keycloak_jdbc_engine == 'mariadb' }}"
driver_class: org.mariadb.jdbc.Driver
xa_datasource_class: org.mariadb.jdbc.MySQLDataSource
xa_datasource_class: org.mariadb.jdbc.MariaDbDataSource
driver_module_name: "org.mariadb"
driver_module_dir: "{{ keycloak_jboss_home }}/modules/org/mariadb/main"
driver_version: "{{ keycloak_jdbc_driver_version }}"
driver_jar_filename: "mariadb-java-client-{{ keycloak_jdbc_driver_version }}.jar"
driver_jar_url: "https://repo1.maven.org/maven2/org/mariadb/jdbc/mariadb-java-client/{{ keycloak_jdbc_driver_version }}/mariadb-java-client-{{ keycloak_jdbc_driver_version }}.jar"
connection_url: "{{ keycloak_jdbc_url }}"
db_user: "{{ keycloak_db_user }}"
db_user: "{{ keycloak_db_user }}"
db_password: "{{ keycloak_db_pass }}"
validate_query: "{{ keycloak_db_valid_conn_sql | default('select 1') }}"
initialize_db: >
CREATE TABLE IF NOT EXISTS JGROUPSPING (
own_addr varchar(200) NOT NULL,
@@ -63,21 +59,46 @@ keycloak_jdbc:
ping_data varbinary(5000) DEFAULT NULL,
PRIMARY KEY (own_addr, cluster_name))
ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
sqlserver:
enabled: "{{ (keycloak_ha_enabled or keycloak_db_enabled) and keycloak_jdbc_engine == 'sqlserver' }}"
driver_class: com.microsoft.sqlserver.jdbc.SQLServerDriver
xa_datasource_class: com.microsoft.sqlserver.jdbc.SQLServerXADataSource
driver_module_name: "com.microsoft.sqlserver"
driver_module_dir: "{{ keycloak_jboss_home }}/modules/com/microsoft/sqlserver/main"
driver_version: "{{ keycloak_jdbc_driver_version }}"
driver_jar_filename: "mssql-java-client-{{ keycloak_jdbc_driver_version }}.jar"
driver_jar_url: "https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/{{ keycloak_jdbc_driver_version }}.jre11/mssql-jdbc-{{ keycloak_jdbc_driver_version }}.jre11.jar" # e.g., https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/12.2.0.jre11/mssql-jdbc-12.2.0.jre11.jar
connection_url: "{{ keycloak_jdbc_url }}"
db_user: "{{ keycloak_db_user }}"
db_password: "{{ keycloak_db_pass }}"
validate_query: "{{ keycloak_db_valid_conn_sql | default('select 1') }}"
initialize_db: >
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[JGROUPSPING]') AND type in (N'U'))
BEGIN
CREATE TABLE JGROUPSPING (
own_addr varchar(200) NOT NULL,
cluster_name varchar(200) NOT NULL,
updated DATETIME2 DEFAULT SYSUTCDATETIME(),
ping_data varbinary(5000) DEFAULT NULL,
PRIMARY KEY (own_addr, cluster_name))
END
# reverse proxy mod_cluster
keycloak_modcluster:
enabled: "{{ keycloak_ha_enabled }}"
reverse_proxy_url: "{{ keycloak_modcluster_url }}"
enabled: "{{ keycloak_ha_enabled or keycloak_modcluster_enabled }}"
reverse_proxy_urls: "{{ keycloak_modcluster_urls }}"
frontend_url: "{{ keycloak_frontend_url }}"
force_frontend_url: "{{ keycloak_frontend_url_force }}"
admin_url: "{{ keycloak_admin_url | default('') }}"
# infinispan
keycloak_remotecache:
enabled: "{{ keycloak_ha_enabled }}"
username: "{{ infinispan_user }}"
password: "{{ infinispan_pass }}"
username: "{{ keycloak_infinispan_user }}"
password: "{{ keycloak_infinispan_pass }}"
realm: default
sasl_mechanism: "{{ infinispan_sasl_mechanism }}"
server_name: "{{ infinispan_url }}"
use_ssl: "{{ infinispan_use_ssl }}"
trust_store_path: "{{ infinispan_trust_store_path }}"
trust_store_password: "{{ infinispan_trust_store_password }}"
sasl_mechanism: "{{ keycloak_infinispan_sasl_mechanism }}"
server_name: "{{ keycloak_infinispan_url }}"
use_ssl: "{{ keycloak_infinispan_use_ssl }}"
trust_store_path: "{{ keycloak_infinispan_trust_store_path }}"
trust_store_password: "{{ keycloak_infinispan_trust_store_password }}"

View File

@@ -1,12 +1,19 @@
keycloak_quarkus
================
Install [keycloak](https://keycloak.org/) >= 17.0.0 (quarkus) server configurations.
Install [keycloak](https://keycloak.org/) >= 20.0.0 (quarkus) server configurations.
Role Defaults
-------------
* Installation options
| Variable | Description | Default |
|:---------|:------------|:--------|
|`keycloak_quarkus_version`| keycloak.org package version | `22.0.1` |
* Service configuration
| Variable | Description | Default |
@@ -20,13 +27,18 @@ Role Defaults
|`keycloak_quarkus_https_port`| TLS HTTP port | `8443` |
|`keycloak_quarkus_ajp_port`| AJP port | `8009` |
|`keycloak_quarkus_jgroups_port`| jgroups cluster tcp port | `7600` |
|`keycloak_quarkus_java_opts`| Additional JVM options | `-Xms1024m -Xmx2048m` |
|`keycloak_quarkus_service_user`| Posix account username | `keycloak` |
|`keycloak_quarkus_service_group`| Posix account group | `keycloak` |
|`keycloak_quarkus_service_pidfile`| Pid file path for service | `/run/keycloak.pid` |
|`keycloak_quarkus_jvm_package`| RHEL java package runtime | `java-11-openjdk-headless` |
|`keycloak_quarkus_jvm_package`| RHEL java package runtime | `java-17-openjdk-headless` |
|`keycloak_quarkus_java_home`| JAVA_HOME of installed JRE, leave empty for using specified keycloak_quarkus_jvm_package RPM path | `None` |
|`keycloak_quarkus_java_opts`| Additional JVM options | `-Xms1024m -Xmx2048m` |
|`keycloak_quarkus_frontend_url`| Service public URL | `http://localhost:8080/auth` |
|`keycloak_quarkus_http_relative_path` | Service context path | `auth` |
|`keycloak_quarkus_http_enabled`| Enable listener on HTTP port | `True` |
|`keycloak_quarkus_https_enabled`| Enable listener on HTTPS port | `False` |
|`keycloak_quarkus_key_file`| The file path to a private key in PEM format | `{{ keycloak.home }}/conf/server.key.pem` |
|`keycloak_quarkus_cert_file`| The file path to a server certificate or certificate chain in PEM format | `{{ keycloak.home }}/conf/server.crt.pem` |
* Database configuration
@@ -59,7 +71,7 @@ Role Defaults
|:---------|:------------|:---------|
|`keycloak_quarkus_offline_install` | Perform an offline install | `False`|
|`keycloak_quarkus_download_url`| Download URL for keycloak | `https://github.com/keycloak/keycloak/releases/download/<version>/<archive>`|
|`keycloak_quarkus_version`| keycloak.org package version | `17.0.1` |
|`keycloak_quarkus_version`| keycloak.org package version | `22.0.1` |
|`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_configure_firewalld` | Ensure firewalld is running and configure keycloak ports | `False` |
@@ -70,6 +82,7 @@ Role Defaults
| Variable | Description | Default |
|:---------|:------------|:--------|
|`keycloak_quarkus_metrics_enabled`| Whether to enable metrics | `False` |
|`keycloak_quarkus_health_enabled`| If the server should expose health check endpoints | `True` |
|`keycloak_quarkus_archive` | keycloak install archive filename | `keycloak-{{ keycloak_quarkus_version }}.zip` |
|`keycloak_quarkus_installdir` | Installation path | `{{ keycloak_quarkus_dest }}/keycloak-{{ keycloak_quarkus_version }}` |
|`keycloak_quarkus_home` | Installation work directory | `{{ keycloak_quarkus_installdir }}` |
@@ -79,14 +92,21 @@ Role Defaults
|`keycloak_force_install` | Remove pre-existing versions of service | `False` |
|`keycloak_url` | URL for configuration rest calls | `http://{{ keycloak_quarkus_host }}:{{ keycloak_http_port }}` |
|`keycloak_management_url` | URL for management console rest calls | `http://{{ keycloak_quarkus_host }}:{{ keycloak_management_http_port }}` |
|`keycloak_quarkus_log`| Enable one or more log handlers in a comma-separated list | `file` |
|`keycloak_quarkus_log_level`| The log level of the root category or a comma-separated list of individual categories and their levels | `info` |
|`keycloak_quarkus_log_file`| Set the log file path and filename relative to keycloak home | `data/log/keycloak.log` |
|`keycloak_quarkus_log_format`| Set a format specific to file log entries | `%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n` |
|`keycloak_quarkus_proxy_mode`| The proxy address forwarding mode if the server is behind a reverse proxy | `edge` |
|`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` |
Role Variables
--------------
| Variable | Description |
|:---------|:------------|
|`keycloak_quarkus_admin_pass`| Password of console admin account |
| Variable | Description | Required |
|:---------|:------------|----------|
|`keycloak_quarkus_admin_pass`| Password of console admin account | `yes` |
License

View File

@@ -1,6 +1,6 @@
---
### Configuration specific to keycloak
keycloak_quarkus_version: 17.0.1
keycloak_quarkus_version: 22.0.1
keycloak_quarkus_archive: "keycloak-{{ keycloak_quarkus_version }}.zip"
keycloak_quarkus_download_url: "https://github.com/keycloak/keycloak/releases/download/{{ keycloak_quarkus_version }}/{{ keycloak_quarkus_archive }}"
keycloak_quarkus_installdir: "{{ keycloak_quarkus_dest }}/keycloak-{{ keycloak_quarkus_version }}"
@@ -9,10 +9,12 @@ keycloak_quarkus_installdir: "{{ keycloak_quarkus_dest }}/keycloak-{{ keycloak_q
keycloak_quarkus_offline_install: False
### Install location and service settings
keycloak_quarkus_jvm_package: java-11-openjdk-headless
keycloak_quarkus_jvm_package: java-17-openjdk-headless
keycloak_quarkus_java_home:
keycloak_quarkus_dest: /opt/keycloak
keycloak_quarkus_home: "{{ keycloak_quarkus_installdir }}"
keycloak_quarkus_config_dir: "{{ keycloak_quarkus_home }}/conf"
keycloak_quarkus_start_dev: False
keycloak_quarkus_service_user: keycloak
keycloak_quarkus_service_group: keycloak
keycloak_quarkus_service_pidfile: "/run/keycloak.pid"
@@ -26,12 +28,18 @@ keycloak_quarkus_master_realm: master
### Configuration settings
keycloak_quarkus_bind_address: 0.0.0.0
keycloak_quarkus_host: localhost
keycloak_quarkus_http_enabled: True
keycloak_quarkus_http_port: 8080
keycloak_quarkus_https_port: 8443
keycloak_quarkus_ajp_port: 8009
keycloak_quarkus_jgroups_port: 7600
keycloak_quarkus_java_opts: "-Xms1024m -Xmx2048m"
### TLS/HTTPS configuration
keycloak_quarkus_https_enabled: False
keycloak_quarkus_key_file: "{{ keycloak.home }}/conf/server.key.pem"
keycloak_quarkus_cert_file: "{{ keycloak.home }}/conf/server.crt.pem"
### Enable configuration for database backend, clustering and remote caches on infinispan
keycloak_quarkus_ha_enabled: False
### Enable database configuration, must be enabled when HA is configured
@@ -41,7 +49,14 @@ keycloak_quarkus_db_enabled: "{{ True if keycloak_quarkus_ha_enabled else False
keycloak_quarkus_http_relative_path: auth
keycloak_quarkus_frontend_url: http://localhost:8080/auth
# proxy address forwarding mode if the server is behind a reverse proxy. [edge, reencrypt, passthrough]
keycloak_quarkus_proxy_mode: edge
# disable xa transactions
keycloak_quarkus_transaction_xa_enabled: True
keycloak_quarkus_metrics_enabled: False
keycloak_quarkus_health_enabled: True
### infinispan remote caches access (hotrod)
keycloak_quarkus_ispn_user: supervisor
@@ -67,4 +82,10 @@ keycloak_quarkus_default_jdbc:
version: 9.4.1212
mariadb:
url: 'jdbc:mariadb://localhost:3306/keycloak'
version: 2.7.4
version: 2.7.4
### logging configuration
keycloak_quarkus_log: file
keycloak_quarkus_log_level: info
keycloak_quarkus_log_file: data/log/keycloak.log
keycloak_quarkus_log_format: '%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n'

View File

@@ -31,6 +31,9 @@ argument_specs:
default: "java-11-openjdk-headless"
description: "RHEL java package runtime"
type: "str"
keycloak_quarkus_java_home:
description: "JAVA_HOME of installed JRE, leave empty for using specified keycloak_jvm_package RPM path"
type: "str"
keycloak_quarkus_dest:
# line 13 of defaults/main.yml
default: "/opt/keycloak"
@@ -91,11 +94,27 @@ argument_specs:
default: "localhost"
description: "hostname"
type: "str"
keycloak_quarkus_http_enabled:
default: true
description: "Enable listener on HTTP port"
type: "bool"
keycloak_quarkus_http_port:
# line 29 of defaults/main.yml
default: 8080
description: "HTTP port"
type: "int"
keycloak_quarkus_https_enabled:
default: false
description: "Enable listener on HTTPS port"
type: "bool"
keycloak_quarkus_key_file:
default: "{{ keycloak.home }}/conf/server.key.pem"
description: "The file path to a private key in PEM format"
type: "str"
keycloak_quarkus_cert_file:
default: "{{ keycloak.home }}/conf/server.crt.pem"
description: "The file path to a server certificate or certificate chain in PEM format"
type: "str"
keycloak_quarkus_https_port:
# line 30 of defaults/main.yml
default: 8443
@@ -141,6 +160,10 @@ argument_specs:
default: false
description: "Whether to enable metrics"
type: "bool"
keycloak_quarkus_health_enabled:
default: true
description: "If the server should expose health check endpoints"
type: "bool"
keycloak_quarkus_ispn_user:
# line 46 of defaults/main.yml
default: "supervisor"
@@ -201,3 +224,31 @@ argument_specs:
default: "{{ keycloak_quarkus_default_jdbc[keycloak_quarkus_jdbc_engine].version }}"
description: "Version for JDBC driver"
type: "str"
keycloak_quarkus_log:
default: "file"
type: "str"
description: "Enable one or more log handlers in a comma-separated list"
keycloak_quarkus_log_level:
default: "info"
type: "str"
description: "The log level of the root category or a comma-separated list of individual categories and their levels"
keycloak_quarkus_log_file:
default: "data/log/keycloak.log"
type: "str"
description: "Set the log file path and filename relative to keycloak home"
keycloak_quarkus_log_format:
default: '%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n'
type: "str"
description: "Set a format specific to file log entries"
keycloak_quarkus_proxy_mode:
default: 'edge'
type: "str"
description: "The proxy address forwarding mode if the server is behind a reverse proxy"
keycloak_quarkus_start_dev:
default: False
type: "bool"
description: "Whether to start the service in development mode (start-dev)"
keycloak_quarkus_transaction_xa_enabled:
default: True
type: "bool"
description: "Enable or disable XA transactions which may not be supported by some DBMS"

View File

@@ -1,6 +1,4 @@
---
collections:
galaxy_info:
role_name: keycloak_quarkus
namespace: middleware_automation

View File

@@ -1,10 +1,8 @@
---
- name: Check packages to be installed
block:
- name: "Check if packages are already installed"
- name: "Check if packages are already installed" # noqa command-instead-of-module this runs faster
ansible.builtin.command: "rpm -q {{ packages_list | join(' ') }}"
args:
warn: no
register: rpm_info
changed_when: rpm_info.failed
@@ -14,9 +12,9 @@
packages_to_install: "{{ packages_to_install | default([]) + rpm_info.stdout_lines | map('regex_findall', 'package (.+) is not installed$') | flatten }}"
when: rpm_info.failed
- name: "Install packages: {{ packages_to_install }}"
- name: "Install packages: {{ packages_to_install | join(',') }}"
become: yes
ansible.builtin.yum:
name: "{{ packages_to_install }}"
state: present
when: packages_to_install | default([]) | length > 0
when: packages_to_install | default([]) | length > 0

View File

@@ -14,7 +14,7 @@
- name: "Configure firewall for {{ keycloak.service_name }} ports"
become: yes
firewalld:
ansible.posix.firewalld:
port: "{{ item }}"
permanent: true
state: enabled

View File

@@ -84,9 +84,9 @@
- local_archive_path.stat.exists
become: yes
- name: "Check target directory: {{ keycloak.home }}"
- name: "Check target directory: {{ keycloak.home }}/bin/"
ansible.builtin.stat:
path: "{{ keycloak.home }}"
path: "{{ keycloak.home }}/bin/"
register: path_to_workdir
become: yes
@@ -95,12 +95,12 @@
remote_src: yes
src: "{{ archive }}"
dest: "{{ keycloak_quarkus_dest }}"
creates: "{{ keycloak.home }}"
creates: "{{ keycloak.home }}/bin/"
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
become: yes
when:
- new_version_downloaded.changed or not path_to_workdir.stat.exists
- (not path_to_workdir.stat.exists) or new_version_downloaded.changed
notify:
- restart keycloak
@@ -108,4 +108,4 @@
ansible.builtin.debug:
msg: "{{ keycloak.home }} already exists and version unchanged, skipping decompression"
when:
- not new_version_downloaded.changed and path_to_workdir.stat.exists
- (not new_version_downloaded.changed) and path_to_workdir.stat.exists

View File

@@ -1,6 +1,5 @@
---
# tasks file for keycloak
- name: Check prerequisites
ansible.builtin.include_tasks: prereqs.yml
tags:
@@ -29,13 +28,42 @@
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
mode: 0644
become: yes
notify:
- restart keycloak
- name: "Configure quarkus config for keycloak service"
ansible.builtin.template:
src: quarkus.properties.j2
dest: "{{ keycloak.home }}/conf/quarkus.properties"
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
mode: 0644
become: yes
notify:
- restart keycloak
- name: Ensure logdirectory exists
ansible.builtin.file:
state: directory
path: "{{ keycloak.log.file | dirname }}"
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
mode: 0775
become: yes
- name: "Start and wait for keycloak service"
ansible.builtin.include_tasks: start.yml
- name: Check service status
ansible.builtin.command: "systemctl status keycloak"
register: keycloak_service_status
changed_when: False
changed_when: False
- name: Link default logs directory
ansible.builtin.file:
state: link
src: "{{ keycloak.log.file | dirname }}"
dest: /var/log/keycloak
force: yes
become: yes

View File

@@ -15,15 +15,6 @@
fail_msg: "Cannot install HA setup without a backend database service. Check keycloak_quarkus_ha_enabled and keycloak_quarkus_db_enabled"
success_msg: "{{ 'Configuring HA' if keycloak_quarkus_ha_enabled else 'Configuring standalone' }}"
# - name: Validate credentials
# ansible.builtin.assert:
# that:
# - (rhn_username is defined and keycloak_rhsso_enable) or not keycloak_rhsso_enable or keycloak_offline_install
# - (rhn_password is defined and keycloak_rhsso_enable) or not keycloak_rhsso_enable or keycloak_offline_install
# quiet: True
# fail_msg: "Cannot install Red Hat SSO without RHN credentials. Check rhn_username and rhn_password are defined"
# success_msg: "{{ 'Installing Red Hat Single Sign-On' if keycloak_rhsso_enable else 'Installing keycloak.org' }}"
- name: Ensure required packages are installed
ansible.builtin.include_tasks: fastpackages.yml
vars:
@@ -31,4 +22,5 @@
- "{{ keycloak_quarkus_jvm_package }}"
- unzip
- procps-ng
- initscripts
- initscripts
- tzdata-java

View File

@@ -1,4 +1,8 @@
---
- name: Determine JAVA_HOME for selected JVM RPM
ansible.builtin.set_fact:
rpm_java_home: "/etc/alternatives/jre_{{ keycloak_quarkus_jvm_package | regex_search('(?<=java-)[0-9.]+') }}"
- name: "Configure sysconfig file for keycloak service"
become: yes
ansible.builtin.template:
@@ -7,6 +11,8 @@
owner: root
group: root
mode: 0644
vars:
keycloak_rpm_java_home: "{{ rpm_java_home }}"
notify:
- restart keycloak

View File

@@ -1,3 +1,5 @@
# {{ ansible_managed }}
KEYCLOAK_ADMIN={{ keycloak_quarkus_admin_user }}
KEYCLOAK_ADMIN_PASSWORD='{{ keycloak_quarkus_admin_pass }}'
KEYCLOAK_ADMIN_PASSWORD='{{ keycloak_quarkus_admin_pass }}'
PATH={{ keycloak_java_home | default(keycloak_rpm_java_home, true) }}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
JAVA_HOME={{ keycloak_java_home | default(keycloak_rpm_java_home, true) }}

View File

@@ -1,51 +1,50 @@
# {{ ansible_managed }}
{% if keycloak_quarkus_db_enabled %}
# Database
# Database vendor [dev-file, dev-mem, mariadb, mssql, mysql, oracle, postgres]
#db=postgres
# The username of the database user.
#db-username=keycloak
# The password of the database user.
#db-password=password
# The full database JDBC URL. If not provided, a default URL is set based on the selected database vendor.
#db-url=jdbc:postgresql://localhost/keycloak
db={{ keycloak_quarkus_jdbc_engine }}
db-url={{ keycloak_quarkus_jdbc_url }}
db-username={{ keycloak_quarkus_db_user }}
db-password={{ keycloak_quarkus_db_pass }}
{% endif %}
# Observability
# If the server should expose metrics and healthcheck endpoints.
#metrics-enabled=true
metrics-enabled={{ keycloak_quarkus_metrics_enabled }}
health-enabled={{ keycloak_quarkus_health_enabled }}
# HTTP
http-enabled=true
http-port=8080
https-port=8443
# The file path to a server certificate or certificate chain in PEM format.
#https-certificate-file=${kc.home.dir}conf/server.crt.pem
# The file path to a private key in PEM format.
#https-certificate-key-file=${kc.home.dir}conf/server.key.pem
# The proxy address forwarding mode if the server is behind a reverse proxy.
#proxy=reencrypt
# Do not attach route to cookies and rely on the session affinity capabilities from reverse proxy
#spi-sticky-session-encoder-infinispan-should-attach-route=false
http-enabled={{ keycloak_quarkus_http_enabled }}
http-port={{ keycloak_quarkus_http_port }}
# HTTPS
https-port={{ keycloak_quarkus_https_port }}
{% if keycloak_quarkus_https_enabled %}
https-certificate-file={{ keycloak_quarkus_cert_file}}
https-certificate-key-file={{ keycloak_quarkus_key_file }}
{% endif %}
# Hostname for the Keycloak server.
hostname={{ keycloak_quarkus_host }}
hostname-path={{ keycloak_quarkus_http_relative_path }}
# Cluster
#cache=ispn
#Defines the cache mechanism for high-availability. [local, ispn]
#cache-config-file=conf/cache-ispn.xml
#Defines the file from which cache configuration should be loaded from.
#cache-stack=tcp
#Define the default stack to use for cluster communication and node discovery. [tcp, udp, kubernetes, ec2, azure, google]
{% if keycloak_quarkus_ha_enabled %}
cache=ispn
cache-config-file=cache-ispn.xml
cache-stack=tcp
{% endif %}
# Proxy
# The proxy address forwarding mode if the server is behind a reverse proxy. [edge, reencrypt, passthrough]
#proxy=
proxy={{ keycloak_quarkus_proxy_mode }}
# Do not attach route to cookies and rely on the session affinity capabilities from reverse proxy
#spi-sticky-session-encoder-infinispan-should-attach-route=false
# Transaction
transaction-xa-enabled={{ keycloak_quarkus_transaction_xa_enabled }}
# Logging
# The format of log entries.
#log-format=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n
# The log level of the root category or a comma-separated list of individual categories and their levels.
#log-level=info
log={{ keycloak_quarkus_log }}
log-level={{ keycloak.log.level }}
log-file={{ keycloak.log.file }}
log-file-format={{ keycloak.log.format }}

View File

@@ -7,8 +7,12 @@ After=network.target
Type=simple
EnvironmentFile=-/etc/sysconfig/keycloak
PIDFile={{ keycloak_quarkus_service_pidfile }}
ExecStart={{ keycloak.home }}/bin/kc.sh start
#--http-relative-path={{ keycloak_quarkus_http_relative_path }}
{% if keycloak_quarkus_start_dev %}
ExecStart={{ keycloak.home }}/bin/kc.sh start-dev
{% else %}
ExecStart={{ keycloak.home }}/bin/kc.sh start --log={{ keycloak_quarkus_log }}
{% endif %}
User={{ keycloak.service_user }}
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,19 @@
# {{ ansible_managed }}
{% if keycloak_quarkus_ha_enabled %}
quarkus.infinispan-client.server-list={{ keycloak_quarkus_ispn_url }}
quarkus.infinispan-client.client-intelligence=HASH_DISTRIBUTION_AWARE
quarkus.infinispan-client.use-auth=true
quarkus.infinispan-client.auth-username={{ keycloak_quarkus_ispn_user }}
quarkus.infinispan-client.auth-password={{ keycloak_quarkus_ispn_pass }}
quarkus.infinispan-client.auth-realm=default
quarkus.infinispan-client.auth-server-name=infinispan
quarkus.infinispan-client.sasl-mechanism={{ keycloak_quarkus_ispn_sasl_mechanism }}
{% if keycloak_quarkus_ispn_use_ssl %}
quarkus.infinispan-client.trust-store={{ keycloak_quarkus_ispn_trust_store_path }}
quarkus.infinispan-client.trust-store-password={{ keycloak_quarkus_ispn_trust_store_password }}
quarkus.infinispan-client.trust-store-type=jks
{% endif %}
#quarkus.infinispan-client.use-schema-registration=true
#quarkus.infinispan-client.auth-client-subject
#quarkus.infinispan-client.auth-callback-handler
{% endif %}

View File

@@ -4,8 +4,12 @@ keycloak:
config_dir: "{{ keycloak_quarkus_config_dir }}"
bundle: "{{ keycloak_quarkus_archive }}"
service_name: "keycloak"
health_url: "http://localhost:8080/realms/master/.well-known/openid-configuration"
health_url: "http://{{ keycloak_quarkus_host }}:{{ keycloak_quarkus_http_port }}/realms/master/.well-known/openid-configuration"
cli_path: "{{ keycloak_quarkus_home }}/bin/kcadm.sh"
service_user: "{{ keycloak_quarkus_service_user }}"
service_group: "{{ keycloak_quarkus_service_group }}"
offline_install: "{{ keycloak_quarkus_offline_install }}"
offline_install: "{{ keycloak_quarkus_offline_install }}"
log:
file: "{{ keycloak_quarkus_home }}/{{ keycloak_quarkus_log_file }}"
level: "{{ keycloak_quarkus_log_level }}"
format: "{{ keycloak_quarkus_log_format }}"

View File

@@ -8,13 +8,13 @@ Role Defaults
-------------
| Variable | Description | Default |
|:---------|:------------|:---------|
|:---------|:------------|:--------|
|`keycloak_admin_user`| Administration console user account | `admin` |
|`keycloak_host`| hostname | `localhost` |
|`keycloak_context`| Context path for rest calls | `/auth` |
|`keycloak_http_port`| HTTP port | `8080` |
|`keycloak_https_port`| TLS HTTP port | `8443` |
|`keycloak_auth_realm`| Name of the main authentication realm | `master` |
|`keycloak_rhsso_enable`| Define service is an upstream(Keycloak) or RHSSO | `master` |
|`keycloak_management_http_port`| Management port | `9990` |
|`keycloak_auth_client`| Authentication client for configuration REST calls | `admin-cli` |
|`keycloak_client_public`| Configure a public realm client | `True` |
@@ -71,6 +71,8 @@ Refer to [docs](https://docs.ansible.com/ansible/latest/collections/community/ge
```yaml
- name: <name of the client>
id: <id of the client>
client_id: <id of the client>
roles: <keycloak_client_default_roles>
realm: <name of the realm that contains the client>
public_client: <true for public, false for confidential>
@@ -78,6 +80,9 @@ Refer to [docs](https://docs.ansible.com/ansible/latest/collections/community/ge
users: <keycloak_client_users>
```
`name` and either `id` or `client_id` are required.
* `keycloak_client_users`, a list of:
```yaml

View File

@@ -4,7 +4,6 @@ keycloak_host: localhost
keycloak_http_port: 8080
keycloak_https_port: 8443
keycloak_management_http_port: 9990
keycloak_rhsso_enable: False
### Keycloak administration console user
keycloak_admin_user: admin
@@ -33,6 +32,7 @@ keycloak_admin_password: ''
# realm: "{{ keycloak_realm }}"
# public_client: "{{ keycloak_client_public }}"
# web_origins: "{{ keycloak_client_web_origins }}"
# redirect_uris: "{{ keycloak_client_redirect_uris }}"
# users: "{{ keycloak_client_users }}"
keycloak_clients: []

View File

@@ -26,11 +26,6 @@ argument_specs:
default: 9990
description: "Management port"
type: "int"
keycloak_rhsso_enable:
# line 7 of keycloak_realm/defaults/main.yml
default: false
description: "Enable Red Hat Single Sign-on"
type: "bool"
keycloak_admin_user:
# line 10 of keycloak_realm/defaults/main.yml
default: "admin"
@@ -96,3 +91,25 @@ argument_specs:
default: "http://{{ keycloak_host }}:{{ keycloak_management_http_port }}"
description: "URL for management console rest calls"
type: "str"
downstream:
options:
sso_version:
default: "7.5.0"
description: "Red Hat Single Sign-On version"
type: "str"
sso_dest:
default: "/opt/sso"
description: "Root installation directory"
type: "str"
sso_installdir:
default: "{{ sso_dest }}/rh-sso-{{ sso_version.split('.')[0] }}.{{ sso_version.split('.')[1] }}"
description: "Installation path for Red Hat SSO"
type: "str"
sso_apply_patches:
default: False
description: "Install Red Hat SSO most recent cumulative patch"
type: "bool"
sso_enable:
default: True
description: "Enable Red Hat Single Sign-on installation"
type: "str"

View File

@@ -19,5 +19,4 @@ galaxy_info:
- keycloak
- redhat
- rhel
- rhn
- sso

View File

@@ -5,7 +5,7 @@
method: POST
body: "client_id={{ keycloak_auth_client }}&username={{ keycloak_admin_user }}&password={{ keycloak_admin_password }}&grant_type=password"
validate_certs: no
no_log: True
no_log: "{{ keycloak_no_log | default('True') }}"
register: keycloak_auth_response
until: keycloak_auth_response.status == 200
retries: 5
@@ -27,7 +27,7 @@
ansible.builtin.uri:
url: "{{ keycloak_url }}{{ keycloak_context }}/admin/realms"
method: POST
body: "{{ lookup('template','realm.json.j2') }}"
body: "{{ lookup('template', 'realm.json.j2') }}"
validate_certs: no
body_format: json
headers:
@@ -36,7 +36,7 @@
when: keycloak_realm_exists.status == 404
- name: Create user federation
community.general.keycloak_user_federation:
middleware_automation.keycloak.keycloak_user_federation:
auth_keycloak_url: "{{ keycloak_url }}{{ keycloak_context }}"
auth_realm: "{{ keycloak_auth_realm }}"
auth_username: "{{ keycloak_admin_user }}"
@@ -48,13 +48,24 @@
provider_type: "{{ item.provider_type | default(org.keycloak.storage.UserStorageProvider) }}"
config: "{{ item.config }}"
mappers: "{{ item.mappers | default(omit) }}"
no_log: True
no_log: "{{ keycloak_no_log | default('True') }}"
register: create_user_federation_result
loop: "{{ keycloak_user_federation | flatten }}"
when: keycloak_user_federation is defined
- name: Validate Keycloak clients
ansible.builtin.assert:
that:
- item.name is defined and item.name | length > 0
- (item.client_id is defined and item.client_id | length > 0) or (item.id is defined and item.id | length > 0)
fail_msg: "For each keycloak client, attributes `name` and either `id` or `client_id` is required"
quiet: True
loop: "{{ keycloak_clients | flatten }}"
loop_control:
label: "{{ item.name | default('unnamed client') }}"
- name: Create or update a Keycloak client
community.general.keycloak_client:
middleware_automation.keycloak.keycloak_client:
auth_client_id: "{{ keycloak_auth_client }}"
auth_keycloak_url: "{{ keycloak_url }}{{ keycloak_context }}"
auth_realm: "{{ keycloak_auth_realm }}"
@@ -79,8 +90,9 @@
service_accounts_enabled: "{{ item.service_accounts_enabled | default(omit) }}"
public_client: "{{ item.public_client | default(False) }}"
protocol: "{{ item.protocol | default(omit) }}"
attributes: "{{ item.attributes | default(omit) }}"
state: present
no_log: True
no_log: "{{ keycloak_no_log | default('True') }}"
register: create_client_result
loop: "{{ keycloak_clients | flatten }}"
when: (item.name is defined and item.client_id is defined) or (item.name is defined and item.id is defined)
@@ -97,4 +109,4 @@
loop: "{{ keycloak_clients | flatten }}"
loop_control:
loop_var: client
when: "'users' in client"
when: "'users' in client"

View File

@@ -1,5 +1,5 @@
- name: Create client roles
community.general.keycloak_role:
middleware_automation.keycloak.keycloak_role:
name: "{{ item }}"
realm: "{{ client.realm }}"
client_id: "{{ client.name }}"
@@ -10,4 +10,4 @@
auth_password: "{{ keycloak_admin_password }}"
state: present
loop: "{{ client.roles | flatten }}"
no_log: True
no_log: "{{ keycloak_no_log | default('True') }}"

View File

@@ -14,7 +14,7 @@
body: "client_id={{ keycloak_auth_client }}&username={{ keycloak_admin_user }}&password={{ keycloak_admin_password }}&grant_type=password"
validate_certs: no
register: keycloak_auth_response
no_log: True
no_log: "{{ keycloak_no_log | default('True') }}"
until: keycloak_auth_response.status == 200
retries: 5
delay: 2