[ SEA-GHOST MINI SHELL]

Path : /proc/2/root/var/lib/zabbix/
FILE UPLOADER :
Current File : //proc/2/root/var/lib/zabbix/zabbix-ldap-sync

#!/usr/bin/env python
#
# Copyright (c) 2013-2014 Marin Atanasov Nikolov <dnaeon@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer
#    in this position and unchanged.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""
The zabbix-ldap-sync script is used for syncing LDAP users with Zabbix.
"""

import random
import string
import logging
import ConfigParser
import sys
import ldap
import ldap.filter
from pyzabbix import ZabbixAPI, ZabbixAPIException
from docopt import docopt


class LDAPConn(object):
    """
    LDAP connector class
    Defines methods for retrieving users and groups from LDAP server.
    """

    def __init__(self, uri, base, user, passwd):
        self.uri = uri
        self.base = base
        self.ldap_user = user
        self.ldap_pass = passwd

    def connect(self):
        """
        Establish a connection to the LDAP server.
        Raises:
            SystemExit
        """
        self.conn = ldap.initialize(self.uri)
        self.conn.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)

        try:
            self.conn.simple_bind_s(self.ldap_user, self.ldap_pass)
        except ldap.SERVER_DOWN as e:
            raise SystemExit('Cannot connect to LDAP server: %s' % e)

    def disconnect(self):
        """
        Disconnect from the LDAP server.
        """
        self.conn.unbind()

    def remove_ad_referrals(self, result):
        """
        Remove referrals from AD query result
        """
        return [i for i in result if i[0] != None]

    def get_group_members(self, group):
        """
        Retrieves the members of an LDAP group
        Args:
            group (str): The LDAP group name
        Returns:
            A list of all users in the LDAP group
        """
        attrlist = [group_member_attribute]
        filter = group_filter % group

        result = self.conn.search_s(base=self.base,
                                    scope=ldap.SCOPE_SUBTREE,
                                    filterstr=filter,
                                    attrlist=attrlist)

        if not result:
            print '>>> Unable to find group %s, skipping group' % group
            return None

        # Get DN for each user in the group
        if active_directory:

            result = self.remove_ad_referrals(result)

            final_listing = {}

            for members in result:
                result_dn = members[0]
                result_attrs = members[1]

            group_members = []
            attrlist = [uid_attribute]

            if recursive:
                # Get a DN for all users in a group (recursive)
                # It's available only on domain controllers with Windows Server 2003 SP2 or later

                member_of_filter_dn = memberof_filter % result_dn

                if skipdisabled:
                    filter = "(&%s%s%s)" % (user_filter, member_of_filter_dn, disabled_filter)
                else:
                    filter = "(&%s%s)" % (user_filter, member_of_filter_dn)

                uid = self.conn.search_s(base=self.base,
                                         scope=ldap.SCOPE_SUBTREE,
                                         filterstr=filter,
                                         attrlist=attrlist)

                for item in self.remove_ad_referrals(uid):
                    group_members.append(item)
            else:
                # Otherwise, just get a DN for each user in the group
                for member in result_attrs[group_member_attribute]:
                    if skipdisabled:
                        filter = "(&%s%s)" % (user_filter, disabled_filter)
                    else:
                        filter = "(&%s)" % user_filter

                    uid = self.conn.search_s(base=member,
                                             scope=ldap.SCOPE_BASE,
                                             filterstr=filter,
                                             attrlist=attrlist)
                    for item in uid:
                        group_members.append(item)

            # Fill dictionary with usernames and corresponding DNs
            for item in group_members:
                dn = item[0]
                username = item[1][uid_attribute]

                if lowercase:
                    username = str(username)[2: -2].lower()
                else:
                    username = str(username)[2: -2]

                final_listing[username] = dn

            return final_listing

        else:

            dn, users = result.pop()

            final_listing = {}

            group_members = []

            # Get info for each user in the group
            for memberid in users[group_member_attribute]:

                if openldap_type == "groupofnames":

                  filter = "(objectClass=*)"
                  # memberid is user dn
                  base = memberid

                else:

                  # memberid is user attribute, most likely uid
                  filter = user_filter % memberid
                  base = self.base

                attrlist = [uid_attribute]

                # get the actual LDAP object for each group member
                uid = self.conn.search_s(base=base,
                                          scope=ldap.SCOPE_SUBTREE,
                                          filterstr=filter,
                                          attrlist=attrlist)

                for item in uid:
                        group_members.append(item)

                # Fill dictionary with usernames and corresponding DNs
                for item in group_members:
                  dn = item[0]
                  username = item[1][uid_attribute]
                  user = ''.join(username)

                final_listing[user] = dn

            return final_listing

    def get_groups_with_wildcard(self, groups_wildcard):
        print(">>> Search group with wildcard: %s"% groups_wildcard)

        filter = group_filter % groups_wildcard
        result_groups=[]

        result = self.conn.search_s(base=self.base,
                                    scope=ldap.SCOPE_SUBTREE,
                                    filterstr=filter,)

        for group in result:
            # Skip refldap (when Active Directory used)
            # [0]==None
            if group[0]:
                group_name=group[1]['name'][0]
                print("Find group %s" % group_name)
                result_groups.append(group_name)

        if not result_groups:
            print('>>> Unable to find group %s, skipping group wildcard' % groups_wildcard)

        return result_groups



    def get_user_media(self, dn, ldap_media):
        """
        Retrieves the 'media' attribute of an LDAP user
        Args:
            username (str): The LDAP distinguished name to lookup
            ldap_media (str): The name of the field containing the media address
        Returns:
            The user's media attribute value
        """
        attrlist = [ldap_media]

        result = self.conn.search_s(base=dn,
                                    scope=ldap.SCOPE_BASE,
                                    attrlist=attrlist)

        if not result:
            return None

        dn, data = result.pop()

        mail = data.get(ldap_media)

        if not mail:
            return None

        return mail.pop()

    def get_user_sn(self, dn):
        """
        Retrieves the 'sn' attribute of an LDAP user
        Args:
            username (str): The LDAP distinguished name to lookup
        Returns:
            The user's surname attribute
        """
        attrlist = ['sn']

        result = self.conn.search_s(base=dn,
                                    scope=ldap.SCOPE_BASE,
                                    attrlist=attrlist)

        if not result:
            return None

        dn, data = result.pop()

        sn = data.get('sn')

        if not sn:
            return None

        return sn.pop()

    def get_user_givenName(self, dn):
        """
        Retrieves the 'givenName' attribute of an LDAP user
        Args:
            username (str): The LDAP distinguished name to lookup
        Returns:
            The user's given name attribute
        """
        attrlist = ['givenName']

        result = self.conn.search_s(base=dn,
                                    scope=ldap.SCOPE_BASE,
                                    attrlist=attrlist)

        if not result:
            return None

        dn, data = result.pop()

        name = data.get('givenName')

        if not name:
            return None

        return name.pop()


class ZabbixConn(object):
    """
    Zabbix connector class
    Defines methods for managing Zabbix users and groups
    """

    def __init__(self, server, username, password):
        self.server = server
        self.username = username
        self.password = password

    def verbose(self):
        # Use logger to log information
        logger = logging.getLogger()
        logger.setLevel(logging.DEBUG)
        formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')

        # Log to stdout
        ch = logging.StreamHandler()
        ch.setLevel(logging.DEBUG)
        ch.setFormatter(formatter)
        logger.addHandler(ch)  # Use logger to log information

        # Log from pyzabbix
        log = logging.getLogger('pyzabbix')
        log.addHandler(ch)
        log.setLevel(logging.DEBUG)

    def connect(self, nocheckcertificate):
        """
        Establishes a connection to the Zabbix server
        Args:
            nocheckcertificate (bool): Don't check the server certificate
        Raises:
            SystemExit
        """

        self.conn = ZabbixAPI(self.server)

        if nocheckcertificate:
            self.conn.session.verify = False

        try:
            self.conn.login(self.username, self.password)
        except ZabbixAPIException as e:
            raise SystemExit('Cannot login to Zabbix server: %s' % e)

    def get_users(self):
        """
        Retrieves the existing Zabbix users
        Returns:
            A list of the existing Zabbix users
        """
        result = self.conn.user.get(output='extend')

        users = [user['alias'] for user in result]

        return users

    def get_mediatype_id(self, description):
        """
        Retrieves the mediatypeid by description
        Args:
            description (str): Zabbix media type description
        Returns:
            The mediatypeid for specified media type description
        """
        result = self.conn.mediatype.get(filter={'description': description})

        if result:
            mediatypeid = result[0]['mediatypeid']
        else:
            mediatypeid = None

        return mediatypeid

    def get_user_id(self, user):
        """
        Retrieves the userid of a specified user
        Args:
            user (str): The Zabbix username to lookup
        Returns:
            The userid of the specified user
        """
        result = self.conn.user.get(output='extend')

        userid = [u['userid'] for u in result if u['alias'] == user].pop()

        return userid

    def get_groups(self):
        """
        Retrieves the existing Zabbix groups
        Returns:
            A dict of the existing Zabbix groups and their group ids
        """
        result = self.conn.usergroup.get(status=0, output='extend')

        groups = [{'name': group['name'], 'usrgrpid': group['usrgrpid']} for group in result]

        return groups

    def get_group_members(self, groupid):
        """
        Retrieves group members for a Zabbix group
        Args:
            groupid (int): The group id
        Returns:
            A list of the Zabbix users for the specified group id
        """
        result = self.conn.user.get(output='extend', usrgrpids=groupid)

        users = [user['alias'] for user in result]

        return users

    def create_group(self, group):
        """
        Creates a new Zabbix group
        Args:
            group (str): The Zabbix group name to create
        Returns:
            The groupid of the newly created group
        """
        result = self.conn.usergroup.create(name=group, permission=3)

        groupid = result['usrgrpids'].pop()

        return groupid

    def create_user(self, user, groupid, user_opt):
        """
        Creates a new Zabbix user
        Args:
            user     (dict): A dict containing the user details
            groupid   (int): The groupid for the new user
            user_opt (dict): User options
        """
        random_passwd = ''.join(random.sample(string.ascii_letters + string.digits, 32))

        user_defaults = {'autologin': 0, 'type': 1, 'usrgrps': [{'usrgrpid': str(groupid)}], 'passwd': random_passwd}
        user_defaults.update(user_opt)
        user.update(user_defaults)

        result = self.conn.user.create(user)

        return result

    def delete_user(self, user):
        """
        Deletes Zabbix user
        Args:
            user (string): Zabbix username
        """
        userid = self.get_user_id(user)

        result = self.conn.user.delete(userid)

        return result

    def update_user(self, user, groupid):
        """
        Adds an existing Zabbix user to a group
        Args:
            user    (dict): A dict containing the user details
            groupid  (int): The groupid to add the user to
        """
        userid = self.get_user_id(user)

        result = self.conn.usergroup.massadd(usrgrpids=[str(groupid)], userids=[str(userid)])

        return result

    def update_media(self, user, description, sendto, media_opt):
        """
        Adds media to an existing Zabbix user
        Args:
            user        (dict): A dict containing the user details
            description  (str): A string containing Zabbix media description
            sendto       (str): A string containing address, phone number, etc...
            media_opt    (dict): Media options
        """

        userid = self.get_user_id(user)
        mediatypeid = self.get_mediatype_id(description)

        if mediatypeid:
            media_defaults = {
                'mediatypeid': mediatypeid,
                'sendto': sendto,
                'active': '0',
                'severity': '63',
                'period': '1-7,00:00-24:00'
            }
            media_defaults.update(media_opt)

            self.delete_media_by_description(user, description)
            result = self.conn.user.addmedia(users=[{"userid": str(userid)}], medias=media_defaults)
        else:
            result = None

        return result


    def delete_media_by_description(self, user, description):
        """
        Remove all media from user (with specific mediatype)
        Args:
            user        (dict): A dict containing the user details
            description  (str): A string containing Zabbix media description
        """

        userid = self.get_user_id(user)
        mediatypeid = self.get_mediatype_id(description)

        if mediatypeid:
            user_full = self.conn.user.get(output="extend", userids=userid, selectMedias = ["mediatypeid", "mediaid"])
            media_ids = [int(u['mediaid']) for u in user_full[0]['medias'] if u['mediatypeid'] == mediatypeid]

            if media_ids:
                print '>>> Remove other exist media from user %s (type=%s)' % (user, description)
                for id in media_ids:
                    self.conn.user.deletemedia(id)

    def create_missing_groups(self, ldap_groups):
        """
        Creates any missing LDAP groups in Zabbix
        Args:
            ldap_groups (list): A list of LDAP groups to create
        """
        missing_groups = set(ldap_groups) - set([g['name'] for g in self.get_groups()])

        for eachGroup in missing_groups:
            print '>>> Creating Zabbix group %s' % eachGroup
            grpid = self.create_group(eachGroup)
            print '>>> Group %s created with groupid %s' % (eachGroup, grpid)

    def sync_users(self, ldap_uri, ldap_base, ldap_groups, ldap_user, ldap_pass, ldap_media, user_opt, media_description, media_opt):
        """
        Syncs Zabbix with LDAP users
        """
        ldap_conn = LDAPConn(ldap_uri, ldap_base, ldap_user, ldap_pass)
        ldap_conn.connect()

        zabbix_all_users = self.get_users()

        for eachGroup in ldap_groups:
            ldap_users = ldap_conn.get_group_members(eachGroup)

            # Do nothing if LDAP group contains no users and "--delete-orphans" is not specified
            if not ldap_users and not deleteorphans:
                continue

            zabbix_grpid = [g['usrgrpid'] for g in self.get_groups() if g['name'] == eachGroup].pop()

            zabbix_group_users = self.get_group_members(zabbix_grpid)

            missing_users = set(list(ldap_users.keys())) - set(zabbix_group_users)

            # Add missing users
            for eachUser in missing_users:

                # Create new user if it does not exists already
                if eachUser not in zabbix_all_users:
                    print '>>> Creating user %s, member of Zabbix group %s' % (eachUser, eachGroup)
                    user = {'alias': eachUser}
                    user['name'] = ldap_conn.get_user_givenName(ldap_users[eachUser])
                    user['surname'] = ldap_conn.get_user_sn(ldap_users[eachUser])
                    self.create_user(user, zabbix_grpid, user_opt)
                    zabbix_all_users.append(eachUser)
                else:
                    # Update existing user to be member of the group
                    print '>>> Updating user %s, adding to group %s' % (eachUser, eachGroup)
                    self.update_user(eachUser, zabbix_grpid)

            # Handle any extra users in the groups
            extra_users = set(zabbix_group_users) - set(list(ldap_users.keys()))
            if extra_users:
                print '>>> Users in group %s which are not found in LDAP group:' % eachGroup

                for eachUser in extra_users:
                    if deleteorphans:
                        print 'Deleting user: %s' % eachUser
                        self.delete_user(eachUser)
                    else:
                        print ' * %s' % eachUser

            # update users media
            zabbix_group_users = self.get_group_members(zabbix_grpid)
            for eachUser in set(zabbix_group_users):
                print '>>> Updating user media for %s, update %s' % (eachUser, media_description)
                sendto = ldap_conn.get_user_media(ldap_users[eachUser], ldap_media)

                if sendto:
                    self.update_media(eachUser, media_description, sendto, media_opt)

        ldap_conn.disconnect()


class ZabbixLDAPConf(object):
    """
    Zabbix-LDAP configuration class
    Provides methods for parsing and retrieving config entries
    """

    def __init__(self, config):
        self.config = config

    def try_get_item(self, parser, section, option, default):
        """
        Gets config item
        Args:
            parser  (ConfigParser): ConfigParser
            section          (str): Config section name
            option           (str): Config option name
            default               : Value to return if item doesn't exist
        Returns:
            Config item value or default value
        """

        try:
            result = parser.get(section, option)
        except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
            result = default

        return result

    def try_get_section(self, parser, section, default):
        """
        Gets config section
        Args:
            parser  (ConfigParser): ConfigParser
            section          (str): Config section name
            default               : Value to return if section doesn't exist
        Returns:
            Config section dict or default value
        """

        try:
            result = parser.items(section)
        except ConfigParser.NoSectionError:
            result = default

        return result

    def remove_config_section_items(self, section, items):
        """
        Removes items from config section
        Args:
            section     (list of tuples): Config section
            items                 (list): Item names to remove
        Returns:
            Config section without specified items
        """

        return [i for i in section if i[0] not in items]

    def load_config(self):
        """
        Loads the configuration file
        Raises:
            ConfigParser.NoOptionError
        """
        parser = ConfigParser.ConfigParser()
        parser.read(self.config)

        try:
            self.ldap_type          = self.try_get_item(parser, 'ldap', 'type', None)

            self.ldap_uri           = parser.get('ldap', 'uri')
            self.ldap_base          = parser.get('ldap', 'base')

            self.ldap_groups        = [i.strip() for i in parser.get('ldap', 'groups').split(',')]

            self.ldap_user          = parser.get('ldap', 'binduser')
            self.ldap_pass          = parser.get('ldap', 'bindpass')

            self.ldap_media         = self.try_get_item(parser, 'ldap', 'media', 'mail')

            self.ad_filtergroup = parser.get ('ad', 'filtergroup','(&(objectClass=group)(name=%s))')
            self.ad_filteruser = parser.get ('ad','filteruser', '(objectClass=user)(objectCategory=Person))')
            self.ad_filterdisabled = parser.get ('ad', 'filterdisabled','(!(userAccountControl:1.2.840.113556.1.4.803:=2))')
            self.ad_filtermemberof = parser.get ('ad', 'filtermemberof','(memberOf:1.2.840.113556.1.4.1941:=%s)')
            self.ad_groupattribute = parser.get ('ad', 'groupattribute','member')
            self.ad_userattribute = parser.get ('ad', 'userattribute','sAMAccountName')

            self.openldap_type = parser.get ('openldap', 'type','posixgroup')
            self.openldap_filtergroup = parser.get ('openldap', 'filtergroup','(&(objectClass=posixGroup)(cn=%s))')
            self.openldap_filteruser = parser.get ('openldap', 'filteruser','(&(objectClass=posixAccount)(uid=%s))')
            self.openldap_groupattribute = parser.get ('openldap', 'groupattribute','memberUid')
            self.openldap_userattribute = parser.get ('openldap', 'userattribute','uid')

            self.zbx_server         = parser.get('zabbix', 'server')
            self.zbx_username       = parser.get('zabbix', 'username')
            self.zbx_password       = parser.get('zabbix', 'password')

            self.user_opt           = self.try_get_section(parser, 'user', {})

            self.media_description   = self.try_get_item(parser, 'media', 'description', 'Email')
            self.media_opt          = self.remove_config_section_items(self.try_get_section(parser, 'media', {}), ('description', 'userid'))

        except ConfigParser.NoOptionError as e:
            raise SystemExit('Configuration issues detected in %s' % self.config)

    def set_groups_with_wildcard(self):
        """
        Set group from LDAP with wildcard
        :return:
        """
        result_groups=[]
        ldap_conn = LDAPConn(self.ldap_uri, self.ldap_base, self.ldap_user, self.ldap_pass)
        ldap_conn.connect()

        for group in self.ldap_groups:
            groups = ldap_conn.get_groups_with_wildcard(group)
            result_groups = result_groups + groups

        if result_groups:
            self.ldap_groups = result_groups
        else:
            raise SystemExit('ERROR - No groups found with wildcard' )


def main():
    usage = """
