LDAP Abfrage fuer getmail

Code-Stücke können hier veröffentlicht werden.
Antworten
sharbich
User
Beiträge: 9
Registriert: Freitag 15. März 2019, 15:38

Hallo Ihr Lieben,

ich finde den Fehler in mein Python Script nicht. Ich verwende das Scricpt um Nachrichten von meiner alten Mailadresse abzuholen. Eigentlich funktioniert alles. Ich bekomme nur eine Fehlermeldung am Ende. Also was möchte ich erreichen?
Ein LDAp User der angelegt ist unter uid=stefan.example, ou=users, dc=example, dc=com hat folgende ObjectClass und Attribute:

Code: Alles auswählen

root@dsme01:~# ldapsearch -xZZ -b "ou=users,dc=harnet,dc=de" uid=stefan.exemple
# extended LDIF
#
# LDAPv3
# base <ou=users,dc=example,dc=com> with scope subtree
# filter: uid=stefan.exemple
# requesting: ALL
#
# stefan.exemple, users, example.com
dn: uid=stefan.exemple,ou=users,dc=exemple,dc=com
facsimileTelephoneNumber: ###########
gidNumber: 10000
homePhone: ###########
homePostalAddress: ...
loginShell: /bin/bash
mailEnabled: TRUE
mailQuota: 100M
uid: stefan.exemple
cn: Stefan exemple
givenName: Stefan
homeDirectory: /home/stefan.exemple
initials: STH
mobile: ###########
sn: exemple
telephoneNumber: ############
uidNumber: 10000
userPassword:: ...
title: admin
postalAddress: ...
birthDate: yyyy-mm-dd
fileAs: Stefan exemple
dcMailMessageStore: /vmail/mailboxes/example.com/stefan.exemple/mail
objectClass: evolutionPerson
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: mailExtension
objectClass: posixAccount
objectClass: dcMailUser
mail: stefan.exemple@exemple.com
und den dazu gehörigen subtree

Code: Alles auswählen

# user@t-online.de, stefan.example, users, exemple.com
dn: dcSubMailAddress=user@t-online.de,uid=stefan.example,ou=users,dc=example,dc=com
dcAccountStatus: active
dcSubMailAddress: user@t-online.de
objectClass: dcExternalMailAccount
objectClass: top
objectClass: dcPosixSubAccount
dcPosixOwnerURL: ldap:///uid=stefan.exemple,ou=users,dc=exemple,dc=com?uid,uidNu
 mber,gidNumber,dcMailMessageStore?base?(&(objectClass=posixAccount)(objectClass=dcMailUser))
gidNumber: 10000
uid: stefan.example
uidNumber: 10000
dcMailMessageStore: /vmail/mailboxes/exemple.com/stefan.exemple/mail
Zum Python Script getmail-ldap-py (als Anlage beigefügt) gehört noch folgende conf Datei

Code: Alles auswählen

root@dsme01:~# cat /home/secmail/getmail-ldap.cfg 
[Main]
# Path to getmail
GetmailBinary=/usr/bin/getmail
# Directory that should be used as a storage by getmail
GetmailDir=/home/secmail/getmail_data
# Read default values for getmail from this file
DefaultGetmailConfigFile=/home/secmail/getmailrc_template.cfg
# Save the final configuration files which include the LDAP details to this directory
ConfigFileOutputDir=/home/secmail/getmail_config
 
[Logging]
verbose = 2
# Write messages to the following log file
LogFile=/var/log/getmail-ldap.log
# If a severe error occures a mail goes to the admin
# SMTP-Server to use for sending this error notification
MailServer=dsme01.intern.exempla.com
# Mail address of the sender of this error notification
MailFrom=secmail@example.com
# Recipients of this error notification
# separate multiple recipients by comma
MailTo=root@example.com
# Subject of the error notification
MailSubject=Getmail-LDAP Error
 
