Skip to content

add support for Keycloak / UCS 5.2 #207

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ push-files:
i18n/en/README_POST_UPDATE_EN \
i18n/de/README_POST_UPDATE_DE
univention-appcenter-control set --noninteractive $(ucs_version)/$(app_name)=$(app_version) \
--json '{"DockerImage": "ghcr.io/nextcloud/univention-app:$(app_version)", "UMCOptionsAttributes": "nextcloudEnabled", "WebInterface": "/nextcloud", "MinPhysicalRam": "512", "RequiredUcsVersion": "5.0-0", "SupportedUCSVersions": "5.0-0", "RequiredAppVersionUpgrade": "$(app_upgrade_from)"}'
--json '{"DockerImage": "ghcr.io/nextcloud/univention-app:$(app_version)", "UMCOptionsAttributes": "nextcloudEnabled", "WebInterface": "/nextcloud", "MinPhysicalRam": "512", "RequiredUcsVersion": "5.0-3", "SupportedUCSVersions": "5.0-3", "RequiredAppVersionUpgrade": "$(app_upgrade_from)"}'

.PHONY: docker
docker:
Expand Down
83 changes: 56 additions & 27 deletions inst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

VERSION=3
VERSION=4
SERVICE="Nextcloud"

ARGS=("$@")
Expand Down Expand Up @@ -260,34 +260,57 @@ nextcloud_urlEncode() {
}

