Cybersecurity Lab

PW2 — OpenLDAP Directory Services

Deployed and configured OpenLDAP (slapd 2.6.13) on Kali Linux: directory structure with OUs, users and groups, LDIF entries, ldapsearch verification, and a comparison of LDAP vs local Linux accounts with file permission testing.

CompletedAug 2026Intermediate

Objective

Deploy a working LDAP directory, populate it with organizational units, users and groups, then demonstrate how directory identity and local Linux file permissions form two independent layers of an access control model.

Tools Used

OpenLDAPLDAPLinuxIdentity Management

Steps Performed

  • Installed slapd and ldap-utils, and set the base DN to dc=lab,dc=local via dpkg-reconfigure.
  • Verified the suffix by reading the configuration database directly with slapcat.
  • Created three organizational units (Staff, Students, IT_Admins) from an LDIF file.
  • Added posixAccount users alice and bob, and a posixGroup IT_Admins.
  • Verified the tree with four ldapsearch queries covering filters and attribute selection.
  • Created local Linux users and departmental groups, then compared them against the directory.
  • Secured /srv/hr_docs (770) and /srv/finance_docs (750) and tested access as each user.

Key Findings

  • A directory entry with a full posixAccount definition still does not create a Linux login account — id alice failed while ldapsearch returned her complete record.
  • Group membership and permission are separate things: bob_finance belonged to finance and could list the directory, but still could not write to it.
  • Linux evaluates owner, then group, then others, and the first match wins — bob_finance was never evaluated against the others bits.
  • Without the setgid bit, files created in a shared directory inherit the creator's private group rather than the departmental group, undermining collaboration.

Lessons Learned

  • The base DN is never typed directly — slapd derives it from a DNS domain name, so lab.local becomes dc=lab,dc=local.
  • Verifying the service is running proves less than querying the directory; both checks are needed.
  • posixAccount is the object class that makes a directory entry usable as a Unix login account later, via SSSD or nss-pam-ldapd.
  • A permission model should be tested empirically rather than assumed from the mode bits.

Future Improvements

  • Integrate the directory with the operating system through SSSD so uidNumber 10000 becomes Alice's real Linux UID.
  • Apply the setgid bit (chmod 2770) so files created in shared directories inherit the departmental group.
  • Enable LDAPS/StartTLS so binds are not transmitted in cleartext.

References

  • OpenLDAP Software 2.6 Administrator's Guide
  • RFC 4519 — Lightweight Directory Access Protocol (LDAP): Schema for User Applications
  • Debian slapd package documentation

Section 1: Installing & Configuring OpenLDAP

What packages and tools do you install for the LDAP server and administration?

Two packages were installed, and it is worth distinguishing three separate things that are often confused:

  • LDAP is the protocol — the set of rules for querying a directory over a network. It is not software.
  • slapd is the server daemon (Standalone LDAP Daemon). This is the software that actually stores and serves the directory database.
  • ldap-utils is the client toolkit, providing ldapadd, ldapsearch, ldapmodify and ldapwhoami. These are required even on the server itself, because all administration is performed by connecting to slapd as a client.

This mirrors the Windows model, where AD DS is the service and Active Directory Users and Computers is the administrative tool.

sudo apt update
sudo apt install slapd ldap-utils -y

Two lines of the installer output are significant. "Creating initial configuration... done" confirms slapd built a starter directory, and "slapd.service is a disabled or a static unit, not starting it" shows that Kali does not auto-start network services — the daemon had to be enabled manually in a later step.

How do you set the base DN (e.g. dc=example,dc=local) and define a secure admin password?

The base DN is configured through the debconf interface:

sudo dpkg-reconfigure slapd

The important concept here is that the base DN is never typed directly. The prompt asks for a DNS domain name, and slapd converts it: each dot-separated label becomes a domain component. Entering lab.local therefore produces the base DN dc=lab,dc=local, which becomes the root of the directory tree. Every entry created afterwards is placed beneath it.

The administrator account is created as cn=admin,dc=lab,dc=local, with the password supplied during the same dialogue. This account is the LDAP equivalent of a domain administrator and must be used to authenticate any write operation to the directory.

The configured suffix was then verified by reading the configuration database directly, which proves the result rather than the intent:

sudo slapcat -n 0 | grep olcSuffix
olcSuffix: dc=lab,dc=local

Which commands or service checks verify that OpenLDAP is running correctly?

Verification was performed at two levels — the service, and the directory itself. First, the daemon was enabled and started. The enable flag registers slapd to start automatically at boot, which was necessary because Kali had left the unit disabled:

sudo systemctl enable --now slapd
sudo systemctl status slapd

