Active Directory/LDAP User & Group Imports via Director

Motivation:

This thread helped me a lot to get started.
Because of this, here is a little write up on where I went with it:

Goal:

Use AD groups to give selective access in Icingaweb2 and use the same groups for Notifications.

Problem:

This is bad! As required by AGDLP, we have business groups as members of permission groups.
Impossible with only the director I would guess:

https://github.com/Icinga/icingaweb2-module-director/issues/2222

My solution for the moment:

https://github.com/Icinga/icingaweb2-module-fileshipper

And this quick hacked together script:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8 ts=4 sw=4 sts=4 et :

from ldap3 import Server, Connection, ALL, NTLM
from ldap3.utils.conv import escape_filter_chars
import json
import sys

server = Server('ADSERVER', use_ssl=True, get_info=ALL)

def get_user_groups(dn):
    with Connection(server, user="BINDUSER", password="BINDPW", authentication=NTLM) as conn:
        dn = escape_filter_chars(dn)
        conn.search(
            search_base='SEARCHBASE',
            search_filter='(member:1.2.840.113556.1.4.1941:=%s)' % dn,)
        return conn.entries

with Connection(server, user="BINDUSER", password="BINDPW", authentication=NTLM) as conn:
    conn.search(
        search_base='SEARCHBASE',
        search_filter='\
        (&\
            (memberOf:1.2.840.113556.1.4.1941:=DNOFMAINGROUP)\
                    (!(userAccountControl:1.2.840.113556.1.4.803:=2))\
                        (mail=*)\
                        (objectClass=user)\
        )',
        attributes=[
            'SamAccountName',
            'displayName',
            'mail',
        ]
    )

    #print(conn.entries[0].entry_dn)
    icinga_users = []
    for user in conn.entries:
        user_json = user.entry_to_json()
        user_dict = json.loads(user_json)
        user_dict.update(user_dict['attributes'])
        del user_dict['attributes']
        user_dict['groups'] = []
        #print(user_dict)
        groups = get_user_groups(user.entry_dn)
        for group in groups:
            group_json = group.entry_to_json()
            group_dict = json.loads(group_json)
            #print(group_dict)
            user_dict['groups'].append(group_dict['dn'])
        #print(json.dumps(user_dict, indent=4, sort_keys=True))
        icinga_users.append(user_dict)
        #break
    #print(json.dumps(icinga_users, indent=4, sort_keys=True))
    with open('icinga_users.json', 'w') as outfile:
        json.dump(icinga_users, outfile)

Replace the following with your values:

  • ADSERVER
  • BINDUSER
  • BINDPW
  • DNOFMAINGROUP - I had to put all my icinga permisson groups in to one group - this is the one to rule them all

Next some cleanup. My Icingaweb2 permission groups have a ICT_Icinga_P prefix.
Excerpt from Basket:

"ImportSource": {
        "Json from icinga_aduser.py": {
            "description": null,
            "key_column": "sAMAccountName",
            "modifiers": [
                {
                    "description": null,
                    "priority": "1",
                    "property_name": "sAMAccountName",
                    "provider_class": "Icinga\\Module\\Director\\PropertyModifier\\PropertyModifierJoin",
                    "settings": {
                        "glue": ""
                    },
                    "target_property": null
                },
                {
                    "description": null,
                    "priority": "2",
                    "property_name": "displayName",
                    "provider_class": "Icinga\\Module\\Director\\PropertyModifier\\PropertyModifierJoin",
                    "settings": {
                        "glue": ""
                    },
                    "target_property": null
                },
                {
                    "description": null,
                    "priority": "3",
                    "property_name": "groups",
                    "provider_class": "Icinga\\Module\\Director\\PropertyModifier\\PropertyModifierArrayFilter",
                    "settings": {
                        "filter_method": "wildcard",
                        "filter_string": "*ICT_P_Icinga*",
                        "policy": "keep",
                        "when_empty": "empty_array"
                    },
                    "target_property": null
                },
                {
                    "description": null,
                    "priority": "4",
                    "property_name": "groups",
                    "provider_class": "Icinga\\Module\\Director\\PropertyModifier\\PropertyModifierRegexReplace",
                    "settings": {
                        "pattern": "\/^cn=(.+?),ou=.*\/i",
                        "replacement": "\\1"
                    },
                    "target_property": null
                },
                {
                    "description": null,
                    "priority": "5",
                    "property_name": "groups",
                    "provider_class": "Icinga\\Module\\Director\\PropertyModifier\\PropertyModifierRegexReplace",
                    "settings": {
                        "pattern": "\/^ICT_P_Icinga_(.+?)$\/i",
                        "replacement": "\\1"
                    },
                    "target_property": null
                },
                {
                    "description": null,
                    "priority": "6",
                    "property_name": "groups",
                    "provider_class": "Icinga\\Module\\Director\\PropertyModifier\\PropertyModifierArrayFilter",
                    "settings": {
                        "filter_method": "wildcard",
                        "filter_string": "ICT_P_Icinga",
                        "policy": "reject",
                        "when_empty": "empty_array"
                    },
                    "target_property": null
                },
                {
                    "description": null,
                    "priority": "7",
                    "property_name": "mail",
                    "provider_class": "Icinga\\Module\\Director\\PropertyModifier\\PropertyModifierJoin",
                    "settings": {
                        "glue": ""
                    },
                    "target_property": null
                }
            ],
            "originalId": "6",
            "provider_class": "Icinga\\Module\\Fileshipper\\ProvidedHook\\Director\\ImportSource",
            "settings": {
                "basedir": "\/var\/cache\/icinga_aduser",
                "file_format": "json",
                "file_name": "icinga_users.json"
            },
            "source_name": "Json from icinga_aduser.py"
        },

Groups are easy and and don’t requre a external script The use of:

(&(memberOf:1.2.840.113556.1.4.1941:=DNOFMAINGROUP)(objectClass=group))

in the disector source ldap query was enough for me. The cleanup details are left as excercise to the reader…