Univention Bugzilla – Bug 49006
OpenID-Connect Login at UMC
Last modified: 2024-02-15 11:40:45 CET
The UMC-Server doesn't provide a login via OpenID connect. Would be nice to implement this.
A implementation is available at: https://github.com/univention/univention-corporate-server/tree/fbest/43633-unify-umc-server-oidc
(In reply to Florian Best from comment #1) > A implementation is available at: > https://github.com/univention/univention-corporate-server/tree/fbest/43633- > unify-umc-server-oidc This has been moved to MR https://git.knut.univention.de/univention/ucs/-/merge_requests/802.
> commit 531ca18e924e8b425513f8b62ab9d9ba3c7cd0a8 feat(crudeoauth): Implement crudeoauth Introduce a new project `crudeoauth`, which is a homage to `crudesaml` but instead validating OAuth 2.0 Access Tokens in form of a JWT. It is following RFC 7628 (A Set of Simple Authentication and Security Layer (SASL) Mechanisms for OAuth) to provide a SASL mechanism `OAUTHBEARER` in the package `libsasl2-modules-oauthbearer`. This is suitable to be used the openldap server via the `/etc/ldap/sasl2/slapd.conf` configuration file. It provides configuration options like: ``` mech_list: … OAUTHBEARER oauthbearer_grace: 3 oauthbearer_userid: preferred_username oauthbearer_trusted_jwks0: /usr/share/oidc/file_containing_the_authorization_server_certificates_as.jwks oauthbearer_trusted_iss0: https://sso.example.org/realms/master oauthbearer_trusted_aud0: ldaps://example.org/ oauthbearer_trusted_azp0: https:/client.example.org/oidc/ ``` The username is read from the access token and used as `authcid`. A optional `authzid` might be provided, and is used if the LDAP server allows it. Example configuration: ``` authz-regexp uid=([^,]*),cn=oauthbearer,cn=auth ldap:///dc=example,dc=org??sub?uid=$1 ``` Additionally a PAM module `pam_oauthbearer.so` is provided in the package `libpam-oauthbearer`, doing the same validations and having equivalent configuration options via the pam stack definition. ``` auth sufficient pam_oauthbearer.so grace=3 userid=preferred_username iss=https://sso.example.org/realms/master jwks=/usr/share/oidc/file_containing_the_authorization_server_certificates_as.jwks trusted_aud=ldaps://example.org/ trusted_azp=https:/client.example.org/oidc/ ``` univention/customers/dataport/upx/iam-team#18 > commit ae19809ce37cbcb93b005c989610a99128822b78 feat(uldap): add bind_oauthbearer(authzid, access_token) provide `bind_oauthbearer(authzid, access_token)` method for `univention.uldap.access` and `univention.admin.uldap.access` which does a SASL LDAP bind using the `OAUTHBEARER` scheme. The `authzid` is optional and should be given as None. Maybe in the future this is helpful to impersonate other users for certain service accounts (e.g. the Portal). > commit 4a1941af242bfdc65f174430a69f42cd90664ed8 feat(ldap): integrate libsasl2-modules-oauthbearer into LDAP server Configure `slapd` to provide the `OAUTHBEARER` SASL mechanism. By configuring `/etc/ldap/sasl2/slapd.conf` via the UCR variables `ldap/server/sasl/oauthbearer/.*` and map `uid=.*,cn=oauthbearer,cn=auth` to users in the `authz-regexp`. TODO: we should move `management/univention-management-console/conffiles/etc/ldap/sasl2/slapd.conf` to the `univention-ldap` package. > commit d6203df6682d34355c7c15358608db1963463529 feat(umc): Implement OpenID Connect authentication for UMC Implement UMC as OpenID Connect relying party. Authentication is done via the Authorization Code Flow, which is extended with PKCE (Proof Code for Code Exchange). For private clients PKCE is necessary to prevent "authorization code injection" in certain scenarios. It provides user initiated logout, which will make a global logout at the OpenID-Provider. Both frontchannel and backchannel is implemented, while UMC only uses frontchannel. It provides a Single Logout endpoint for logouts initiated by the OpenID Provider, e.g. when another App logs out or manually via the Keycloak Admin Console. Both frontchannel and backchannel is implemented, while the Relying Party is configured to do the backchannel logout. The ID token is verified using the keys from the OpenID provider, which are downloaded when configuring the issuer via UCR. This happens during the executing the joinscript, but only if keycloak is installed. The upgrade doesn't increase the joinscript version. To make it work manually after the latest version of Keycloak is installed, `univention-run-join-scripts --force --run-scripts 92univention-management-console-web-server.inst` can be executed. This also sets up the OAUTHBEARER SASL module configuration for the LDAP server and the PAM configuration for the UMC PAM stack. And a pseudo-audience `ldap://$domainname` is added to the UMC Relying Party client. The SASL and PAM module will require one of the audience `ldap://$domainname` or `ldap://$hostname.$domainname`. UMC additionally verifies the authorized party (azp) of the ID token, which must match the Client ID of UMC. At the authorization server, the user must consent once to give access to UDM REST API and LDAP server while logging in. For easier testing purposes and probably future service-to-service communication UMC also acceept HTTP Bearer Authentication with a Access Token as JWT, which must contain the `uid` claim. This will probably be useful for the portal. Currently we cannot access the userinfo endpoint because our Keycloak doesn't provide the `openid` scope. The access tokens are by default valid for 5 minutes, the refresh token for 30 minutes. If a request comes in after the access token is expired it is tried to get a new one using the refresh token - via backchannel operation which doesn't involve the browser. Via `/univention/oidc/.well-known/outh-client` the client metadata, suitable for Dynamic Client Registration can be obtained. The URL is officially incorrect, as it must be `/.well-known/outh-client/univention/oidc` instead. Testing can be done via: 1. Login via Browser Authorization Code Flow with PKC `firefox http://FQDN/univention/oidc/` 2. Login via HTTP Bearer authentication, using "Resource Owner Password Credentials" grant. ```bash client_id=$(ucr get umc/oidc/$(hostname -f)/client-id) token=$(curl -s "https://$(ucr get keycloak/server/sso/fqdn)/realms/ucs/protocol/openid-connect/token" -d "grant_type=password&username=Administrator&password=univention&scope=openid&client_id=$client_id&client_secret=$(cat /etc/umc-oidc.secret)" | jq -r '.access_token') echo "$token" | python3 -m univention.management.console.oauth curl http://localhost/univention/command/udm/query -H "Authorization: Bearer $token" -H 'Content-Type: application/json' -d '{"options": {"objectProperty": "username", "objectPropertyValue":"Administrator"},"flavor": "users/user"}' ``` > commit 5d3f345c81d7a3e369b03c3ed8fa15d9c3903906 feat(udm-rest): Add OAuth 2.0 authorization for UDM REST API Implement the UDM REST API as OAuth 2.0 Resource Server by accepting Access Tokens using the HTTP Bearer authentication schema. The UDM REST API just forwards the access token to the LDAP server by using the OAUTHBEARER SASL mechanism for the LDAP bind. Therefor UDM REST API is something like a proxy/gateway and not a OAuth 2.0 client or OIDC Relying Party. The access tokens created by the UMC Relying Party are suitable to use this, as they contain the required `ldap://$domainname/` audience. The access token is accessible by all UMC modules. Example usage for test cases: ```bash client_id=$(ucr get umc/oidc/$(hostname -f)/client-id) secret=$(cat /etc/umc-oidc.secret)" token=$(curl -s "https://$(ucr get keycloak/server/sso/fqdn)/realms/ucs/protocol/openid-connect/token" -d "grant_type=password&username=Administrator&password=univention&scope=openid&client_id=$client_id&client_secret=$secret" | jq -r '.access_token') echo "$token" | python3 -m univention.management.console.oidc curl -H 'Accept: application/json' http://localhost/univention/udm/ldap/base/ -H "Authorization: Bearer $token" -i ``` > commit 7fcefb436a922fdc74e6b8a41274a4cb78110a45 refactor(keycloak): unify JSON printing > commit 7aa7fe69345046db892f1834ea36f222ee554de5 feat(keycloak): Add more required oidc/rp options * add various `oidc/rp` creation parameters * add `oidc/rp get --client-id …` * add `oidc/rp remove` * add `secrets --as-json` yder.yaml 531ca18e924e | feat(crudeoauth): Implement crudeoauth univention-python.yaml ae19809ce37c | feat(uldap): add bind_oauthbearer(authzid, access_token) univention-python (13.0.5-1) ae19809ce37c | feat(uldap): add bind_oauthbearer(authzid, access_token) univention-management-console.yaml d6203df6682d | feat(umc): Implement OpenID Connect authentication for UMC univention-management-console-module-diagnostic.yaml d6203df6682d | feat(umc): Implement OpenID Connect authentication for UMC univention-management-console-module-diagnostic (6.0.7-2) d6203df6682d | feat(umc): Implement OpenID Connect authentication for UMC univention-management-console (12.0.32-9) f1f8472333f3 | Bug #49006: make versioned dependencies d6203df6682d | feat(umc): Implement OpenID Connect authentication for UMC univention-management-console (12.0.32-8) 4a1941af242b | feat(ldap): integrate libsasl2-modules-oauthbearer into LDAP server univention-ldap.yaml 4a1941af242b | feat(ldap): integrate libsasl2-modules-oauthbearer into LDAP server univention-ldap (16.0.14-3) 4a1941af242b | feat(ldap): integrate libsasl2-modules-oauthbearer into LDAP server univention-keycloak.yaml 7aa7fe693450 | feat(keycloak): Add more required oidc/rp options univention-keycloak (1.0.10-4) 7fcefb436a92 | refactor(keycloak): unify JSON printing 7aa7fe693450 | feat(keycloak): Add more required oidc/rp options univention-directory-manager-rest.yaml 5d3f345c81d7 | feat(udm-rest): Add OAuth 2.0 authorization for UDM REST API univention-directory-manager-rest (10.0.7-5) f1f8472333f3 | Bug #49006: make versioned dependencies 5d3f345c81d7 | feat(udm-rest): Add OAuth 2.0 authorization for UDM REST API univention-directory-manager-modules.yaml ae19809ce37c | feat(uldap): add bind_oauthbearer(authzid, access_token) univention-directory-manager-modules (15.0.25-3) f1f8472333f3 | Bug #49006: make versioned dependencies ae19809ce37c | feat(uldap): add bind_oauthbearer(authzid, access_token) ulfius.yaml 531ca18e924e | feat(crudeoauth): Implement crudeoauth ucs-test (4.0.164-21) r49006 | 10_ldap/49replication_modrdn_with_previous_modification: modify and ucs-test (10.0.20-16) 046f9ed5dbae | test(keycloak): refactor conftest orcania.yaml 531ca18e924e | feat(crudeoauth): Implement crudeoauth crudeoauth.yaml 531ca18e924e | feat(crudeoauth): Implement crudeoauth crudeoauth (1.0.0-1) 531ca18e924e | feat(crudeoauth): Implement crudeoauth
How to enable it? univention-app install keycloak univention-run-join-scripts --force --run-scripts 92univention-management-console-web-server.inst ucr set 'umc/web/oidc/enabled'=true
for crudeoauth we took the rhonabwy version from Debian bookwork, because it fixed some security issue regarding null-signature. We also had to apply the patch https://github.com/babelouest/rhonabwy/commit/84cebbd6b049a6e861810128a132efab1de4d3df (fix r_jwt_validate_claims for aud claim (Closes: #34)). The package and it's build dependencies have been added to: https://hutten.knut.univention.de/mediawiki/index.php/Security_Updates#Spezielle_Pakete Everything has been build for UCS 5.1 and UCS 5.2 as well.
REOPEN: slapd won't start anymore if 92univention-management-console-web-server.inst is updated and run if keycloak is installed in domain slapd only says backup2 slapd[29032]: 65b69a02 slap_sasl_init: server init failed slapschema: slap_init failed!. and won't get up anymore until I unset these ucr variables, the joinscript sets if keycloak is installed in the domain. "ldap/server/sasl/oauthbearer/trusted-audience/$domainname?ldaps://$domainname/" "ldap/server/sasl/oauthbearer/trusted-audience/$hostname.$domainname?ldaps://$hostname.$domainname/" "ldap/server/sasl/oauthbearer/trusted-issuer/$hostname.$domainname?$(ucr get umc/oidc/issuer)" The sasl2/slapd.conf looks like this in my domain: mech_list: EXTERNAL gssapi DIGEST-MD5 CRAM-MD5 LOGIN SAML OAUTHBEARER PLAIN saml_grace: 600 saml_userid: urn:oid:0.9.2342.19200300.100.1.1 saml_idp0: /usr/share/univention-management-console/saml/idp/ucs-sso-ng.ucs.test.xml saml_trusted_sp0: https://master.ucs.test/univention/saml/metadata saml_trusted_sp1: https://backup.ucs.test/univention/saml/metadata saml_trusted_sp2: https://backup2.ucs.test/univention/saml/metadata saml_trusted_sp3: https://member.ucs.test/univention/saml/metadata saml_trusted_sp4: https://slave.ucs.test/univention/saml/metadata oauthbearer_grace: 3 oauthbearer_userid: uid oauthbearer_trusted_jwks0: /usr/share/univention-management-console/oidc/*.jwks oauthbearer_trusted_iss0: oauthbearer_trusted_aud0: ldaps://master.ucs.test/ oauthbearer_trusted_aud1: ldaps://ucs.test/ oauthbearer_trusted_azp0: https://backup.ucs.test/univention/oidc/ oauthbearer_trusted_azp1: https://backup2.ucs.test/univention/oidc/ oauthbearer_trusted_azp2: https://master.ucs.test/univention/oidc/ oauthbearer_trusted_jwks0 looks wrong but removing that line or setting umc/oidc/issuer and setting ldap/server/sasl/oauthbearer/trusted-jwks/hostname.domainname manually results in this (better looking?) line oauthbearer_trusted_jwks0: https%3A%2F%2Fucs-sso-ng.ucs.test%2Frealms%2Fucs.jwks but still slapd won't start.
Ah correcting oauthbearer_trusted_jwks0 to reference the specific file and setting ldap/server/sasl/oauthbearer/trusted-issuer/hostname.domainname again to $(ucr get umc/oidc/issuer) works. umc/oidc/issuer is not set yet, when that UCR is created in the joinscript. And it is only set if umc/web/oidc/enabled is set. So slapd is broken in the default case (if keycloak is installed). These two issues need to be fixed. Also, the sasl module logs should be a little more helpful in these cases. It is tedious to guess what went wrong.
Oh yes, that was some broken by some latest refactoring of the joinscript, which broke the order in which things happened. Fixed in: univention-management-console (12.0.32-15) 95967f4a7cd8 | fix(umc): fix order of OIDC setup in joinscript TBD: slapd should start if the SASL module configuration conaints invalid data / files.
(In reply to Julia Bremer from comment #8) > Also, the sasl module logs should be a little more helpful in these cases. > It is tedious to guess what went wrong. This was not an issue of the SASL module but the parsing of the SASL configuration file itself. The problem was, that it contained `oauthbearer_trusted_iss0: `. Therefore we cannot enhance the log of this situation. Instead the UCR template has been adjusted so that it's impossible to reach this situation. univention-management-console (12.0.32-16) 73fbce8c7a02 | fix(umc): hardening the UCR template for sasl configuration
Verified: * Not activated/loaded by default * OAUTHBEARER in UCR default for mech_list but filtered out in UCR template if essential ldap/server/sasl/oauthbearer/* UCR variables are not set * Code review (mostly the C part) * Functional test (mostly the C part) * Package update * Developer docs under management/univention-management-console/README.md * No product documentation/support yet New external dependencies taken from Debian bookworm and added to https://hutten.knut.univention.de/mediawiki/index.php/Security_Updates#Spezielle_Pakete * rhonabwy (JWK, JWKS, JWS, JWE and JWT library) https://babelouest.github.io/rhonabwy/ * orcania (general purpose C library) https://babelouest.github.io/orcania/ * yder (logging library written in C) https://babelouest.github.io/yder/ * ulfius (Web Framework for REST Applications in C) https://babelouest.github.io/ulfius/ Advisories: * crudeoauth.yaml Dependencies: * orcania.yaml * ulfius.yaml * yder.yaml * univention-management-console.yaml * univention-ldap.yaml * univention-directory-manager-rest.yaml * univention-directory-manager-modules.yaml * univention-keycloak.yaml * univention-management-console-module-diagnostic.yaml * univention-python.yaml New UCR variables: * ldap/server/sasl/mech_list * ldap/server/sasl/oauthbearer/trusted-audience/* * ldap/server/sasl/oauthbearer/trusted-issuer/* * ldap/server/sasl/oauthbearer/required-scopes/* * ldap/server/sasl/oauthbearer/trusted-jwks/* * ldap/server/sasl/oauthbearer/trusted-authorized-party/* * ldap/server/sasl/oauthbearer/grace-time * umc/web/oidc/enabled * umc/oidc/default-op * umc/oidc/.*/issuer * umc/oidc/.*/client-id * umc/oidc/.*/client-secret-file * umc/oidc/.*/openid-configuration * umc/oidc/.*/openid-certs New UCR category: * oidc New UCR module * setup_oidc_rp.py (triggered by umc/oidc/issuer)
(In reply to Julia Bremer from comment #12) > Hi, since yesterday the python2/3 compatibility test for the > sasl2/slapd.conf started failing, could you have a look? Hm, don't know how this can happen. I added the default value as current workaround: univention-management-console (12.0.32-18) c691de1fa19c | fix(umc): workaround for not detected default value of UCR value → no diff for py2/py3: diff <(ucr filter < /etc/univention/templates/files/etc/ldap/sasl2/slapd.conf) <(python $(which ucr) filter < /etc/univention/templates/files/etc/ldap/sasl2/slapd.conf)
Ok that works.
<https://errata.software-univention.de/#/?erratum=5.0x936> <https://errata.software-univention.de/#/?erratum=5.0x937> <https://errata.software-univention.de/#/?erratum=5.0x938> <https://errata.software-univention.de/#/?erratum=5.0x939> <https://errata.software-univention.de/#/?erratum=5.0x941> <https://errata.software-univention.de/#/?erratum=5.0x942> <https://errata.software-univention.de/#/?erratum=5.0x943> <https://errata.software-univention.de/#/?erratum=5.0x944> <https://errata.software-univention.de/#/?erratum=5.0x945> <https://errata.software-univention.de/#/?erratum=5.0x946> <https://errata.software-univention.de/#/?erratum=5.0x947>