The status output confirmed Active: active (running), together with the process ID and the listening sockets ldap:/// and ldapi:///. A running service does not, however, prove the directory is usable. The second and stronger check queries the directory:

ldapsearch -x -LLL -b dc=lab,dc=local

The flags are: -x for simple authentication rather than SASL, -LLL to suppress comments and version information from the output, and -b to specify the search base. The command returned the root entry with dc: lab, confirming an empty but functional directory ready to be populated.

Section 2: Directory Structure (LDAP OUs, Users, Groups)

LDAP stores data as a hierarchical tree. The base DN is the root, organizational units are branches, and users and groups are leaves. Every entry is addressed by its Distinguished Name, which is read right to left — uid=alice,ou=Staff,dc=lab,dc=local means the user alice, inside the Staff OU, inside lab.local.

Entries are defined in LDIF (LDAP Data Interchange Format) files, which are plain text descriptions loaded into the directory with ldapadd.

2.1 Organizational Units — Three OUs were created as specified: Staff, Students and IT_Admins. The file base.ldif contained:

dn: ou=Staff,dc=lab,dc=local
objectClass: organizationalUnit
ou: Staff

dn: ou=Students,dc=lab,dc=local
objectClass: organizationalUnit
ou: Students

dn: ou=IT_Admins,dc=lab,dc=local
objectClass: organizationalUnit
ou: IT_Admins

Each entry declares its position in the tree (dn), what kind of object it is (objectClass), and its name (ou). The objectClass is the schema definition and determines which attributes are permitted or required — organizationalUnit requires ou.

ldapadd -x -D "cn=admin,dc=lab,dc=local" -W -f base.ldif

Here -D specifies the identity to bind as, -W prompts for its password, and -f names the input file.

2.2 User Entries — Two users were added: Alice under Staff and Bob under Students. Each entry declares three object classes, which stack to provide the required attributes:

  • top — the root of the schema hierarchy, required on every entry
  • inetOrgPerson — supplies person attributes such as cn (common name) and sn (surname)
  • posixAccount — supplies the Unix attributes uidNumber, gidNumber, homeDirectory and loginShell

The posixAccount class is significant. Without it the entry would be a directory record only; with it, the entry carries everything a Linux system needs to treat it as a login account, which is what makes later integration through SSSD possible.

dn: uid=alice,ou=Staff,dc=lab,dc=local
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: top
cn: Alice
sn: Alice
uid: alice
uidNumber: 10000
gidNumber: 10000
homeDirectory: /home/alice
loginShell: /bin/bash

2.3 Group Creation — The IT_Admins group was created as a posixGroup with alice as a member:

dn: cn=IT_Admins,ou=IT_Admins,dc=lab,dc=local
objectClass: posixGroup
objectClass: top
cn: IT_Admins
gidNumber: 20001
memberUid: alice

A posixGroup lists its members by short username using memberUid. This differs from the other common group class, groupOfNames, which references members by their full Distinguished Name. posixGroup was chosen here because it matches the Unix group model used later in Sections 3 and 4.

2.4 Verification with ldapsearch — Four queries were run to demonstrate both filters and attribute selection:

ldapsearch -x -LLL -b dc=lab,dc=local
ldapsearch -x -LLL -b dc=lab,dc=local "(objectClass=inetOrgPerson)"
ldapsearch -x -LLL -b dc=lab,dc=local "(uid=alice)" cn homeDirectory
ldapsearch -x -LLL -b dc=lab,dc=local "(cn=IT_Admins)"

The first returned all seven entries — the root, three OUs, two users and one group. The second applied a filter and returned only Alice and Bob, excluding the OUs and the group.

The third query is the most instructive. By supplying both a filter and an attribute list, it returned only cn and homeDirectory for Alice. The dn is always included because it identifies the entry, but every other attribute was suppressed. The distinction is that the filter controls which entries are returned, while the attribute list controls which fields are returned. On a directory containing tens of thousands of users, this is the difference between a targeted query and dumping the entire database. The fourth confirmed the group entry with memberUid: alice.

Section 3: Linux Users & Groups (System Accounts)

Local Linux accounts are entirely separate from the LDAP entries created in Section 2. Creating uid=alice in the directory did not create a Linux user, and this was demonstrated directly:

id alice
id: 'alice': no such user

id alice_hr
uid=1010(alice_hr) gid=1013(alice_hr) groups=1013(alice_hr),1001(hr)

Alice exists in LDAP with a complete posixAccount definition, yet the operating system cannot resolve her. This is the central point of this section: two directories exist on the same machine and, by default, they do not communicate.

