Skip to content

Instantly share code, notes, and snippets.

@Mario-F
Last active February 8, 2021 14:23
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Mario-F/15e43f39a6363bc2c890e1c2775fbc1f to your computer and use it in GitHub Desktop.
Save Mario-F/15e43f39a6363bc2c890e1c2775fbc1f to your computer and use it in GitHub Desktop.
Creating dynamic catchall rule for Univention Corporate Servers
#!/usr/bin/env python
#
# This script creates catchall configuration with postfix and "Univention Coporate Server"
#
# Quick example:
# 1. Download and Run ucs_create_catchall (on UCS Server as root !):
# wget -O - https://gist.githubusercontent.com/Mario-F/15e43f39a6363bc2c890e1c2775fbc1f/raw/ff9d92823d8aa599d039fc3596bc0b18984bd1ef/ucs_create_catchall.py | python
# 2. Read the instructions output on successfull run.
#
import sys, optparse, re, subprocess, os
# Commandline arguments
parser = optparse.OptionParser()
parser.add_option('-n', '--name', dest='name', default='catchall', help='Name of the catchall address, default: catchall')
parser.add_option('-p', '--postfix', dest='postfix', default='/etc/postfix', help='Path to postfix configuration directory, default: /etc/postfix')
parser.add_option('-x', '--dry-run', dest='dryrun', action='store_true', help='Run in dry-mode, dont modify anything')
parser.add_option('-v', '--debug', dest='debug', action='store_true', help='Turn on debugmode and print all logs to stdout')
options, args = parser.parse_args()
# Variables
MY_URL = 'https://gist.github.com/Mario-F/15e43f39a6363bc2c890e1c2775fbc1f'
CFG_UCR_KEY_ALIAS_MAPS = 'mail/postfix/virtual/alias/maps'
CFG_POSTFIX_DIR = options.postfix.rstrip('/')
CFG_POSTFIX_MAIN = CFG_POSTFIX_DIR + '/main.cf'
CFG_POSTFIX_CATCHALL = CFG_POSTFIX_DIR + '/ldap.custom_catchall'
CFG_POSTFIX_VIRTUAL = CFG_POSTFIX_DIR + '/ldap.virtual'
CFG_LDAP_VIRTUAL_MAILBOX = 'ldap:' + CFG_POSTFIX_DIR + '/ldap.virtual_mailbox'
CFG_LDAP_CUSTOM_CATCHALL = 'ldap:' + CFG_POSTFIX_CATCHALL
#
# Main executor
#
def main():
_dbg('Name: ' + options.name)
_dbg('Postfix: ' + options.postfix)
# UCS Commit postfix main.cf to ensure actual state
if not options.dryrun:
_ucrCommit(CFG_POSTFIX_MAIN)
# Read postfix config for virtual alias maps
_dbg('Parsing '+CFG_POSTFIX_MAIN+' for virtual_alias_maps...')
virtual_alias_maps = []
found_start = False
for line in open(CFG_POSTFIX_MAIN):
if found_start and re.search('=', line):
break
if re.search('virtual_alias_maps\W?=\W?', line):
_dbg('Found virtual_alias_maps start.')
found_start = True
if found_start:
virtual_alias_maps = virtual_alias_maps + re.findall('\w+:[^ ,\n]+', line)
_dbg('Found virtual alias maps:')
_dbgObj(virtual_alias_maps)
if len(virtual_alias_maps) < 3:
_exitError('There was to few virtual_alias_maps found, please repair installation first')
# Extend virtual_alias_maps
if CFG_LDAP_VIRTUAL_MAILBOX not in virtual_alias_maps:
_dbg('Adding to alias_map: ' + CFG_LDAP_VIRTUAL_MAILBOX)
virtual_alias_maps.append(CFG_LDAP_VIRTUAL_MAILBOX)
if CFG_LDAP_CUSTOM_CATCHALL not in virtual_alias_maps:
_dbg('Adding to alias_map: ' + CFG_LDAP_CUSTOM_CATCHALL)
virtual_alias_maps.append(CFG_LDAP_CUSTOM_CATCHALL)
_dbg('New virtual alias maps:')
_dbgObj(virtual_alias_maps)
virtual_alias_maps_value = ','.join(virtual_alias_maps)
_dbg('virtual_alias_maps as ucr value: '+virtual_alias_maps_value)
# Create custom catchall ldap adapter from ucs ldap.virtual
_dbg('Parsing '+CFG_POSTFIX_VIRTUAL+' for creating '+CFG_POSTFIX_CATCHALL)
found_query = False
ldap_custom_catchall = [
'# This file not part of UCS is auto generated by ucs_create_catchall.py',
'# For information see: '+MY_URL
]
for line in open(CFG_POSTFIX_VIRTUAL):
# Dont include comments we will write our own comments
if re.match('#', line):
continue
# Find query_filter line and chacnge mailAlternativeAddress to "NAME"@%d
if re.search('mailAlternativeAddress=%s', line):
line = line.replace('mailAlternativeAddress=%s', 'mailAlternativeAddress='+options.name+'@%d')
found_query = True
ldap_custom_catchall.append(line.rstrip('\n'))
# Error if query_filter was not found
if not found_query:
_exitError('Error: Failed to create custom catchall ldap file from '+CFG_POSTFIX_VIRTUAL)
_dbg('ldap.custom_catchall will be:')
_dbgObj(ldap_custom_catchall)
# In dry mode exit now and report
if options.dryrun:
print 'DryRun complete!'
print 'If there was no error till this point you can now run without dry-run option.'
return
# Create custom catchall ldap file and set needed ucr variable
if os.path.isfile(CFG_POSTFIX_CATCHALL):
os.chmod(CFG_POSTFIX_CATCHALL, 0660)
file_custom_ldap = open(CFG_POSTFIX_CATCHALL, 'w')
file_custom_ldap.write('\n'.join(ldap_custom_catchall))
os.chmod(CFG_POSTFIX_CATCHALL, 0440)
ucrSet(CFG_UCR_KEY_ALIAS_MAPS, virtual_alias_maps_value)
# Finished show user quick guide what todo next
print "--- Creating catchall configuration finished ---"
print "Now you can assign users alternate E-Mail addresses like "+options.name+"@your.tld"
print "With this alternate set all Mails for @your.tld not assigned to a specific recipient/group/other-rule will redirected to this user."
print "Even if multiple users have the same alternate "+options.name+"@your.tld this will lead to any user become a copy of the Mail."
#
# Helpers
#
def _exitError(msg):
print msg
exit(1)
def _dbg(msg):
if options.debug:
print msg
def _dbgObj(obj):
if options.debug:
print obj
def _ucrCommit(file):
_dbg('Calling UCR Commit function on: '+file)
try:
ret_code = subprocess.call(['ucr','commit',file])
except OSError as e:
print >>sys.stderr, "Execution failed:", e, file
_dbg('Call returned with code: '+str(ret_code))
if ret_code == 0:
return
_exitError('Calling "ucr commit" failed with exit code: '+str(ret_code))
def ucrSet(key, value):
_dbg('Calling UCR and set key: '+key+' to: '+value)
try:
ret_code = subprocess.call(['ucr','set',key+'='+value])
except OSError as e:
print >>sys.stderr, "Execution failed:", e, file
_dbg('Call returned with code: '+str(ret_code))
if ret_code == 0:
return
_exitError('Calling "ucr set" failed with exit code: '+str(ret_code))
# exec
if __name__ == "__main__":
_dbg('-- START --')
main()
_dbg('--- END ---')
@bang-uin
Copy link

bang-uin commented Jan 5, 2021

Hi,

thank you for the work.
I created a Debian package inspired by your work to make use of the templating and server password change features.

https://github.com/bang-uin/univention-postfix-catchall

Regards

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment