Skip to content

Commit 234cc50

Browse files
rjeffmanabbra
authored andcommitted
ipa-keycloak: add demo for external IDP using Keycloak
This demo is based on the ipalab-config configuration of Keycloak, which is based on the official Keycloak container image, using a self-signed certificate for HTTPS connection. Keycloak runs in production mode. Helper scripts and playbooks for configuring Keycloak and IPA are provided. Signed-off-by: Rafael Guterres Jeffman <[email protected]>
1 parent a1208e2 commit 234cc50

File tree

8 files changed

+555
-0
lines changed

8 files changed

+555
-0
lines changed

ipalab-config/ipa-keycloak/README.md

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
# FreeIPA external IDP integration with Keycloak
2+
3+
In this example an environment with a [FreeIPA](https://freeipa.org)
4+
and a [Keycloak](https://keycloak.org) servers is created so that user
5+
authentication in IPA, using an external IDP is performed.
6+
7+
## Preparing the environment
8+
9+
Create the configuration:
10+
11+
```
12+
python3 -m venv /tmp/ipalab
13+
. /tmp/ipalab/bin/activate
14+
pip install -r requirements.txt
15+
```
16+
17+
Build the container image and instantiate containers:
18+
19+
```
20+
ipalab-config lab_ipa_keycloak.yml
21+
cd ipa-keycloak-idp
22+
podman-compose build
23+
podman-compose up -d
24+
```
25+
26+
At this point, you'll have two containers, one based on the oficial
27+
Keycloak container image, and one that can have IPA deployed to.
28+
29+
Deploy the IPA cluster using
30+
[ansible-freeipa](https://gtihub.com/freeipa/ansible-freeipa):
31+
32+
```
33+
ansible-galaxy collection install \
34+
freeipa.ansible_freeipa \
35+
containers.podman
36+
ansible-playbook -i inventory.yml \
37+
${HOME}/.ansible/collections/ansible_collections/freeipa/ansible_freeipa/playbooks/install-cluster.yml
38+
```
39+
40+
The provided Keycloak container uses a self-signed certificate that is
41+
unkown to the IPA container. The certificate is found in the container
42+
`keycloak` at the path `/opt/keycloak/conf/cert.pem`. This certificate
43+
must be added to the list of trusted certificates on the `server`
44+
container. This can be achieved by executing:
45+
46+
```
47+
keycloak/trust_keycloak.sh server
48+
```
49+
50+
## Using Keycloak web interface
51+
52+
Since the whole environment runs using rootles containers, in a Podman
53+
virtual network, direct access to the host ports is not possible, but
54+
can be achieved using `podman unshare`. For example, to _ssh_ into the
55+
container (if `sshd` is available) or to access the `httpd` server.
56+
57+
When using [Firefox](https://mozilla.org/firefox) a profile is needed
58+
to access the containers URLs, and to ease access the script
59+
`scripts/open-firefox.sh` is provided. This script will manage the
60+
Firefox profile and call `firefox` with the proper configuration for
61+
`podman unshare`, allowing access to Keycloak and WebUI.
62+
63+
Before starting Firefox, add the entries found in the generated `hosts`
64+
file to your machine `/etc/hosts` so the host names can be resolved. The
65+
file `hosts` has all the containers entries needed, add it with:
66+
67+
```
68+
sudo bash -c "cat hosts >> /etc/hosts"
69+
```
70+
71+
Start the Keycloak web interface with:
72+
73+
```
74+
scripts/open-firefox.sh https://keycloak.example.test:8443
75+
```
76+
77+
In a similar fashion you can access the IPA WebUI with:
78+
79+
```
80+
scripts/open-firefox.sh https://server.ipa.test:443
81+
```
82+
83+
84+
## Setting up Keycloak as an External IDP for IPA
85+
86+
In order to perform OAuth 2.0 device authorization grant flow against
87+
an IdP, an OAuth 2.0 client has to be registered with the IdP and a
88+
capability to allow the device authorization grant has to be given to it.
89+
90+
On Keycloak, this is achieved by setting _OAuth 2.0 Device Authorization Grant_,
91+
in the _Authentication Flow_, to `true`.
92+
93+
The configuration required for the Keycloak client is:
94+
95+
```json
96+
{
97+
"enabled" : true,
98+
"clientAuthenticatorType" : "client-secret",
99+
"redirectUris" : [ "https://${IPASERVER}/ipa/idp/*" ],
100+
"webOrigins" : [ "https://${IPASERVER}" ],
101+
"protocol" : "openid-connect",
102+
"attributes" : {
103+
"oauth2.device.authorization.grant.enabled" : "true",
104+
"oauth2.device.polling.interval": "5"
105+
}
106+
}
107+
```
108+
109+
Note that `oauth2.device.authorization.grant.enabled` is enabled.
110+
111+
To create the OIDC client for Keycloak, you can use the script provided
112+
by `ipalab-config`. The script requires the IPA FQDN, an OIDC client ID
113+
and the OIDC client password. Execute:
114+
115+
```
116+
keycloak/keycloak_add_oidc_client.sh \
117+
server.ipa.test \
118+
ipa_oidc_client \
119+
Secret123
120+
```
121+
122+
Now, we can set the external IDP on IPA, either by using the `idp-add`
123+
CLI command, or with a playbook, using ansible-freeipa:
124+
125+
```
126+
ansible-playbook -i inventory.yml playbooks/idp_keycloak.yml
127+
```
128+
129+
## Testing the setup
130+
131+
To test the setup, create a user on Keycloak using:
132+
133+
```
134+
keycloak/keycloak_add_user.sh jdoe [email protected] userPASS
135+
```
136+
137+
Perform login with user `jdoe` on Keycloak web interface:
138+
139+
```
140+
scripts/open-firefox.sh https://keycloak.example.test:8443/realms/master/account
141+
```
142+
143+
And add a user on IPA, with authorization through IDP:
144+
145+
```
146+
ansible-playbook -i inventory.yml playbooks/add_user_auth_idp.yml
147+
```
148+
149+
Now to authorize the new user, the commands should be execute on the
150+
`server` container:
151+
152+
```
153+
podman exec -it server bash
154+
```
155+
156+
On the `server`, execute:
157+
158+
```
159+
[server]$ kinit -n -c ./fast.ccache
160+
[server]$ kinit -T ./fast.ccache jdoe
161+
Authenticate at https://keycloak.example.test:8443/realms/master/device?user_code=GKTH-BJSS and press ENTER.:
162+
```
163+
164+
Copy the provided link to the same Firefox window as `jdoe` has logged in, and grant the required authorization.
165+
166+
When back to the console, type ENTER, and if everything went fine, user will have a TGT for `jdoe` on IPA side:
167+
168+
```
169+
[server]$ klist -A
170+
Ticket cache: FILE:/tmp/krb5cc_0
171+
Default principal: [email protected]
172+
173+
Valid starting Expires Service principal
174+
05/09/25 16:34:00 05/10/25 16:18:52 krbtgt/[email protected]
175+
```
176+
177+
## Troubleshooting
178+
179+
If anything goes wrong, you can search `journalctl` for `ipa-otpd`
180+
entries.
181+
182+
To increase the log level, set the `oidc_child` debug level in
183+
`/etc/ipa/default.conf` by setting:
184+
185+
```
186+
[global]
187+
oidc_child_debug_level=10
188+
```
189+
190+
Valid values are between 0 and 10 and any value above 6 includes debug
191+
output from `libcurl` utility.
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
Output out.webm
2+
Set FontSize 16
3+
Set Width 1200
4+
Set Height 800
5+
Require python3
6+
Require podman
7+
Sleep 2s
8+
Hide
9+
Type "export PS1='[\W]$ '"
10+
Enter
11+
Wait /.*[\$#] *$/
12+
Type "command -v deactivate && deactivate"
13+
Enter
14+
Wait /.*[\$#] *$/
15+
Type "rm -rf /tmp/ipalab-ipa-keycloak"
16+
Enter
17+
Wait /.*[\$#] *$/
18+
Ctrl+L
19+
Show
20+
Type "`# Install support software`"
21+
Sleep 500ms
22+
Enter
23+
Sleep 5s
24+
Type "python3 -m venv /tmp/ipalab-ipa-keycloak"
25+
Sleep 500ms
26+
Enter
27+
Wait /.*[\$#] *$/
28+
Type ". /tmp/ipalab-ipa-keycloak/bin/activate"
29+
Sleep 500ms
30+
Enter
31+
Wait /.*[\$#] *$/
32+
Type "pip install -r requirements.txt"
33+
Sleep 500ms
34+
Enter
35+
Wait /.*[\$#] *$/
36+
Sleep 3s
37+
Ctrl+L
38+
Type "`# Create and activate the configuration`"
39+
Sleep 500ms
40+
Enter
41+
Sleep 5s
42+
Type "ipalab-config lab_ipa_keycloak.yml"
43+
Sleep 500ms
44+
Enter
45+
Wait /.*[\$#] *$/
46+
Type "cd ipa-keycloak-idp"
47+
Sleep 500ms
48+
Enter
49+
Wait /.*[\$#] *$/
50+
Type "podman-compose build"
51+
Sleep 500ms
52+
Enter
53+
Wait /.*[\$#] *$/
54+
Type "podman-compose up -d"
55+
Sleep 500ms
56+
Enter
57+
Wait /.*[\$#] *$/
58+
Type "`# Trust Keycloak self-signed certificate`"
59+
Sleep 500ms
60+
Enter
61+
Sleep 5s
62+
Type "keycloak/trust_keycloak.sh server"
63+
Sleep 500ms
64+
Enter
65+
Wait /.*[\$#] *$/
66+
Sleep 3s
67+
Ctrl+L
68+
Type "`# Install containers.podman collection`"
69+
Sleep 500ms
70+
Enter
71+
Sleep 5s
72+
Type "ansible-galaxy collection install containers.podman"
73+
Sleep 500ms
74+
Enter
75+
Wait /.*[\$#] *$/
76+
Type "`# Install freeipa.ansible_freeipa collection`"
77+
Sleep 500ms
78+
Enter
79+
Sleep 5s
80+
Type "ansible-galaxy collection install freeipa.ansible_freeipa"
81+
Sleep 500ms
82+
Enter
83+
Wait /.*[\$#] *$/
84+
Sleep 3s
85+
Ctrl+L
86+
Type "`# Video cut to deploy IPA cluster`"
87+
Sleep 500ms
88+
Enter
89+
Sleep 5s
90+
Type "`# ansible-playbook -i inventory.yml ${ansible_freeipa_collection_path}install-cluster.yml`"
91+
Sleep 500ms
92+
Enter
93+
Sleep 5s
94+
Hide
95+
Type "ansible-playbook -i inventory.yml ~/.ansible/collections/ansible_collections/freeipa/ansible_freeipa/playbooks/install-cluster.yml"
96+
Enter
97+
Wait@10m /.*[\$#] *$/
98+
Show
99+
Type "`# Create Keycloak OIDC client`"
100+
Sleep 500ms
101+
Enter
102+
Sleep 5s
103+
Type "keycloak/keycloak_add_oidc_client.sh server.ipa.test ipa_oidc_client Secret123"
104+
Sleep 500ms
105+
Enter
106+
Wait /.*[\$#] *$/
107+
Type "`# Configure IDP endpoint in IPA`"
108+
Sleep 500ms
109+
Enter
110+
Sleep 5s
111+
Type "ansible-playbook -i inventory.yml playbooks/idp_keycloak.yml"
112+
Sleep 500ms
113+
Enter
114+
Wait /.*[\$#] *$/
115+
Type "`# Test IDP connection with user 'jdoe'`"
116+
Sleep 500ms
117+
Enter
118+
Sleep 5s
119+
Type "`# Create user on Keycloak`"
120+
Sleep 500ms
121+
Enter
122+
Sleep 5s
123+
Type "keycloak/keycloak_add_user.sh jdoe [email protected] userPASS"
124+
Sleep 500ms
125+
Enter
126+
Wait /.*[\$#] *$/
127+
Type "`# Add user to IPA`"
128+
Sleep 500ms
129+
Enter
130+
Sleep 5s
131+
Type "ansible-playbook -i inventory.yml playbooks/add_user_auth_idp.yml"
132+
Sleep 500ms
133+
Enter
134+
Wait /.*[\$#] *$/
135+
Sleep 5s
136+
Ctrl+L
137+
Type "`# kinit user jdoe on IPA server`"
138+
Sleep 500ms
139+
Enter
140+
Sleep 5s
141+
Type "podman exec server kinit -n -c /fast.ccache"
142+
Sleep 500ms
143+
Enter
144+
Wait /.*[\$#] *$/
145+
Type "podman exec server kinit -T /fast.ccache jdoe"
146+
Sleep 500ms
147+
Enter
148+
Wait@5s /.*[\$#] *$/
149+
Type "`# Some issues may be present in this demo, and for a complete execution, access to a browser is needed.`"
150+
Sleep 500ms
151+
Enter
152+
Sleep 5s
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
lab_name: ipa-keycloak-idp
3+
subnet: "192.168.14.0/24"
4+
extra_data:
5+
- playbooks
6+
- scripts
7+
external:
8+
hosts:
9+
- name: keycloak
10+
hostname: keycloak.example.test
11+
role: keycloak
12+
options:
13+
admin_username: admin
14+
admin_password: Secret123
15+
ipa_deployments:
16+
- name: ipa
17+
domain: ipa.test
18+
admin_password: SomeADMINpassword
19+
dm_password: SomeDMpassword
20+
cluster:
21+
servers:
22+
- name: server
23+
# capabilities: ["DNS"]
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
- name: Add IPA user using IDP authorization
3+
hosts: ipaserver
4+
become: false
5+
gather_facts: false
6+
7+
collections:
8+
- freeipa.ansible_freeipa
9+
10+
module_defaults:
11+
group/freeipa.ansible_freeipa.modules:
12+
ipaadmin_password: SomeADMINpassword
13+
14+
tasks:
15+
- name: Ensure user 'jdoe' exists and uses 'idp' for authorization
16+
ipauser:
17+
name: jdoe
18+
first: John
19+
last: doe
20+
21+
idp: keycloak-idp
22+
idp_user_id: [email protected]
23+
userauthtype: idp

0 commit comments

Comments
 (0)