[LDAP]
# Read LDAP information from this server
LDAPServer=ldaps://ldap.intern.example.com
# Authenticate with the following DN
BindDN=uid=secmail, ou=users, dc=example, dc=com
# Authenticate with the following password
BindPassword=#######
# Restrict search of external mail accounts to this DN
SearchDN=ou=users, dc=example, dc=com
# Scope of search for external mail accounts
# Possible values include SUB, ONE and BASE
SearchScope=SUB
# Identify external mail accounts with the following filter
SearchFilter=(&(dcSubMailAddress=*)(objectClass=dcExternalMailAccount)(dcAccountStatus=active)(dcRetrieveType=*)(dcRetrieveLogin=*)(dcRetrievePassword=*))
# List of LDAP-Attributes used to determine the following variables
#       1. Name for resulting getmail configuration file (must be unique)
#	2. Type for mail collection e.g. BrokenUIDLPOP3Retriever
#	3. Mail server to collect mails from
#	4. Login for mail server
#       5. Password for mail server
#       6. UID for User
# separate by comma
RelevantAttributes=dcSubMailAddress,dcRetrieveType,dcRetrieveServer,dcRetrieveLogin,dcRetrievePassword,uid
Starte ich das getmail-ldap-py Script bekomme ich immer folgende Fehlermeldung.

Code: Alles auswählen

secmail@dsme01:~$ ./getmail-ldap.py
Traceback (most recent call last):
  File "/usr/lib/python2.7/logging/handlers.py", line 958, in emit
    smtp.sendmail(self.fromaddr, self.toaddrs, msg)
  File "/usr/lib/python2.7/smtplib.py", line 737, in sendmail
    raise SMTPSenderRefused(code, resp, from_addr)
SMTPSenderRefused: (530, '5.7.0 Must issue a STARTTLS command first', 'secmail@example.com')
Logged from file getmail-ldap.py, line 179
Sorry das ich hier das Python Script im Code Block poste. Ich wusste nicht wie ich den Code als Datei Anhang beifügen konnte.

Code: Alles auswählen

secmail@dsme01:~$ cat getmail-ldap.py
#!/usr/bin/python
# File: getmail-ldap.py
try:
	import errno
	import string
	import logging
	import logging.handlers
	import ldap
	import ConfigParser
	import ldif
	import threading
	from StringIO import StringIO
	from ldap.cidict import cidict
	from os.path import os
	from subprocess import Popen,PIPE
except ImportError:
	print """Cannot find all required libraries please install them and try again"""
	raise SystemExit

config_file_location = '/home/secmail/getmail-ldap.cfg'

def pid_exists(pid):
    """Is there a process with PID pid?"""
    if pid < 0:
        return False

    exist = False
    try:
        os.kill(pid, 0)
        exist = 1
    except OSError, x:
        if x.errno != errno.ESRCH:
            raise

    return exist

def get_search_results(results):
    """Given a set of results, return a list of LDAPSearchResult
    objects.
    """
    res = []

    if type(results) == tuple and len(results) == 2 :
        (code, arr) = results
    elif type(results) == list:
        arr = results

    if len(results) == 0:
        return res

    for item in arr:
        res.append( LDAPSearchResult(item) )

    return res

