The repository you are looking into is work in progress. It contains proof of concept and preview builds in development.
The repository's content provides you with first insights into the containerized cloud IAM from Univention, derived from the UCS appliance.
This component searches user and group entries via LDAP, e.g. in MS Active Directory or OpenLDAP, and syncs the users and groups found via UDM REST API to UCS.
The synchronization always processes the full source data and thus does not have to maintain any local state.
[[TOC]]
- The connector always reads the full source and target data (users and groups) to calculate the required modifications in the target.
- The connector does not store any local state.
- Only a single source is configured. If you want to sync from multiple sources simply run multiple instances with separate config files.
- The OU structure of the AD source is ignored and all user and groups are written into a single dedicated target container (OU).
- A configurable primary key is used to correctly synchronize renamed entries. Note that renaming a group which is referenced as nested group may require two connector runs to fully update the target entries.
- The source directory should have a dedicated service account configured which allows searching/reading all wanted users and groups entries.
- A service account has to be created in UCS which allows to write users and groups in the configured target container (OU).
- The connector has to reach the target UCS system on port 443/tcp.
- The connector has to reach the source directory via TCP (e.g. port 636/tcp).
- No inbound connections are needed.
Because this component sends clear-text passwords when connecting it is required that all source and target systems are configured to properly support encrypted connections via transport layer security (TLS).
See also: BSI TR-02102 Kryptographische Verfahren: Empfehlungen und Schlüssellängen
Copy the example configuration and customize it to fit your environment.
cp ./config/ad-domain-config.yaml.example ./config/ad-domain-config.yaml
Then start the Directory importer using docker compose:
docker compose up --build
You can use TLS to connect to AD and Nubus, for doing that you should mount the
ca-certificates.crt
of those services in the respective place:
- Nubus:
/etc/ssl/certs/ca-certificates-nubus.crt
- AD:
/etc/ssl/certs/ca-certificates-ad.crt
After that, you have to uncomment the respective lines in the configuration file.
Also, you will have to adapt your ad-domain-config.yaml
if you mount the certificates
in a different place.
The configuration file is written in YAML syntax and is structured as a hierarchy.
See configuration example in source distribution directory config/.
At the top hierarchy level there are these config dictionaries:
- udm: Parameters for configuring how to reach UDM and some more.
- source: Parameters for configuring how to connect to the LDAP source directory and some data transformation rules.
- uri: (mandatory) URI including base path for accessing UDM REST API
- user: (mandatory) User's name used for authenticating to UDM
- password: (mandatory) User's password used for authenticating to UDM
- ca_cert: (optional) Path name of the trusted CA certificate bundle file. Defaults to your platform-specific CA bundle file.
- skip_writes: (optional)
If
true
this skips write operations to UDM (defaultfalse
). - connect_timeout: (optional) Timeout in seconds to wait for connection to UDM (default 6.0 secs).
- read_timeout: (optional) Timeout in seconds to wait for UDM results (default 1800 secs).
- user_ou: (mandatory) Name of the OU used as target container for user entries.
- user_primary_key_property: (optional) UDM property to use for storing the remote primary key for users.
- user_properties: (optional) List of user property names the connector writes to.
- group_ou: (mandatory) Name of the OU used as target container for group entries.
- group_primary_key_property: (optional) UDM property to use for storing the remote primary key for groups.
- group_properties: (optional) List of group property names the connector writes to.
- ldap_uri: (mandatory)
LDAP URI of the source directory to connect to.
Ideally you should configure an URI starting with
ldaps://
here to ensure LDAP over TLS is used right from the beginning. Note that the OpenLDAP client library (libldap) used by python-ldap implements a TLS hostname check strictly requring 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. - bind_dn: (mandatory) The bind DN to use authenticate to the source directory via LDAP simple bind operation.
- bind_pw: (mandatory) The clear-text password to use with LDAP simple bind operation.
- ca_cert: (optional) Path name of the trusted CA certificate bundle file. Defaults to your platform-specific CA bundle file.
- timeout: (optional) Timeout in seconds to wait for network (default 5 secs).
- search_pagesize: (optional) Page size to used when searching with Simple Paged Results control.
- user_base: (mandatory) search base used when searching user entries.
- user_scope: "sub" Search scope used when searching user entries. ("one" or "sub", default "sub").
- user_filter: (optional) LDAP filter used when searching user entries.
- user_attrs: (optional) LDAP attributes to be requested while searching for users. Recommendation is to only list the attributes actually used in transformation/mapping later.
- user_range_attrs: (optional) LDAP user attributes for which values are optionally retrieved by Range Retrieval (for MS AD)
- user_trans: Data transformation configuration applied to user entries.
- group_base: (mandatory) search base used when searching group entries.
- group_scope: "sub" Search scope used when searching group entries. ("one" or "sub", default "sub").
- group_filter: (optional) LDAP filter used when searching group entries.
- group_attrs: (optional) LDAP attributes to be requested while searching for groups. Recommendation is to only list the attributes actually used in transformation/mapping later.
- group_range_attrs: (optional) LDAP group attributes for which values are optionally retrieved by Range Retrieval (for MS AD)
- group_trans: Data transformation configuration applied to group entries.
The connector writes log messages at different log level:
- DEBUG Very detailed messages only used for development and debugging. Do not use in production.
- INFO The normal log level used for production especially for logging all changes done to the target.
- WARN Messages indicating something went wrong to be investigated at a later time.
- ERROR Messages indicating something went wrong to be investigated immediately.
The following environment variables are used to influence logging before the connector reads its normal configuration file:
- LOG_LEVEL Minimum log level really written to logs (defaults to INFO)
- LOG_CONF Full path name of a Python logging configuration file. If not set all log messages are simply written to stderr with a format including a time-stamp.
See also:
The connector does not provide a monitoring end-point itself.
Some metrics could be extracted from log messages with tools like mtail, promtail or similar.
Deploy the dependencies:
- Deploy an Active Directory server https://jenkins2022.knut.univention.de/job/UCS-5.2/job/UCS-5.2-0/view/Personal%20environments/job/UcsW2k19ADEnvironment/ The Joined UCS machine is redundant, but it's good enough until we figure out a better solution as part of the e2e test automation. This server is started inside the Univention VPN and thus the directory connector needs to also be started inside the VPN. Both Docker compose locall aswell as the gitlab pipelines fulfill this requirement.
- Deploy Nubus for Kubernetes. Many possibilities, ums_stack pipeline, helmfile, helm... It needs to be reachable by the directory connector N4K does not need to be inside the VPN. The directory connector can also be configured to talk to a public or local IP.
- Configure the Active Directory and UDM REST API connection and authorization parameters in the config.yaml file.
Executing the directory-connector inside a docker container using docker compose:
docker compose up --build
Alternatively run it locally: Install shared objects for ldap python library to your system
uv sync -p /usr/bin/python3.10
(use system python to get shared objects)uv run udm-directory-connector config/ad-domain-config.yaml.example
The integration tests require a UDM REST API and an openldap server
to act as the Nubus destination
and a local slapd and related slaptest infrastructure
to act as the source directory.
This environment can be automatically set up
with the docker-compose-test.yaml
file.
To run them, just execute the following commands:
cd tests
# Start the test dependencies
docker compose down -v && docker compose up --pull always udm-rest-api ldap-server -d
# Create the example.org maildomain
./maildomain.sh
# Run the integration tests
docker compose run --build test .venv/bin/python3 -m pytest
For running the tests you need:
- Locally installed OpenLDAP server software (aka slapd)
- python-ldap 3.4.0+ (3.4.0 or newer because of recent changes in slapdtest using cn=config instead of slapd.conf)
You can directly execute the tests by invoking module unittest from the command-line:
cd udm-directory-connector/
python3 -m venv /path/to/venv
/path/to/venv/bin/pip install -e .
/path/to/venv/bin/python3 -W error -I -bb -m unittest
The command-line arguments -W error -I -bb
are used to run the tests
in very strict mode (for details see
Python 3 Command line -- Miscellaneous options).
You might have to set environment vars BIN and SBIN to indicate where the OpenLDAP command-line tools and the slapd executable can be found.
Note that modifying source entries and verifying the change in UCS depends on the actual configuration (attribute mapping/composition etc.). So this section does not contain detailed attribute/property values.
Action in the source directory:
- Add a new user not present in UCS yet.
Expected result:
- New user was added after next connector run.
- Attribute value uid matches configured user name in the source directory.
- Attribute value univentionObjectIdentifier matches the configured primary attribute value in the source directory.
Action in the source directory:
- Modify some attributes in the source directory which are mapped to UDM properties in the configuration, e.g. mail, description, but not(!) the username (uid or sAMAccountName).
Expected result:
- Existing user was modified after next connector run.
Action in the source directory:
- Modify username attributes in the source directory, e.g. uid or sAMAccountName.
Expected result:
- Existing user was renamed after next connector run.
- Attribute value uid matches configured username in the source directory.
- User is still member of the same groups.
Action in the source directory:
- Delete a user entry in the source directory.
Expected result:
- Existing user was removed after next connector run.
Action in the source directory:
- Add a new group not present in UCS yet.
Expected result:
- New group was added after next connector run.
- Attribute value cn matches configured group name in the source directory.
- Attribute value univentionObjectIdentifier matches the configured primary attribute value in the source directory.
Action in the source directory:
- Modify some attributes in the source directory which are mapped to UDM properties in the configuration, e.g. description.
Expected result:
- Existing group was modified after next connector run.
Action in the source directory:
- Modify group name attributes in the source directory, e.g. cn.
Expected result:
- Existing group was renamed after next connector run.
- Attribute value cn matches configured group name in the source directory.
Action in the source directory:
- Delete a group entry in the source directory.
Expected result:
- Existing group was removed after next connector run.