Keycloak lab SSO — federate and prove the issuer

Projects · Guides

Keycloak lab SSO — federate and prove the issuer

Done: Keycloak realm lab answers on the TLS edge, federates FreeIPA read-only, and you did not bounce compose to make the check pass.

Requirements

This page checks a Keycloak guest that is already up. It does not clone a guest or run compose as a health check.

Hardware

  • An existing Keycloak guest with postgres and keycloak in compose.
  • An existing FreeIPA primary. Keycloak reads it. It does not store a second password.
  • A TLS reverse proxy. Browsers hit the edge, not the guest backend port.
  • A jump host that can reach the edge and the guest.

OS

  • Jump host with dig, curl, jq, and nc.
  • Keycloak guest with Docker Compose at /opt/keycloak. Change the path if yours differs.

Names — change these

Placeholder Change it Role
keycloak.example.lab Yes Public name. Points at the TLS edge, not the guest
ipa.example.lab Yes FreeIPA primary for LDAP
dns-path.example.lab Yes Fleet DNS that clients query
lab Yes, if your app realm differs App realm. Keep master for break-glass admin
freeipa-ldap Yes, if the federation name differs User federation
dc=idm,dc=example,dc=lab Yes LDAP suffix. Match the IPA domain
People/hosts (ipa.example.lab)
        ↓ LDAP federation (read-only bind)
Keycloak realm lab (OIDC issuer)
        ↓ OIDC
Apps (Forgejo Sign in with Keycloak)
        ↑ HTTPS
TLS reverse proxy → keycloak.example.lab

DNS

  • keycloak.example.lab resolves through dns-path.example.lab to the TLS edge.
  • Resolve ipa.example.lab to the current FreeIPA face. Not a retired address.

Ports

Service Port Protocol Notes
Issuer 443 TCP https://keycloak.example.lab/realms/lab
Admin console 443 TCP /admin/ — lab/edge only
Compose backend 8080 TCP HTTP on the guest, lab LAN only
LDAP 389 TCP Keycloak guest to ipa.example.lab
LDAPS 636 TCP Keycloak guest to ipa.example.lab

Packages and compose

Where What Notes
Jump host dig, curl, jq, nc Read-only check
Keycloak guest compose services keycloak and postgres Pattern /opt/keycloak

Federation

Field Value Change it
Issuer https://keycloak.example.lab/realms/lab Yes — host and realm
Discovery https://keycloak.example.lab/realms/lab/.well-known/openid-configuration Yes
Vendor FreeIPA / RHDS-compatible No
Edit mode READ_ONLY No
Users DN cn=users,cn=accounts,dc=idm,dc=example,dc=lab Yes — dc components
Bind DN uid=keycloak-ldap,cn=users,cn=accounts,dc=idm,dc=example,dc=lab Yes — a service user, not IPA admin

Procedure

Step 1 — Open the admin console

This step exists because a browser on guest port 8080 is the backend, not the admin console the TLS edge serves, and because a healthy guest is a check, not a compose bounce.

  1. If keycloak.example.lab already answers, leave the install alone. Do not clone a guest, bounce compose, rewrite the hostname URL env, or move the TLS proxy to make this check pass.

  2. Open the admin URL in a browser. Use the TLS name, not :8080.

    https://keycloak.example.lab/admin/

    Worked when the browser redirects into a login form. Do not publish admin.

  3. Sign in as the break-glass admin for realm master. The form asks:

    Username: admin
    Password: (the break-glass admin password from install)

    Type those values. This is not an IPA user unless you already wired that.

  4. In the top-left realm menu, choose lab. Do not stay in master.

    Worked when the overview for realm lab loads.

  5. From the jump host, ask the same admin URL for its status code.

    curl -sk -o /dev/null -w 'admin:%{http_code}\n' \
      https://keycloak.example.lab/admin/

    Worked when:

    admin:302
Keycloak admin console Lab SSO realm overview; hostname scrubbed; no password dialog
Keycloak admin console — realm lab overview (hostname scrubbed; disposable sandbox capture).

Step 2 — Confirm LDAP federation

