Changeset - c1b844e6a76f
[Not reviewed]
0 6 1
Branko Majic (branko) - 10 months ago 2025-01-25 00:31:04
branko@majic.rs
MAR-236: Add auxiliary object class to LDAP directory for optional countryName and friendlyCountryName attributes:

- Makes it easier to specify free-form optional country name in
LDAP-backed address books.
- Object class uses Majic-specific PEN (Private Enterprise Number)
"namespace" - 1.3.6.1.4.1.40926.417.2.1 (40926 is Majic PEN, 417
stands for LDAP, 2 stands for object class, and 1 is incremental
sequence number). The 417 is result of adding together lower-cased
letters LDAP in their decimal value.
7 files changed with 87 insertions and 3 deletions:
0 comments (0 inline, 0 general)
docs/releasenotes.rst
Show inline comments
 
@@ -39,24 +39,27 @@ Upgraded to Ansible 10.4.x. Dropped support for Debian 11
 

	
 
**New features/improvements**
 

	
 
* ``backup_client`` role
 

	
 
  * Switched to using Paramiko + SFTP backend (instead of pexpect +
 
    SFTP), which should improve the backup performance.
 

	
 
* ``ldap_server`` role
 

	
 
  * TLSv1.3 is now enabled by default (with RFC-defined mandatory
 
    ciphers), in addition to TLSv1.2.
 
  * Additional schema is enabled that introduces auxilliary object
 
    class ``optionalCountry`` with optional ``countryName / c`` and
 
    ``friendlyCountryName / co`` attributes. Useful for address books.
 

	
 
* ``mail_server`` role
 

	
 
  * TLSv1.3 is now enabled by default (with RFC-defined mandatory
 
    ciphers), in addition to TLSv1.2, for IMAP and SMTP submission.
 

	
 
* ``web_server`` role
 

	
 
  * TLSv1.3 is now enabled by default (with RFC-defined mandatory
 
    ciphers), in addition to TLSv1.2.
 

	
 
* ``xmpp_server`` role
docs/rolereference.rst
Show inline comments
 
@@ -592,24 +592,31 @@ LDAP Server
 

	
 
The ``ldap_server`` role can be used for setting-up an OpenLDAP server on
 
destination machine.
 

	
 
The role implements the following:
 

	
 
* Deploys LDAP TLS private key and certificate.
 
* Configures TLS versions and ciphers suppported by the server.
 
* Installs OpenLDAP server (package ``slapd``).
 
* Configures OpenLDAP server (base DN - domain, organisation, TLS, SSF, log levels).
 
* Enables the ``misc`` LDAP schema (from ``/etc/ldap/schema/misc.ldif``). This
 
  is necessary for the mail server role.
 
* Deploys and enables the ``optionalcountry`` LDAP schema that adds
 
  auxilliary object class ``optionalCountry`` which allows two
 
  optional attributes to be specified - ``countryName / c`` and
 
  ``friendlyCountryName / co``. This is particularly useful for
 
  address book functionality (for applications that use LDAP directory
 
  as backend). No built-in object class seems to cover this particular
 
  use-case.
 
