How to Package and Register a Restricted LDAP Read-Only ACL in UCS
This article describes how to create a Debian package that registers a restricted LDAP read-only ACL in Univention Corporate Server (UCS). The example grants a dedicated LDAP account read access to a specific LDAP subtree while preventing access to other branches and denying write operations.
1. Create the package directory
mkdir -p ~/ldap-acl-test/debian
mkdir -p ~/ldap-acl-test/usr/share/ldap-acl-readonly
cd ~/ldap-acl-test
2. Create the ACL file
nano usr/share/ldap-acl-test/65ldap-readonly.acl
Adjust the values to match your environment
Example content:
access to dn.exact="ou=bremen,dc=deadpool,dc=intranet"
by dn.exact="uid=ldap-readonly,cn=users,dc=deadpool,dc=intranet" read
by * +0 break
access to *
by dn.exact="uid=ldap-readonly,cn=users,dc=deadpool,dc=intranet" none stop
by * +0 break
The ACL grants the account:
- Read access just to the
ou=bremen,...branch - No access to other branches
- No write permissions
The file name 65ldap-readonly.acl is also the file name that will later be used in the UCR template directory:
/etc/univention/templates/files/etc/ldap/slapd.conf.d/65ldap-readonly.acl
3. Create the join script
nano 65ldap-acl-test.inst
Content:
#!/bin/sh
VERSION=1
. /usr/share/univention-join/joinscripthelper.lib
. /usr/share/univention-lib/ldap.sh
joinscript_init
export UNIVENTION_APP_IDENTIFIER="ldap-acl-test-1.1"
ucs_registerLDAPExtension "$@" \
--ucsversionstart "5.2-0" \
--ucsversionend "5.99-0" \
--acl /usr/share/ldap-acl-readonly/65ldap-readonly.acl || die
joinscript_save_current_version
exit 0
Important:
- The ACL file is specified with
--acl. - The path points to the source file installed by the package.
--acland the corresponding file path must be specified together.
Make the script executable:
chmod 755 65ldap-acl-test.inst
4. Create the Debian control file
nano debian/control
Content:
Source: ldap-acl-readonly
Section: univention
Priority: optional
Maintainer: Miro <miro@univention.de>
Build-Depends:
debhelper-compat (= 13),
univention-join-dev
Standards-Version: 4.3.0.3
Package: ldap-acl-readonly
Architecture: all
Depends:
univention-join,
shell-univention-lib,
${misc:Depends}
Description: Restricted LDAP read-only ACL
Installs a UCS LDAP ACL for a restricted read-only account.
5. Create the Debian rules file
nano debian/rules
Content:
#!/usr/bin/make -f
%:
dh $@ --with univention-join
Make the file executable:
chmod 755 debian/rules
The --with univention-join option ensures that the join script is correctly processed as a package join script.
6. Create the installation file
nano debian/install
Content:
usr/share/ldap-acl-readonly/65ldap-readonly.acl usr/share/ldap-acl-readonly/
After installing the package, this places the ACL file at:
/usr/share/ldap-acl-test/65ldap-readonly.acl
The final destination directory is managed by the UCS LDAP listener.
7. Create the Debian changelog
Using dch:
dch --create \
--package ldap-acl-readonly \
--newversion 1.0-1 \
"Register restricted LDAP read-only ACL"
Alternatively, create the file manually:
nano debian/changelog
ldap-acl-readonly (1.0-1) UNRELEASED; urgency=medium
* Register restricted LDAP read-only ACL
-- root <root@ucs5primary.deadpool.intranet> Wed, 30 Sep 2026 13:18:32 +0200
8. Build the package
From the package directory:
dpkg-buildpackage -us -uc -b
Output:
root@ucs5primary:~/ldap-acl-test# dpkg-buildpackage -us -uc -b
dpkg-buildpackage: info: source package ldap-acl-readonly
dpkg-buildpackage: info: source version 1.0-1
dpkg-buildpackage: info: source distribution UNRELEASED
dpkg-buildpackage: info: source changed by root <root@ucs5primary.deadpool.intranet>
dpkg-buildpackage: info: host architecture amd64
dpkg-source --before-build .
debian/rules clean
dh clean --with univention-join
dh_clean
debian/rules build
dh build --with univention-join
dh_update_autotools_config
dh_autoreconf
create-stamp debian/debhelper-build-stamp
debian/rules binary
dh binary --with univention-join
dh_testroot
dh_prep
dh-univention-join-install
dh_auto_install --destdir=debian/ldap-acl-readonly/
dh_install
dh_installdocs
dh_installchangelogs
dh_perl
dh_link
dh_strip_nondeterminism
dh_compress
dh_fixperms
dh_missing
dh_installdeb
dh_gencontrol
dh_md5sums
dh_builddeb
dpkg-deb: building package 'ldap-acl-readonly' in '../ldap-acl-readonly_1.0-1_all.deb'.
dpkg-genbuildinfo --build=binary -O../ldap-acl-readonly_1.0-1_amd64.buildinfo
dpkg-genchanges --build=binary -O../ldap-acl-readonly_1.0-1_amd64.changes
dpkg-genchanges: info: binary-only upload (no source code included)
dpkg-source --after-build .
dpkg-buildpackage: info: binary-only upload (no source included)
The resulting package is located one directory level above:
root@ucs5primary:~/ldap-acl-test# ls -l ~/ldap-acl-readonly_*.deb
-rw-r--r-- 1 root root 1788 Sep 30 13:23 /root/ldap-acl-readonly_1.0-1_all.deb
9. Install the package
dpkg -i ~/ldap-acl-test_*.deb
root@ucs5primary:~/ldap-acl-test# dpkg -i ~/ldap-acl-readonly_1.0-1_all.deb
Selecting previously unselected package ldap-acl-readonly.
(Reading database ... 129297 files and directories currently installed.)
Preparing to unpack .../ldap-acl-readonly_1.0-1_all.deb ...
Unpacking ldap-acl-readonly (1.0-1) ...
Setting up ldap-acl-readonly (1.0-1) ...
Calling joinscript 65ldap-acl-readonly.inst ...
2026-09-30 13:24:17.855868526+02:00 (in joinscript_init)
Object exists: cn=ldapacl,cn=univention,dc=deadpool,dc=intranet
Object created: cn=65ldap-readonly,cn=ldapacl,cn=univention,dc=deadpool,dc=intranet
Object modified: cn=65ldap-readonly,cn=ldapacl,cn=univention,dc=deadpool,dc=intranet
Waiting for activation of the extension object 65ldap-readonly: .......OK
2026-09-30 13:24:41.957207790+02:00 (in joinscript_save_current_version)
Joinscript 65ldap-acl-readonly.inst finished with exitcode 0
Verify that the ACL file has been installed:
ls -l /usr/share/ldap-acl-test/65ldap-readonly.acl
The join script should have been installed by dh-univention-join:
ls -l /usr/lib/univention-install/65ldap-acl-readonly.inst
10. Run the join script correctly
Use:
univention-run-join-scripts
11. Verify that the LDAP extension has been registered
udm settings/ldapacl list --filter cn="65ldap-readonly"
DN: cn=65ldap-readonly,cn=ldapacl,cn=univention,dc=deadpool,dc=intranet
active: TRUE
appidentifier: ldap-acl-test-1.1
createTimestamp: 2026-09-30 11:24:19
creatorsName: cn=admin,dc=deadpool,dc=intranet
data: QlpoOTFBWSZTWdmTQHoAACnZgEAQUB9AAj4v3mAwALhEQAAAABQADQAACSplP0JoaNEw<skip>
entryCSN: 20260930112440.191139Z#000000#000#000000
entryUUID: 39053dda-510d-1041-8a58-cd1d38d3c311
filename: 65ldap-readonly.acl
modifiersName: cn=admin,dc=deadpool,dc=intranet
modifyTimestamp: 2026-09-30 11:24:40
name: 65ldap-readonly
package: ldap-acl-readonly
packageversion: 1.0-1
ucsversionend: 5.99-0
ucsversionstart: 5.2-0
univentionObjectIdentifier: 832e19c1-b916-4ab8-88d3-5ec6bab093cf
Also verify that the file has been placed in the intended UCR template directory:
ls -lah /etc/univention/templates/files/etc/ldap/slapd.conf.d/65ldap-readonly.*
-rw-r--r-- 1 root nogroup 257 Sep 30 13:24 /etc/univention/templates/files/etc/ldap/slapd.conf.d/65ldap-readonly.acl
-rw-r--r-- 1 root nogroup 68 Sep 30 13:24 /etc/univention/templates/files/etc/ldap/slapd.conf.d/65ldap-readonly.acl.info
12. Verify the LDAP configuration
The new acl should be configured in the /etc/ldap/slapd.conf
grep -A6 'ou=bremen,dc=deadpool,dc=intranet' /etc/ldap/slapd.conf
access to dn.exact="ou=bremen,dc=deadpool,dc=intranet"
by dn.exact="uid=ldap-readonly,cn=users,dc=deadpool,dc=intranet" read
by * +0 break
access to *
by dn.exact="uid=ldap-readonly,cn=users,dc=deadpool,dc=intranet" none stop
by * +0 break
If not, should be possible to fix that with:
univention-run-join-scriptsucr commit /etc/ldap/slapd.confsystemctl restart slapd
Running ucr commit manually is normally not required, but it can be used for verification or after correcting the configuration.
13. Test access
Test the permitted branch:
ldapsearch -x \
-D "uid=ldap-readonly,cn=users,dc=deadpool,dc=intranet" \
-W -b "ou=bremen,dc=deadpool,dc=intranet"
Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# base <ou=bremen,dc=deadpool,dc=intranet> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#
# Bremen, deadpool.intranet
dn: ou=Bremen,dc=deadpool,dc=intranet
ou: Bremen
univentionObjectIdentifier: 388014b3-6fc2-4a0c-bc61-3f1baf7acf33
displayName: Bremen
ucsschoolRole: school:school:Bremen
univentionObjectType: container/ou
ucsschoolHomeShareFileServer: cn=Bremen,cn=dc,cn=server,cn=computers,ou=Bremen
,dc=deadpool,dc=intranet
ucsschoolClassShareFileServer: cn=Bremen,cn=dc,cn=server,cn=computers,ou=Breme
n,dc=deadpool,dc=intranet
univentionPolicyReference: cn=dhcp-dns-clear,cn=policies,ou=Bremen,dc=deadpool
,dc=intranet
univentionPolicyReference: cn=ou-default-ucr-policy,cn=policies,ou=Bremen,dc=d
eadpool,dc=intranet
objectClass: univentionObject
objectClass: organizationalUnit
objectClass: top
objectClass: ucsschoolOrganizationalUnit
objectClass: univentionPolicyReference
# search result
search: 2
result: 0 Success
# numResponses: 2
# numEntries: 1
Test a branch that is not permitted:
ldapsearch -x \
-D "uid=ldap-readonly,cn=users,dc=deadpool,dc=intranet" \
-W -b "cn=users,ou=bremen,dc=deadpool,dc=intranet"
root@ucs5primary:~/ldap-acl-test# ldapsearch -x -D "uid=ldap-readonly,cn=users,dc=deadpool,dc=intranet" -W -b "cn=users,ou=bremen,dc=deadpool,dc=intranet"
Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# base <cn=users,ou=bremen,dc=deadpool,dc=intranet> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#
# search result
search: 2
result: 32 No such object
# numResponses: 1
or
ldapsearch -x \
-D "uid=ldap-readonly,cn=users,dc=deadpool,dc=intranet" \
-W -b "dc=deadpool,dc=intranet"
root@ucs5primary:~/ldap-acl-test# ldapsearch -x -D "uid=ldap-readonly,cn=users,dc=deadpool,dc=intranet" -W -b "dc=deadpool,dc=intranet"
Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# base <dc=deadpool,dc=intranet> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#
# search result
search: 2
result: 32 No such object
Documentation
- Packaging LDAP ACL Extensions (5. Lightweight Directory Access Protocol (LDAP) in UCS — Univention Corporate Server - Manual for developers)
- ucs_registerLDAPExtension (https://docs.software-univention.de/developer-reference/5.2/en/join.html#ucs-registerldapextension)
See also: