Last active
February 8, 2021 14:23
-
-
Save Mario-F/15e43f39a6363bc2c890e1c2775fbc1f to your computer and use it in GitHub Desktop.
Creating dynamic catchall rule for Univention Corporate Servers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 ---') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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