* Enables the ``memberof`` overlay on top of default database. The overlay is
 
  configured to keep track of membership changes for object class
 
  ``groupOfUniqueNames`` via attribute ``uniqueMember``. Enforcement of
 
  referential integrity is turned on as well (modifications of ``memberof``
 
  attribute will update corresponding group as well.
 
* Creates a basic directory structure used by most of the other roles.
 
* Creates a basic directory structure used by the mail server role.
 
* Creates login entries for services that need to consume LDAP directory data in
 
  some way.
 
* Creates user-supplied groups in LDAP.
 
* Configures permissions.
 
* Creates LDAP entries.
roles/ldap_server/files/optionalcountry.ldif
Show inline comments
 
new file 100644
 
dn: cn=optionalcountry,cn=schema,cn=config
 
objectClass: olcSchemaConfig
 
cn: optionalcountry
 
olcObjectClasses: (
 
  1.3.6.1.4.1.40926.417.2.1
 
  NAME 'optionalCountry'
 
  DESC 'Optional country code and friendly country name'
 
  SUP top AUXILIARY
 
  MAY ( countryName $ friendlyCountryName )
 
  )
roles/ldap_server/molecule/default/group_vars/parameters-optional.yml
Show inline comments
 
@@ -15,24 +15,42 @@ ldap_entries:
 
      uid: john
 
      cn: John Doe
 
      sn: Doe
 
  - dn: uid=jane,dc=local
 
    attributes:
 
      objectClass:
 
        - inetOrgPerson
 
        - simpleSecurityObject
 
      userPassword: janepassword
 
      uid: jane
 
      cn: Jane Doe
 
      sn: Doe
 
  - dn: uid=blank-optional-country,dc=local
 
    attributes:
 
      objectClass:
 
        - inetOrgPerson
 
        - optionalCountry
 
      uid: blank-optional-country
 
      cn: Blank Optional Country
 
      sn: Blank Optional Country
 
  - dn: uid=optional-country,dc=local
 
    attributes:
 
      objectClass:
 
        - inetOrgPerson
 
        - optionalCountry
 
      uid: optional-country
 
      cn: Optional Country
 
      sn: Optional Country
 
      c: RS
 
      co: Serbia
 

	
 
ldap_permissions:
 
  - >
 
    to *
 
    by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage
 
    by self write
 
    by * read
 
    by dn="cn=admin,dc=local" write
 
    by * none
 

	
 
ldap_server_consumers:
 
  - name: consumer1
roles/ldap_server/molecule/default/tests/test_default.py
Show inline comments
 
@@ -51,24 +51,36 @@ def test_ldap_server_service(host):
 
def test_misc_schema_presence(host):
 
    """
 
    Tests if the misc LDAP schema has been imported.
 
    """
 

	
 
    with host.sudo():
 

	
 
        misc_schema = host.run('ldapsearch -H ldapi:/// -Q -LLL -Y EXTERNAL -b cn=config dn')
 
        assert misc_schema.rc == 0
 
        assert 'dn: cn={4}misc,cn=schema,cn=config' in misc_schema.stdout
 

	
 

	
 
def test_optional_country_schema_presence(host):
 
    """
 
    Tests if the LDAP schema with object class for optional
 
    country code/name has been imported.
 
    """
 

	
 
    with host.sudo():
 
        country_schema = host.run('ldapsearch -H ldapi:/// -Q -LLL -Y EXTERNAL -b cn=config dn')
 
        assert country_schema.rc == 0
 
        assert 'dn: cn={5}optionalcountry,cn=schema,cn=config' in country_schema.stdout
 

	
 

	
 
def test_memberof_module(host):
 
    """
 
    Tests if the memberof overlay has been enabled for the main database.
 
    """
 

	
 
    with host.sudo():
 
        memberof = host.run('ldapsearch -H ldapi:/// -Q -LLL -Y EXTERNAL -b cn=config dn')
 

	
 
        assert memberof.rc == 0
 
        assert 'dn: olcOverlay={0}memberof,olcDatabase={1}mdb,cn=config' in memberof.stdout
 

	
 

	
roles/ldap_server/molecule/default/tests/test_optional.py
Show inline comments
 
@@ -210,18 +210,34 @@ objectClass: inetOrgPerson
 
objectClass: simpleSecurityObject
 
userPassword:: am9obnBhc3N3b3Jk
 
cn: John Doe
 
sn: Doe
 
uid: john
 

	
 
dn: uid=jane,dc=local
 
objectClass: inetOrgPerson
 
objectClass: simpleSecurityObject
 
userPassword:: amFuZXBhc3N3b3Jk
 
cn: Jane Doe
 
sn: Doe
 
uid: jane""")
 
uid: jane
 

	
 
        entries = host.run("ldapsearch -H ldapi:/// -Q -LLL -Y EXTERNAL -b dc=local '(|(entrydn=uid=john,dc=local)(entrydn=uid=jane,dc=local))'")
 
dn: uid=blank-optional-country,dc=local
 
objectClass: inetOrgPerson
 
objectClass: optionalCountry
 
uid: blank-optional-country
 
cn: Blank Optional Country
 
sn: Blank Optional Country
 

	
 
dn: uid=optional-country,dc=local
 
objectClass: inetOrgPerson
 
objectClass: optionalCountry
 
uid: optional-country
 
cn: Optional Country
 
sn: Optional Country
 
c: RS
 
co: Serbia""")
 

	
 
        entries = host.run("ldapsearch -H ldapi:/// -Q -LLL -Y EXTERNAL -b dc=local '(objectClass=inetOrgPerson)'")
 

	
 
        assert entries.rc == 0
 
        assert parse_ldif(entries.stdout) == expected_entries
roles/ldap_server/tasks/main.yml
Show inline comments
 
@@ -60,29 +60,47 @@
 
- name: Change log level for slapd
 
  community.general.ldap_attrs:
 
    dn: cn=config
 
    attributes:
 
      olcLogLevel: "{{ ldap_server_log_level }}"
 
    state: exact
 

	
 
- name: Test if LDAP misc schema has been applied
 
  ansible.builtin.command: "ldapsearch -H ldapi:/// -Q -LLL -A -Y EXTERNAL -b cn=schema,cn=config -s one '(cn={*}misc)' cn"
 
  register: ldap_misc_schema_present
 
  changed_when: false
 

	
 
- name: Deploy LDAP misc schema
 
- name: Apply LDAP misc schema
 
  ansible.builtin.command: "ldapadd -H ldapi:/// -Q -Y EXTERNAL -f /etc/ldap/schema/misc.ldif"
 
  when: not ldap_misc_schema_present.stdout
 
  changed_when: true  # Always results in change due to task logic.
 

	
 
- name: Deploy optional country schema definition
 
  ansible.builtin.copy:
 
    src: optionalcountry.ldif
 
    dest: "/etc/ldap/schema/optionalcountry.ldif"
 
    owner: root
 
    group: root
 
    mode: "0644"
 

	
 
- name: Test if optional country schema is present
 
  ansible.builtin.command: "ldapsearch -H ldapi:/// -Q -LLL -A -Y EXTERNAL -b cn=schema,cn=config -s one '(cn={*}optionalcountry)' cn"
 
  register: ldap_optionalcountry_schema_present
 
  changed_when: false
 

	
 
- name: Apply LDAP optional country schema
 
  ansible.builtin.command: "ldapadd -H ldapi:/// -Q -Y EXTERNAL -f /etc/ldap/schema/optionalcountry.ldif"
 
  when: not ldap_optionalcountry_schema_present.stdout
 
  changed_when: true  # Always results in change due to task logic.
 

	
 
# Technically, the only thing this does is pick the size of DH
 
# parameters to use, with GnuTLS (against which slapd is linked
 
# against under Debian) picking a matching DH parameter from RFC-7919
 
# (https://www.ietf.org/rfc/rfc7919.txt).
 
- name: Generate the LDAP server Diffie-Hellman parameter
 
  community.crypto.openssl_dhparam:
 
    owner: root
 
    group: openldap
 
    mode: "0640"
 
    path: "/etc/ssl/private/{{ ansible_fqdn }}_ldap.dh.pem"
 
    size: 2048
 
  notify:
0 comments (0 inline, 0 general)