3.1 Groups and Users Created — Departmental groups hr and finance already existed on the system, so users were added to them:

sudo useradd -m -G hr alice_hr
sudo passwd alice_hr
sudo useradd -m -G finance bob_finance
sudo passwd bob_finance

The -m flag creates the user's home directory and -G adds them to the named group as a supplementary group. Membership was confirmed with:

groups alice_hr    -> alice_hr : alice_hr hr
groups bob_finance -> bob_finance : bob_finance finance

getent group hr finance
hr:x:1001:kay,bob,smith,alice_hr
finance:x:1003:ajay,sanjay,munni,bob_finance

Note that each user belongs to two groups. Debian-based systems use User Private Groups, giving every account a personal group of the same name which becomes its primary group. The departmental group is supplementary. This distinction matters in Section 4, because new files are created with the user's primary group by default.

3.2 Comparing Linux and LDAP Group Membership — There is no overlap whatsoever. alice_hr exists locally and can log in but appears nowhere in the directory; alice exists in the directory with a full posixAccount but has no local account at all.

Local accounts are defined in /etc/passwd and /etc/group and apply only to this machine. They function without a network, but they scale poorly: the same person across fifty servers means fifty separate accounts, fifty password resets, and fifty opportunities to miss one during offboarding.

LDAP centralises identity. One entry is visible to every system that queries the directory, so adding a user to IT_Admins takes effect everywhere immediately, and deprovisioning is a single deletion rather than fifty.

These two layers are not connected in this deployment, which is why id alice fails. Integration would require SSSD or nss-pam-ldapd, which configure the Name Service Switch to consult LDAP in addition to /etc/passwd. Once configured, the uidNumber: 10000 defined in the LDIF would become Alice's actual Linux UID — which is precisely why the posixAccount object class was applied to those entries.

The resulting model is layered: the directory service defines who exists and what role they hold, while Linux file permissions enforce what those identities may access on a given machine. Identity is central; enforcement is local.

Section 4: File Permissions & Access Rights

Two departmental directories were created and secured according to the requirement that HR receive full access to its data while Finance receives read-only access:

sudo mkdir -p /srv/hr_docs
sudo mkdir -p /srv/finance_docs

sudo chown root:hr /srv/hr_docs
sudo chown root:finance /srv/finance_docs

sudo chmod 770 /srv/hr_docs
sudo chmod 750 /srv/finance_docs

ls -ld /srv/hr_docs /srv/finance_docs
drwxrwx--- 2 root hr      4096 /srv/hr_docs
drwxr-x--- 2 root finance 4096 /srv/finance_docs

4.1 Command Usage

  • chmod changes the permission bits of a file or directory. It does not change ownership.
  • chown changes the user owner, and optionally the group owner when written as user:group. Writing chown :group changes only the group.
  • chgrp changes only the group owner, and is equivalent to the colon-prefixed form of chown.

4.2 How Permission Bits Map to Effective Rights — Each permission digit is the sum of three bits: read is 4, write is 2 and execute is 1. The three digits apply to the owner, the group, and all other users respectively.

DirectoryModeOwnerGroupOthers
/srv/hr_docs770rwx (7)rwx (7)--- (0)
/srv/finance_docs750rwx (7)r-x (5)--- (0)

The middle digit carries the requirement. HR's group receives 7, which includes write, so members can create and delete files. Finance's group receives 5, which is read and execute only, so members can enter and list the directory but cannot create anything within it.

On a directory the execute bit does not mean "run this file". It grants permission to enter the directory and access its contents by name. Without it, read permission alone allows a user to list filenames but not open any of them. This is why directories require 7 and 5 where an equivalent file would use 6 and 4.

Both directories set the final digit to 0, so any user outside the owning group is denied entirely. This implements default-deny: access is granted explicitly to a named group, and no rule needs to be written about anyone else.

Section 5: Verification & Test Matrix

Each configured permission was tested by executing commands as the relevant user with sudo -u, which runs a command under another identity without requiring an interactive login.

sudo -u alice_hr    touch /srv/hr_docs/test.txt
sudo -u alice_hr    touch /srv/finance_docs/test.txt
sudo -u bob_finance touch /srv/finance_docs/test.txt
sudo -u bob_finance ls -l /srv/finance_docs
sudo -u bob_finance touch /srv/hr_docs/test.txt
sudo ls -l /srv/hr_docs /srv/finance_docs
UserDirectoryActionResultReason
alice_hr/srv/hr_docscreateGrantedMember of hr; group bits are rwx
alice_hr/srv/finance_docscreateDeniedNot in finance; falls to others (---)
bob_finance/srv/finance_docscreateDeniedIn finance, but group bits are r-x — no write
bob_finance/srv/finance_docslistGrantedRead and execute are permitted
bob_finance/srv/hr_docscreateDeniedNot in hr; falls to others (---)