nextcloud_configure_saml() {
udm saml/serviceprovider create "$@" \
--ignore_exists \
--position "cn=saml-serviceprovider,cn=univention,$ldap_base" \
--set isActivated=TRUE \
--set Identifier="https://$hostname.$domainname/nextcloud/apps/user_saml/saml/metadata" \
--set NameIDFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" \
--set simplesamlAttributes=TRUE \
--set AssertionConsumerService="https://$hostname.$domainname/nextcloud/apps/user_saml/saml/acs" \
--set simplesamlNameIDAttribute="uid" \
--set singleLogoutService="https://$hostname.$domainname/nextcloud/apps/user_saml/saml/sls" || die

IDP_CERT=$(curl -s https://"${ucs_server_sso_fqdn:-ucs-sso.$domainname}"/simplesamlphp/saml2/idp/certificate | sed -ne '
/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p # got the range, ok
/-END CERTIFICATE-/q # bailing out soon as the cert end seen
')

SETCMD="univention-app shell nextcloud sudo -u www-data php /var/www/html/occ config:app:set user_saml"
$SETCMD type --value="saml"
$SETCMD general-require_provisioned_account --value="1"
$SETCMD general-allow_multiple_user_back_ends --value="1"

univention-app shell nextcloud sudo -u www-data php /var/www/html/occ saml:config:set \
--idp-x509cert="${IDP_CERT}" \
--general-uid_mapping="uid" \
--idp-singleLogoutService.url="https://${ucs_server_sso_fqdn}/simplesamlphp/saml2/idp/SingleLogoutService.php" \
--idp-singleSignOnService.url="https://${ucs_server_sso_fqdn}/simplesamlphp/saml2/idp/SSOService.php" \
--idp-entityId="https://${ucs_server_sso_fqdn}/simplesamlphp/saml2/idp/metadata.php" \
1

if ! ucs_needsKeycloakSetup "$@"; then
if dpkg --compare-versions "${version_version}" gt "5.0"; then
echo "Skipping SAML configuration. No IDP configured for use."
return
fi
# SimpleSAMLphp (UCS 5.0 or lower)
udm saml/serviceprovider create "$@" \
--ignore_exists \
--position "cn=saml-serviceprovider,cn=univention,$ldap_base" \
--set isActivated=TRUE \
--set Identifier="https://$hostname.$domainname/nextcloud/apps/user_saml/saml/metadata" \
--set NameIDFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" \
--set simplesamlAttributes=TRUE \
--set AssertionConsumerService="https://$hostname.$domainname/nextcloud/apps/user_saml/saml/acs" \
--set simplesamlNameIDAttribute="uid" \
--set singleLogoutService="https://$hostname.$domainname/nextcloud/apps/user_saml/saml/sls" || die

IDP_CERT=$(curl -s https://"${ucs_server_sso_fqdn:-ucs-sso.$domainname}"/simplesamlphp/saml2/idp/certificate | sed -ne '
/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p # got the range, ok
/-END CERTIFICATE-/q # bailing out soon as the cert end seen
')

univention-app shell nextcloud sudo -u www-data php /var/www/html/occ saml:config:set \
--idp-x509cert="${IDP_CERT}" \
--general-uid_mapping="uid" \
--idp-singleLogoutService.url="https://${ucs_server_sso_fqdn}/simplesamlphp/saml2/idp/SingleLogoutService.php" \
--idp-singleSignOnService.url="https://${ucs_server_sso_fqdn}/simplesamlphp/saml2/idp/SSOService.php" \
--idp-entityId="https://${ucs_server_sso_fqdn}/simplesamlphp/saml2/idp/metadata.php" \
1 || die "Could not configure simpleSAMLphp as Nextcloud Identity Provider"
else
IDP_CERT=$(univention-keycloak "$@" saml/idp/cert get --as-pem --output /dev/stdout)
SSO_URL="$(univention-keycloak "$@" get-keycloak-base-url)"
univention-app shell nextcloud sudo -u www-data php /var/www/html/occ saml:config:set \
--idp-x509cert="${IDP_CERT}" \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On my test run with #208 the joinscript ran through, but the IDP certificate was not set. Actually non of the key material was set, only:

root@nextc-17696382:/var/www/html# sudo -u www-data php occ saml:config:get
  - 1:
    - general-uid_mapping: uid
    - idp-entityId: https://ucs-sso.lorem-ipsum.intranet/simplesamlphp/saml2/idp/metadata.php
    - idp-singleLogoutService.url: https://ucs-sso.lorem-ipsum.intranet/simplesamlphp/saml2/idp/SingleLogoutService.php
    - idp-singleSignOnService.url: https://ucs-sso.lorem-ipsum.intranet/simplesamlphp/saml2/idp/SSOService.php

Seems like the univention-keycloak commands did not succeed?

What's the best way to invoke it manually for testing? I forgot what was passed with $@ 🙊.

I am also not seeing much in the UMC web unterface, where would these things be now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you show the log file /var/log/univention/join.log after executing the joinscript.

What's the best way to invoke it manually for testing? I forgot what was passed with $@ 🙊.

univention-run-join-scripts --force --run-scripts 50nextcloud.inst

$@ is often empty on DC Master, and contains on other roles --binddn "$dn_of_Administrator" --bindpwdfile /some/secret/file.

I am also not seeing much in the UMC web unterface, where would these things be now?

They are only in Keycloak. We didn't add UDM modules for these settings :-/

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The log does not contain much, but setting of the keys is missing:

RUNNING 50nextcloud.inst
2025-04-11 22:05:52.448610434+02:00 (in joinscript_init)
Object exists: cn=services,cn=univention,dc=lorem-ipsum,dc=intranet
Object created: cn=Nextcloud Hub,cn=services,cn=univention,dc=lorem-ipsum,dc=intranet
Object modified: cn=ucs-2792,cn=dc,cn=computers,dc=lorem-ipsum,dc=intranet
Create nextcloud/ucs/modifyUsersFilter
Create nextcloud/ucs/userEnabled
Create nextcloud/ucs/userQuota
Create nextcloud/ucs/debug
Create nextcloud/ldap/cacheTTL
Create nextcloud/ldap/homeFolderAttribute
Create nextcloud/ldap/userSearchAttributes
Create nextcloud/ldap/userDisplayName
Create nextcloud/ldap/groupDisplayName
Create nextcloud/ldap/base
Create nextcloud/ldap/baseUsers
Create nextcloud/ldap/baseGroups
Create nextcloud/ldap/filterLogin
Create nextcloud/ldap/filterUsers
Create nextcloud/ldap/filterGroups
Config value 'type' for app 'user_saml' is now set to 'saml', stored as mixed in fast cache
Config value 'general-require_provisioned_account' for app 'user_saml' is now set to '1', stored as mixed in fast cache
Config value 'general-allow_multiple_user_back_ends' for app 'user_saml' is now set to '1', stored as mixed in fast cache
Object created: SAMLServiceProviderIdentifier=https://ucs-2792.lorem-ipsum.intranet/nextcloud/apps/user_saml/saml/metadata,cn=saml-serviceprovider,cn=univention,dc=lorem-ipsum,dc=intranet
The provider's config was updated.
<?xml version="1.0"?>
<ocs>
 <meta>
  <status>ok</status>
  <statuscode>200</statuscode>
  <message>OK</message>
 </meta>
 <data/>
</ocs>
Check for richdocuments app
Check for onlyoffice app
2025-04-11 22:06:00.856943343+02:00 (in joinscript_save_current_version)
EXITCODE=0
RUNNING 81univention-nfs-server.inst

$@ is often empty on DC Master, and contains on other roles --binddn "$dn_of_Administrator" --bindpwdfile /some/secret/file.

So when i run it with the empty string, I get a usage error.

# univention-keycloak "" saml/idp/cert get --as-pem --output /dev/stdout
usage: univention-keycloak [-h] [--binddn BINDDN] [--binduser BINDUSER] [--bindpwd BINDPWD] [--bindpwdfile BINDPWDFILE] [--realm REALM]
                           [--keycloak-pwd KEYCLOAK_PWD] [--keycloak-url KEYCLOAK_URL] [--no-ssl-verify]
                           {realms,proxy-realms,saml/sp,saml-client-user-attribute-mapper,saml-client-nameid-mapper,oidc/rp,saml/idp/cert,oidc/op/cert,init,kerberos-config,ldap-federation,ad-hoc,2fa,extension,upgrade-config,domain-config,user-attribute-ldap-mapper,get-keycloak-base-url,client-auth-flow,messages,login-links,legacy-authentication-flow,conditional-krb-authentication-flow}
                           ...
univention-keycloak: error: argument command: invalid choice: '' (choose from 'realms', 'proxy-realms', 'saml/sp', 'saml-client-user-attribute-mapper', 'saml-client-nameid-mapper', 'oidc/rp', 'saml/idp/cert', 'oidc/op/cert', 'init', 'kerberos-config', 'ldap-federation', 'ad-hoc', '2fa', 'extension', 'upgrade-config', 'domain-config', 'user-attribute-ldap-mapper', 'get-keycloak-base-url', 'client-auth-flow', 'messages', 'login-links', 'legacy-authentication-flow', 'conditional-krb-authentication-flow')

When I leave it out an Name or service not known:

# univention-keycloak saml/idp/cert get --as-pem --output /dev/stdout
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 174, in _new_conn
    conn = connection.create_connection(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/urllib3/util/connection.py", line 73, in create_connection
    for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/socket.py", line 962, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
socket.gaierror: [Errno -2] Name or service not known

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 705, in urlopen
    httplib_response = self._make_request(
                       ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 388, in _make_request
    self._validate_conn(conn)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 1050, in _validate_conn
    conn.connect()
  File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 358, in connect
    self.sock = conn = self._new_conn()
                       ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 186, in _new_conn
    raise NewConnectionError(
urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPSConnection object at 0x7f32e0c7a390>: Failed to establish a new connection: [Errno -2] Name or service not known

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/requests/adapters.py", line 489, in send
    resp = conn.urlopen(
           ^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 789, in urlopen
    retries = retries.increment(
              ^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/urllib3/util/retry.py", line 594, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='ucs-sso-ng.lorem-ipsum.intranet', port=443): Max retries exceeded with url: /realms/ucs/protocol/saml/descriptor (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7f32e0c7a390>: Failed to establish a new connection: [Errno -2] Name or service not known'))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/sbin/univention-keycloak", line 3436, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/sbin/univention-keycloak", line 3432, in main
    return opt.func(opt) or 0
           ^^^^^^^^^^^^^
  File "/usr/sbin/univention-keycloak", line 1460, in download_cert_saml
    saml_descriptor = requests.get(saml_descriptor_url)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/requests/api.py", line 73, in get
    return request("get", url, params=params, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/requests/api.py", line 59, in request
    return session.request(method=method, url=url, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/requests/sessions.py", line 587, in request
    resp = self.send(prep, **send_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/requests/sessions.py", line 701, in send
    r = adapter.send(request, **kwargs)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/requests/adapters.py", line 565, in send
    raise ConnectionError(e, request=request)
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='ucs-sso-ng.lorem-ipsum.intranet', port=443): Max retries exceeded with url: /realms/ucs/protocol/saml/descriptor (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7f32e0c7a390>: Failed to establish a new connection: [Errno -2] Name or service not known'))

Can it be that this is not even running? i do not see either keycloak nor java in the ps output, not a related systemd service. – Seeing your next comment it is probably not installed.

And the domain dig ucs-sso-ng.lorem-ipsum.intranet does not resolve (the "old" one – dig ucs-sso.lorem-ipsum.intranet – still does).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bash-special: "$@" does not expand to empty string but to nothing, if there are no args.

Please do univention-app install keycloak. After this, the service should run.

Copy link
Contributor Author

@spaceone spaceone Apr 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also cherry-pick ab2fbb8, where I added handling for not-installed Keycloak. And added clearer error messages in the joinscript.

--general-uid_mapping="uid" \
--idp-singleLogoutService.url="$SSO_URL/realms/ucs/protocol/saml" \
--idp-singleSignOnService.url="$SSO_URL/realms/ucs/protocol/saml" \
--idp-entityId="$SSO_URL/realms/ucs" \
1 || die "Could not configure Keycloak as Nextcloud Identity Provider"

# Keycloak (starting with UCS 5.1 or optionally manually migrated UCS 5.0)
univention-keycloak "$@" saml/sp create \
--metadata-url="https://$hostname.$domainname/nextcloud/apps/user_saml/saml/metadata" \
--role-mapping-single-value || die "Could not configure Nextcloud Service Provider as Keycloak"
fi
}

# Enables all Users that fit the filter to access Nextcloud
Expand All @@ -299,12 +322,18 @@ nextcloud_modify_users() {

SP_DN=$(univention-ldapsearch -LLL SAMLServiceProviderIdentifier=https://$hostname.$domainname/nextcloud/apps/user_saml/saml/metadata dn | cut -d ' ' -f 2)

has_simplesamlphp=false
if ! ucs_needsKeycloakSetup "$@" && dpkg --compare-versions "${version_version}" lt "5.1"; then
has_simplesamlphp=true
fi

for dn in $(udm users/user list "$@" --filter "$nextcloud_ucs_modifyUsersFilter" | sed -ne 's/^DN: //p') ; do
echo "modifying $dn .."
udm users/user modify "$@" --dn "$dn" \
--set nextcloudEnabled="$nextcloud_ucs_userEnabled" \
--set nextcloudQuota="$nextcloud_ucs_userQuota" \
--append serviceprovider="$SP_DN"
--set nextcloudQuota="$nextcloud_ucs_userQuota"

[ "$has_simplesamlphp" = "true" ] && udm users/user modify "$@" --dn "$dn" --append serviceprovider="$SP_DN"
done
}

Expand Down Expand Up @@ -366,4 +395,4 @@ if [ "$JS_LAST_EXECUTED_VERSION" = 2 ]; then
joinscript_save_current_version
else
nextcloud_main "$@"
fi
fi
6 changes: 4 additions & 2 deletions preinst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

. /usr/share/univention-lib/base.sh

NC_PERMCONFDIR="/var/lib/univention-appcenter/apps/nextcloud/data/integration"

NC_UCR_FILE="$NC_PERMCONFDIR/ucr"
Expand All @@ -28,8 +30,8 @@ if [ ! -d "$NC_PERMCONFDIR" ]; then
fi

cat >"$NC_UCR_FILE" <<EOL
export NC_HOST_IPS="`ucr dump | grep interfaces | grep address | cut -d ":" -f2`"
export NC_TRUSTED_PROXY_IP="`ucr get docker/daemon/default/opts/bip | cut -d "/" -f 1`"
export NC_HOST_IPS="$(get_default_ip_address)"
export NC_TRUSTED_PROXY_IP="$(ucr get docker/daemon/default/opts/bip | cut -d "/" -f 1)"
Comment on lines +33 to +34
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we sure this change does not have potential to cause a regression?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. The current code is broken and randomly uses the first found interface, regardless of whether it's IPv4 or IPv6. At least it would be deterministic now.

EOL

chmod +x "$NC_UCR_FILE"
Expand Down