class LDAPSearchResult:
    """A class to model LDAP results.
    """

    dn = ''

    def __init__(self, entry_tuple):
        """Create a new LDAPSearchResult object."""
        (dn, attrs) = entry_tuple
        if dn:
            self.dn = dn
        else:
            return

        self.attrs = cidict(attrs)

    def get_attributes(self):
        """Get a dictionary of all attributes.
        get_attributes()->{'name1':['value1','value2',...],
				'name2: [value1...]}
        """
        return self.attrs

    def set_attributes(self, attr_dict):
        """Set the list of attributes for this record.

        The format of the dictionary should be string key, list of
        string alues. e.g. {'cn': ['M Butcher','Matt Butcher']}

        set_attributes(attr_dictionary)
        """

        self.attrs = cidict(attr_dict)

    def has_attribute(self, attr_name):
        """Returns true if there is an attribute by this name in the
        record.

        has_attribute(string attr_name)->boolean
        """
        return self.attrs.has_key( attr_name )

    def get_attr_values(self, key):
        """Get a list of attribute values.
        get_attr_values(string key)->['value1','value2']
        """
        return self.attrs[key]

    def get_attr_names(self):
        """Get a list of attribute names.
        get_attr_names()->['name1','name2',...]
        """
        return self.attrs.keys()

    def get_dn(self):
        """Get the DN string for the record.
        get_dn()->string dn
        """
        return self.dn

    def pretty_print(self):
        """Create a nice string representation of this object.

        pretty_print()->string
        """
        str = "DN: " + self.dn + "\n"
        for a, v_list in self.attrs.iteritems():
            str = str + "Name: " + a + "\n"
            for v in v_list:
                str = str + "  Value: " + v + "\n"
        str = str + "========"
        return str

    def to_ldif(self):
        """Get an LDIF representation of this record.

        to_ldif()->string
        """
        out = StringIO()
        ldif_out = ldif.LDIFWriter(out)
        ldif_out.unparse(self.dn, self.attrs)
        return out.getvalue()

class RetrieveMails(threading.Thread):
	def __init__(self, getmail_binary, config_filename, config_data_dir):
		threading.Thread.__init__(self)
		self.getmail_binary, self.config_filename, self.config_data_dir = \
			getmail_binary, config_filename, config_data_dir
	def run(self):
		try:
			command = [self.getmail_binary, \
				#'--quiet', \
				'--rcfile=' + self.config_filename, \
				'--getmaildir=' + self.config_data_dir]
			self.pid_filename = self.config_filename + '.pid'
			# Check for a pidfile to see if the daemon already runs
			try:
				pid_file = file(self.pid_filename,'r')
				pid_number = pid = int(pid_file.read().strip())
				pid_file.close()
			except IOError:
				pid = None
			# Check whether process is really running
			if pid:
				pid = pid_exists(pid)
			if not pid:
				getmail_process = Popen(command, shell=False,stdout=PIPE,stderr=PIPE)
				try:
					file(self.pid_filename,'w+').write("%s\n" % getmail_process.pid)
					getmail_process.wait()
				finally:
					os.remove(self.pid_filename)
					# Zur Sicherheit die erstellte Konfigurationsdatei loeschen (Login-Daten!)
					os.remove(self.config_filename)
				stderr_output=string.join(getmail_process.stderr.readlines())
				if getmail_process.returncode <> 0 or len(stderr_output.strip())>0 :
					raise Exception, "Getmail command failed for " + " ".join(command) \
						+"\nStdErr: \n" + string.join(stderr_output.strip()) \
						+"\nStdOut: \n" + string.join(getmail_process.stdout.readlines())
			else:
				log_object.info("Command " + " ".join(command) +\
					" not executed, existing pid " + str(pid_number) + " found")
		except:
			log_object.exception("An error occured!")

class RetrieveAccount:
	account_name = None
	account_type = None
	login = None
	password = None
	uid = None
	server = None
	def __init__(self, account_name=None, account_type=None, server=None, login=None, password=None, uid=None):
		self.account_name, self.account_type, self.login, self.password, self.uid, self.server = \
			account_name, account_type, login, password, uid, server

