6. How-to connect to external IAM#

Important

This section describes a how-to that involves software that’s not yet released as product by Univention.

To use the connector anyway, contact Univention GmbH directly.

This how-to describes the general steps to connect external IAM systems and single sign-on through the protocols SAML and OpenID Connect to Univention Nubus. The setup is for a unidirectional import of directory objects, such as user accounts and user groups, from the external IAM system to the directory service in Nubus.

For your understanding, this how-to uses the following definitions:

Source IAM system

is the external IAM system that has directory objects designated for import to Nubus. Nubus can use it as source for user account objects, user group objects, and as identity provider for user sign-in.

Target

The target of the import is the Identity Provider component in Nubus for Kubernetes.

The connection uses the following processes:

Provisioning

During provisioning, the connector reads user account objects and user group objects through LDAP from the source and the target, calculates the differences and adds them to the target using a HTTP REST API from the Directory Manager in Univention Nubus.

Single sign-on

For single sign-on, Nubus uses the external IAM system as SAML Identity Provider or as OpenID Connect Relying Party. The Identity Provider from Univention Nubus becomes a client to the external IAM system.

See also

Nubus Directory Importer in the functional components section

for information about the architecture of the Nubus Directory Importer.

6.1. Requirements#

Before you begin, you need to fulfill the following requirements:

  1. The source must provide access to user account objects and user group objects through LDAP.

  2. The source must provide an identity provider that supports the protocols SAML or OpenID Connect.

  3. You have a running instance of Univention Nubus for Kubernetes. For installation, see Deployment.

  4. For the synchronization of user accounts and user groups between the source and the target you need the Nubus Directory Importer.

  5. You have a user account and its credentials that has permission to use the UDM HTTP REST API in Nubus.

6.2. Setup authentication with UDM HTTP REST API#

First, you need to authenticate with the UDM HTTP REST API. For authentication with the UDM HTTP REST API, follow the steps outlined in Authentication in Univention Corporate Server - Manual for developers [4].

6.2.1. URL for REST interface schema#

You can reach UDM HTTP REST API at https://portal.$(global.domain)/univention/udm/.

You can reach the UDM HTTP REST API interface schema through a URL using the following schema: https://portal.$(global.domain)/univention/udm/schema/.

Examples

https://portal.example.com/univention/udm/

https://portal.example.com/univention/udm/schema/

Tip

The value of global.domain in your custom_values.yaml deployment file defines the domain of your cluster.

6.2.2. Default authentication user groups#

The default configuration for Nubus defines the following groups as authorized groups for the UDM HTTP REST API:

  • DC Backup Hosts

  • DC Slave Hosts

  • Domain Admins

If you need additional user accounts with access to the UDM HTTP REST API, you can add them to the Domain Admins user group, for example through the Users module in the Univention Portal. Note that accounts in this group have extensive administration rights.

Tip

To render the Helm Chart template and lookup the authorization groups, use the following commands:

  1. Set the version of the Helm Chart in the variable VERSION as in Listing 3.6.

  2. Render the template and save the result for later use:

    $ helm template \
       --values custom_values.yaml \
       --version "$VERSION" \
       oci://artifacts.software-univention.de/nubus/charts/nubus > rendered_template.yaml
    
  3. Open the YAML file rendered_template.yaml in your favorite text editor and look for the ConfigMap that contains stack-data-ums-ucr in metadata.name.

6.2.3. Add user group as authentication group#

If you don’t need an extra user group and want to use the Default authentication user groups, you can continue with the next section Setup Nubus Directory Importer.