Usage: zabbix-ldap-sync [-lsrwdn] [--verbose] -f <config>
       zabbix-ldap-sync -v
       zabbix-ldap-sync -h
Options:
  -h, --help                    Display this usage info
  -v, --version                 Display version and exit
  -l, --lowercase               Create AD user names as lowercase
  -s, --skip-disabled           Skip disabled AD users
  -r, --recursive               Resolves AD group members recursively (i.e. nested groups)
  -w, --wildcard-search         Search AD group with wildcard (e.g. R.*.Zabbix.*) - TESTED ONLY with Active Directory
  -d, --delete-orphans          Delete Zabbix users that don't exist in a LDAP group
  -n, --no-check-certificate    Don't check Zabbix server certificate
  --verbose                     Print debug message from ZabbixAPI
  -f <config>, --file <config>  Configuration file to use
"""
    args = docopt(usage, version="0.1.1")

    config = ZabbixLDAPConf(args['--file'])
    config.load_config()


    # set up AD differences, if necessary
    global active_directory
    global openldap_type
    global group_filter
    global disabled_filter
    global user_filter
    global memberof_filter
    global group_member_attribute
    global uid_attribute
    global lowercase
    global skipdisabled
    global deleteorphans
    global recursive
    lowercase = args['--lowercase']
    skipdisabled = args['--skip-disabled']
    deleteorphans = args['--delete-orphans']
    nocheckcertificate = args['--no-check-certificate']
    recursive = args['--recursive']
    verbose = args['--verbose']
    if config.ldap_type == 'activedirectory':
        active_directory = "true"
        group_filter = config.ad_filtergroup
        user_filter = config.ad_filteruser
        disabled_filter = config.ad_filterdisabled
        memberof_filter = config.ad_filtermemberof
        group_member_attribute = config.ad_groupattribute
        uid_attribute = config.ad_userattribute
    else:
        active_directory = None
        openldap_type = config.openldap_type
        group_filter = config.openldap_filtergroup
        user_filter = config.openldap_filteruser
        group_member_attribute = config.openldap_groupattribute
        uid_attribute = config.openldap_userattribute

    wildcard_search = args['--wildcard-search']
    if wildcard_search:
        config.set_groups_with_wildcard()

    if nocheckcertificate:
        from requests.packages.urllib3 import disable_warnings
        disable_warnings()

    zabbix_conn = ZabbixConn(config.zbx_server, config.zbx_username, config.zbx_password)
    if verbose:
        zabbix_conn.verbose()
    zabbix_conn.connect(nocheckcertificate)

    zabbix_conn.create_missing_groups(config.ldap_groups)
    zabbix_conn.sync_users(config.ldap_uri,
                           config.ldap_base,
                           config.ldap_groups,
                           config.ldap_user,
                           config.ldap_pass,
                           config.ldap_media,
                           config.user_opt,
                           config.media_description,
                           config.media_opt)

if __name__ == '__main__':
    main()

SEA-GHOST - SHELL CODING BY SEA-GHOST