class GetmailConfigFile(ConfigParser.SafeConfigParser):
	output_filename = None
	def __init__(self, defaults, default_config_filename=None, output_filename=None):
		ConfigParser.SafeConfigParser.__init__(self, defaults)
		if default_config_filename is not None:
			self.read(default_config_filename)
		self.output_filename = output_filename
	def set_pop3_account(self, newRetrieveAccount):
		self.set('retriever','server',newRetrieveAccount.server)
		self.set('retriever','type',newRetrieveAccount.account_type)
		self.set('retriever','username',newRetrieveAccount.login)
		self.set('retriever','password',newRetrieveAccount.password)
		self.set('retriever','uid',newRetrieveAccount.uid)
		self.set('destination','arguments','("-i", "'+newRetrieveAccount.uid+'@example.com",)')
	def write(self):
		if self.output_filename is not None:
			"""try:
				output_file = open(self.output_filename, 'wb')
			except:
				raise Exception, "Unable to open " + \
					self.output_filename + "for writing"
			finally:
				output_file.close()
			"""
			os.umask(0077)
			output_file = open(self.output_filename, 'wb')
			ConfigParser.SafeConfigParser.write(self, output_file)
		else:
			raise Exception, "No output file for configuration defined"

# Konfigurationsdatei lesen
config_object = ConfigParser.SafeConfigParser()
config_object.read(config_file_location)

# Set-up Logging
log_object = logging.getLogger("getmail-ldap")
log_object.setLevel(logging.DEBUG)

# This handler writes everything to a log file.
log_file_handler = logging.FileHandler(config_object.get('Logging','LogFile'))
log_file_formatter = logging.Formatter("%(levelname)s %(asctime)s %(funcName)s %(lineno)d %(message)s")
log_file_handler.setFormatter(log_file_formatter)
log_file_handler.setLevel(logging.DEBUG)
log_object.addHandler(log_file_handler)

# This handler emails anything that is an error or worse.
log_smtp_handler = logging.handlers.SMTPHandler(\
	config_object.get('Logging','MailServer'),\
	config_object.get('Logging','MailFrom'),\
	config_object.get('Logging','MailTo').split(','),\
	config_object.get('Logging','MailSubject'))
log_smtp_handler.setLevel(logging.ERROR)
log_smtp_handler.setFormatter(log_file_formatter)
log_object.addHandler(log_smtp_handler)

def main_call():

	## first you must open a connection to the LDAP server
	ldap_object = ldap.initialize(config_object.get('LDAP','LDAPServer'))
	ldap_object.simple_bind_s(\
		config_object.get('LDAP','BindDN'),\
		config_object.get('LDAP','BindPassword'))
	# searching doesn't require a bind in LDAP V3.
	# If you're using LDAP v2, set the next line appropriately
	# and do a bind as shown in the above example.
	# you can also set this to ldap.VERSION2 if you're using a v2 directory
	# you should  set the next option to ldap.VERSION2 if you're using a v2 directory
	ldap_object.protocol_version = ldap.VERSION3

	## The next lines will also need to be changed to support your search requirements and directory
	## retrieve all attributes - again adjust to your needs - see documentation for more options

	if config_object.get('LDAP','SearchScope').upper() == "SUB":
            search_scope = ldap.SCOPE_SUBTREE
        elif config_object.get('LDAP','SearchScope').upper() == "ONE":
            search_scope = ldap.SCOPE_ONELEVEL
        else:
            search_scope = ldap.SCOPE_BASE

	ldap_result_id = ldap_object.search( \
		config_object.get('LDAP','SearchDN'), \
		search_scope,
		config_object.get('LDAP','SearchFilter'), \
		None)

	ldap_results = []

	while 1:
		result_type, result_data = ldap_object.result(ldap_result_id, 0)
		if (result_data == []):
			break
		else:
			## here you don't have to append to a list
			## you could do whatever you want with the individual entry
			## The appending to list is just for illustration.
			if result_type == ldap.RES_SEARCH_ENTRY:
				ldap_results += get_search_results(result_data)
	for ldap_result in ldap_results:
		account = RetrieveAccount( \
			# Account Name \
			ldap_result.get_attr_values(\
				config_object.get('LDAP','RelevantAttributes').split(',')[0])[0] ,\
			# Account Type \
			ldap_result.get_attr_values(\
				config_object.get('LDAP','RelevantAttributes').split(',')[1])[0],\
			# Server \
			ldap_result.get_attr_values(\
				config_object.get('LDAP','RelevantAttributes').split(',')[2])[0],\
			# Login \
			ldap_result.get_attr_values(\
				config_object.get('LDAP','RelevantAttributes').split(',')[3])[0],\
			# Password \
			ldap_result.get_attr_values(\
				config_object.get('LDAP','RelevantAttributes').split(',')[4])[0],\
			# UID \
			ldap_result.get_attr_values(\
				config_object.get('LDAP','RelevantAttributes').split(',')[5])[0],\
		)
		config_output_filename = os.path.join(\
			config_object.get('Main','ConfigFileOutputDir'), \
			"getmail_" + \
			account.account_name + \
			".cfg")
		config_file = GetmailConfigFile(None, \
			config_object.get('Main','DefaultGetmailConfigFile'), config_output_filename)
		config_file.set_pop3_account(account)
		log_object.info("Writing Account Configuration for " + account.account_name + \
				" to file " + config_output_filename)
		config_file.write()
		RetrieveMails(\
			config_object.get('Main','GetmailBinary'), \
			config_output_filename, \
			config_object.get('Main','GetmailDir')\
		).start()
		#print config_output_filename
		#print "Name " + account.account_name
		#print "Type " + account.account_type
		#print "Server " + account.server
		#print "Login " + account.login
		#print "Password " + account.password
		#print "UID" + account.uid
		#print "-----------------"
		#print ldap_result.pretty_print()