Creating a dedicated user group and using it as authorized group for the UDM HTTP REST API is useful, if you want to give the user accounts in that group just those rights for the API.

  1. In the Univention Portal, create the user group, for example udm-rest-api-users

  2. Add user accounts as members to the user group.

  3. Add the group’s LDAP DN to your custom_values.yaml deployment file using the following steps:

    1. Add the global.configUcr section.

    2. Add the UCR variable directory/manager/rest/authorized-groups/group-name.

      Replace group-name with the name of your group.

      Set the value to the LDAP DN of your group. The default schema is cn=group-name,cn=groups,global.ldap.baseDn. You find the value of global.ldap.baseDn in your custom_values.yaml.

    The parts look like the following example in Listing 6.1:

    Listing 6.1 Add authorization group for UDM HTTP REST API to custom_values.yaml#
    global:
      configUcr:
        directory:
          manager:
             rest:
                authorized-groups:
                   udm-rest-api-users: cn=udm-rest-users,cn=groups,dc=example,dc=internal
    
    Where can I find the LDAP DN for the group?

    You can ask the system for the LDAP DN of your group with the following steps:

    1. Open the Univention Directory Manager REST interface. For the URL, see URL for REST interface schema.

    2. Navigate to the groups/group section.

    3. Open the GET request section and click Try it out.

    4. Enter cn=group-name into the field filter and click Execute.

    5. In the Reponses ‣ Response body field look for a line that includes name and an LDAP DN as value. Use the value for the UCR variable in your deployment file.

  4. You need to announce the group to the UDM HTTP REST API through the Helm Chart. This action updates the configuration map that includes the UCR variable.

    Run the command in Listing 6.2. For the environment variable values, see Listing 3.7.

    Listing 6.2 Apply changes from the custom_values.yaml to Nubus#
    $ helm upgrade \
       --namespace="$YOUR_NAMESPACE" \
       --values custom_values.yaml \
       --version "$VERSION" \
       --timeout 10m \
       "$RELEASE_NAME" \
       oci://artifacts.software-univention.de/nubus/charts/nubus
    
  5. Finally, you need to restart the Kubernetes pod for the UDM HTTP REST API to apply the changes to the UCR variables.

    Run the commands in Listing 6.3.

    Listing 6.3 Restart pod for UDM HTTP REST API to apply UCR variable changes#
    $ kubectl get pods --namespace "$NAMESPACE_FOR_NUBUS" | grep "udm-rest-api"
    $ export NAME_OF_POD="Name of the pod in the output of the previous command."
    $ kubectl delete pod "$NAME_OF_POD"
    

Note

Adding the group for access to the UDM HTTP REST API only grants permission for the API access. Permissions for reading and writing in OpenLDAP based on the API access need dedicated access control lists (ACLs) in LDAP.

For information about modifying LDAP ACLs, see Univention Nubus for Kubernetes - Nubus Customization and Modification Manual [5].

See also

UDM HTTP REST API

for more information about how to use the UDM HTTP REST API in Univention Corporate Server - Manual for developers [4].

6.3. Setup Nubus Directory Importer#

Important

The Nubus Directory Importer isn’t an official Univention product yet.

Nubus Directory Importer is a service, that searches for user account objects and user group objects in the source and the target through LDAP. It synchronizes the found objects to the Directory Manager in Nubus through the UDM HTTP REST API.

Nubus Directory Importer reads both source and target directory services and determines the differences as a basis for the modification. The connector doesn’t store local states.

The synchronization process writes the complete OU structure of the source directory service to a dedicated OU in the target directory service, meaning Nubus. The connector uses the preconfigured UDM property univentionObjectIdentifier as primary key in the target system to identify user objects and user group objects.

Note

Renaming a group, that references a nested group, may require two runs of Nubus Directory Importer to update the entries on the target system.

See also

Nubus Directory Importer

for information on the architecture of the Nubus Directory Impporter in Univention Nubus for Kubernetes - Architecture Manual [2].

6.3.1. Requirements for Nubus Directory Importer#

Before you setup the Nubus Directory Importer, you need to fulfill the following requirements:

Service accounts
  1. In the source system you need a service account with the permission to search and read all user objects and user group objects.

  2. In Univention Nubus for Kubernetes you need a service account with the permission to write user objects and user group objects to the respective OU.

    For information about authentication groups, see Default authentication user groups and Add user group as authentication group.

Network

The Nubus Directory Importer:

  • must reach the UDM HTTP REST API in the Directory Manager component in Univention Nubus for Kubernetes on port 443/tcp.

  • must reach the source system on port 636/tcp. The port depends on the source system.

  • doesn’t need inbound connections.

Encrypted connections with TLS

The Nubus Directory Importer sends clear text passwords to authenticate with the source and target through LDAP. You need to ensure that all source and target systems have properly configured encrypted connections through TLS.

For Nubus for Kubernetes being the target system, the TLS configuration is already in place in the default setup. For the URL of the API, see URL for REST interface schema.