5.2 Analysis — The third and fourth results are the most informative, because they concern the same user acting on the same directory with different outcomes. bob_finance is a member of the finance group and still could not create a file in /srv/finance_docs, yet the listing command succeeded and returned without error.

This demonstrates that group membership and permission are two separate things. Membership determines which of the three permission sets applies to a user; the bits within that set determine what is actually allowed. Being in the correct group is necessary but not sufficient.

Linux evaluates access in a fixed order: it checks whether the requester is the owner, then whether they belong to the owning group, and only then falls through to others. The first match wins and no further sets are considered. bob_finance was therefore never evaluated against the others bits for finance_docs, because he matched at the group stage.

5.3 Observed Limitation — The final directory listing showed the file created by alice_hr as:

-rw-rw-r-- 1 alice_hr alice_hr 0 test.txt

The file belongs to the group alice_hr rather than hr. Other members of the hr group can therefore read the file but cannot modify it, which undermines the intent of a shared departmental directory. The cause is the User Private Group behaviour noted in Section 3: new files inherit the creator's primary group.

The 770 mode governs who may enter the directory, but it does not govern the group ownership of files created inside it. The correct remedy is the setgid bit, applied with chmod 2770, which causes new files and subdirectories to inherit the group of the parent directory instead. The configuration above follows the permissions specified in the assignment; this limitation is documented here because it materially affects collaboration within the group.

By contrast, NTFS on Windows applies inheritance by default, so a file created in a shared folder automatically receives the folder's access control entries. The same problem exists in both systems but the defaults are opposite.

Section 6: IAM Awareness & Integration Takeaways

6.1 Why Access Rights Management Is Essential to IAM — Authentication establishes who a user is, but on its own it grants nothing useful and prevents nothing harmful. Access rights management is the stage that decides what a verified identity may actually reach, and without it every authenticated user would hold equivalent power over every resource. It is the mechanism that turns least privilege from a principle into an enforced configuration.

Its value is visible across the whole account lifecycle. At provisioning, rights are granted according to role rather than individually, so a new employee receives a consistent and reviewable set of permissions. During a role change, old rights must be removed as new ones are added, otherwise privilege accumulates silently over a career — one of the most common causes of excessive access in real organisations. At deprovisioning, revoking rights immediately closes the window in which a departed user's credentials remain valuable to an attacker.

Access rights management also underpins accountability. Because permissions attach to unique identities, log entries can be traced to a specific person rather than to a shared account, which supports both incident investigation and regulatory compliance. In this practical, the denial recorded for bob_finance is meaningful precisely because it identifies a single, uniquely named account acting outside its authorised scope.

6.2 How LDAP and Linux Permissions Complement Each Other — The two mechanisms address different halves of the same problem, and neither is sufficient alone.

LDAP centralises identity. It answers the questions of who exists, what attributes describe them, and which roles they hold. A single entry is authoritative for every system that queries the directory, so a change made once takes effect everywhere. This solves the scaling problem that local accounts cannot: managing identities across many machines from one place, with a single deletion sufficient to revoke access at offboarding.

Linux file permissions enforce access locally. They answer what a given identity may do to a specific resource on a specific machine, and the kernel evaluates them on every access. Ownership, group membership and the read, write and execute bits are what stopped bob_finance from writing to the HR directory.

The relationship is a division of labour: the directory defines identity and role, and the filesystem enforces authorisation. LDAP grants no file access by itself, and local permissions cannot scale beyond a single host. This separation was demonstrated directly in this practical, where id alice failed while ldapsearch returned her complete record — the two layers were operating independently because no integration mechanism such as SSSD had been configured.

Together they enforce least privilege end to end. Central identity ensures a user is provisioned once into the correct role, and local permissions ensure that role grants only the access required for their duties and nothing more.

Conclusion

An OpenLDAP directory was deployed with the base DN dc=lab,dc=local, populated with three organizational units, two user entries and one group, and verified using filtered ldapsearch queries. Local Linux users and groups were created and compared against the directory, demonstrating that the two identity layers operate independently without an integration mechanism.

Departmental directories were secured with group ownership and permission modes matching the required access model, and each configuration was tested empirically. All five tests produced the expected outcome, confirming that the permission model enforces least privilege as designed. One limitation was identified and documented: without the setgid bit, files created within a shared directory do not inherit the departmental group, which restricts collaboration between members of the same group.