if __name__ == "__main__":
	try:
		main_call();
	except:
		log_object.exception("An error occured!")
Findet Ihr den Fehler? Lieben Gruß von Stefan Harbich
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

In der `RetrieveMails.run()` tritt ein Fehler auf der versucht wird in Zeile 179 zu loggen, aber Du hast das so konfiguriert dass dafür eine Mail verschickt wird, aber der Mailserver nimmt die Daten nur verschlüsselt an, deswegen bekommst die Ausnahme zu sehen die Du da zu sehen bekommst.

Du solltest das bei Gelegenheit mal nach Python 3 portieren.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
sharbich
User
Beiträge: 9
Registriert: Freitag 15. März 2019, 15:38

__blackjack__ hat geschrieben: Montag 3. Mai 2021, 13:03 In der `RetrieveMails.run()` tritt ein Fehler auf
Ich finde den Fehler einfach nicht. Wohl Knöpfe auf den Linsen.
__blackjack__ hat geschrieben: Montag 3. Mai 2021, 13:03 Du solltest das bei Gelegenheit mal nach Python 3 portieren.
Was meinst Du mit 2to3 müsste es funktionieren, oder?

Gruß von Stefan Harbich
sharbich
User
Beiträge: 9
Registriert: Freitag 15. März 2019, 15:38

Hallo,

ich habe mein debugger angeworfen

Code: Alles auswählen

The program finished and will be restarted
> /home/secmail/getmail-ldap.py(3)<module>()
-> try:
(Pdb) Exception in thread Thread-3:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
    self.run()
  File "getmail-ldap.py", line 179, in run
    log_object.exception("An error occured!")
NameError: global name 'log_object' is not defined
Aber log_object habe ich doch definiert?

Ich verstehe das nicht.

Gruß von Stefan Harbich
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Fehlermeldung passt nicht zu dem von dir gezeigten Code, denn dort wird log_object als globale Variable definiert.
sharbich
User
Beiträge: 9
Registriert: Freitag 15. März 2019, 15:38

Sirius3 hat geschrieben: Mittwoch 5. Mai 2021, 09:35 Die Fehlermeldung passt nicht zu dem von dir gezeigten Code...
Debugge ich falsch?

Code: Alles auswählen

python -m pdb getmail-ldap.py
dann mit c und Enter, kommt die obere Fehlermeldung.
Kannst Du mir sagen welcher Code Snipsel falsch ist und wie ich verschlüsselt Mails versende?

Gruß von Stefan
Antworten