Note

The Nubus Directory Importer doesn’t read and synchronize user account passwords, or their hashes from the directories.

To complete the setup and allow the synchronized user accounts to authenticate, you need to Setup single sign-on.

6.3.2. Installation of Nubus Directory Importer#

The Nubus Directory Importer is available as Python package. You can install it with pip from the Git repository. To use Nubus Directory Importer on Kubernetes, you need to create a container and deploy it to your Kubernetes cluster in the same namespace as Univention Nubus.

See also

Git Repository for Nubus Directory Importer

for more information about the importer.

6.3.3. Configuration of Nubus Directory Importer#

For the configuration of the Nubus Directory Importer, you use a text file in YAML format. At the top-level hierarchy it has the following configuration dictionaries:

udm:

Configuration parameters that define the connection to UDM.

source:

Configuration parameters that define the connection to the source and rules for transformation.

6.3.3.1. Example configuration#

Listing 6.4 shows a configuration example. You can also download it.

Listing 6.4 Example configuration YAML file for Nubus Directory Importer#
---
# Configuration example for syncing from a typical MS AD domain controller
# the name 'ad-domain-example' is used for various parameters

udm:
    uri: "https://ucs-master.example.com/univention/udm/"
    user: "Administrator"
    password: "supersecret"
    ca_cert: "/etc/ssl/certs/ca-certificates.crt"
    skip_writes: false
    # FIX ME! Use more suitable attributes for storing primary keys
    user_ou: "ou=ad-domain-example"
    user_pkey_property: "univentionObjectIdentifier"
    group_ou: "ou=ad-domain-example"
    group_pkey_property: "univentionObjectIdentifier"

source:
    ldap_uri: "ldaps://dc1.ad-domain.example.com"
    bind_dn: "CN=Administrator,CN=Users,DC=ad-domain,DC=example,DC=com"
    bind_pw: "supersecret"
    ca_cert: "/etc/ssl/certs/ca-certificates.crt"
    timeout: 5
    search_pagesize: 500
    # search paramaters for groups
    group_base: "OU=UCS-Users,DC=ad-domain,DC=example,DC=com"
    group_scope: "sub"
    #group_filter: "(&(objectClass=group)(!(|(isCriticalSystemObject=TRUE)(cn=Domain *)(cn=* Controllers))))"
    # search paramaters for users
    user_base: "OU=UCS-Users,DC=ad-domain,DC=example,DC=com"
    user_scope: "sub"
    user_filter: "(&(objectClass=user)(sAMAccountType=805306368)(givenName=*)(sn=*)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"
    user_attrs:
        - "objectGUID"
        - "sAMAccountName"
        - "givenName"
        - "description"
        - "sn"
        - "ou"
        - "o"
        - "street"
        - "l"
        - "postalCode"
        - "st"
        - "c"
        - "telephoneNumber"
        - "mobile"
        - "employeeNumber"
        - "employeeType"
        - "proxyAddresses"
    user_trans:
        # function sequences to sanitize input values
        sanitizer:
            objectGUID:
              - udm_directory_connector.sanitize:guid2uuid
            telephoneNumber:
              - udm_directory_connector.sanitize:phone_sanitizer
            mobile:
              - udm_directory_connector.sanitize:phone_sanitizer
            mobileTelephoneNumber:
              - udm_directory_connector.sanitize:phone_sanitizer
            homePhone:
              - udm_directory_connector.sanitize:phone_sanitizer
            homeTelephoneNumber:
              - udm_directory_connector.sanitize:phone_sanitizer
            facsimileTelephoneNumber:
              - udm_directory_connector.sanitize:phone_sanitizer
            fax:
              - udm_directory_connector.sanitize:phone_sanitizer
            mail:
              - udm_directory_connector.sanitize:mail_sanitizer
              - bytes.lower
            mailPrimaryAddress:
              - udm_directory_connector.sanitize:mail_sanitizer
              - bytes.lower
            mailLocalAddress:
              - udm_directory_connector.sanitize:mail_sanitizer
              - bytes.lower
            mailAlternativeAddress:
              - udm_directory_connector.sanitize:mail_sanitizer
              - bytes.lower
        # attributes to be renamed (dict-key is UDM property name)
        rename_attrs:
            # the primary key property <- attribute mapping
            univentionSourceIdentifier: "objectGUID"
            # the e-mail address for contact addressbooks (mailbox not managed by UCS)
            "e-mail": "mail"
            username: "sAMAccountName"
            firstname: "givenName"
            lastname: "sn"
            mobileTelephoneNumber: "mobile"
            organisation: "o"
            phone: "telephoneNumber"
            city: "l"
            country: "c"
            postcode: "postalCode"
        # attributes set to a fixed attribute value list no matter what
        # typically those are not mentioned in search_attrs
        #fixed_attrs:
        #    primaryGroup:
        #        - "cn=Domain Users,cn=groups,dc=ucs-4,dc=local"
        # single-valued(!) attributes which will be composed from other attributes no matter what
        # the 1st attr value of referenced attrs is used
        # composing stops at first completed variant (no KeyError)
        # Note: Composed attributes must not depend on each other!
        #compose_attrs:
        #    # the primary e-mail address for a mailbox managed by UCS (e.g. for OX)
        #    mailPrimaryAddress:
        #      - "{e-mail}"
        # List of attributes to be removed after compose pass
        remove_attrs:
            - "objectGUID"
            - "objectSid"
            - "proxyAddresses"
        # mapping defined per attribute type for deleting certain attribute values
        remove_values:
            telephoneNumber:
                - "+49"
                - "+49 ???"
                - "0"
        # mapping defined per attribute type for efficiently replacing certain attribute values
        replace_values:
            c:
                "Deutschland": "DE"
                "Bundesrepublik Deutschland": "DE"
                "Frankreich": "FR"
        # decompose attribute values into potentially multiple other attributes
        # by using name-based regex matching
        # first regex-match terminates processing!
        decompose_attrs:
            proxyAddresses:
                - "^SMTP:(?P<mail>.+)$"
                - "^smtp:(?P<mailAlternativeAddress>.+)$"

    group_trans:
        # attributes to be renamed
        rename_attrs:
            # the primary key property <- attribute mapping
            univentionSourceIdentifier: "objectGUID"
            name: "cn"
            users: "member"
        # List of attributes to be removed after compose pass
        remove_attrs:
            - "objectGUID"
            - "objectSid"