This step exists because the issuer can answer while LDAP to FreeIPA is down, and because a writable federation would store passwords in Keycloak.

  1. Stay in realm lab. In the left menu, open User federation.

    Worked when the list shows freeipa-ldap. Change that name if yours differs.

  2. Open freeipa-ldap.

    Worked when the settings page loads. Do not click Save if you did not change anything.

  3. Read Vendor. Worked when it is FreeIPA / RHDS-compatible.

  4. Read Edit mode. Worked when it is READ_ONLY.

  5. Read Users DN. Worked when it matches the table:

    cn=users,cn=accounts,dc=idm,dc=example,dc=lab
  6. Read Bind DN. Worked when it is a service user, not IPA admin:

    uid=keycloak-ldap,cn=users,cn=accounts,dc=idm,dc=example,dc=lab

    The bind password stays on the guest. Leave that field alone.

    Keycloak LDAP user federation settings with Bind DN and Users DN placeholders; bind credentials empty
    LDAP user federation — Bind DN and Users DN (example.lab; bind credentials empty).
  7. SSH to the Keycloak guest. Check LDAP reachability from there, not only from the jump host. The guest is the LDAP client.

    nc -zv ipa.example.lab 389

    Worked when:

    Connection to ipa.example.lab 389 port [tcp/ldap] succeeded!
  8. Check LDAPS the same way.

    nc -zv ipa.example.lab 636

    Worked when:

    Connection to ipa.example.lab 636 port [tcp/ldaps] succeeded!

Step 3 — Check the issuer

This step exists because a 302 on /admin/ is not the app issuer, and discovery must print the HTTPS realm URL apps will use.

  1. From the jump host, resolve the public name through fleet DNS.

    dig @dns-path.example.lab keycloak.example.lab +short

    Worked when you get the current TLS-edge A, not a retired address. Shape:

    192.0.2.20
  2. Ask the realm issuer for its status code. Substitute your names.

    curl -sk -o /dev/null -w 'issuer:%{http_code}\n' \
      https://keycloak.example.lab/realms/lab

    Worked when:

    issuer:200
  3. Read the issuer field from OpenID discovery.

    curl -sk https://keycloak.example.lab/realms/lab/.well-known/openid-configuration \
      | jq -r .issuer

    Worked when:

    https://keycloak.example.lab/realms/lab
  4. On the Keycloak guest, check compose. Do not restart it.

    cd /opt/keycloak && docker compose ps

    Worked when keycloak and postgres show Up. Shape:

    NAME                    SERVICE     STATUS
    keycloak-postgres-1     postgres    Up
    keycloak-keycloak-1     keycloak    Up

    Metadata may still advertise the HTTPS issuer while the backend is HTTP. That is fine.

OpenID well-known JSON for realm lab with issuer host scrubbed to auth.example.lab
OpenID discovery — well-known JSON for realm lab (hostnames example.lab).

Step 4 — Confirm the app client

This step exists because Forgejo will not redirect until the client redirect URI is the app’s public HTTPS URL through the TLS edge, not the raw backend address.

  1. Stay in realm lab. In the left menu, open Clients.

    Worked when the client list loads.

  2. Open the openid-connect client for the app. Forgejo is the documented third piece. The client id in this pattern is forgejo. Change it if yours differs. Do not create a new client on a healthy guest.

  3. Open the Settings tab. Read Valid redirect URIs.

    Worked when the URI is the app’s public HTTPS callback through the TLS edge, not the raw backend. Shape:

    https://git.example.lab/user/oauth2/Keycloak/callback
  4. Read Web origins.

    Worked when the origin is the app’s public HTTPS URL through the TLS edge. Shape:

    https://git.example.lab
    Keycloak Clients — Forgejo openid-connect client (Valid redirect URIs and Web origins). Shot missing.
  5. Point the app at issuer https://keycloak.example.lab/realms/lab. The sign-in button and redirect check live in the SSO path guide. Do not run forgejo admin auth add-oauth on a healthy Git guest as a health check.

Verification

$ dig @dns-path.example.lab keycloak.example.lab +short
# expect: current TLS-edge A — not a retired name

$ curl -sk -o /dev/null -w 'issuer:%{http_code}\n' \
  https://keycloak.example.lab/realms/lab
issuer:200

$ curl -sk https://keycloak.example.lab/realms/lab/.well-known/openid-configuration \
  | jq -r .issuer
https://keycloak.example.lab/realms/lab

This check writes no install log.

Next

Prove Forgejo hands the browser to this issuer, then stop at the redirect. See the lab SSO path guide.

Lab only · Keycloak check · recut 2026-10-04