Extending OpenLDAP: Implementing Custom TOTP Secret Schemas
A Deep Dive into OIDs, Auxiliary Classes, and UUID-based Identification
In modern infrastructure, the Directory Service (LDAP) often serves as the “Source of Truth” for authentication. However, when implementing Multi-Factor Authentication (MFA/TOTP), we frequently encounter a limitation: the standard LDAP schema does not have a dedicated field for TOTP secrets.
This article documents the process of safely extending the OpenLDAP schema using a globally unique approach, ensuring scalability and professional-grade data organization.
1. The Foundation: Understanding OIDs
Every element in an LDAP directory must be uniquely identified by an Object Identifier (OID). Think of an OID as a global, immutable DNA sequence for data structures.
Why not just use a name?
LDAP is a distributed protocol. If two different organizations created an attribute named totpSecret with different technical specifications, integrating their systems would cause a collision. An OID (e.g., 2.25.x...) prevents this.
The Strategy: IANA vs. UUID
While many organizations apply for a Private Enterprise Number (PEN) at IANA (under the 1.3.6.1.4.1 arc), this process can take weeks. For immediate, technically sound implementation, we use the UUID-to-OID mapping (2.25 arc).
https://www.iana.org/assignments/enterprise-numbers
Generating a collision-free OID via Python:
Python
python3 -c "import uuid; print('2.25.' + str(uuid.uuid4().int))"
2.25.2689462794085719547432000316709609255692. Architecting the Schema: Attributes and Classes
An LDAP extension requires two components: an Attribute Type (the field) and an Object Class (the container).
The Attribute: totpSecret
We define the secret as a SINGLE-VALUE string. Using the caseIgnoreMatch equality ensures robust searching, while the syntax 1.3.6.1.4.1.1466.115.121.1.15 (Directory String) allows for Base64 encoded secrets.
[ OID: 2.25.268946279408571954743200031670960925569 ] <-- Your unique "Company ID" in the world. |
+--> [ .1 ] Attribute: devopsdbSecrets
| (Where do you save the Base64 code?)
|
+--> [ .2 ] ObjectClass: totpUser (AUXILIARY)
(The "folder" authorizing the use of totpSecret)
The Class: devopsdbSecrets
Instead of creating a “Fixed” user type, we define an AUXILIARY class.
- Why Auxiliary? It acts as a “plug-in.” You can attach it to any existing user (whether they are a
person,posixAccount, orinetOrgPerson) without altering their primary identity.
DN: cn=Fausto Branco,ou=UserGroups,dc=ldap,dc=devops-db,dc=info
|
+-- ObjectClass: top
+-- ObjectClass: person
+-- ObjectClass: organizationalPerson
+-- ObjectClass: inetOrgPerson <-- (Main Class: Name, Email, Password)
|
+-- ObjectClass: devopsdbSecrets <-- (Your Auxiliary Class added)
|
+-- Atributo: totpSecret: "R0VUVEhJU0NPREU=" (O valor em Base64)
ldapsearch -x -H ldap://ldap.devops-db.info:389 \
-b "cn=Fausto Branco,ou=UserGroups,dc=ldap,dc=devops-db,dc=info" \
-s base objectClass
# extended LDIF
#
# LDAPv3
# base <cn=Fausto Branco,ou=UserGroups,dc=ldap,dc=devops-db,dc=info> with scope baseObject
# filter: (objectclass=*)
# requesting: objectClass
#
# Fausto Branco, UserGroups, ldap.devops-db.info
dn: cn=Fausto Branco,ou=UserGroups,dc=ldap,dc=devops-db,dc=info
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: devopsdbSecrets3. Implementation: The LDIF Workflow
Using the vi editor—the standard for Unix systems—we create the Schema definition. Crucial Tip: In LDIF files, line continuations must start with a leading space.
Step 1: Injecting the Schema
We use the SASL/EXTERNAL mechanism over a Unix socket (ldapi:///). This allows the system root to modify the configuration (cn=config) without needing a separate LDAP password.
Bash
add_totp_schema.ldif
# Schema definition using arc 2.25 (UUID)
dn: cn=devops-schema,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: devops-schema
olcAttributeTypes: ( 2.25.268946279408571954743200031670960925569.1
NAME 'totpSecret'
DESC 'TOTP Secret encoded in Base64'
EQUALITY caseIgnoreMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
olcObjectClasses: ( 2.25.268946279408571954743200031670960925569.2
NAME 'devopsdbSecrets'
DESC 'Auxiliary class for DevOps-DB infrastructure secrets'
SUP top
AUXILIARY
MAY totpSecret )Apply:
sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// -f add_totp_schema.ldif
Step 2: Modifying the User
Targeting a user requires their Full Distinguished Name (DN). Even if you know their uid, LDAP modification operations require the exact path in the tree.
The Update LDIF (update_user_totp.ldif):
LDIF
dn: cn=Fausto Branco,ou=UserGroups,dc=ldap,dc=devops-db,dc=info
changetype: modify
add: objectClass
objectClass: devopsdbSecrets
-
add: totpSecret
totpSecret: VFJLUVdXWlVDQjJaSzZXTUFKUTdHVkpOVlZOR0hXRjQ=
4. Verification: Ensuring Data Integrity
To validate the work, we perform a scoped search. If the schema was applied correctly, the server will return both the auxiliary class and the custom attribute.
Bash
ldapsearch -x -H ldap://ldap.devops-db.info:389 \
-b "cn=Fausto Branco,ou=UserGroups,dc=ldap,dc=devops-db,dc=info" \
-s base "totpSecret" "objectClass"
# extended LDIF
#
# LDAPv3
# base <cn=Fausto Branco,ou=UserGroups,dc=ldap,dc=devops-db,dc=info> with scope baseObject
# filter: (objectclass=*)
# requesting: totpSecret objectClass
#
# Fausto Branco, UserGroups, ldap.devops-db.info
dn: cn=Fausto Branco,ou=UserGroups,dc=ldap,dc=devops-db,dc=info
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: devopsdbSecrets
totpSecret: VFJLUVdXWlVDQjJaSzZXTUFKUTdHVkpOVlZOR0hXRjQ=
# search result
search: 2
result: 0 Success
# numResponses: 2
# numEntries: 1
Conclusion
Extending OpenLDAP is a powerful way to centralize security infrastructure. By using OIDs under the 2.25 arc and designing auxiliary classes, we create a flexible, standards-compliant environment ready for modern MFA requirements.
Next Steps: Implement Access Control Lists (ACLs) to ensure that totpSecret is only readable by the owner and the administrative service, preventing unauthorized exposure of MFA seeds.
Encoding: TOTP secrets should be stored in Base64 to handle non-standard character sets safely across different LDAP clients.