6.3.3.2. Configuration reference#

This section provides a reference for the configuration values of the Nubus Directory Importer.

6.3.3.2.1. Section: udm#
udm.uri#

URI including base path for accessing UDM HTTP REST API.

udm.user#

Username used for authenticating to UDM.

udm.password#

User password used for authenticating to UDM.

udm.ca_cert(optional)#

Path of the trusted CA certificate bundle file. Defaults to your platform-specific CA bundle file.

udm.skip_writes(optional)#

If true, skip write operations to UDM. Default: false.

udm.connect_timeout(optional)#

Timeout in seconds to wait for connection to UDM. Default: 6.0 seconds.

udm.read_timeout(optional)#

Timeout in seconds to wait for UDM results. Default: 1800 seconds.

udm.user_ou#

Name of the OU used as target container for user entries.

udm.user_pkey_property(optional)#

UDM property to use for storing the remote primary key for users.

udm.user_properties(optional)#

List of user property names the connector writes to.

udm.group_ou#

Name of the OU used as target container for group entries.

udm.group_pkey_property(optional)#

UDM property to use for storing the remote primary key for groups.

udm.group_properties(optional)#

List of group property names the connector writes to.

6.3.3.2.2. Section source#
source.ldap_uri#

LDAP URI of the source directory to connect to. Configure an URI starting with ldaps:// to ensure that the connector uses LDAP over TLS right from the beginning.

source.bind_dn#

The bind DN to use authenticate to the source directory through LDAP simple bind operation.

source.bind_pw#

The clear-text password to use with LDAP simple bind operation.

source.ca_cert(optional)#

Path of the trusted CA certificate bundle file. Defaults to your platform-specific CA bundle file.

source.timeout(optional)#

Timeout in seconds to wait for network. Default: 5 seconds.

source.search_pagesize(optional)#

Page size to used when searching with _Simple Paged Results_ control.

source.user_base#

search base used when searching user entries.

source.user_scope#

Search scope used when searching user entries.

Default: "sub".

Possible values: "one", "sub".

source.user_filter(optional)#

LDAP filter used when searching user entries.

source.user_attrs(optional)#

Request LDAP attributes while searching for users. Recommendation: only list the attributes actually used in the transformation and mapping later.

source.user_range_attrs(optional)#

LDAP user attributes for which values are optionally retrieved by _Range Retrieval_ for Microsoft Active Directory.

source.user_trans#

Data transformation configuration applied to user entries.

source.group_base#

search base used when searching group entries.

source.group_scope#

Search scope used when searching group entries.

Default: "sub".

Possible values: "one", "sub".

source.group_filter(optional)#

LDAP filter used when searching group entries.

source.group_attrs(optional)#

Request LDAP attributes while searching for groups. Recommendation: only list the attributes actually used in the transformation and mapping later.

source.group_range_attrs(optional)#

LDAP group attributes for which values are optionally retrieved by _Range Retrieval_ for Microsoft Active Directory.

source.group_trans#

Data transformation configuration applied to group entries.

Note

python-ldap uses the OpenLDAP client library libldap. And libldap implements a TLS hostname verification that strictly requires the hostname in the LDAP URI to match one of the DNS values of X.509v3 extension _subjectAltName_ in the source directory’s TLS server certificate.

6.4. Setup single sign-on#

This section describes how to set up single sign-on between the source system as identity provider and the Identity Provider within Univention Nubus for Kubernetes as service provider.

The Identity Provider within Nubus uses Keycloak and supports both SAML and OpenID Connect protocols. With its broker functionality, Keycloak can delegate the authentication to other identity providers. Depending on the configuration of the Identity Broker, end users can choose the identity provider in the Keycloak login dialog. Keycloak acts as a proxy service, forwarding the user’s sign-in request to the source system and translating between SAML and OpenID Connect, if necessary.

For this how-to, you need to configure Keycloak as an Identity Broker to use the source system as a source for identities.

Before you start, make sure that the external IAM system and Keycloak communicate through port 443.

6.4.1. Add source system to Keycloak#

To add the source system to Keycloak, use the following steps:

  1. Sign in to the Keycloak Admin Console in Univention Nubus. Refer to Univention Keycloak app manual [6].

  2. Go to the section Identity Provider.

  3. Select a user-defined identity provider based on either OpenID Connect v1.0 or SAML v2.0.

  4. Provide the information about the source system.

    For OpenID Connect you need to provide the following values:

    • Alias

    • Discovery endpoint

    • Client ID

    • Client Secret

    • Other optional values

    For SAML you need to provide the following values:

    • Alias

    • Service provider entity ID

    • SAML entity descriptor

    • Other optional values

  5. Go to the section Authentication to configure different login flows. For the use case in this how-to, select the default flow browser.

  6. Copy the login flow browser, provide a name and customize it to your needs.

    Important

    Change the parameter Identity Provider Redirector to Required. The parameter enforces the redirection to the source system and end users don’t have to select the source system in the login dialog.

  7. In the step Identity Provider Redirector, click the cog wheel and select your previously created identity provider for your source system.

See also

Refer to the following resources from Keycloak Server Administration Guide [7]. Make sure to select the documentation that matches the Keycloak version in Univention Nubus for Kubernetes.

Integrating identity providers

for the Keycloak documentation about external identity providers.

OpenID Connect v1.0 identity providers

for the procedure to add a user-defined OpenID Connect v1.0 identity provider.

SAML v2.0 identity providers

for the procedure to add a user-defined SAML v2.0 identity provider.

Authentication flows

for the description of authentication flows.

6.4.2. Add Keycloak as client to source system#

To add Keycloak as client in the source system, use the following steps:

  1. Create a client configuration for Keycloak in the source system.

    For OpenID Connect you need to provide the following values:

    • Client ID

    • Client Secret

    • Redirect URI

    For SAML you need to provide the XML metadata from Keycloak.

  2. Finally, you need to define the attributes that the source system forwards to Keycloak.

6.5. Verify the sign-in#

After you configured all pieces, you can sign in to Univention Nubus for Kubernetes with credentials for user accounts from the source system.

In the browser, Keycloak redirects the end user to the login dialog of the source system. The end users sign in one-time. Further browser-based access to the same application then no longer requires an additional sign-in.