Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117786098
D732.1775252308.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
146 KB
Referenced Files
None
Subscribers
None
D732.1775252308.diff
View Options
diff --git a/.arclint b/.arclint
--- a/.arclint
+++ b/.arclint
@@ -19,11 +19,13 @@
"E131": "disabled",
"E201": "disabled",
"E202": "disabled",
+ "E221": "disabled",
"E225": "disabled",
"E231": "disabled",
"E251": "disabled",
"E261": "disabled",
"E265": "disabled",
+ "E266": "disabled",
"E302": "disabled",
"E303": "disabled",
"E402": "disabled",
diff --git a/conf/kolab.conf b/conf/kolab.conf
--- a/conf/kolab.conf
+++ b/conf/kolab.conf
@@ -226,7 +226,7 @@
; The same as for users, but applicable to groups
group_base_dn = ou=Groups,%(base_dn)s
-group_filter = (|(objectclass=groupofuniquenames)(objectclass=groupofurls))
+group_filter = (|(objectclass=%(group_objectclass)s)(objectclass=groupofurls))
group_scope = sub
kolab_group_filter = (|(objectclass=kolabgroupofuniquenames)(objectclass=kolabgroupofurls))
diff --git a/pykolab/auth/__init__.py b/pykolab/auth/__init__.py
--- a/pykolab/auth/__init__.py
+++ b/pykolab/auth/__init__.py
@@ -49,7 +49,6 @@
Login is a simple list of username, password, service and,
optionally, the realm.
"""
-
if len(login) == 3:
# The realm has not been specified. See if we know whether or not
# to use virtual_domains, as this may be a cause for the realm not
@@ -58,12 +57,12 @@
# TODO: Insert debug statements
#if use_virtual_domains == "userid":
- #print "# Derive domain from login[0]"
+ # print "# Derive domain from login[0]"
#elif not use_virtual_domains:
- #print "# Explicitly do not user virtual domains??"
+ # print "# Explicitly do not user virtual domains??"
#else:
- ## Do use virtual domains, derive domain from login[0]
- #print "# Derive domain from login[0]"
+ # ## Do use virtual domains, derive domain from login[0]
+ # print "# Derive domain from login[0]"
if len(login[0].split('@')) > 1:
domain = login[0].split('@')[1]
@@ -86,7 +85,7 @@
back to the primary domain specified by the configuration.
"""
- log.debug(_("Called for domain %r") % (domain), level=8)
+ log.debug(_("Called for domain %r") % (domain), level=5)
if not self._auth == None:
return
@@ -99,14 +98,10 @@
section = 'kolab'
domain = conf.get('kolab', 'primary_domain')
else:
+ log.debug(_("Getting list of domains for %s ...") % (domain), level=5)
self.list_domains(domain)
section = domain
- log.debug(
- _("Using section %s and domain %s") % (section,domain),
- level=8
- )
-
if not self.domains == None and self.domains.has_key(domain):
section = self.domains[domain]
domain = self.domains[domain]
@@ -116,13 +111,6 @@
level=8
)
- log.debug(
- _("Connecting to Authentication backend for domain %s") % (
- domain
- ),
- level=8
- )
-
if not conf.has_section(section):
section = 'kolab'
@@ -142,21 +130,32 @@
level=8
)
+ _auth_mechanism = conf.get(section, 'auth_mechanism')
+
# Get the actual authentication and authorization backend.
- if conf.get(section, 'auth_mechanism') == 'ldap':
- log.debug(_("Starting LDAP..."), level=8)
+ if _auth_mechanism == 'ldap':
+ log.debug(_("Initializing LDAP..."), level=8)
from pykolab.auth import ldap
self._auth = ldap.LDAP(self.domain)
- elif conf.get(section, 'auth_mechanism') == 'sql':
+ elif _auth_mechanism == 'sql':
+ log.debug(_("Initializing SQL..."), level=8)
from pykolab.auth import sql
self._auth = sql.SQL(self.domain)
else:
- log.debug(_("Starting LDAP..."), level=8)
+ log.debug(_("Fallback to LDAP. Initializing ..."), level=5)
from pykolab.auth import ldap
self._auth = ldap.LDAP(self.domain)
+ log.debug(
+ _("Connecting to Authentication %s backend for domain %s") % (
+ _auth_mechanism,
+ domain
+ ),
+ level=5
+ )
+
self._auth.connect()
def disconnect(self, domain=None):
@@ -277,6 +276,8 @@
for secondary in secondaries:
self.domains[secondary.lower()] = primary.lower()
+ log.debug(_("List of domains for %s is: %s") % (domain, ", ".join(self.domains)), level=8)
+
return self.domains
def synchronize(self, mode=0, callback=None):
@@ -291,6 +292,9 @@
def primary_domain_for_naming_context(self, domain):
return self._auth._primary_domain_for_naming_context(domain)
+ def add_entry(self, domain, entry):
+ return self._auth._add_entry(entry)
+
def get_entry_attribute(self, domain, entry, attribute):
return self._auth.get_entry_attribute(entry, attribute)
diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py
--- a/pykolab/auth/ldap/__init__.py
+++ b/pykolab/auth/ldap/__init__.py
@@ -1525,6 +1525,26 @@
log.debug(_("bind_priv() called but already bound"), level=8)
return True
+ def _add_entry(self, entry):
+ """
+ Add generic type of entry.
+
+ Initialy created for setup-kolab to be able to add AD schema
+ by parsing Kolab AD schema ldif file
+
+ entry - dictionary, same as search results
+ """
+ self._bind()
+
+ entry_dn, entry_attrs = entry
+
+ log.debug(_("Entry DN: %s, with attributes: %s") % (entry_dn, entry_attrs), level=8)
+
+ self.ldap.add_s(
+ entry_dn,
+ entry_attrs
+ )
+
def _change_add_group(self, entry, change):
"""
An entry of type group was added.
diff --git a/pykolab/conf/__init__.py b/pykolab/conf/__init__.py
--- a/pykolab/conf/__init__.py
+++ b/pykolab/conf/__init__.py
@@ -223,8 +223,10 @@
log.error(_("Configuration file %s not readable") % config_file)
config = SafeConfigParser()
- log.debug(_("Reading configuration file %s") % config_file, level=8)
+ log.debug(_("Setting default configuration options: %s") % config.sections(), level=8)
+ config.readfp(self.defaults.default_conf())
try:
+ log.debug(_("Reading configuration file %s") % config_file, level=8)
config.read(config_file)
except:
log.error(_("Invalid configuration file %s") % config_file)
@@ -329,7 +331,7 @@
if self.cli_args:
if len(self.cli_args) >= 1:
if hasattr(self,"command_%s" % self.cli_args[0].replace('-','_')):
- exec("self.command_%s(%r)" % (self.cli_args[0].replace('-','_'), self.cli_args[1:]))
+ exec("self.command_%s(%r)" % (self.cli_args[0].replace('-','_'), self.cli_args[1:]))
else:
print >> sys.stderr, _("No command supplied")
@@ -367,7 +369,7 @@
continue
for key in keys:
- print "%s_%s = %s" % (mode, key ,self.cfg_parser.get(mode,key))
+ print "%s_%s = %s" % (mode, key, self.cfg_parser.get(mode,key))
def read_config(self, value=None):
"""
@@ -381,11 +383,12 @@
value = self.cli_keywords.config_file
self.cfg_parser = SafeConfigParser()
+ self.cfg_parser.readfp(self.defaults.default_conf())
self.cfg_parser.read(value)
if hasattr(self, 'cli_keywords') and hasattr(self.cli_keywords, 'config_file'):
self.cli_keywords.config_file = value
- self.defaults.config_file = value
+
self.config_file = value
def command_get(self, *args, **kw):
diff --git a/pykolab/conf/defaults.py b/pykolab/conf/defaults.py
--- a/pykolab/conf/defaults.py
+++ b/pykolab/conf/defaults.py
@@ -18,8 +18,10 @@
#
import logging
+from StringIO import StringIO
class Defaults(object):
+
def __init__(self, plugins=None):
self.loglevel = logging.CRITICAL
@@ -39,4 +41,37 @@
self.kolab_default_locale = 'en_US'
self.ldap_unique_attribute = 'nsuniqueid'
- self.wallace_resource_calendar_expire_days = 100
\ No newline at end of file
+ # LDAPS connection certificate verification
+ self.ldap_verify_certs = 'never'
+
+ # Static group object class, because ActiveDirectory uses objectClass=group
+ self.ldap_group_objectclass = 'groupOfUniqueNames'
+ self.ldap_group_member_attr = 'uniqueMember'
+
+ self.wallace_resource_calendar_expire_days = 100
+
+ def default_conf(self):
+ default_config = """
+[cyrus]
+annotations_retry_interval = %(cyrus_annotations_retry_interval)s
+
+[ldap]
+group_objectclass = %(ldap_group_objectclass)s
+group_member_attr = %(ldap_group_member_attr)s
+verify_certs = %(ldap_verify_certs)s
+unique_attribute = %(ldap_unique_attribute)s
+mail_attributes = %(mail_attributes)s
+mailserver_attribute = %(mailserver_attribute)s
+
+[kolab]
+default_locale = %(kolab_default_locale)s
+domain_sync_interval = %(kolab_domain_sync_interval)s
+
+[kolab_smtp_access_policy]
+address_search_attrs = %(address_search_attrs)s
+
+[wallace]
+resource_calendar_expire_days = %(wallace_resource_calendar_expire_days)s
+""" % self.__dict__
+
+ return StringIO(default_config)
diff --git a/pykolab/constants.py.in b/pykolab/constants.py.in
--- a/pykolab/constants.py.in
+++ b/pykolab/constants.py.in
@@ -20,6 +20,7 @@
import math
import socket
import sys
+import os
from pykolab.translate import _
@@ -88,6 +89,12 @@
'one': ldap.SCOPE_ONELEVEL
}
+LDAP_VERIFY_CERTS = {
+ 'allow': ldap.OPT_X_TLS_ALLOW,
+ 'demand': ldap.OPT_X_TLS_DEMAND,
+ 'never': ldap.OPT_X_TLS_NEVER
+ }
+
SUPPORTED_LDAP_CONTROLS = {
0: {
'desc': 'Persistent Search Control',
@@ -118,3 +125,17 @@
# 'func': '_sync_repl'
# }
# }
+
+
+# Binay attributes which should not be stripped
+BINARY_ATTRS = (
+ 'objectguid',
+ 'objectsid'
+ )
+
+TEMPLATE_LOCATIONS = (
+ '/etc/kolab/templates/',
+ '/usr/share/kolab/templates/',
+ '/usr/share/doc/pykolab/templates',
+ os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates'))
+ )
diff --git a/pykolab/setup/setup_ldap.py b/pykolab/setup/setup_ldap.py
--- a/pykolab/setup/setup_ldap.py
+++ b/pykolab/setup/setup_ldap.py
@@ -17,11 +17,15 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
+import base64
import ldap
import ldap.modlist
+import ldap.dn
+import ldif
import os
import pwd
import shutil
+import socket
import subprocess
import tempfile
import time
@@ -35,6 +39,9 @@
from pykolab.constants import *
from pykolab.translate import _
+from StringIO import StringIO
+from jinja2 import Template
+
log = pykolab.getLogger('pykolab.setup')
conf = pykolab.getConf()
@@ -95,20 +102,468 @@
def description():
return _("Setup LDAP.")
+def get_answers():
+
+ answers = {}
+
+ if conf.fqdn:
+ answers['fqdn'] = conf.fqdn
+ answers['hostname'] = conf.fqdn.split('.')[0]
+ answers['domain'] = '.'.join(conf.fqdn.split('.')[1:])
+ else:
+ answers['fqdn'] = fqdn
+ answers['hostname'] = hostname.split('.')[0]
+ answers['domain'] = domainname
+
+ print >> sys.stderr, utils.multiline_message(
+ _("""
+ This setup procedure plans to set up Kolab Groupware for
+ the following domain name space. This domain name is
+ obtained from the reverse DNS entry on your network
+ interface. Please confirm this is the appropriate domain
+ name space.
+ """)
+ )
+
+ answers['domain'] = utils.ask_question(
+ _("Domain name to use"),
+ default = answers['domain']
+ )
+
+ answers['nodotdomain'] = answers['domain'].replace('.','_')
+
+ print >> sys.stderr, utils.multiline_message(
+ _("""
+ The standard root dn we composed for you follows. Please
+ confirm this is the root dn you wish to use.
+ """)
+ )
+
+ answers['base_dn'] = utils.ask_question(
+ _("LDAP base DN"),
+ default = utils.standard_root_dn(answers['domain'])
+ )
+
+ if conf.with_ad:
+ print >> sys.stderr, utils.multiline_message(
+ _("""
+ Please supply Active Directory Administrator bind DN.
+ This will be used to bind to Active Directory LDAP to
+ perform various administrative tasks. This user should
+ have necessary privileges in Active Directory already.
+ """)
+ )
+
+ answers['bind_dn'] = utils.ask_question(
+ _("AD Administrator bind DN"),
+ default="CN=Administrator,CN=Users,%s" % answers['base_dn']
+ )
+ elif not conf.with_ad and not conf.with_openldap:
+ # Assuming Dirsrv
+ answers['bind_dn'] = "cn=Directory Manager"
+ log.info(_("Default administrative bind dn for Directory Server: %s") % answers['bind_dn'])
+
+ # Get cn=Directory Manger password
+ if conf.directory_manager_pwd is not None:
+ # password was set with command line options
+ answers['bind_pw'] = conf.directory_manager_pwd
+ else:
+ # Ask for password
+ if conf.with_ad:
+ print >> sys.stderr, utils.multiline_message(
+ _("""
+ Please supply a password for the Active Directory Administrator user.
+ """)
+ )
+ _short_text = _("Active Directory administrator password")
+ else:
+ print >> sys.stderr, utils.multiline_message(
+ _("""
+ Please supply a password for the LDAP Directory Manager
+ user, which is the administrator user you will be using
+ to at least initially log in to the Web Admin, and that
+ Kolab uses to perform administrative tasks.
+ """)
+ )
+ _short_text = _("Directory Manager password")
+
+ answers['bind_pw'] = utils.ask_question(
+ _short_text,
+ default=utils.generate_password(),
+ password=True,
+ confirm=True
+ )
+
+ # Ask only if LDAP is DS
+ if not conf.with_openldap and not conf.with_ad:
+ # Get Dirsrv Admin server admin user pass
+ print >> sys.stderr, utils.multiline_message(
+ _("""
+ Please supply a password for the LDAP administrator user
+ 'admin', used to login to the graphical console of 389
+ Directory server.
+ """)
+ )
+
+ answers['admin_pass'] = utils.ask_question(
+ _("Administrator password"),
+ default=utils.generate_password(),
+ password=True,
+ confirm=True
+ )
+
+
+ # Set users for DS LDAP
+ print >> sys.stderr, utils.multiline_message(
+ _("""
+ Please choose the system user and group the service
+ should use to run under. These should be existing,
+ unprivileged, local system POSIX accounts with no shell.
+ """)
+ )
+
+ try:
+ pw = pwd.getpwnam("dirsrv")
+ except:
+ answers['userid'] = utils.ask_question(_("User"), default="nobody")
+ answers['group'] = utils.ask_question(_("Group"), default="nobody")
+ else:
+ answers['userid'] = utils.ask_question(_("User"), default="dirsrv")
+ answers['group'] = utils.ask_question(_("Group"), default="dirsrv")
+
+ # Ask if Active Directory
+ elif conf.with_ad and not conf.with_openldap:
+ # Get answers for Active Directory setup
+ print >> sys.stderr, utils.multiline_message(
+ _("""
+ Specify basic Active Directory connection information.
+ """)
+ )
+
+ print >> sys.stderr, utils.multiline_message(
+ _("""
+ Specify LDAP tree DN for Kolab related data. Kolab specific
+ LDAP records such as Kolab domain information, special users
+ for Kolab, shared folders and resources LDAP information.
+ """)
+ )
+
+ answers['kolab_tree_dn'] = utils.ask_question(
+ _("AD LDAP tree for Kolab:"),
+ default="OU=Kolab,%s" % answers['base_dn']
+ )
+
+ answers['specialuser_base_dn'] = "OU=Special Users,%s" % answers['kolab_tree_dn']
+ answers['cyrus_admin_dn'] = "CN=Cyrus Admin,%s" % answers['specialuser_base_dn']
+ answers['service_bind_dn'] = "CN=Kolab Service,%s" % answers['specialuser_base_dn']
+ answers['domain_base_dn'] = "OU=Domains,%s" % answers['kolab_tree_dn']
+ answers['group_base_dn'] = "OU=Groups,%s" % answers['kolab_tree_dn']
+ answers['resource_base_dn'] = "OU=Resources,%s" % answers['kolab_tree_dn']
+ answers['sharedfolder_base_dn'] = "OU=Shared Folders,%s" % answers['kolab_tree_dn']
+ answers['user_base_dn'] = "CN=Users,%s" % answers['base_dn']
+ answers['kolab_user_base_dn'] = answers['user_base_dn']
+
+ print >> sys.stderr, utils.multiline_message(
+ _("""
+ Please provide Active Directory server URI. Setup will use it to load
+ Kolab schema into AD and then will configure Kolab to run with AD as
+ LDAP server. Schema can be loaded to schema master server only wherefore
+ this needs to be URI specifying AD schema master. You can change AD server
+ URI later, in kolab.conf file.
+ """)
+ )
+
+ #answers['ldap_uri'] = utils.ask_question(
+ # _("AD server URI"),
+ # default="ldap://adserver.domain:389"
+ # )
+ answers['ldap_uri'] = utils.ask_question(
+ _("AD server URI"),
+ default="ldaps://w2k16-1.ad-lab.loc:636"
+ )
+
+ print >> sys.stderr, utils.multiline_message(
+ _("""
+ Please supply a Cyrus Administrator password. This
+ password is used by Kolab to execute administrative
+ tasks in Cyrus IMAP. You may also need the password
+ yourself to troubleshoot Cyrus IMAP and/or perform
+ other administrative tasks against Cyrus IMAP directly.
+ """)
+ )
+
+ answers['cyrus_admin_password'] = utils.ask_question(
+ _("Cyrus Administrator password"),
+ default=utils.generate_password(),
+ password=True,
+ confirm=True
+ )
+
+ print >> sys.stderr, utils.multiline_message(
+ _("""
+ Please supply a Kolab Service account password. This
+ account is used by various services such as Postfix,
+ and Roundcube, as anonymous binds to the LDAP server
+ will not be allowed.
+ """)
+ )
+
+ answers['service_bind_pw'] = utils.ask_question(
+ _("Kolab Service password"),
+ default=utils.generate_password(),
+ password=True,
+ confirm=True
+ )
+
+ return answers
+
+def ad_schema_import(*args, **kwargs):
+ """
+ Function to load Kolab schema to Active Directory
+ Does checks if ldap_uri points to Schema master
+ """
+ conf.ldap_uri = kwargs['ldap_uri']
+
+ # Create LDAP connection
+ try:
+ auth = Auth(kwargs['domain'])
+ auth.connect(immediate=True)
+ auth._auth.connect()
+ auth._auth._bind(kwargs['bind_dn'], kwargs['bind_pw'])
+
+ ad_schema_naming_context = auth._auth.get_entry_attribute("", "schemaNamingContext")
+
+ log.debug("AD Schema Naming context is %s" % ad_schema_naming_context, level=8)
+
+ # Find out AD schema master server. Only this server has writable copy of
+ # AD schema. First need to get fsmoRoleOwner attribute value.
+ ad_fsmo_attribute = auth._auth.get_entry_attribute(ad_schema_naming_context, "fSMORoleOwner")
+
+ log.debug(_("AD FSMO Role Owner entry is: %s") % ad_fsmo_attribute, level=8)
+ except Exception as errmsg:
+ log.error(_("Could not connect to LDAP. Error: %s") % errmsg)
+ sys.exit(1)
+
+ # Now get parent of fsmoRoleOwner
+ if ad_fsmo_attribute is not None:
+ ad_fsmo_dn = ldap.dn.explode_dn(ad_fsmo_attribute)
+ ad_fsmo_parent = ','.join(ad_fsmo_dn[1:])
+
+ log.debug(_("AD FSMO entry parent is: %s") % ad_fsmo_parent, level=8)
+
+ # Look for dNSHostName attribute of the parent
+ if ad_fsmo_parent is not None:
+ ad_schema_master = auth._auth.get_entry_attribute(ad_fsmo_parent, "dNSHostName")
+ log.debug(_("AD schema master server is: %s") % ad_schema_master, level=8)
+
+ (
+ _ad_protocol,
+ _ad_server,
+ _ad_port,
+ _ad_base_dn,
+ _ad_attr,
+ _ad_scope,
+ _ad_filter
+ ) = utils.parse_ldap_uri(conf.ldap_uri)
+
+ if not _ad_server == ad_schema_master:
+ log.debug(_("AD schema master %s is not the same as %s.") % (ad_schema_master, _ad_server), level=5)
+ log.error(_("Cannot install Kolab schema on AD server which is not schema master"))
+ sys.exit(1)
+
+ template_file = utils.find_template(file_name='kolab3-ad-schema.ldif.j2')
+
+ if not template_file == None:
+ log.debug(_("Using template file %r") % (template_file), level=8)
+ fp = open(template_file, 'r')
+ template_definition = fp.read()
+ fp.close()
+
+ schema_template = Template(template_definition)
+
+ log.debug(
+ _("Successfully compiled template %r, ready to add schema to AD server") % (template_file),
+ level=8
+ )
+
+ schema_ldif = StringIO(
+ schema_template.render(ad_schema_naming_context=ad_schema_naming_context).__str__()
+ )
+
+ parser = ldif.LDIFRecordList(schema_ldif)
+ parser.parse()
+
+ # If silent install or install without asking questions
+ # when no need to ask confirmation
+ if kwargs['confirm']:
+
+ print >> sys.stderr, utils.multiline_message(
+ _("""
+ Setup is going to add Kolab schema elements to
+ Active Directory. This is ireversable operation.
+ """)
+ )
+
+ confirmed = utils.ask_confirmation(
+ _("Modify Active Directory schema?"),
+ default="y"
+ )
+
+ else:
+ # this is silent install
+ confirmed = True
+
+ if confirmed:
+ for dn, attributes in parser.all_records:
+ log.debug("Adding DN: %s" % dn, level=5)
+ try:
+ add_attributes = ldap.modlist.addModlist(attributes)
+ auth._auth.ldap.add_s(dn, add_attributes)
+ except ldap.ALREADY_EXISTS:
+ log.warning(_("Schema element already exist: %s") % dn)
+ continue
+ except ldap.NO_SUCH_ATTRIBUTE:
+ log.warning(_("Could not add %s schema element. Force update and retry.") % dn)
+ try:
+ auth._auth.set_entry_attribute("", "schemaUpdateNow", "1")
+ time.sleep(2)
+ auth._auth.ldap.add_s(dn, add_attributes)
+ except:
+ log.error(_("Failed to add %s Kolab schema element.") % dn)
+ sys.exit(1)
+ except Exception, errorMsg:
+ log.error(_("Could not add schema element: %s") % dn)
+ log.error(_("Error: %s") % errorMsg )
+ sys.exit(1)
+ else:
+ log.error(_("Kolab schema must be installed before you continue."))
+ sys.exit(1)
+
+def ldap_data_load(*args, **kwargs):
+ """
+ Function to setup LDAP tree
+ """
+ conf.ldap_uri = kwargs['ldap_uri']
+
+ # Create LDAP connection
+ auth = Auth(kwargs['domain'])
+ auth.connect()
+ auth._auth.connect()
+ auth._auth._bind(kwargs['bind_dn'], kwargs['bind_pw'])
+
+ (
+ _protocol,
+ _server,
+ _port,
+ _base_dn,
+ _attr,
+ _scope,
+ _filter
+ ) = utils.parse_ldap_uri(conf.ldap_uri)
+
+ ad_ldaps = False
+ if _protocol == 'ldap' and conf.with_ad:
+ log.info(_("Active Directory passwords cannot be set over %s protocol.") % (_protocol))
+ elif _protocol == "ldaps" and conf.with_ad:
+ ad_ldaps = True
+
+ if not kwargs['template'] == None:
+ log.debug(_("Using template file %r") % (kwargs['template']), level=8)
+ fp = open(kwargs['template'], 'r')
+ template_definition = fp.read()
+ fp.close()
+
+ _template_vars = kwargs
+ _template_vars['ad_ldaps'] = ad_ldaps
+ _template_vars['cyrus_admin_password_base64'] = base64.b64encode(
+ unicode("\"" + kwargs['cyrus_admin_password'] + "\"", "iso-8859-1").encode('utf-16-le')
+ )
+ _template_vars['service_bind_pw_base64'] = base64.b64encode(
+ unicode("\"" + kwargs['service_bind_pw'] + "\"", "iso-8859-1").encode('utf-16-le')
+ )
+
+ compiled_template = Template(template_definition)
+
+ log.debug(_("Successfully compiled template %r") % (kwargs['template']), level=1)
+
+ rendered_template = compiled_template.render(**_template_vars).__str__()
+
+ log.debug(_("Importing LDIF %s") % rendered_template, level=8)
+
+ template_ldif = StringIO(rendered_template)
+
+ parser = ldif.LDIFRecordList(template_ldif)
+ parser.parse()
+
+ for dn, attributes in parser.all_records:
+ log.debug("Adding DN: %s" % dn, level=5)
+ try:
+ add_attributes = ldap.modlist.addModlist(attributes)
+ # Do the actual synchronous add-operation to the ldapserver
+ auth._auth.ldap.add_s(dn, add_attributes)
+ except ldap.ALREADY_EXISTS:
+ log.warning(_("Object already exist: %s") % dn)
+ continue
+ except Exception, errorMsg:
+ log.error(_("Could not add object: %s") % dn)
+ log.error(_("Error: %s") % errorMsg )
+ sys.exit(1)
+
+def ldap_get_supported_controls(*args, **kwargs):
+ """
+ Function to find which controls are supported by given LDAP server
+ and pykolab. Return the list as a string.
+ """
+ auth = Auth(kwargs['domain'])
+ auth.connect(immediate=True)
+ auth._auth.connect()
+ auth._auth._bind(kwargs['bind_dn'], kwargs['bind_pw'])
+
+ _ldap_controls = auth._auth.get_entry_attribute("", "supportedControl")
+ supported_controls = []
+ for _ldap_control in _ldap_controls:
+ for _control_id, _control_def in SUPPORTED_LDAP_CONTROLS.iteritems():
+ if _control_def['oid'] == _ldap_control:
+ log.debug(_("Found control supported by pykolab: Id:%s Def: %s") % (_control_id, _control_def), level=8)
+ supported_controls.append(_control_id)
+
+ # Return supported controls as a string
+ return ','.join(map(str, supported_controls))
+
def execute(*args, **kw):
ask_questions = True
+ _input = {}
+
if not conf.config_file == conf.defaults.config_file:
+ log.debug(_("Using supplied config file to answer all questions"), level=5)
ask_questions = False
+ if conf.fqdn is not None:
+ log.debug(_("FQDN specified. Setting other dependend values"), level=8)
+ _input['fqdn'] = conf.fqdn
+ _input['hostname'] = conf.fqdn.split('.')[0]
+ _input['domain'] = '.'.join(conf.fqdn.split('.')[1:])
+ else:
+ log.debug(_("FQDN not specified. Derriving from defalts"), level=8)
+ _input['fqdn'] = fqdn
+ _input['hostname'] = hostname.split('.')[0]
+
+ if conf.get('kolab', 'primary_domain') is not None:
+ _input['domain'] = conf.get('kolab', 'primary_domain')
+ else:
+ _input['domain'] = domainname
+
+ if conf.get('ldap', 'base_dn') is not None:
+ _input['base_dn'] = conf.get('ldap', 'base_dn')
+ else:
+ _input['base_dn'] = utils.standard_root_dn(_input['domain'])
if conf.without_ldap:
print >> sys.stderr, _("Skipping setup of LDAP, as specified")
return
- _input = {}
-
if conf.with_openldap and not conf.with_ad:
-
+ conf.command_set('ldap', 'type', 'openldap')
conf.command_set('ldap', 'unique_attribute', 'entryuuid')
fp = open(conf.defaults.config_file, "w+")
@@ -118,14 +573,70 @@
return
elif conf.with_ad and not conf.with_openldap:
- conf.command_set('ldap', 'auth_attributes', 'samaccountname')
- conf.command_set('ldap', 'modifytimestamp_format', '%%Y%%m%%d%%H%%M%%S.0Z')
- conf.command_set('ldap', 'unique_attribute', 'userprincipalname')
+
+ if ask_questions:
+ _input = get_answers()
+ else:
+ # Get all the information from config file supplied as CLI -c param
+ _input['ldap_uri'] = conf.get('ldap', 'ldap_uri')
+ _input['bind_dn'] = conf.get('ldap', 'bind_dn')
+ _input['bind_pw'] = conf.get('ldap', 'bind_pw')
+ _input['service_bind_dn'] = conf.get('ldap', 'service_bind_dn')
+ _input['service_bind_pw'] = conf.get('ldap', 'service_bind_pw')
+ _input['user_base_dn'] = conf.get('ldap', 'user_base_dn')
+ _input['kolab_user_base_dn'] = conf.get('ldap', 'kolab_user_base_dn')
+ _input['group_base_dn'] = conf.get('ldap', 'group_base_dn')
+ _input['sharedfolder_base_dn'] = conf.get('ldap', 'sharedfolder_base_dn')
+ _input['resource_base_dn'] = conf.get('ldap', 'resource_base_dn')
+ _input['domain_base_dn'] = conf.get('ldap', 'domain_base_dn')
+ _input['cyrus_admin_password'] = conf.get('cyrus-imap', 'admin_password')
+
+ # Things which are specific to AD and are not in config file
+ _input['kolab_tree_dn'] = "OU=Kolab,%s" % _input['base_dn']
+ _input['specialuser_base_dn'] = "OU=Special Users,%s" % _input['kolab_tree_dn']
+ _input['cyrus_admin_dn'] = "CN=Cyrus Admin,%s" % _input['specialuser_base_dn']
+
+ conf.command_set('ldap', 'type', 'ad')
+ conf.command_set('ldap', 'auth_attributes', 'mail, kolabAlias, uid, samaccountname')
+ conf.command_set('ldap', 'mail_attributes', 'mail, kolabAlias')
+ conf.command_set('ldap', 'modifytimestamp_format', '%Y%m%d%H%M%S.0Z')
+ conf.command_set('ldap', 'unique_attribute', 'objectguid')
+ conf.command_set('ldap', 'sharedfolder_acl_entry_attribute', 'kolabImapACL')
+ conf.command_set('ldap', 'mailserver_attribute', 'kolabMailhost')
+ conf.command_set('ldap', 'quota_attribute', 'kolabMailquota')
+ conf.command_set('ldap', 'group_objectclass', 'group')
+ conf.command_set('ldap', 'group_member_attr', 'member')
# TODO: These attributes need to be checked
- conf.command_set('ldap', 'mail_attributes', 'mail')
- conf.command_set('ldap', 'mailserver_attributes', 'mailhost')
- conf.command_set('ldap', 'quota_attribute', 'mailquota')
+ conf.command_set('ldap', 'ldap_uri', _input['ldap_uri'])
+ conf.command_set('ldap', 'supported_controls', ldap_get_supported_controls(**_input))
+ conf.command_set('ldap', 'base_dn', _input['base_dn'])
+ conf.command_set('ldap', 'bind_dn', _input['bind_dn'])
+ conf.command_set('ldap', 'bind_pw', _input['bind_pw'])
+ conf.command_set('ldap', 'service_bind_dn', _input['service_bind_dn'])
+ conf.command_set('ldap', 'service_bind_pw', _input['service_bind_pw'])
+ conf.command_set('ldap', 'user_base_dn', _input['user_base_dn'])
+ conf.command_set('ldap', 'kolab_user_base_dn', _input['kolab_user_base_dn'])
+ conf.command_set('ldap', 'group_base_dn', _input['group_base_dn'])
+ conf.command_set('ldap', 'sharedfolder_base_dn', _input['sharedfolder_base_dn'])
+ conf.command_set('ldap', 'resource_base_dn', _input['resource_base_dn'])
+ conf.command_set('ldap', 'domain_base_dn', _input['domain_base_dn'])
+
+ conf.command_set('kolab', 'primary_domain', _input['domain'])
+ conf.command_set('cyrus-imap', 'admin_password', _input['cyrus_admin_password'])
+
+ _input['confirm'] = ask_questions
+
+ ad_schema_import(**_input)
+
+ templates = ['kolab-ad-tree.ldif.j2', 'kolab-ad-special-users.ldif.j2', 'kolab-ad-domain.ldif.j2']
+ for template in templates:
+ _input['template'] = utils.find_template(file_name=template)
+
+ if _input['template'] is not None:
+ ldap_data_load(**_input)
+ else:
+ continue
return
@@ -139,6 +650,7 @@
sys.exit(1)
+ # This is a point where setup starts cofigure local Directory Server
# Pre-execution checks
for path, directories, files in os.walk('/etc/dirsrv/'):
for direct in directories:
@@ -155,63 +667,11 @@
sys.exit(1)
- _input = {}
-
if ask_questions:
- print >> sys.stderr, utils.multiline_message(
- _("""
- Please supply a password for the LDAP administrator user
- 'admin', used to login to the graphical console of 389
- Directory server.
- """)
- )
-
- _input['admin_pass'] = utils.ask_question(
- _("Administrator password"),
- default=utils.generate_password(),
- password=True,
- confirm=True
- )
-
- if conf.directory_manager_pwd is not None:
- _input['dirmgr_pass'] = conf.directory_manager_pwd
- else:
- print >> sys.stderr, utils.multiline_message(
- _("""
- Please supply a password for the LDAP Directory Manager
- user, which is the administrator user you will be using
- to at least initially log in to the Web Admin, and that
- Kolab uses to perform administrative tasks.
- """)
- )
-
- _input['dirmgr_pass'] = utils.ask_question(
- _("Directory Manager password"),
- default=utils.generate_password(),
- password=True,
- confirm=True
- )
-
- print >> sys.stderr, utils.multiline_message(
- _("""
- Please choose the system user and group the service
- should use to run under. These should be existing,
- unprivileged, local system POSIX accounts with no shell.
- """)
- )
-
- try:
- pw = pwd.getpwnam("dirsrv")
- except:
- _input['userid'] = utils.ask_question(_("User"), default="nobody")
- _input['group'] = utils.ask_question(_("Group"), default="nobody")
- else:
- _input['userid'] = utils.ask_question(_("User"), default="dirsrv")
- _input['group'] = utils.ask_question(_("Group"), default="dirsrv")
-
+ _input = get_answers()
else:
_input['admin_pass'] = conf.get('ldap', 'bind_pw')
- _input['dirmgr_pass'] = conf.get('ldap', 'bind_pw')
+ _input['bind_pw'] = conf.get('ldap', 'bind_pw')
try:
pw = pwd.getpwnam("dirsrv")
except:
@@ -228,101 +688,33 @@
#
# TODO^2: This should be confirmed.
- if conf.fqdn:
- _input['fqdn'] = conf.fqdn
- _input['hostname'] = conf.fqdn.split('.')[0]
- _input['domain'] = '.'.join(conf.fqdn.split('.')[1:])
- else:
- _input['fqdn'] = fqdn
- _input['hostname'] = hostname.split('.')[0]
- _input['domain'] = domainname
_input['nodotdomain'] = _input['domain'].replace('.','_')
- _input['rootdn'] = utils.standard_root_dn(_input['domain'])
+ _input['ldap_uri'] = 'ldap://localhost:389/'
- if ask_questions:
- print >> sys.stderr, utils.multiline_message(
- _("""
- This setup procedure plans to set up Kolab Groupware for
- the following domain name space. This domain name is
- obtained from the reverse DNS entry on your network
- interface. Please confirm this is the appropriate domain
- name space.
- """)
- )
+ # TODO: Loudly complain if the fqdn does not resolve back to this system.
- answer = utils.ask_confirmation("%s" % (_input['domain']))
-
- if not answer:
- positive_answer = False
- while not positive_answer:
- _input['domain'] = utils.ask_question(_("Domain name to use"))
- if not _input['domain'] == None and not _input['domain'] == "":
- positive_answer = True
- else:
- print >> sys.stderr, utils.multiline_message(
- _("""
- Invalid input. Please try again.
- """)
- )
+ template_file = utils.find_template(file_name='kolab-ds-setup.inf.j2')
- _input['nodotdomain'] = _input['domain'].replace('.','_')
- _input['rootdn'] = utils.standard_root_dn(_input['domain'])
+ if template_file is not None:
+ log.debug(_("Setup Directory server using %s template") % (template_file), level=8)
- print >> sys.stderr, utils.multiline_message(
- _("""
- The standard root dn we composed for you follows. Please
- confirm this is the root dn you wish to use.
- """)
- )
+ fpr = open(template_file, 'r')
+ template_definition = fpr.read()
+ fpr.close()
- answer = utils.ask_confirmation("%s" % (_input['rootdn']))
-
- if not answer:
- positive_answer = False
- while not positive_answer:
- _input['rootdn'] = utils.ask_question(_("Root DN to use"))
- if not _input['rootdn'] == None and not _input['rootdn'] == "":
- positive_answer = True
- else:
- print >> sys.stderr, utils.multiline_message(
- _("""
- Invalid input. Please try again.
- """)
- )
+ setup_inf_template = Template(template_definition)
- # TODO: Loudly complain if the fqdn does not resolve back to this system.
+ log.debug(
+ _("Successfully compiled template %r, ready to add schema to AD server") % (template_file),
+ level=8
+ )
- data = """
-[General]
-FullMachineName = %(fqdn)s
-SuiteSpotUserID = %(userid)s
-SuiteSpotGroup = %(group)s
-AdminDomain = %(domain)s
-ConfigDirectoryLdapURL = ldap://%(fqdn)s:389/o=NetscapeRoot
-ConfigDirectoryAdminID = admin
-ConfigDirectoryAdminPwd = %(admin_pass)s
-
-[slapd]
-SlapdConfigForMC = Yes
-UseExistingMC = 0
-ServerPort = 389
-ServerIdentifier = %(hostname)s
-Suffix = %(rootdn)s
-RootDN = cn=Directory Manager
-RootDNPwd = %(dirmgr_pass)s
-ds_bename = %(nodotdomain)s
-AddSampleEntries = No
-
-[admin]
-Port = 9830
-ServerAdminID = admin
-ServerAdminPwd = %(admin_pass)s
-""" % (_input)
-
- (fp, filename) = tempfile.mkstemp(dir="/tmp/")
- os.write(fp, data)
- os.close(fp)
+ data = setup_inf_template.render(**_input).__str__()
+
+ (fpw, filename) = tempfile.mkstemp(dir="/tmp/")
+ os.write(fpw, data)
+ os.close(fpw)
if os.path.isfile("/usr/sbin/setup-ds-admin.pl"):
setup_ds_admin = "/usr/sbin/setup-ds-admin.pl"
@@ -438,53 +830,20 @@
log.error(_("Could not configure to start on boot, the " + \
"directory server service."))
- if ask_questions:
- print >> sys.stderr, utils.multiline_message(
- _("""
- Please supply a Cyrus Administrator password. This
- password is used by Kolab to execute administrative
- tasks in Cyrus IMAP. You may also need the password
- yourself to troubleshoot Cyrus IMAP and/or perform
- other administrative tasks against Cyrus IMAP directly.
- """)
- )
-
- _input['cyrus_admin_pass'] = utils.ask_question(
- _("Cyrus Administrator password"),
- default=utils.generate_password(),
- password=True,
- confirm=True
- )
-
- print >> sys.stderr, utils.multiline_message(
- _("""
- Please supply a Kolab Service account password. This
- account is used by various services such as Postfix,
- and Roundcube, as anonymous binds to the LDAP server
- will not be allowed.
- """)
- )
-
- _input['kolab_service_pass'] = utils.ask_question(
- _("Kolab Service password"),
- default=utils.generate_password(),
- password=True,
- confirm=True
- )
-
- else:
- _input['cyrus_admin_pass'] = conf.get('cyrus-imap', 'admin_password')
- _input['kolab_service_pass'] = conf.get('ldap', 'service_bind_pw')
+ if not ask_questions:
+ _input['cyrus_admin_password'] = conf.get('cyrus-imap', 'admin_password')
+ _input['service_bind_pw'] = conf.get('ldap', 'service_bind_pw')
log.info(_("Writing out configuration to kolab.conf"))
# Write out kolab configuration
conf.command_set('kolab', 'primary_domain', _input['domain'])
- conf.command_set('ldap', 'base_dn', _input['rootdn'])
+ conf.command_set('ldap', 'ldap_uri', _input['ldap_uri'])
+ conf.command_set('ldap', 'base_dn', _input['base_dn'])
conf.command_set('ldap', 'bind_dn', 'cn=Directory Manager')
- conf.command_set('ldap', 'bind_pw', _input['dirmgr_pass'])
- conf.command_set('ldap', 'service_bind_dn', 'uid=kolab-service,ou=Special Users,%s' % (_input['rootdn']))
- conf.command_set('ldap', 'service_bind_pw', _input['kolab_service_pass'])
+ conf.command_set('ldap', 'bind_pw', _input['bind_pw'])
+ conf.command_set('ldap', 'service_bind_dn', 'uid=kolab-service,ou=Special Users,%s' % (_input['base_dn']))
+ conf.command_set('ldap', 'service_bind_pw', _input['service_bind_pw'])
fp = open(conf.defaults.config_file, "w+")
conf.cfg_parser.write(fp)
@@ -496,9 +855,9 @@
auth = Auth(_input['domain'])
auth.connect()
auth._auth.connect()
- auth._auth._bind(bind_dn='cn=Directory Manager', bind_pw=_input['dirmgr_pass'])
+ auth._auth._bind(bind_dn='cn=Directory Manager', bind_pw=_input['bind_pw'])
- dn = 'uid=%s,ou=Special Users,%s' % (conf.get('cyrus-imap', 'admin_login'), _input['rootdn'])
+ dn = 'uid=%s,ou=Special Users,%s' % (conf.get('cyrus-imap', 'admin_login'), _input['base_dn'])
# A dict to help build the "body" of the object
attrs = {}
@@ -507,7 +866,7 @@
attrs['givenname'] = "Cyrus"
attrs['surname'] = "Administrator"
attrs['cn'] = "Cyrus Administrator"
- attrs['userPassword'] = _input['cyrus_admin_pass']
+ attrs['userPassword'] = _input['cyrus_admin_password']
# Convert our dict to nice syntax for the add-function using modlist-module
ldif = ldap.modlist.addModlist(attrs)
@@ -515,9 +874,9 @@
# Do the actual synchronous add-operation to the ldapserver
auth._auth.ldap.add_s(dn, ldif)
- conf.command_set('cyrus-imap', 'admin_password', _input['cyrus_admin_pass'])
+ conf.command_set('cyrus-imap', 'admin_password', _input['cyrus_admin_password'])
- dn = 'uid=kolab-service,ou=Special Users,%s' % (_input['rootdn'])
+ dn = 'uid=kolab-service,ou=Special Users,%s' % (_input['base_dn'])
# A dict to help build the "body" of the object
attrs = {}
@@ -526,7 +885,7 @@
attrs['givenname'] = "Kolab"
attrs['surname'] = "Service"
attrs['cn'] = "Kolab Service"
- attrs['userPassword'] = _input['kolab_service_pass']
+ attrs['userPassword'] = _input['service_bind_pw']
attrs['nslookthroughlimit'] = '-1'
attrs['nssizelimit'] = '-1'
attrs['nstimelimit'] = '-1'
@@ -538,7 +897,7 @@
# Do the actual synchronous add-operation to the ldapserver
auth._auth.ldap.add_s(dn, ldif)
- dn = 'ou=Resources,%s' % (_input['rootdn'])
+ dn = 'ou=Resources,%s' % (_input['base_dn'])
# A dict to help build the "body" of the object
attrs = {}
@@ -551,7 +910,7 @@
# Do the actual synchronous add-operation to the ldapserver
auth._auth.ldap.add_s(dn, ldif)
- dn = 'ou=Shared Folders,%s' % (_input['rootdn'])
+ dn = 'ou=Shared Folders,%s' % (_input['base_dn'])
# A dict to help build the "body" of the object
attrs = {}
@@ -572,7 +931,7 @@
attrs = {}
attrs['objectclass'] = ['top','extensibleobject']
attrs['cn'] = "kolab"
- attrs['aci'] = '(targetattr = "*") (version 3.0;acl "Kolab Services";allow (read,compare,search)(userdn = "ldap:///uid=kolab-service,ou=Special Users,%s");)' % (_input['rootdn'])
+ attrs['aci'] = '(targetattr = "*") (version 3.0;acl "Kolab Services";allow (read,compare,search)(userdn = "ldap:///uid=kolab-service,ou=Special Users,%s");)' % (_input['base_dn'])
# Convert our dict to nice syntax for the add-function using modlist-module
ldif = ldap.modlist.addModlist(attrs)
@@ -596,13 +955,13 @@
attrs['associateddomain'].pop(attrs['associateddomain'].index(_input['domain']))
attrs['associateddomain'] = [ _input['domain'] ] + attrs['associateddomain']
- attrs['aci'] = '(targetattr = "*") (version 3.0;acl "Read Access for %(domain)s Users";allow (read,compare,search)(userdn = "ldap:///%(rootdn)s??sub?(objectclass=*)");)' % (_input)
+ attrs['aci'] = '(targetattr = "*") (version 3.0;acl "Read Access for %(domain)s Users";allow (read,compare,search)(userdn = "ldap:///%(base_dn)s??sub?(objectclass=*)");)' % (_input)
# Add inetdomainbasedn in case the configured root dn is not the same as the
# standard root dn for the domain name configured
- if not _input['rootdn'] == utils.standard_root_dn(_input['domain']):
+ if not _input['base_dn'] == utils.standard_root_dn(_input['domain']):
attrs['objectclass'].append('inetdomain')
- attrs['inetdomainbasedn'] = _input['rootdn']
+ attrs['inetdomainbasedn'] = _input['base_dn']
ldif = ldap.modlist.addModlist(attrs)
auth._auth.ldap.add_s(dn, ldif)
@@ -644,7 +1003,7 @@
# Add kolab-admin role
log.info(_("Adding the kolab-admin role"))
- dn = "cn=kolab-admin,%s" % (_input['rootdn'])
+ dn = "cn=kolab-admin,%s" % (_input['base_dn'])
attrs = {}
attrs['description'] = "Kolab Administrator"
attrs['objectClass'] = ['top','ldapsubentry','nsroledefinition','nssimpleroledefinition','nsmanagedroledefinition']
@@ -654,8 +1013,8 @@
auth._auth.ldap.add_s(dn, ldif)
# User writeable attributes on root_dn
- log.info(_("Setting access control to %s") % (_input['rootdn']))
- dn = _input['rootdn']
+ log.info(_("Setting access control to %s") % (_input['base_dn']))
+ dn = _input['base_dn']
aci = []
if schema_error:
@@ -663,7 +1022,7 @@
else:
aci.append('(targetattr = "carLicense || description || displayName || facsimileTelephoneNumber || homePhone || homePostalAddress || initials || jpegPhoto || l || labeledURI || mobile || o || pager || photo || postOfficeBox || postalAddress || postalCode || preferredDeliveryMethod || preferredLanguage || registeredAddress || roomNumber || secretary || seeAlso || st || street || telephoneNumber || telexNumber || title || userCertificate || userPassword || userSMIMECertificate || x500UniqueIdentifier || kolabDelegate || kolabInvitationPolicy || kolabAllowSMTPSender") (version 3.0; acl "Enable self write for common attributes"; allow (read,compare,search,write)(userdn = "ldap:///self");)')
- aci.append('(targetattr = "*") (version 3.0;acl "Directory Administrators Group";allow (all)(groupdn = "ldap:///cn=Directory Administrators,%(rootdn)s" or roledn = "ldap:///cn=kolab-admin,%(rootdn)s");)' % (_input))
+ aci.append('(targetattr = "*") (version 3.0;acl "Directory Administrators Group";allow (all)(groupdn = "ldap:///cn=Directory Administrators,%(base_dn)s" or roledn = "ldap:///cn=kolab-admin,%(base_dn)s");)' % (_input))
aci.append('(targetattr="*")(version 3.0; acl "Configuration Administrators Group"; allow (all) groupdn="ldap:///cn=Configuration Administrators,ou=Groups,ou=TopologyManagement,o=NetscapeRoot";)')
aci.append('(targetattr="*")(version 3.0; acl "Configuration Administrator"; allow (all) userdn="ldap:///uid=admin,ou=Administrators,ou=TopologyManagement,o=NetscapeRoot";)')
aci.append('(targetattr = "*")(version 3.0; acl "SIE Group"; allow (all) groupdn = "ldap:///cn=slapd-%(hostname)s,cn=389 Directory Server,cn=Server Group,cn=%(fqdn)s,ou=%(domain)s,o=NetscapeRoot";)' % (_input))
diff --git a/pykolab/setup/setup_mta.py b/pykolab/setup/setup_mta.py
--- a/pykolab/setup/setup_mta.py
+++ b/pykolab/setup/setup_mta.py
@@ -18,7 +18,7 @@
#
from augeas import Augeas
-from Cheetah.Template import Template
+from jinja2 import Template
import os
import shutil
import subprocess
@@ -42,206 +42,38 @@
def execute(*args, **kw):
- group_filter = conf.get('ldap','kolab_group_filter')
- if group_filter == None:
- group_filter = conf.get('ldap','group_filter')
-
- user_filter = conf.get('ldap','kolab_user_filter')
- if user_filter == None:
- user_filter = conf.get('ldap','user_filter')
-
- resource_filter = conf.get('ldap', 'resource_filter')
-
- sharedfolder_filter = conf.get('ldap', 'sharedfolder_filter')
-
- server_host = utils.parse_ldap_uri(conf.get('ldap', 'ldap_uri'))[1]
-
- files = {
- "/etc/postfix/ldap/local_recipient_maps.cf": """
-server_host = %(server_host)s
-server_port = 389
-version = 3
-search_base = %(base_dn)s
-scope = sub
-
-domain = ldap:/etc/postfix/ldap/mydestination.cf
-
-bind_dn = %(service_bind_dn)s
-bind_pw = %(service_bind_pw)s
-
-query_filter = (&(|(mail=%%s)(alias=%%s))(|%(kolab_user_filter)s%(kolab_group_filter)s%(resource_filter)s%(sharedfolder_filter)s))
-result_attribute = mail
-""" % {
- "base_dn": conf.get('ldap', 'base_dn'),
- "server_host": server_host,
- "service_bind_dn": conf.get('ldap', 'service_bind_dn'),
- "service_bind_pw": conf.get('ldap', 'service_bind_pw'),
- "kolab_user_filter": user_filter,
- "kolab_group_filter": group_filter,
- "resource_filter": resource_filter,
- "sharedfolder_filter": sharedfolder_filter,
- },
- "/etc/postfix/ldap/mydestination.cf": """
-server_host = %(server_host)s
-server_port = 389
-version = 3
-search_base = %(domain_base_dn)s
-scope = sub
-
-bind_dn = %(service_bind_dn)s
-bind_pw = %(service_bind_pw)s
-
-query_filter = %(domain_filter)s
-result_attribute = %(domain_name_attribute)s
-""" % {
- "server_host": server_host,
- "domain_base_dn": conf.get('ldap', 'domain_base_dn'),
- "domain_filter": conf.get('ldap', 'domain_filter').replace('*', '%s'),
- "domain_name_attribute": conf.get('ldap', 'domain_name_attribute'),
- "service_bind_dn": conf.get('ldap', 'service_bind_dn'),
- "service_bind_pw": conf.get('ldap', 'service_bind_pw'),
- },
- "/etc/postfix/ldap/mailenabled_distgroups.cf": """
-server_host = %(server_host)s
-server_port = 389
-version = 3
-search_base = %(group_base_dn)s
-scope = sub
-
-domain = ldap:/etc/postfix/ldap/mydestination.cf
-
-bind_dn = %(service_bind_dn)s
-bind_pw = %(service_bind_pw)s
-
-# This finds the mail enabled distribution group LDAP entry
-query_filter = (&(|(mail=%%s)(alias=%%s))(objectClass=kolabgroupofuniquenames)(objectclass=groupofuniquenames)(!(objectclass=groupofurls)))
-# From this type of group, get all uniqueMember DNs
-special_result_attribute = uniqueMember
-# Only from those DNs, get the mail
-result_attribute =
-leaf_result_attribute = mail
-""" % {
- "server_host": server_host,
- "group_base_dn": conf.get('ldap', 'group_base_dn'),
- "service_bind_dn": conf.get('ldap', 'service_bind_dn'),
- "service_bind_pw": conf.get('ldap', 'service_bind_pw'),
- },
- "/etc/postfix/ldap/mailenabled_dynamic_distgroups.cf": """
-server_host = %(server_host)s
-server_port = 389
-version = 3
-search_base = %(group_base_dn)s
-scope = sub
-
-domain = ldap:/etc/postfix/ldap/mydestination.cf
-
-bind_dn = %(service_bind_dn)s
-bind_pw = %(service_bind_pw)s
-
-# This finds the mail enabled dynamic distribution group LDAP entry
-query_filter = (&(|(mail=%%s)(alias=%%s))(objectClass=kolabgroupofuniquenames)(objectClass=groupOfURLs))
-# From this type of group, get all memberURL searches/references
-special_result_attribute = memberURL
-# Only from those DNs, get the mail
-result_attribute =
-leaf_result_attribute = mail
-""" % {
- "server_host": server_host,
- "group_base_dn": conf.get('ldap', 'group_base_dn'),
- "service_bind_dn": conf.get('ldap', 'service_bind_dn'),
- "service_bind_pw": conf.get('ldap', 'service_bind_pw'),
- },
- "/etc/postfix/ldap/transport_maps.cf": """
-server_host = %(server_host)s
-server_port = 389
-version = 3
-search_base = %(base_dn)s
-scope = sub
-
-domain = ldap:/etc/postfix/ldap/mydestination.cf
-
-bind_dn = %(service_bind_dn)s
-bind_pw = %(service_bind_pw)s
-
-query_filter = (&(|(mailAlternateAddress=%%s)(alias=%%s)(mail=%%s))(objectclass=kolabinetorgperson))
-result_attribute = mail
-result_format = lmtp:unix:/var/lib/imap/socket/lmtp
-""" % {
- "base_dn": conf.get('ldap', 'base_dn'),
- "server_host": server_host,
- "service_bind_dn": conf.get('ldap', 'service_bind_dn'),
- "service_bind_pw": conf.get('ldap', 'service_bind_pw'),
- },
- "/etc/postfix/ldap/virtual_alias_maps.cf": """
-server_host = %(server_host)s
-server_port = 389
-version = 3
-search_base = %(base_dn)s
-scope = sub
-
-domain = ldap:/etc/postfix/ldap/mydestination.cf
-
-bind_dn = %(service_bind_dn)s
-bind_pw = %(service_bind_pw)s
-
-query_filter = (&(|(mail=%%s)(alias=%%s))(objectclass=kolabinetorgperson))
-result_attribute = mail
-""" % {
- "base_dn": conf.get('ldap', 'base_dn'),
- "server_host": server_host,
- "service_bind_dn": conf.get('ldap', 'service_bind_dn'),
- "service_bind_pw": conf.get('ldap', 'service_bind_pw'),
- },
- "/etc/postfix/ldap/virtual_alias_maps_mailforwarding.cf": """
-server_host = %(server_host)s
-server_port = 389
-version = 3
-search_base = %(base_dn)s
-scope = sub
-
-domain = ldap:/etc/postfix/ldap/mydestination.cf
-
-bind_dn = %(service_bind_dn)s
-bind_pw = %(service_bind_pw)s
-
-query_filter = (&(|(mail=%%s)(alias=%%s))(objectclass=mailrecipient)(objectclass=inetorgperson)(mailforwardingaddress=*))
-result_attribute = mailForwardingAddress
-""" % {
- "base_dn": conf.get('ldap', 'base_dn'),
- "server_host": server_host,
- "service_bind_dn": conf.get('ldap', 'service_bind_dn'),
- "service_bind_pw": conf.get('ldap', 'service_bind_pw'),
- },
- "/etc/postfix/ldap/virtual_alias_maps_sharedfolders.cf": """
-server_host = %(server_host)s
-server_port = 389
-version = 3
-search_base = %(base_dn)s
-scope = sub
-
-domain = ldap:/etc/postfix/ldap/mydestination.cf
-
-bind_dn = %(service_bind_dn)s
-bind_pw = %(service_bind_pw)s
-
-query_filter = (&(|(mail=%%s)(alias=%%s))(objectclass=kolabsharedfolder)(kolabFolderType=mail))
-result_attribute = kolabtargetfolder
-result_format = "shared+%%s"
-""" % {
- "base_dn": conf.get('ldap', 'base_dn'),
- "server_host": server_host,
- "service_bind_dn": conf.get('ldap', 'service_bind_dn'),
- "service_bind_pw": conf.get('ldap', 'service_bind_pw'),
- },
- }
+ postfix_ldap_dir = '/etc/postfix/ldap'
+ if not os.path.isdir(postfix_ldap_dir):
+ os.mkdir(postfix_ldap_dir, 0770)
- if not os.path.isdir('/etc/postfix/ldap'):
- os.mkdir('/etc/postfix/ldap/', 0770)
+ # Get ldap config section into dictionary with interpolation
+ config_ldap_dict = {
+ 'ldap': dict(conf.cfg_parser.items('ldap', raw=False))
+ }
- for filename in files.keys():
- fp = open(filename, 'w')
- fp.write(files[filename])
- fp.close()
+ postfix_lookup_tables = (
+ 'local_recipient_maps.cf',
+ 'mydestination.cf',
+ 'mailenabled_distgroups.cf',
+ 'mailenabled_dynamic_distgroups.cf',
+ 'transport_maps.cf',
+ 'virtual_alias_maps.cf',
+ 'virtual_alias_maps_mailforwarding.cf',
+ 'virtual_alias_maps_sharedfolders.cf'
+ )
+
+ for lookup_table in postfix_lookup_tables:
+ try:
+ with open(utils.find_template("%s.j2" % lookup_table)) as _template_def:
+ compiled_template = Template(_template_def.read())
+
+ with open(os.path.join(postfix_ldap_dir, lookup_table), 'w') as _template_dest:
+ _template_dest.write(compiled_template.render(**config_ldap_dict))
+
+ log.debug(_("Saved Postfix LDAP lookup table: %s") % _template_dest.name, level=8)
+ except Exception, errmsg:
+ log.error(_("Failed to save Postfix LDAP lookup table: %s") % errmsg)
+ continue
fp = open('/etc/postfix/transport', 'a')
fp.write("\n# Shared Folder Delivery for %(domain)s:\nshared@%(domain)s\t\tlmtp:unix:/var/lib/imap/socket/lmtp\n" % {'domain': conf.get('kolab', 'primary_domain')})
@@ -327,27 +159,19 @@
else:
postfix_master_settings['kolab_sap_executable_path'] = '/usr/libexec/postfix/kolab_smtp_access_policy'
- template_file = None
+ try:
+ with open(utils.find_template('master.cf.j2')) as postfix_master_template:
+ compiled_template = Template(postfix_master_template.read())
- if os.path.isfile('/etc/kolab/templates/master.cf.tpl'):
- template_file = '/etc/kolab/templates/master.cf.tpl'
- elif os.path.isfile('/usr/share/kolab/templates/master.cf.tpl'):
- template_file = '/usr/share/kolab/templates/master.cf.tpl'
- elif os.path.isfile(os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', 'master.cf.tpl'))):
- template_file = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', 'master.cf.tpl'))
+ with open('/etc/postfix/master.cf', 'w') as postfix_master_file:
+ postfix_master_file.write(compiled_template.render(**postfix_master_settings))
- if not template_file == None:
- fp = open(template_file, 'r')
- template_definition = fp.read()
- fp.close()
-
- t = Template(template_definition, searchList=[postfix_master_settings])
- fp = open('/etc/postfix/master.cf', 'w')
- fp.write(t.__str__())
- fp.close()
-
- else:
- log.error(_("Could not write out Postfix configuration file /etc/postfix/master.cf"))
+ log.debug(_("Saved Postfix /etc/postfix/master.cf file"), level=8)
+ except Exception, errmsg:
+ log.error(
+ _("Could not write out Postfix configuration file /etc/postfix/master.cf: %s") %
+ errmsg
+ )
return
if os.path.isdir('/etc/postfix/sasl/'):
@@ -356,18 +180,18 @@
fp.write("mech_list: plain login\n")
fp.close()
+ # Get raw config values for kolab section. Don't need to interpolate
+ config_kolab_dict = {
+ 'kolab': dict(conf.cfg_parser.items('kolab', raw=True))
+ }
+
+ # Merge ldap and kolab sections into one dictionalry
amavisd_settings = {
- 'ldap_server': '%(server_host)s',
- 'ldap_bind_dn': conf.get('ldap', 'service_bind_dn'),
- 'ldap_bind_pw': conf.get('ldap', 'service_bind_pw'),
- 'primary_domain': conf.get('kolab', 'primary_domain'),
- 'ldap_filter': "(|(mail=%m)(alias=%m))",
- 'ldap_base_dn': conf.get('ldap', 'base_dn'),
'clamdsock': '/var/spool/amavisd/clamd.sock',
+ 'kolab': config_kolab_dict['kolab'],
+ 'ldap': config_ldap_dict['ldap']
}
- template_file = None
-
# On RPM installations, Amavis configuration is contained within a single file.
amavisconf_paths = [
"/etc/amavisd.conf",
@@ -379,38 +203,24 @@
for amavisconf_path in amavisconf_paths:
if os.path.isfile(amavisconf_path):
amavis_conf = amavisconf_path
- break
-
- if os.path.isfile(amavis_conf):
- if os.path.isfile('/etc/kolab/templates/amavisd.conf.tpl'):
- template_file = '/etc/kolab/templates/amavisd.conf.tpl'
- elif os.path.isfile('/usr/share/kolab/templates/amavisd.conf.tpl'):
- template_file = '/usr/share/kolab/templates/amavisd.conf.tpl'
- elif os.path.isfile(os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', 'amavisd.conf.tpl'))):
- template_file = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', 'amavisd.conf.tpl'))
-
- if not template_file == None:
- fp = open(template_file, 'r')
- template_definition = fp.read()
- fp.close()
-
if os.path.isfile('/etc/clamd.d/amavisd.conf'):
amavisdconf_content = file('/etc/clamd.d/amavisd.conf')
for line in amavisdconf_content:
- if line.startswith('LocalSocket'):
- amavisd_settings['clamdsock'] = line[len('LocalSocket '):].strip()
-
- t = Template(template_definition, searchList=[amavisd_settings])
+ if line.startswith('LocalSocket'):
+ amavisd_settings['clamdsock'] = line[len('LocalSocket '):].strip()
+ break
- fp = None
- fp = open(amavis_conf, 'w')
+ if os.path.isfile(amavis_conf):
+ try:
+ with open(utils.find_template("%s.j2" % os.path.basename(amavis_conf))) as amavis_conf_template:
+ compiled_template = Template(amavis_conf_template.read())
- if not fp == None:
- fp.write(t.__str__())
- fp.close()
+ with open(amavis_conf, 'w') as amavis_conf_file:
+ amavis_conf_file.write(compiled_template.render(**amavisd_settings))
- else:
- log.error(_("Could not write out Amavis configuration file amavisd.conf"))
+ log.debug(_("Saved Amavisd configuration file %s") % (amavis_conf), level=8)
+ except Exception, errmsg:
+ log.error(_("Could not write out Amavisd configuration file %s: %s") % (amavis_conf, errmsg))
return
# On APT installations, /etc/amavis/conf.d/ is a directory with many more files.
@@ -475,7 +285,7 @@
subprocess.call([
'/usr/bin/freshclam',
'--quiet',
- '--datadir="/var/lib/clamav"'
+ '--datadir=/var/lib/clamav'
])
amavisservice = 'amavisd.service'
@@ -487,9 +297,6 @@
if os.path.isfile('/lib/systemd/system/amavis.service'):
amavisservice = 'amavis.service'
- if os.path.isfile('/etc/init.d/amavis'):
- amavisservice = 'amavis.service'
-
if os.path.isfile('/usr/lib/systemd/system/clamd.service'):
clamavservice = 'clamd.service'
@@ -500,38 +307,36 @@
clamavservice = 'clamav-daemon.service'
if os.path.isfile('/bin/systemctl'):
- subprocess.call(['systemctl', 'restart', 'postfix.service'])
- subprocess.call(['systemctl', 'restart', amavisservice])
- subprocess.call(['systemctl', 'restart', clamavservice])
- subprocess.call(['systemctl', 'restart', 'wallace.service'])
+ # Reload systemctl daemon in case any service file changed
+ subprocess.call(['systemctl', 'daemon-reload'])
+ for _srv in ('postfix.service', amavisservice, clamavservice, 'wallace.service'):
+ log.debug(_("Restarting %s systemd service ... ") % _srv, level=8)
+ subprocess.call(['systemctl', 'restart', _srv])
+ subprocess.call(['systemctl', 'enable', _srv])
elif os.path.isfile('/sbin/service'):
- subprocess.call(['service', 'postfix', 'restart'])
- subprocess.call(['service', 'amavisd', 'restart'])
- subprocess.call(['service', 'clamd.amavisd', 'restart'])
- subprocess.call(['service', 'wallace', 'restart'])
+ for _srv in ('postfix', 'amavisd', 'clamd.amavisd', 'wallace'):
+ log.debug(_("Restarting %s service ... ") % _srv, level=8)
+ subprocess.call(['service', _srv, 'restart'])
+ subprocess.call(['chkconfig', _srv, 'on'])
elif os.path.isfile('/usr/sbin/service'):
- subprocess.call(['/usr/sbin/service','postfix','restart'])
- subprocess.call(['/usr/sbin/service','amavis','restart'])
- subprocess.call(['/usr/sbin/service','clamav-daemon','restart'])
- subprocess.call(['/usr/sbin/service','wallace','restart'])
+ for _srv in ('postfix', 'amavis', 'clamav-daemon', 'wallace'):
+ log.debug(_("Restarting %s service ... ") % _srv, level=8)
+ subprocess.call(['/usr/sbin/service', _srv, 'restart'])
else:
log.error(_("Could not start the postfix, clamav and amavisd services services."))
if os.path.isfile('/bin/systemctl'):
- subprocess.call(['systemctl', 'enable', 'postfix.service'])
- subprocess.call(['systemctl', 'enable', amavisservice])
- subprocess.call(['systemctl', 'enable', clamavservice])
- subprocess.call(['systemctl', 'enable', 'wallace.service'])
+ for _srv in ('postfix.service', amavisservice, clamavservice, 'wallace.service'):
+ log.debug(_("Enabling %s systemd service to start on boot") % _srv, level=8)
+ subprocess.call(['systemctl', 'enable', _srv])
elif os.path.isfile('/sbin/chkconfig'):
- subprocess.call(['chkconfig', 'postfix', 'on'])
- subprocess.call(['chkconfig', 'amavisd', 'on'])
- subprocess.call(['chkconfig', 'clamd.amavisd', 'on'])
- subprocess.call(['chkconfig', 'wallace', 'on'])
+ for _srv in ('postfix', 'amavisd', 'clamd.amavisd', 'wallace'):
+ log.debug(_("Enabling %s service to start on boot") % _srv, level=8)
+ subprocess.call(['chkconfig', _srv, 'on'])
elif os.path.isfile('/usr/sbin/update-rc.d'):
- subprocess.call(['/usr/sbin/update-rc.d', 'postfix', 'defaults'])
- subprocess.call(['/usr/sbin/update-rc.d', 'amavis', 'defaults'])
- subprocess.call(['/usr/sbin/update-rc.d', 'clamav-daemon', 'defaults'])
- subprocess.call(['/usr/sbin/update-rc.d', 'wallace', 'defaults'])
+ for _srv in ('postfix', 'amavis', 'clamav-daemon', 'wallace'):
+ log.debug(_("Enabling %s service to start on boot ") % _srv, level=8)
+ subprocess.call(['/usr/sbin/update-rc.d', _srv, 'defaults'])
else:
log.error(_("Could not configure to start on boot, the " + \
"postfix, clamav and amavisd services."))
diff --git a/pykolab/utils.py b/pykolab/utils.py
--- a/pykolab/utils.py
+++ b/pykolab/utils.py
@@ -364,7 +364,12 @@
if _object[key] is None:
continue
- val = map(_strip, _object[key])
+ # Dont run strip anything from attributes which
+ # hold byte strings
+ if key.lower() in constants.BINARY_ATTRS:
+ val = _object[key]
+ else:
+ val = map(_strip, _object[key])
if len(val) == 1:
result[key.lower()] = ''.join(val)
@@ -601,3 +606,19 @@
_other_services.append(service)
return (_service,_other_services)
+
+
+def find_template(file_name=None):
+ """
+ Function to find template file.
+ """
+
+ for template_location in constants.TEMPLATE_LOCATIONS:
+ if os.path.isfile('%s/%s' % (template_location, file_name)):
+ template_file = '%s/%s' % (template_location, file_name)
+ return template_file
+
+ import errno
+ raise IOError(
+ errno.ENOENT, os.strerror(errno.ENOENT), file_name)
+ log.error(_("Template %s not found.") % file_name)
diff --git a/share/templates/amavisd.conf.tpl b/share/templates/amavisd.conf.j2
copy from share/templates/amavisd.conf.tpl
copy to share/templates/amavisd.conf.j2
--- a/share/templates/amavisd.conf.tpl
+++ b/share/templates/amavisd.conf.j2
@@ -12,178 +12,178 @@
# @bypass_virus_checks_maps = (1); # controls running of anti-virus code
# @bypass_spam_checks_maps = (1); # controls running of anti-spam code
-# \$bypass_decode_parts = 1; # controls running of decoders&dearchivers
-
-\$max_servers = 2; # num of pre-forked children (2..30 is common), -m
-\$daemon_user = 'amavis'; # (no default; customary: vscan or amavis), -u
-\$daemon_group = 'amavis'; # (no default; customary: vscan or amavis), -g
-
-\$mydomain = '$primary_domain'; # a convenient default for other settings
-
-\$MYHOME = '/var/spool/amavisd'; # a convenient default for other settings, -H
-\$TEMPBASE = "\$MYHOME/tmp"; # working directory, needs to exist, -T
-\$ENV{TMPDIR} = \$TEMPBASE; # environment variable TMPDIR, used by SA, etc.
-\$QUARANTINEDIR = undef; # -Q
-# \$quarantine_subdir_levels = 1; # add level of subdirs to disperse quarantine
-# \$release_format = 'resend'; # 'attach', 'plain', 'resend'
-# \$report_format = 'arf'; # 'attach', 'plain', 'resend', 'arf'
-
-# \$daemon_chroot_dir = \$MYHOME; # chroot directory or undef, -R
-
-\$db_home = "\$MYHOME/db"; # dir for bdb nanny/cache/snmp databases, -D
-# \$helpers_home = "\$MYHOME/var"; # working directory for SpamAssassin, -S
-\$lock_file = "/var/run/amavisd/amavisd.lock"; # -L
-\$pid_file = "/var/run/amavisd/amavisd.pid"; # -P
-#NOTE: create directories \$MYHOME/tmp, \$MYHOME/var, \$MYHOME/db manually
-
-\$log_level = 9; # verbosity 0..5, -d
-\$log_recip_templ = undef; # disable by-recipient level-0 log entries
-\$DO_SYSLOG = 1; # log via syslogd (preferred)
-\$syslog_facility = 'mail'; # Syslog facility as a string
+# $bypass_decode_parts = 1; # controls running of decoders&dearchivers
+
+$max_servers = 2; # num of pre-forked children (2..30 is common), -m
+$daemon_user = 'amavis'; # (no default; customary: vscan or amavis), -u
+$daemon_group = 'amavis'; # (no default; customary: vscan or amavis), -g
+
+$mydomain = '{{ kolab.primary_domain }}'; # a convenient default for other settings
+
+$MYHOME = '/var/spool/amavisd'; # a convenient default for other settings, -H
+$TEMPBASE = "$MYHOME/tmp"; # working directory, needs to exist, -T
+$ENV{TMPDIR} = $TEMPBASE; # environment variable TMPDIR, used by SA, etc.
+$QUARANTINEDIR = undef; # -Q
+# $quarantine_subdir_levels = 1; # add level of subdirs to disperse quarantine
+# $release_format = 'resend'; # 'attach', 'plain', 'resend'
+# $report_format = 'arf'; # 'attach', 'plain', 'resend', 'arf'
+
+# $daemon_chroot_dir = $MYHOME; # chroot directory or undef, -R
+
+$db_home = "$MYHOME/db"; # dir for bdb nanny/cache/snmp databases, -D
+# $helpers_home = "$MYHOME/var"; # working directory for SpamAssassin, -S
+$lock_file = "/var/run/amavisd/amavisd.lock"; # -L
+$pid_file = "/var/run/amavisd/amavisd.pid"; # -P
+#NOTE: create directories $MYHOME/tmp, $MYHOME/var, $MYHOME/db manually
+
+$log_level = 3; # verbosity 0..5, -d
+$log_recip_templ = undef; # disable by-recipient level-0 log entries
+$DO_SYSLOG = 1; # log via syslogd (preferred)
+$syslog_facility = 'mail'; # Syslog facility as a string
# e.g.: mail, daemon, user, local0, ... local7
-\$syslog_priority = 'debug'; # Syslog base (minimal) priority as a string,
+$syslog_priority = 'debug'; # Syslog base (minimal) priority as a string,
# choose from: emerg, alert, crit, err, warning, notice, info, debug
-\$enable_db = 1; # enable use of BerkeleyDB/libdb (SNMP and nanny)
-\$enable_global_cache = 1; # enable use of libdb-based cache if \$enable_db=1
-\$nanny_details_level = 2; # nanny verbosity: 1: traditional, 2: detailed
-\$enable_dkim_verification = 1; # enable DKIM signatures verification
-\$enable_dkim_signing = 1; # load DKIM signing code, keys defined by dkim_key
+$enable_db = 1; # enable use of BerkeleyDB/libdb (SNMP and nanny)
+$enable_global_cache = 1; # enable use of libdb-based cache if $enable_db=1
+$nanny_details_level = 2; # nanny verbosity: 1: traditional, 2: detailed
+$enable_dkim_verification = 1; # enable DKIM signatures verification
+$enable_dkim_signing = 1; # load DKIM signing code, keys defined by dkim_key
-\$enable_ldap = 1;
-\$default_ldap = {
- hostname => [ 'localhost' ],
+$enable_ldap = 1;
+$default_ldap = {
+ hostname => [ '{{ ldap.ldap_uri}}' ],
version => 3,
timeout => 5,
tls => 0,
- base => '$ldap_base_dn',
- query_filter => '$ldap_filter',
- bind_dn => '$ldap_bind_dn',
- bind_password => '$ldap_bind_pw'
+ base => '{{ ldap.base_dn }}',
+ query_filter => '(|{% for item in ldap.mail_attributes.split(',') -%}({{ item | trim }}=%m){% endfor -%})',
+ bind_dn => '{{ ldap.service_bind_dn }}',
+ bind_password => '{{ ldap.service_bind_pw }}'
};
-@local_domains_maps = ( [".\$mydomain"] ); # list of all local domains
+@local_domains_maps = ( [".$mydomain"] ); # list of all local domains
@mynetworks = qw( 127.0.0.0/8 [::1] [FE80::]/10 [FEC0::]/10
10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 );
-\$unix_socketname = "\$MYHOME/amavisd.sock"; # amavisd-release or amavis-milter
- # option(s) -p overrides \$inet_socket_port and \$unix_socketname
+$unix_socketname = "$MYHOME/amavisd.sock"; # amavisd-release or amavis-milter
+ # option(s) -p overrides $inet_socket_port and $unix_socketname
-\$inet_socket_port = 10024; # listen on this local TCP port(s)
-# \$inet_socket_port = [10024,10026]; # listen on multiple TCP ports
+$inet_socket_port = 10024; # listen on this local TCP port(s)
+# $inet_socket_port = [10024,10026]; # listen on multiple TCP ports
-\$policy_bank{'MYNETS'} = { # mail originating from @mynetworks
- originating => 1, # is true in MYNETS by default, but let's make it explicit
- os_fingerprint_method => undef, # don't query p0f for internal clients
+$policy_bank{'MYNETS'} = { # mail originating from @mynetworks
+ originating => 1, # is true in MYNETS by default, but let's make it explicit
+ os_fingerprint_method => undef, # don't query p0f for internal clients
};
# it is up to MTA to re-route mail from authenticated roaming users or
# from internal hosts to a dedicated TCP port (such as 10026) for filtering
-\$interface_policy{'10026'} = 'ORIGINATING';
+$interface_policy{'10026'} = 'ORIGINATING';
-\$policy_bank{'ORIGINATING'} = { # mail supposedly originating from our users
- originating => 1, # declare that mail was submitted by our smtp client
- allow_disclaimers => 1, # enables disclaimer insertion if available
+$policy_bank{'ORIGINATING'} = { # mail supposedly originating from our users
+ originating => 1, # declare that mail was submitted by our smtp client
+ allow_disclaimers => 1, # enables disclaimer insertion if available
# notify administrator of locally originating malware
- virus_admin_maps => ["virusalert\@\$mydomain"],
- spam_admin_maps => ["virusalert\@\$mydomain"],
+ virus_admin_maps => ["virusalert\@$mydomain"],
+ spam_admin_maps => ["virusalert\@$mydomain"],
warnbadhsender => 1,
# forward to a smtpd service providing DKIM signing service
forward_method => 'smtp:[127.0.0.1]:10027',
# force MTA conversion to 7-bit (e.g. before DKIM signing)
smtpd_discard_ehlo_keywords => ['8BITMIME'],
- bypass_banned_checks_maps => [1], # allow sending any file names and types
- terminate_dsn_on_notify_success => 0, # don't remove NOTIFY=SUCCESS option
+ bypass_banned_checks_maps => [1], # allow sending any file names and types
+ terminate_dsn_on_notify_success => 0, # don't remove NOTIFY=SUCCESS option
};
-\$interface_policy{'SOCK'} = 'AM.PDP-SOCK'; # only applies with \$unix_socketname
+$interface_policy{'SOCK'} = 'AM.PDP-SOCK'; # only applies with $unix_socketname
# Use with amavis-release over a socket or with Petr Rehor's amavis-milter.c
# (with amavis-milter.c from this package or old amavis.c client use 'AM.CL'):
-\$policy_bank{'AM.PDP-SOCK'} = {
+$policy_bank{'AM.PDP-SOCK'} = {
protocol => 'AM.PDP',
- auth_required_release => 0, # do not require secret_id for amavisd-release
+ auth_required_release => 0, # do not require secret_id for amavisd-release
};
-\$sa_tag_level_deflt = -10; # add spam info headers if at, or above that level
-\$sa_tag2_level_deflt = 6.2; # add 'spam detected' headers at that level
-\$sa_kill_level_deflt = 6.9; # triggers spam evasive actions (e.g. blocks mail)
-\$sa_dsn_cutoff_level = 10; # spam level beyond which a DSN is not sent
-\$sa_crediblefrom_dsn_cutoff_level = 18; # likewise, but for a likely valid From
-# \$sa_quarantine_cutoff_level = 25; # spam level beyond which quarantine is off
-\$penpals_bonus_score = 8; # (no effect without a @storage_sql_dsn database)
-\$penpals_threshold_high = \$sa_kill_level_deflt; # don't waste time on hi spam
-\$bounce_killer_score = 100; # spam score points to add for joe-jobbed bounces
+$sa_tag_level_deflt = -10; # add spam info headers if at, or above that level
+$sa_tag2_level_deflt = 6.2; # add 'spam detected' headers at that level
+$sa_kill_level_deflt = 6.9; # triggers spam evasive actions (e.g. blocks mail)
+$sa_dsn_cutoff_level = 10; # spam level beyond which a DSN is not sent
+$sa_crediblefrom_dsn_cutoff_level = 18; # likewise, but for a likely valid From
+# $sa_quarantine_cutoff_level = 25; # spam level beyond which quarantine is off
+$penpals_bonus_score = 8; # (no effect without a @storage_sql_dsn database)
+$penpals_threshold_high = $sa_kill_level_deflt; # don't waste time on hi spam
+$bounce_killer_score = 100; # spam score points to add for joe-jobbed bounces
-\$sa_mail_body_size_limit = 400*1024; # don't waste time on SA if mail is larger
-\$sa_local_tests_only = 0; # only tests which do not require internet access?
+$sa_mail_body_size_limit = 400*1024; # don't waste time on SA if mail is larger
+$sa_local_tests_only = 0; # only tests which do not require internet access?
# @lookup_sql_dsn =
# ( ['DBI:mysql:database=mail;host=127.0.0.1;port=3306', 'user1', 'passwd1'],
# ['DBI:mysql:database=mail;host=host2', 'username2', 'password2'],
-# ["DBI:SQLite:dbname=\$MYHOME/sql/mail_prefs.sqlite", '', ''] );
+# ["DBI:SQLite:dbname=$MYHOME/sql/mail_prefs.sqlite", '', ''] );
# @storage_sql_dsn = @lookup_sql_dsn; # none, same, or separate database
-# \$timestamp_fmt_mysql = 1; # if using MySQL *and* msgs.time_iso is TIMESTAMP;
+# $timestamp_fmt_mysql = 1; # if using MySQL *and* msgs.time_iso is TIMESTAMP;
# defaults to 0, which is good for non-MySQL or if msgs.time_iso is CHAR(16)
-\$virus_admin = undef; # notifications recip.
+$virus_admin = undef; # notifications recip.
-\$mailfrom_notify_admin = undef; # notifications sender
-\$mailfrom_notify_recip = undef; # notifications sender
-\$mailfrom_notify_spamadmin = undef; # notifications sender
-\$mailfrom_to_quarantine = ''; # null return path; uses original sender if undef
+$mailfrom_notify_admin = undef; # notifications sender
+$mailfrom_notify_recip = undef; # notifications sender
+$mailfrom_notify_spamadmin = undef; # notifications sender
+$mailfrom_to_quarantine = ''; # null return path; uses original sender if undef
@addr_extension_virus_maps = ('virus');
@addr_extension_banned_maps = ('banned');
@addr_extension_spam_maps = ('spam');
@addr_extension_bad_header_maps = ('badh');
-# \$recipient_delimiter = '+'; # undef disables address extensions altogether
+# $recipient_delimiter = '+'; # undef disables address extensions altogether
# when enabling addr extensions do also Postfix/main.cf: recipient_delimiter=+
-\$path = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/sbin:/usr/bin:/bin';
-# \$dspam = 'dspam';
+$path = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/sbin:/usr/bin:/bin';
+# $dspam = 'dspam';
-\$MAXLEVELS = 14;
-\$MAXFILES = 1500;
-\$MIN_EXPANSION_QUOTA = 100*1024; # bytes (default undef, not enforced)
-\$MAX_EXPANSION_QUOTA = 300*1024*1024; # bytes (default undef, not enforced)
+$MAXLEVELS = 14;
+$MAXFILES = 1500;
+$MIN_EXPANSION_QUOTA = 100*1024; # bytes (default undef, not enforced)
+$MAX_EXPANSION_QUOTA = 300*1024*1024; # bytes (default undef, not enforced)
-\$sa_spam_subject_tag = '***SPAM*** ';
-\$defang_virus = 1; # MIME-wrap passed infected mail
-\$defang_banned = 1; # MIME-wrap passed mail containing banned name
+$sa_spam_subject_tag = '***SPAM*** ';
+$defang_virus = 1; # MIME-wrap passed infected mail
+$defang_banned = 1; # MIME-wrap passed mail containing banned name
# for defanging bad headers only turn on certain minor contents categories:
-\$defang_by_ccat{+CC_BADH.",3"} = 1; # NUL or CR character in header
-\$defang_by_ccat{+CC_BADH.",5"} = 1; # header line longer than 998 characters
-\$defang_by_ccat{+CC_BADH.",6"} = 1; # header field syntax error
+$defang_by_ccat{+CC_BADH.",3"} = 1; # NUL or CR character in header
+$defang_by_ccat{+CC_BADH.",5"} = 1; # header line longer than 998 characters
+$defang_by_ccat{+CC_BADH.",6"} = 1; # header field syntax error
# OTHER MORE COMMON SETTINGS (defaults may suffice):
-# \$myhostname = 'host.example.com'; # must be a fully-qualified domain name!
+# $myhostname = 'host.example.com'; # must be a fully-qualified domain name!
-# \$notify_method = 'smtp:[127.0.0.1]:10025';
-# \$forward_method = 'smtp:[127.0.0.1]:10025'; # set to undef with milter!
+# $notify_method = 'smtp:[127.0.0.1]:10025';
+# $forward_method = 'smtp:[127.0.0.1]:10025'; # set to undef with milter!
-\$final_virus_destiny = D_DISCARD;
-\$final_banned_destiny = D_BOUNCE;
-\$final_spam_destiny = D_DISCARD;
-\$final_bad_header_destiny = D_BOUNCE;
-# \$bad_header_quarantine_method = undef;
+$final_virus_destiny = D_DISCARD;
+$final_banned_destiny = D_BOUNCE;
+$final_spam_destiny = D_DISCARD;
+$final_bad_header_destiny = D_BOUNCE;
+# $bad_header_quarantine_method = undef;
-# \$os_fingerprint_method = 'p0f:*:2345'; # to query p0f-analyzer.pl
+# $os_fingerprint_method = 'p0f:*:2345'; # to query p0f-analyzer.pl
-\#\# hierarchy by which a final setting is chosen:
-\#\# policy bank (based on port or IP address) -> *_by_ccat
-\#\# *_by_ccat (based on mail contents) -> *_maps
-\#\# *_maps (based on recipient address) -> final configuration value
+## hierarchy by which a final setting is chosen:
+## policy bank (based on port or IP address) -> *_by_ccat
+## *_by_ccat (based on mail contents) -> *_maps
+## *_maps (based on recipient address) -> final configuration value
# SOME OTHER VARIABLES WORTH CONSIDERING (see amavisd.conf-default for all)
-# \$warnbadhsender,
-# \$warnvirusrecip, \$warnbannedrecip, \$warnbadhrecip, (or @warn*recip_maps)
+# $warnbadhsender,
+# $warnvirusrecip, $warnbannedrecip, $warnbadhrecip, (or @warn*recip_maps)
#
# @bypass_virus_checks_maps, @bypass_spam_checks_maps,
# @bypass_banned_checks_maps, @bypass_header_checks_maps,
@@ -193,67 +193,67 @@
#
# @blacklist_sender_maps, @score_sender_maps,
#
-# \$clean_quarantine_method, \$virus_quarantine_to, \$banned_quarantine_to,
-# \$bad_header_quarantine_to, \$spam_quarantine_to,
+# $clean_quarantine_method, $virus_quarantine_to, $banned_quarantine_to,
+# $bad_header_quarantine_to, $spam_quarantine_to,
#
-# \$defang_bad_header, \$defang_undecipherable, \$defang_spam
+# $defang_bad_header, $defang_undecipherable, $defang_spam
# REMAINING IMPORTANT VARIABLES ARE LISTED HERE BECAUSE OF LONGER ASSIGNMENTS
@keep_decoded_original_maps = (new_RE(
- qr'^MAIL\$', # retain full original message for virus checking
- qr'^MAIL-UNDECIPHERABLE\$', # recheck full mail if it contains undecipherables
+ qr'^MAIL$', # retain full original message for virus checking
+ qr'^MAIL-UNDECIPHERABLE$', # recheck full mail if it contains undecipherables
qr'^(ASCII(?! cpio)|text|uuencoded|xxencoded|binhex)'i,
# qr'^Zip archive data', # don't trust Archive::Zip
));
-# for \$banned_namepath_re (a new-style of banned table) see amavisd.conf-sample
+# for $banned_namepath_re (a new-style of banned table) see amavisd.conf-sample
-\$banned_filename_re = new_RE(
+$banned_filename_re = new_RE(
-\#\## BLOCKED ANYWHERE
-# qr'^UNDECIPHERABLE\$', # is or contains any undecipherable components
- qr'^\.(exe-ms|dll)\$', # banned file(1) types, rudimentary
-# qr'^\.(exe|lha|tnef|cab|dll)\$', # banned file(1) types
+### BLOCKED ANYWHERE
+# qr'^UNDECIPHERABLE$', # is or contains any undecipherable components
+ qr'^\.(exe-ms|dll)$', # banned file(1) types, rudimentary
+# qr'^\.(exe|lha|tnef|cab|dll)$', # banned file(1) types
-\#\## BLOCK THE FOLLOWING, EXCEPT WITHIN UNIX ARCHIVES:
-# [ qr'^\.(gz|bz2)\$' => 0 ], # allow any in gzip or bzip2
- [ qr'^\.(rpm|cpio|tar)\$' => 0 ], # allow any in Unix-type archives
+### BLOCK THE FOLLOWING, EXCEPT WITHIN UNIX ARCHIVES:
+# [ qr'^\.(gz|bz2)$' => 0 ], # allow any in gzip or bzip2
+ [ qr'^\.(rpm|cpio|tar)$' => 0 ], # allow any in Unix-type archives
- qr'.\.(pif|scr)\$'i, # banned extensions - rudimentary
-# qr'^\.zip\$', # block zip type
+ qr'.\.(pif|scr)$'i, # banned extensions - rudimentary
+# qr'^\.zip$', # block zip type
-\#\## BLOCK THE FOLLOWING, EXCEPT WITHIN ARCHIVES:
-# [ qr'^\.(zip|rar|arc|arj|zoo)\$'=> 0 ], # allow any within these archives
+### BLOCK THE FOLLOWING, EXCEPT WITHIN ARCHIVES:
+# [ qr'^\.(zip|rar|arc|arj|zoo)$'=> 0 ], # allow any within these archives
- qr'^application/x-msdownload\$'i, # block these MIME types
- qr'^application/x-msdos-program\$'i,
- qr'^application/hta\$'i,
+ qr'^application/x-msdownload$'i, # block these MIME types
+ qr'^application/x-msdos-program$'i,
+ qr'^application/hta$'i,
-# qr'^message/partial\$'i, # rfc2046 MIME type
-# qr'^message/external-body\$'i, # rfc2046 MIME type
+# qr'^message/partial$'i, # rfc2046 MIME type
+# qr'^message/external-body$'i, # rfc2046 MIME type
-# qr'^(application/x-msmetafile|image/x-wmf)\$'i, # Windows Metafile MIME type
-# qr'^\.wmf\$', # Windows Metafile file(1) type
+# qr'^(application/x-msmetafile|image/x-wmf)$'i, # Windows Metafile MIME type
+# qr'^\.wmf$', # Windows Metafile file(1) type
# block certain double extensions in filenames
- qr'\.[^./]*[A-Za-z][^./]*\.\s*(exe|vbs|pif|scr|bat|cmd|com|cpl|dll)[.\s]*\$'i,
+ qr'\.[^./]*[A-Za-z][^./]*\.\s*(exe|vbs|pif|scr|bat|cmd|com|cpl|dll)[.\s]*$'i,
# qr'\{[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}\}?'i, # Class ID CLSID, strict
# qr'\{[0-9a-z]{4,}(-[0-9a-z]{4,}){0,7}\}?'i, # Class ID extension CLSID, loose
- qr'.\.(exe|vbs|pif|scr|cpl)\$'i, # banned extension - basic
-# qr'.\.(exe|vbs|pif|scr|cpl|bat|cmd|com)\$'i, # banned extension - basic+cmd
+ qr'.\.(exe|vbs|pif|scr|cpl)$'i, # banned extension - basic
+# qr'.\.(exe|vbs|pif|scr|cpl|bat|cmd|com)$'i, # banned extension - basic+cmd
# qr'.\.(ade|adp|app|bas|bat|chm|cmd|com|cpl|crt|emf|exe|fxp|grp|hlp|hta|
# inf|ins|isp|js|jse|lnk|mda|mdb|mde|mdw|mdt|mdz|msc|msi|msp|mst|
# ops|pcd|pif|prg|reg|scr|sct|shb|shs|vb|vbe|vbs|
-# wmf|wsc|wsf|wsh)\$'ix, # banned ext - long
-# qr'.\.(ani|cur|ico)\$'i, # banned cursors and icons filename
-# qr'^\.ani\$', # banned animated cursor file(1) type
+# wmf|wsc|wsf|wsh)$'ix, # banned ext - long
+# qr'.\.(ani|cur|ico)$'i, # banned cursors and icons filename
+# qr'^\.ani$', # banned animated cursor file(1) type
-# qr'.\.(mim|b64|bhx|hqx|xxe|uu|uue)\$'i, # banned extension - WinZip vulnerab.
+# qr'.\.(mim|b64|bhx|hqx|xxe|uu|uue)$'i, # banned extension - WinZip vulnerab.
);
# See http://support.microsoft.com/default.aspx?scid=kb;EN-US;q262631
# and http://www.cknow.com/vtutor/vtextensions.htm
@@ -264,13 +264,13 @@
@score_sender_maps = ({ # a by-recipient hash lookup table,
# results from all matching recipient tables are summed
-# \#\# per-recipient personal tables (NOTE: positive: black, negative: white)
+# ## per-recipient personal tables (NOTE: positive: black, negative: white)
# 'user1@example.com' => [{'bla-mobile.press@example.com' => 10.0}],
# 'user3@example.com' => [{'.ebay.com' => -3.0}],
# 'user4@example.com' => [{'cleargreen@cleargreen.com' => -7.0,
# '.cleargreen.com' => -5.0}],
- \#\# site-wide opinions about senders (the '.' matches any recipient)
+ ## site-wide opinions about senders (the '.' matches any recipient)
'.' => [ # the _first_ matching sender determines the score boost
new_RE( # regexp-type lookup table, just happens to be all soft-blacklist
@@ -362,47 +362,47 @@
@av_scanners = (
-# \#\## http://www.clanfield.info/sophie/ (http://www.vanja.com/tools/sophie/)
+# ### http://www.clanfield.info/sophie/ (http://www.vanja.com/tools/sophie/)
# ['Sophie',
# \&ask_daemon, ["{}/\n", '/var/run/sophie'],
-# qr/(?x)^ 0+ ( : | [\000\r\n]* \$)/m, qr/(?x)^ 1 ( : | [\000\r\n]* \$)/m,
-# qr/(?x)^ [-+]? \d+ : (.*?) [\000\r\n]* \$/m ],
+# qr/(?x)^ 0+ ( : | [\000\r\n]* $)/m, qr/(?x)^ 1 ( : | [\000\r\n]* $)/m,
+# qr/(?x)^ [-+]? \d+ : (.*?) [\000\r\n]* $/m ],
-# \#\## http://www.csupomona.edu/~henson/www/projects/SAVI-Perl/
+# ### http://www.csupomona.edu/~henson/www/projects/SAVI-Perl/
# ['Sophos SAVI', \&sophos_savi ],
-# \#\## http://www.clamav.net/
+# ### http://www.clamav.net/
['ClamAV-clamd',
- \&ask_daemon, ["CONTSCAN {}\n", "$clamdsock"],
- qr/\bOK\$/m, qr/\bFOUND\$/m,
- qr/^.*?: (?!Infected Archive)(.*) FOUND\$/m ],
+ \&ask_daemon, ["CONTSCAN {}\n", "{{ clamdsock }}"],
+ qr/\bOK$/m, qr/\bFOUND$/m,
+ qr/^.*?: (?!Infected Archive)(.*) FOUND$/m ],
# # NOTE: run clamd under the same user as amavisd, or run it under its own
# # uid such as clamav, add user clamav to the amavis group, and then add
# # AllowSupplementaryGroups to clamd.conf;
# # NOTE: match socket name (LocalSocket) in clamav.conf to the socket name in
-# # this entry; when running chrooted one may prefer socket "\$MYHOME/clamd".
+# # this entry; when running chrooted one may prefer socket "$MYHOME/clamd".
-# \#\## http://www.clamav.net/ and CPAN (memory-hungry! clamd is preferred)
+# ### http://www.clamav.net/ and CPAN (memory-hungry! clamd is preferred)
# # note that Mail::ClamAV requires perl to be build with threading!
# ['Mail::ClamAV', \&ask_clamav, "*", [0], [1], qr/^INFECTED: (.+)/m ],
-# \#\## http://www.openantivirus.org/
+# ### http://www.openantivirus.org/
# ['OpenAntiVirus ScannerDaemon (OAV)',
# \&ask_daemon, ["SCAN {}\n", '127.0.0.1:8127'],
# qr/^OK/m, qr/^FOUND: /m, qr/^FOUND: (.+)/m ],
-# \#\## http://www.vanja.com/tools/trophie/
+# ### http://www.vanja.com/tools/trophie/
# ['Trophie',
# \&ask_daemon, ["{}/\n", '/var/run/trophie'],
-# qr/(?x)^ 0+ ( : | [\000\r\n]* \$)/m, qr/(?x)^ 1 ( : | [\000\r\n]* \$)/m,
-# qr/(?x)^ [-+]? \d+ : (.*?) [\000\r\n]* \$/m ],
+# qr/(?x)^ 0+ ( : | [\000\r\n]* $)/m, qr/(?x)^ 1 ( : | [\000\r\n]* $)/m,
+# qr/(?x)^ [-+]? \d+ : (.*?) [\000\r\n]* $/m ],
-# \#\## http://www.grisoft.com/
+# ### http://www.grisoft.com/
# ['AVG Anti-Virus',
# \&ask_daemon, ["SCAN {}\n", '127.0.0.1:55555'],
# qr/^200/m, qr/^403/m, qr/^403 .*?: ([^\r\n]+)/m ],
-# \#\## http://www.f-prot.com/
+# ### http://www.f-prot.com/
# ['F-Prot fpscand', # F-PROT Antivirus for BSD/Linux/Solaris, version 6
# \&ask_daemon,
# ["SCAN FILE {}/*\n", '127.0.0.1:10200'],
@@ -410,7 +410,7 @@
# qr/^([1235679]|1[01345]) |<[^>:]*(?i)(infected|suspicious|unwanted)/m,
# qr/(?i)<[^>:]*(?:infected|suspicious|unwanted)[^>:]*: ([^>]*)>/m ],
-# \#\## http://www.f-prot.com/
+# ### http://www.f-prot.com/
# ['F-Prot f-protd', # old version
# \&ask_daemon,
# ["GET {}/*?-dumb%20-archive%20-packed HTTP/1.0\r\n\r\n",
@@ -420,12 +420,12 @@
# qr/(?i)<summary[^>]*>infected<\/summary>/m,
# qr/(?i)<name>(.+)<\/name>/m ],
-# \#\## http://www.sald.com/, http://www.dials.ru/english/, http://www.drweb.ru/
+# ### http://www.sald.com/, http://www.dials.ru/english/, http://www.drweb.ru/
# ['DrWebD', \&ask_daemon, # DrWebD 4.31 or later
# [pack('N',1). # DRWEBD_SCAN_CMD
# pack('N',0x00280001). # DONT_CHANGEMAIL, IS_MAIL, RETURN_VIRUSES
# pack('N', # path length
-# length("\$TEMPBASE/amavis-yyyymmddTHHMMSS-xxxxx/parts/pxxx")).
+# length("$TEMPBASE/amavis-yyyymmddTHHMMSS-xxxxx/parts/pxxx")).
# '{}/*'. # path
# pack('N',0). # content size
# pack('N',0),
@@ -439,9 +439,9 @@
# qr/\A.{12}(?:infected with )?([^\x00]+)\x00/sm,
# ],
# # NOTE: If using amavis-milter, change length to:
-# # length("\$TEMPBASE/amavis-milter-xxxxxxxxxxxxxx/parts/pxxx").
+# # length("$TEMPBASE/amavis-milter-xxxxxxxxxxxxxx/parts/pxxx").
- \#\## http://www.kaspersky.com/ (kav4mailservers)
+ ### http://www.kaspersky.com/ (kav4mailservers)
['KasperskyLab AVP - aveclient',
['/usr/local/kav/bin/aveclient','/usr/local/share/kav/bin/aveclient',
'/opt/kav/5.5/kav4mailservers/bin/aveclient','aveclient'],
@@ -452,50 +452,50 @@
# NOTE: one may prefer [0],[2,3,4,5], depending on how suspicious,
# currupted or protected archives are to be handled
- \#\## http://www.kaspersky.com/
+ ### http://www.kaspersky.com/
['KasperskyLab AntiViral Toolkit Pro (AVP)', ['avp'],
'-* -P -B -Y -O- {}', [0,3,6,8], [2,4], # any use for -A -K ?
qr/infected: (.+)/m,
- sub {chdir('/opt/AVP') or die "Can't chdir to AVP: \$!"},
- sub {chdir(\$TEMPBASE) or die "Can't chdir back to \$TEMPBASE \$!"},
+ sub {chdir('/opt/AVP') or die "Can't chdir to AVP: $!"},
+ sub {chdir($TEMPBASE) or die "Can't chdir back to $TEMPBASE $!"},
],
- \#\## The kavdaemon and AVPDaemonClient have been removed from Kasperky
- \#\## products and replaced by aveserver and aveclient
+ ### The kavdaemon and AVPDaemonClient have been removed from Kasperky
+ ### products and replaced by aveserver and aveclient
['KasperskyLab AVPDaemonClient',
[ '/opt/AVP/kavdaemon', 'kavdaemon',
'/opt/AVP/AvpDaemonClient', 'AvpDaemonClient',
'/opt/AVP/AvpTeamDream', 'AvpTeamDream',
'/opt/AVP/avpdc', 'avpdc' ],
- "-f=\$TEMPBASE {}", [0,8], [3,4,5,6], qr/infected: ([^\r\n]+)/m ],
+ "-f=$TEMPBASE {}", [0,8], [3,4,5,6], qr/infected: ([^\r\n]+)/m ],
# change the startup-script in /etc/init.d/kavd to:
# DPARMS="-* -Y -dl -f=/var/amavis /var/amavis"
# (or perhaps: DPARMS="-I0 -Y -* /var/amavis" )
- # adjusting /var/amavis above to match your \$TEMPBASE.
+ # adjusting /var/amavis above to match your $TEMPBASE.
# The '-f=/var/amavis' is needed if not running it as root, so it
# can find, read, and write its pid file, etc., see 'man kavdaemon'.
# defUnix.prf: there must be an entry "*/var/amavis" (or whatever
- # directory \$TEMPBASE specifies) in the 'Names=' section.
+ # directory $TEMPBASE specifies) in the 'Names=' section.
# cd /opt/AVP/DaemonClients; configure; cd Sample; make
# cp AvpDaemonClient /opt/AVP/
- # su - vscan -c "\${PREFIX}/kavdaemon \${DPARMS}"
+ # su - vscan -c "${PREFIX}/kavdaemon ${DPARMS}"
- \#\## http://www.centralcommand.com/
+ ### http://www.centralcommand.com/
['CentralCommand Vexira (new) vascan',
['vascan','/usr/lib/Vexira/vascan'],
- "-a s --timeout=60 --temp=\$TEMPBASE -y \$QUARANTINEDIR ".
+ "-a s --timeout=60 --temp=$TEMPBASE -y $QUARANTINEDIR ".
"--log=/var/log/vascan.log {}",
[0,3], [1,2,5],
qr/(?x)^\s* (?:virus|iworm|macro|mutant|sequence|trojan)\ found:\ ( [^\]\s']+ )\ \.\.\.\ /m ],
# Adjust the path of the binary and the virus database as needed.
# 'vascan' does not allow to have the temp directory to be the same as
# the quarantine directory, and the quarantine option can not be disabled.
- # If \$QUARANTINEDIR is not used, then another directory must be specified
+ # If $QUARANTINEDIR is not used, then another directory must be specified
# to appease 'vascan'. Move status 3 to the second list if password
# protected files are to be considered infected.
- \#\## http://www.avira.com/
- \#\## Avira AntiVir (formerly H+BEDV) or (old) CentralCommand Vexira Antivirus
+ ### http://www.avira.com/
+ ### Avira AntiVir (formerly H+BEDV) or (old) CentralCommand Vexira Antivirus
['Avira AntiVir', ['antivir','vexira'],
'--allfiles -noboot -nombr -rs -s -z {}', [0], qr/ALERT:|VIRUS:/m,
qr/(?x)^\s* (?: ALERT: \s* (?: \[ | [^']* ' ) |
@@ -503,32 +503,32 @@
# NOTE: if you only have a demo version, remove -z and add 214, as in:
# '--allfiles -noboot -nombr -rs -s {}', [0,214], qr/ALERT:|VIRUS:/,
- \#\## http://www.commandsoftware.com/
+ ### http://www.commandsoftware.com/
['Command AntiVirus for Linux', 'csav',
'-all -archive -packed {}', [50], [51,52,53],
qr/Infection: (.+)/m ],
- \#\## http://www.symantec.com/
+ ### http://www.symantec.com/
['Symantec CarrierScan via Symantec CommandLineScanner',
'cscmdline', '-a scan -i 1 -v -s 127.0.0.1:7777 {}',
- qr/^Files Infected:\s+0\$/m, qr/^Infected\b/m,
+ qr/^Files Infected:\s+0$/m, qr/^Infected\b/m,
qr/^(?:Info|Virus Name):\s+(.+)/m ],
- \#\## http://www.symantec.com/
+ ### http://www.symantec.com/
['Symantec AntiVirus Scan Engine',
'savsecls', '-server 127.0.0.1:7777 -mode scanrepair -details -verbose {}',
[0], qr/^Infected\b/m,
qr/^(?:Info|Virus Name):\s+(.+)/m ],
# NOTE: check options and patterns to see which entry better applies
-# \#\## http://www.f-secure.com/products/anti-virus/ version 4.65
+# ### http://www.f-secure.com/products/anti-virus/ version 4.65
# ['F-Secure Antivirus for Linux servers',
# ['/opt/f-secure/fsav/bin/fsav', 'fsav'],
# '--delete=no --disinf=no --rename=no --archive=yes --auto=yes '.
# '--dumb=yes --list=no --mime=yes {}', [0], [3,6,8],
# qr/(?:infection|Infected|Suspected): (.+)/m ],
- \#\## http://www.f-secure.com/products/anti-virus/ version 5.52
+ ### http://www.f-secure.com/products/anti-virus/ version 5.52
['F-Secure Antivirus for Linux servers',
['/opt/f-secure/fsav/bin/fsav', 'fsav'],
'--virus-action1=report --archive=yes --auto=yes '.
@@ -537,13 +537,13 @@
# NOTE: internal archive handling may be switched off by '--archive=no'
# to prevent fsav from exiting with status 9 on broken archives
-# \#\## http://www.avast.com/
+# ### http://www.avast.com/
# ['avast! Antivirus daemon',
# \&ask_daemon, # greets with 220, terminate with QUIT
# ["SCAN {}\015\012QUIT\015\012", '/var/run/avast4/mailscanner.sock'],
# qr/\t\[\+\]/m, qr/\t\[L\]\t/m, qr/\t\[L\]\t([^[ \t\015\012]+)/m ],
-# \#\## http://www.avast.com/
+# ### http://www.avast.com/
# ['avast! Antivirus - Client/Server Version', 'avastlite',
# '-a /var/run/avast4/mailscanner.sock -n {}', [0], [1],
# qr/\t\[L\]\t([^[ \t\015\012]+)/m ],
@@ -553,24 +553,24 @@
qr/was infected by virus (.+)/m ],
# see: http://www.flatmtn.com/computer/Linux-Antivirus_CAI.html
- \#\## http://www3.ca.com/Solutions/Product.asp?ID=156 (ex InoculateIT)
+ ### http://www3.ca.com/Solutions/Product.asp?ID=156 (ex InoculateIT)
['CAI eTrust Antivirus', 'etrust-wrapper',
'-arc -nex -spm h {}', [0], [101],
qr/is infected by virus: (.+)/m ],
# NOTE: requires suid wrapper around inocmd32; consider flag: -mod reviewer
# see http://marc.theaimsgroup.com/?l=amavis-user&m=109229779912783
- \#\## http://mks.com.pl/english.html
+ ### http://mks.com.pl/english.html
['MkS_Vir for Linux (beta)', ['mks32','mks'],
'-s {}/*', [0], [1,2],
qr/--[ \t]*(.+)/m ],
- \#\## http://mks.com.pl/english.html
+ ### http://mks.com.pl/english.html
['MkS_Vir daemon', 'mksscan',
'-s -q {}', [0], [1..7],
qr/^... (\S+)/m ],
-# \#\## http://www.nod32.com/, version v2.52 (old)
+# ### http://www.nod32.com/, version v2.52 (old)
# ['ESET NOD32 for Linux Mail servers',
# ['/opt/eset/nod32/bin/nod32cli', 'nod32cli'],
# '--subdir --files -z --sfx --rtp --adware --unsafe --pattern --heur '.
@@ -578,23 +578,23 @@
# '--action-on-notscanned=accept {}',
# [0,3], [1,2], qr/virus="([^"]+)"/m ],
-# \#\## http://www.eset.com/, version v2.7 (old)
+# ### http://www.eset.com/, version v2.7 (old)
# ['ESET NOD32 Linux Mail Server - command line interface',
# ['/usr/bin/nod32cli', '/opt/eset/nod32/bin/nod32cli', 'nod32cli'],
# '--subdir {}', [0,3], [1,2], qr/virus="([^"]+)"/m ],
-# \#\## http://www.eset.com/, version 2.71.12
+# ### http://www.eset.com/, version 2.71.12
# ['ESET Software ESETS Command Line Interface',
# ['/usr/bin/esets_cli', 'esets_cli'],
# '--subdir {}', [0], [1,2,3], qr/virus="([^"]+)"/m ],
- \#\## http://www.eset.com/, version 3.0
+ ### http://www.eset.com/, version 3.0
['ESET Software ESETS Command Line Interface',
['/usr/bin/esets_cli', 'esets_cli'],
'--subdir {}', [0], [1,2,3],
qr/:\s*action="(?!accepted)[^"]*"\n.*:\s*virus="([^"]*)"/m ],
- \#\# http://www.nod32.com/, NOD32LFS version 2.5 and above
+ ## http://www.nod32.com/, NOD32LFS version 2.5 and above
['ESET NOD32 for Linux File servers',
['/opt/eset/nod32/sbin/nod32','nod32'],
'--files -z --mail --sfx --rtp --adware --unsafe --pattern --heur '.
@@ -607,12 +607,12 @@
# ["SCAN {}/*\r\n", '127.0.0.1:8448' ],
# qr/^200 File OK/m, qr/^201 /m, qr/^201 (.+)/m ],
- \#\## http://www.norman.com/products_nvc.shtml
+ ### http://www.norman.com/products_nvc.shtml
['Norman Virus Control v5 / Linux', 'nvcc',
- '-c -l:0 -s -u -temp:\$TEMPBASE {}', [0,10,11], [1,2,14],
+ '-c -l:0 -s -u -temp:$TEMPBASE {}', [0,10,11], [1,2,14],
qr/(?i).* virus in .* -> \'(.+)\'/m ],
- \#\## http://www.pandasoftware.com/
+ ### http://www.pandasoftware.com/
['Panda CommandLineSecure 9 for Linux',
['/opt/pavcl/usr/bin/pavcl','pavcl'],
'-auto -aex -heu -cmp -nbr -nor -nos -eng -nob {}',
@@ -626,7 +626,7 @@
# Please review other options of pavcl, for example:
# -nomalw, -nojoke, -nodial, -nohackt, -nospyw, -nocookies
-# \#\## http://www.pandasoftware.com/
+# ### http://www.pandasoftware.com/
# ['Panda Antivirus for Linux', ['pavcl'],
# '-TSR -aut -aex -heu -cmp -nbr -nor -nso -eng {}',
# [0], [0x10, 0x30, 0x50, 0x70, 0x90, 0xB0, 0xD0, 0xF0],
@@ -639,15 +639,15 @@
# # NOTE: the command line switches changed with scan engine 8.5 !
# # (btw, assigning stdin to /dev/null causes RAV to fail)
- \#\## http://www.nai.com/
+ ### http://www.nai.com/
['NAI McAfee AntiVirus (uvscan)', 'uvscan',
'--secure -rv --mime --summary --noboot - {}', [0], [13],
qr/(?x) Found (?:
\ the\ (.+)\ (?:virus|trojan) |
\ (?:virus|trojan)\ or\ variant\ ([^ ]+) |
:\ (.+)\ NOT\ a\ virus)/m,
- # sub {\$ENV{LD_PRELOAD}='/lib/libc.so.6'},
- # sub {delete \$ENV{LD_PRELOAD}},
+ # sub {$ENV{LD_PRELOAD}='/lib/libc.so.6'},
+ # sub {delete $ENV{LD_PRELOAD}},
],
# NOTE1: with RH9: force the dynamic linker to look at /lib/libc.so.6 before
# anything else by setting environment variable LD_PRELOAD=/lib/libc.so.6
@@ -655,72 +655,72 @@
# NOTE2: to treat encrypted files as viruses replace the [13] with:
# qr/^\s{5,}(Found|is password-protected|.*(virus|trojan))/
- \#\## http://www.virusbuster.hu/en/
+ ### http://www.virusbuster.hu/en/
['VirusBuster', ['vbuster', 'vbengcl'],
- "{} -ss -i '*' -log=\$MYHOME/vbuster.log", [0], [1],
+ "{} -ss -i '*' -log=$MYHOME/vbuster.log", [0], [1],
qr/: '(.*)' - Virus/m ],
# VirusBuster Ltd. does not support the daemon version for the workstation
# engine (vbuster-eng-1.12-linux-i386-libc6.tgz) any longer. The names of
# binaries, some parameters AND return codes have changed (from 3 to 1).
# See also the new Vexira entry 'vascan' which is possibly related.
-# \#\## http://www.virusbuster.hu/en/
+# ### http://www.virusbuster.hu/en/
# ['VirusBuster (Client + Daemon)', 'vbengd',
# '-f -log scandir {}', [0], [3],
# qr/Virus found = (.*);/m ],
# # HINT: for an infected file it always returns 3,
# # although the man-page tells a different story
- \#\## http://www.cyber.com/
+ ### http://www.cyber.com/
['CyberSoft VFind', 'vfind',
- '--vexit {}/*', [0], [23], qr/\#\#==>>>> VIRUS ID: CVDL (.+)/m,
- # sub {\$ENV{VSTK_HOME}='/usr/lib/vstk'},
+ '--vexit {}/*', [0], [23], qr/##==>>>> VIRUS ID: CVDL (.+)/m,
+ # sub {$ENV{VSTK_HOME}='/usr/lib/vstk'},
],
- \#\## http://www.avast.com/
+ ### http://www.avast.com/
['avast! Antivirus', ['/usr/bin/avastcmd','avastcmd'],
'-a -i -n -t=A {}', [0], [1], qr/\binfected by:\s+([^ \t\n\[\]]+)/m ],
- \#\## http://www.ikarus-software.com/
+ ### http://www.ikarus-software.com/
['Ikarus AntiVirus for Linux', 'ikarus',
'{}', [0], [40], qr/Signature (.+) found/m ],
- \#\## http://www.bitdefender.com/
+ ### http://www.bitdefender.com/
['BitDefender', 'bdscan', # new version
'--action=ignore --no-list {}', qr/^Infected files\s*:\s*0+(?!\d)/m,
qr/^(?:Infected files|Identified viruses|Suspect files)\s*:\s*0*[1-9]/m,
- qr/(?:suspected|infected)\s*:\s*(.*)(?:\033|\$)/m ],
+ qr/(?:suspected|infected)\s*:\s*(.*)(?:\033|$)/m ],
- \#\## http://www.bitdefender.com/
+ ### http://www.bitdefender.com/
['BitDefender', 'bdc', # old version
'--arc --mail {}', qr/^Infected files *:0+(?!\d)/m,
qr/^(?:Infected files|Identified viruses|Suspect files) *:0*[1-9]/m,
- qr/(?:suspected|infected): (.*)(?:\033|\$)/m ],
+ qr/(?:suspected|infected): (.*)(?:\033|$)/m ],
# consider also: --all --nowarn --alev=15 --flev=15. The --all argument may
# not apply to your version of bdc, check documentation and see 'bdc --help'
- \#\## ArcaVir for Linux and Unix http://www.arcabit.pl/
+ ### ArcaVir for Linux and Unix http://www.arcabit.pl/
['ArcaVir for Linux', ['arcacmd','arcacmd.static'],
'-v 1 -summary 0 -s {}', [0], [1,2],
qr/(?:VIR|WIR):[ \t]*(.+)/m ],
-# \#\## a generic SMTP-client interface to a SMTP-based virus scanner
+# ### a generic SMTP-client interface to a SMTP-based virus scanner
# ['av_smtp', \&ask_av_smtp,
# ['{}', 'smtp:[127.0.0.1]:5525', 'dummy@localhost'],
-# qr/^2/, qr/^5/, qr/^\s*(.*?)\s*\$/m ],
+# qr/^2/, qr/^5/, qr/^\s*(.*?)\s*$/m ],
# ['File::Scan', sub {Amavis::AV::ask_av(sub{
-# use File::Scan; my(\$fn)=@_;
-# my(\$f)=File::Scan->new(max_txt_size=>0, max_bin_size=>0);
-# my(\$vname) = \$f->scan(\$fn);
-# \$f->error ? (2,"Error: ".\$f->error)
-# : (\$vname ne '') ? (1,"\$vname FOUND") : (0,"Clean")}, @_) },
-# ["{}/*"], [0], [1], qr/^(.*) FOUND\$/m ],
-
-# \#\## fully-fledged checker for JPEG marker segments of invalid length
+# use File::Scan; my($fn)=@_;
+# my($f)=File::Scan->new(max_txt_size=>0, max_bin_size=>0);
+# my($vname) = $f->scan($fn);
+# $f->error ? (2,"Error: ".$f->error)
+# : ($vname ne '') ? (1,"$vname FOUND") : (0,"Clean")}, @_) },
+# ["{}/*"], [0], [1], qr/^(.*) FOUND$/m ],
+
+# ### fully-fledged checker for JPEG marker segments of invalid length
# ['check-jpeg',
# sub { use JpegTester (); Amavis::AV::ask_av(\&JpegTester::test_jpeg, @_) },
-# ["{}/*"], undef, [1], qr/^(bad jpeg: .*)\$/m ],
+# ["{}/*"], undef, [1], qr/^(bad jpeg: .*)$/m ],
# # NOTE: place file JpegTester.pm somewhere where Perl can find it,
# # for example in /usr/local/lib/perl5/site_perl
@@ -729,48 +729,48 @@
@av_scanners_backup = (
- \#\## http://www.clamav.net/ - backs up clamd or Mail::ClamAV
+ ### http://www.clamav.net/ - backs up clamd or Mail::ClamAV
['ClamAV-clamscan', 'clamscan',
- "--stdout --no-summary -r --tempdir=\$TEMPBASE {}",
- [0], qr/:.*\sFOUND\$/m, qr/^.*?: (?!Infected Archive)(.*) FOUND\$/m ],
+ "--stdout --no-summary -r --tempdir=$TEMPBASE {}",
+ [0], qr/:.*\sFOUND$/m, qr/^.*?: (?!Infected Archive)(.*) FOUND$/m ],
- \#\## http://www.f-prot.com/ - backs up F-Prot Daemon, V6
+ ### http://www.f-prot.com/ - backs up F-Prot Daemon, V6
['F-PROT Antivirus for UNIX', ['fpscan'],
'--report --mount --adware {}', # consider: --applications -s 4 -u 3 -z 10
[0,8,64], [1,2,3, 4+1,4+2,4+3, 8+1,8+2,8+3, 12+1,12+2,12+3],
qr/^\[Found\s+[^\]]*\]\s+<([^ \t(>]*)/m ],
- \#\## http://www.f-prot.com/ - backs up F-Prot Daemon (old)
+ ### http://www.f-prot.com/ - backs up F-Prot Daemon (old)
['FRISK F-Prot Antivirus', ['f-prot','f-prot.sh'],
'-dumb -archive -packed {}', [0,8], [3,6], # or: [0], [3,6,8],
- qr/(?:Infection:|security risk named) (.+)|\s+contains\s+(.+)\$/m ],
+ qr/(?:Infection:|security risk named) (.+)|\s+contains\s+(.+)$/m ],
- \#\## http://www.trendmicro.com/ - backs up Trophie
+ ### http://www.trendmicro.com/ - backs up Trophie
['Trend Micro FileScanner', ['/etc/iscan/vscan','vscan'],
'-za -a {}', [0], qr/Found virus/m, qr/Found virus (.+) in/m ],
- \#\## http://www.sald.com/, http://drweb.imshop.de/ - backs up DrWebD
+ ### http://www.sald.com/, http://drweb.imshop.de/ - backs up DrWebD
['drweb - DrWeb Antivirus', # security LHA hole in Dr.Web 4.33 and earlier
['/usr/local/drweb/drweb', '/opt/drweb/drweb', 'drweb'],
'-path={} -al -go -ot -cn -upn -ok-',
- [0,32], [1,9,33], qr' infected (?:with|by)(?: virus)? (.*)\$'m ],
+ [0,32], [1,9,33], qr' infected (?:with|by)(?: virus)? (.*)$'m ],
- \#\## http://www.kaspersky.com/
+ ### http://www.kaspersky.com/
['Kaspersky Antivirus v5.5',
['/opt/kaspersky/kav4fs/bin/kav4fs-kavscanner',
'/opt/kav/5.5/kav4unix/bin/kavscanner',
'/opt/kav/5.5/kav4mailservers/bin/kavscanner', 'kavscanner'],
'-i0 -xn -xp -mn -R -ePASBME {}/*', [0,10,15], [5,20,21,25],
qr/(?:INFECTED|WARNING|SUSPICION|SUSPICIOUS) (.*)/m,
-# sub {chdir('/opt/kav/bin') or die "Can't chdir to kav: \$!"},
-# sub {chdir(\$TEMPBASE) or die "Can't chdir back to \$TEMPBASE \$!"},
+# sub {chdir('/opt/kav/bin') or die "Can't chdir to kav: $!"},
+# sub {chdir($TEMPBASE) or die "Can't chdir back to $TEMPBASE $!"},
],
# Commented out because the name 'sweep' clashes with Debian and FreeBSD
# package/port of an audio editor. Make sure the correct 'sweep' is found
# in the path when enabling.
#
-# \#\## http://www.sophos.com/ - backs up Sophie or SAVI-Perl
+# ### http://www.sophos.com/ - backs up Sophie or SAVI-Perl
# ['Sophos Anti Virus (sweep)', 'sweep',
# '-nb -f -all -rec -ss -sc -archive -cab -mime -oe -tnef '.
# '--no-reset-atime {}',
diff --git a/share/templates/amavisd.conf.tpl b/share/templates/amavisd.conf.tpl
--- a/share/templates/amavisd.conf.tpl
+++ b/share/templates/amavisd.conf.tpl
@@ -18,7 +18,7 @@
\$daemon_user = 'amavis'; # (no default; customary: vscan or amavis), -u
\$daemon_group = 'amavis'; # (no default; customary: vscan or amavis), -g
-\$mydomain = '$primary_domain'; # a convenient default for other settings
+\$mydomain = '{{ kolab.primary_domain }}'; # a convenient default for other settings
\$MYHOME = '/var/spool/amavisd'; # a convenient default for other settings, -H
\$TEMPBASE = "\$MYHOME/tmp"; # working directory, needs to exist, -T
@@ -373,7 +373,7 @@
# \#\## http://www.clamav.net/
['ClamAV-clamd',
- \&ask_daemon, ["CONTSCAN {}\n", "$clamdsock"],
+ \&ask_daemon, ["CONTSCAN {}\n", "{{ clamdsock }}"],
qr/\bOK\$/m, qr/\bFOUND\$/m,
qr/^.*?: (?!Infected Archive)(.*) FOUND\$/m ],
# # NOTE: run clamd under the same user as amavisd, or run it under its own
diff --git a/share/templates/freshclam.conf.tpl b/share/templates/freshclam.conf.tpl
--- a/share/templates/freshclam.conf.tpl
+++ b/share/templates/freshclam.conf.tpl
@@ -10,7 +10,7 @@
# Path to the log file (make sure it has proper permissions)
# Default: disabled
-UpdateLogFile /var/log/clamav/freshclam.log
+UpdateLogFile /var/log/freshclam.log
# Maximum size of the log file.
# Value of 0 disables the limit.
diff --git a/share/templates/master.cf.j2 b/share/templates/master.cf.j2
new file mode 100644
--- /dev/null
+++ b/share/templates/master.cf.j2
@@ -0,0 +1,122 @@
+# Postfix master process configuration file. For details on the format
+# of the file, see the master(5) manual page (command: "man 5 master").
+# Do not forget to execute "postfix reload" after editing this file.
+# ==============================================================================
+# service type private unpriv chroot wakeup maxproc command
+# (yes) (yes) (yes) (never) (100) + args
+# ==============================================================================
+smtp inet n - n - - smtpd
+#smtp inet n - n - 1 postscreen
+#smtpd pass - - n - - smtpd
+#dnsblog unix - - n - 0 dnsblog
+#tlsproxy unix - - n - 0 tlsproxy
+submission inet n - n - - smtpd
+ -o cleanup_service_name=cleanup_submission
+ -o syslog_name=postfix/submission
+ -o smtpd_tls_security_level=encrypt
+ -o smtpd_sasl_auth_enable=yes
+ -o smtpd_sasl_authenticated_header=yes
+ -o smtpd_client_restrictions=permit_sasl_authenticated,reject
+ -o smtpd_data_restrictions=$submission_data_restrictions
+ -o smtpd_recipient_restrictions=$submission_recipient_restrictions
+ -o smtpd_sender_restrictions=$submission_sender_restrictions
+
+#smtps inet n - n - - smtpd
+# -o syslog_name=postfix/smtps
+# -o smtpd_tls_wrappermode=yes
+# -o smtpd_sasl_auth_enable=yes
+# -o smtpd_client_restrictions=permit_sasl_authenticated,reject
+# -o milter_macro_daemon_name=ORIGINATING
+#628 inet n - n - - qmqpd
+pickup fifo n - n 60 1 pickup
+cleanup unix n - n - 0 cleanup
+ -o header_checks=regexp:/etc/postfix/header_checks.inbound
+ -o mime_header_checks=regexp:/etc/postfix/header_checks.inbound
+cleanup_internal unix n - n - 0 cleanup
+ -o header_checks=regexp:/etc/postfix/header_checks.internal
+ -o mime_header_checks=regexp:/etc/postfix/header_checks.internal
+cleanup_submission unix n - n - 0 cleanup
+ -o header_checks=regexp:/etc/postfix/header_checks.submission
+ -o mime_header_checks=regexp:/etc/postfix/header_checks.submission
+qmgr fifo n - n 300 1 qmgr
+#qmgr fifo n - n 300 1 oqmgr
+tlsmgr unix - - n 1000? 1 tlsmgr
+rewrite unix - - n - - trivial-rewrite
+bounce unix - - n - 0 bounce
+defer unix - - n - 0 bounce
+trace unix - - n - 0 bounce
+verify unix - - n - 1 verify
+flush unix n - n 1000? 0 flush
+proxymap unix - - n - - proxymap
+proxywrite unix - - n - 1 proxymap
+smtp unix - - n - - smtp
+relay unix - - n - - smtp
+showq unix n - n - - showq
+error unix - - n - - error
+retry unix - - n - - error
+discard unix - - n - - discard
+local unix - n n - - local
+virtual unix - n n - - virtual
+lmtp unix - - n - - lmtp
+anvil unix - - n - 1 anvil
+scache unix - - n - 1 scache
+
+# Filter email through Amavisd
+smtp-amavis unix - - n - 3 smtp
+ -o smtp_data_done_timeout=1800
+ -o disable_dns_lookups=yes
+ -o smtp_send_xforward_command=yes
+ -o max_use=20
+ -o smtp_bind_address=127.0.0.1
+
+# Listener to re-inject email from Amavisd into Postfix
+127.0.0.1:10025 inet n - n - 100 smtpd
+ -o cleanup_service_name=cleanup_internal
+ -o content_filter=smtp-wallace:[127.0.0.1]:10026
+ -o local_recipient_maps=
+ -o relay_recipient_maps=
+ -o smtpd_restriction_classes=
+ -o smtpd_client_restrictions=
+ -o smtpd_helo_restrictions=
+ -o smtpd_sender_restrictions=
+ -o smtpd_recipient_restrictions=permit_mynetworks,reject
+ -o mynetworks=127.0.0.0/8
+ -o smtpd_authorized_xforward_hosts=127.0.0.0/8
+
+# Filter email through Wallace
+smtp-wallace unix - - n - 3 smtp
+ -o default_destination_recipient_limit=1
+ -o smtp_data_done_timeout=1800
+ -o disable_dns_lookups=yes
+ -o smtp_send_xforward_command=yes
+ -o max_use=20
+
+# Listener to re-inject email from Wallace into Postfix
+127.0.0.1:10027 inet n - n - 100 smtpd
+ -o cleanup_service_name=cleanup_internal
+ -o content_filter=
+ -o local_recipient_maps=
+ -o relay_recipient_maps=
+ -o smtpd_restriction_classes=
+ -o smtpd_client_restrictions=
+ -o smtpd_helo_restrictions=
+ -o smtpd_sender_restrictions=
+ -o smtpd_recipient_restrictions=permit_mynetworks,reject
+ -o mynetworks=127.0.0.0/8
+ -o smtpd_authorized_xforward_hosts=127.0.0.0/8
+
+recipient_policy unix - n n - - spawn
+ user=kolab-n argv={{ kolab_sap_executable_path }} --verify-recipient
+
+recipient_policy_incoming unix - n n - - spawn
+ user=kolab-n argv={{ kolab_sap_executable_path }} --verify-recipient --allow-unauthenticated
+
+sender_policy unix - n n - - spawn
+ user=kolab-n argv={{ kolab_sap_executable_path }} --verify-sender
+
+sender_policy_incoming unix - n n - - spawn
+ user=kolab-n argv={{ kolab_sap_executable_path }} --verify-sender --allow-unauthenticated
+
+submission_policy unix - n n - - spawn
+ user=kolab-n argv={{ kolab_sap_executable_path }} --verify-sender --verify-recipient
+
diff --git a/templates/kolab-ad-domain.ldif.j2 b/templates/kolab-ad-domain.ldif.j2
new file mode 100644
--- /dev/null
+++ b/templates/kolab-ad-domain.ldif.j2
@@ -0,0 +1,13 @@
+dn: CN={{ domain }},{{ domain_base_dn }}
+changetype: add
+objectClass: top
+objectClass: domainRelatedObject
+objectClass: document
+cn: {{ domain }}
+distinguishedName: CN={{ domain }},{{ domain_base_dn }}
+instanceType: 4
+showInAdvancedViewOnly: TRUE
+name: {{ domain }}
+objectCategory: CN=document,CN=Schema,CN=Configuration,{{ base_dn }}
+associatedDomain: {{ domain }}
+
diff --git a/templates/kolab-ad-special-users.ldif.j2 b/templates/kolab-ad-special-users.ldif.j2
new file mode 100644
--- /dev/null
+++ b/templates/kolab-ad-special-users.ldif.j2
@@ -0,0 +1,50 @@
+dn: {{ cyrus_admin_dn }}
+changetype: add
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: user
+objectClass: inetOrgPerson
+cn: Cyrus Admin
+sn: Administrator
+description: User for Cyrus IMAP to connect to LDAP
+givenName: Cyrus
+distinguishedName: {{ cyrus_admin_dn }}
+displayName: Cyrus Administrator
+name: Cyrus Admin
+codePage: 0
+countryCode: 0
+sAMAccountName: cyrus-admin
+userPrincipalName: cyrus-admin@{{ domain }}
+objectCategory: CN=Person,CN=Schema,CN=Configuration,{{ base_dn }}
+uid: cyrus-admin
+{%- if ad_ldaps %}
+unicodePwd:: {{ cyrus_admin_password_base64 }}
+userAccountControl: 66048
+{% endif %}
+
+dn: {{ service_bind_dn }}
+changetype: add
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: user
+objectClass: inetOrgPerson
+cn: Kolab Service
+sn: Service
+description: User for Kolab to connect to LDAP
+givenName: Kolab
+distinguishedName: {{ service_bind_dn }}
+displayName: Kolab Service
+name: Kolab Service
+codePage: 0
+countryCode: 0
+sAMAccountName: kolab-service
+userPrincipalName: kolab-service@{{ domain }}
+objectCategory: CN=Person,CN=Schema,CN=Configuration,{{ base_dn }}
+uid: kolab-service
+{%- if ad_ldaps %}
+unicodePwd:: {{ service_bind_pw_base64 }}
+userAccountControl: 66048
+{% endif %}
+
diff --git a/templates/kolab-ad-tree.ldif.j2 b/templates/kolab-ad-tree.ldif.j2
new file mode 100644
--- /dev/null
+++ b/templates/kolab-ad-tree.ldif.j2
@@ -0,0 +1,66 @@
+dn: {{ kolab_tree_dn }}
+changetype: add
+objectClass: top
+objectClass: organizationalUnit
+ou: Kolab
+description: LDAP tree part for Kolab related data
+distinguishedName: {{ kolab_tree_dn }}
+instanceType: 4
+name: Kolab
+objectCategory: CN=Organizational-Unit,CN=Schema,CN=Configuration,{{ base_dn }}
+
+dn: {{ domain_base_dn }}
+changetype: add
+objectClass: top
+objectClass: organizationalUnit
+ou: Domains
+description: Kolab hosted domains
+distinguishedName: {{ domain_base_dn }}
+instanceType: 4
+name: Domains
+objectCategory: CN=Organizational-Unit,CN=Schema,CN=Configuration,{{ base_dn }}
+
+dn: {{ group_base_dn }}
+changetype: add
+objectClass: top
+objectClass: organizationalUnit
+ou: Groups
+description: Kolab groups
+distinguishedName: {{ group_base_dn }}
+instanceType: 4
+name: Groups
+objectCategory: CN=Organizational-Unit,CN=Schema,CN=Configuration,{{ base_dn }}
+
+dn: {{ resource_base_dn }}
+changetype: add
+objectClass: top
+objectClass: organizationalUnit
+ou: Resources
+description: Kolab resources
+distinguishedName: {{ resource_base_dn }}
+instanceType: 4
+name: Resources
+objectCategory: CN=Organizational-Unit,CN=Schema,CN=Configuration,{{ base_dn }}
+
+dn: {{ sharedfolder_base_dn }}
+changetype: add
+objectClass: top
+objectClass: organizationalUnit
+ou: Shared Folders
+description: Kolab shared folders
+distinguishedName: {{ sharedfolder_base_dn }}
+instanceType: 4
+name: Shared Folders
+objectCategory: CN=Organizational-Unit,CN=Schema,CN=Configuration,{{ base_dn }}
+
+dn: {{ specialuser_base_dn }}
+changetype: add
+objectClass: top
+objectClass: organizationalUnit
+ou: Special Users
+description: Kolab special users
+distinguishedName: ${{ specialuser_base_dn }}
+instanceType: 4
+name: Special Users
+objectCategory: CN=Organizational-Unit,CN=Schema,CN=Configuration,{{ base_dn }}
+
diff --git a/templates/kolab-ds-setup.inf.j2 b/templates/kolab-ds-setup.inf.j2
new file mode 100644
--- /dev/null
+++ b/templates/kolab-ds-setup.inf.j2
@@ -0,0 +1,24 @@
+[General]
+FullMachineName = {{ fqdn }}
+SuiteSpotUserID = {{ userid }}
+SuiteSpotGroup = {{ group }}
+AdminDomain = {{ domain }}
+ConfigDirectoryLdapURL = ldap://{{ fqdn }}:389/o=NetscapeRoot
+ConfigDirectoryAdminID = admin
+ConfigDirectoryAdminPwd = {{ admin_pass }}
+
+[slapd]
+SlapdConfigForMC = Yes
+UseExistingMC = 0
+ServerPort = 389
+ServerIdentifier = {{ hostname }}
+Suffix = {{ base_dn }}
+RootDN = cn=Directory Manager
+RootDNPwd = {{ bind_pw }}
+ds_bename = {{ nodotdomain }}
+AddSampleEntries = No
+
+[admin]
+Port = 9830
+ServerAdminID = admin
+ServerAdminPwd = {{ admin_pass }}
diff --git a/templates/kolab3-ad-schema.ldif.j2 b/templates/kolab3-ad-schema.ldif.j2
new file mode 100644
--- /dev/null
+++ b/templates/kolab3-ad-schema.ldif.j2
@@ -0,0 +1,440 @@
+dn: CN=kolabAlias,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+cn: kolabAlias
+distinguishedName: CN=kolabAlias,{{ ad_schema_naming_context }}
+instanceType: 4
+attributeID: 1.3.6.1.4.1.19414.2.1.3
+attributeSyntax: 2.5.5.5
+isSingleValued: FALSE
+rangeLower: 0
+rangeUpper: 256
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabAlias
+adminDescription: RFC1274: RFC822 Mailbox
+oMSyntax: 22
+lDAPDisplayName: kolabAlias
+name: kolabAlias
+objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }}
+
+dn: CN=kolabDelegate,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+cn: kolabDelegate
+distinguishedName: CN=kolabDelegate,{{ ad_schema_naming_context }}
+instanceType: 4
+attributeID: 1.3.6.1.4.1.19414.1.1.1.3
+attributeSyntax: 2.5.5.5
+isSingleValued: FALSE
+rangeUpper: 256
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabDelegate
+adminDescription:
+ Kolab user allowed to act as delegates - RFC822 Mailbox/Alias
+oMSyntax: 22
+lDAPDisplayName: kolabDelegate
+name: kolabDelegate
+objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }}
+
+dn: CN=kolabInvitationPolicy,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+cn: kolabInvitationPolicy
+distinguishedName:
+ CN=kolabInvitationPolicy,{{ ad_schema_naming_context }}
+instanceType: 4
+attributeID: 1.3.6.1.4.1.19414.1.1.1.4
+attributeSyntax: 2.5.5.5
+isSingleValued: FALSE
+rangeUpper: 256
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabInvitationPolicy
+adminDescription: Defines how to respond to invitations
+oMSyntax: 22
+lDAPDisplayName: kolabInvitationPolicy
+name: kolabInvitationPolicy
+objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }}
+
+dn: CN=kolabVacationBeginDateTime,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+cn: kolabVacationBeginDateTime
+distinguishedName:
+ CN=kolabVacationBeginDateTime,{{ ad_schema_naming_context }}
+instanceType: 4
+attributeID: 1.3.6.1.4.1.19414.1.1.1.8
+attributeSyntax: 2.5.5.11
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabVacationBeginDateTime
+adminDescription: Begin date of vacation
+oMSyntax: 24
+lDAPDisplayName: kolabVacationBeginDateTime
+name: kolabVacationBeginDateTime
+objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }}
+
+dn: CN=kolabVacationEndDateTime,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+cn: kolabVacationEndDateTime
+distinguishedName:
+ CN=kolabVacationEndDateTime,{{ ad_schema_naming_context }}
+instanceType: 4
+attributeID: 1.3.6.1.4.1.19414.1.1.1.9
+attributeSyntax: 2.5.5.11
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabVacationEndDateTime
+adminDescription: Specifies the end of vacation time
+oMSyntax: 24
+lDAPDisplayName: kolabVacationEndDateTime
+name: kolabVacationEndDateTime
+objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }}
+
+dn: CN=kolabVacationResendInterval,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+cn: kolabVacationResendInterval
+distinguishedName:
+ CN=kolabVacationResendInterval,{{ ad_schema_naming_context }}
+instanceType: 4
+attributeID: 1.3.6.1.4.1.19414.1.1.1.10
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabVacationResendInterval
+adminDescription: Vacation notice interval in days
+oMSyntax: 2
+lDAPDisplayName: kolabVacationResendInterval
+name: kolabVacationResendInterval
+objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }}
+
+dn: CN=kolabVacationAddress,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+cn: kolabVacationAddress
+distinguishedName:
+ CN=kolabVacationAddress,{{ ad_schema_naming_context }}
+instanceType: 4
+attributeID: 1.3.6.1.4.1.19414.1.1.1.11
+attributeSyntax: 2.5.5.5
+isSingleValued: TRUE
+rangeUpper: 256
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabVacationAddress
+adminDescription: Email address for vacation to response upon
+oMSyntax: 22
+lDAPDisplayName: kolabVacationAddress
+name: kolabVacationAddress
+objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }}
+
+dn: CN=kolabForwardUCE,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+cn: kolabForwardUCE
+distinguishedName: CN=kolabForwardUCE,{{ ad_schema_naming_context }}
+instanceType: 4
+attributeID: 1.3.6.1.4.1.19414.1.1.1.16
+attributeSyntax: 2.5.5.8
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabForwardUCE
+adminDescription: Enable forwarding of mails known as UCE
+oMSyntax: 1
+lDAPDisplayName: kolabForwardUCE
+name: kolabForwardUCE
+objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }}
+
+dn: CN=kolabAllowSMTPRecipient,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+cn: kolabAllowSMTPRecipient
+distinguishedName:
+ CN=kolabAllowSMTPRecipient,{{ ad_schema_naming_context }}
+instanceType: 4
+attributeID: 1.3.6.1.4.1.19414.1.1.1.18
+attributeSyntax: 2.5.5.5
+isSingleValued: FALSE
+rangeUpper: 512
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabAllowSMTPRecipient
+adminDescription: SMTP address allowed for destination
+oMSyntax: 22
+lDAPDisplayName: kolabAllowSMTPRecipient
+name: kolabAllowSMTPRecipient
+objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }}
+
+dn: CN=kolabAllowSMTPSender,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+cn: kolabAllowSMTPSender
+distinguishedName:
+ CN=kolabAllowSMTPSender,{{ ad_schema_naming_context }}
+instanceType: 4
+attributeID: 1.3.6.1.4.1.19414.1.1.1.43
+attributeSyntax: 2.5.5.5
+isSingleValued: FALSE
+rangeUpper: 512
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabAllowSMTPSender
+adminDescription: SMTP envelope sender address accepted for delivery
+oMSyntax: 22
+lDAPDisplayName: kolabAllowSMTPSender
+name: kolabAllowSMTPSender
+objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }}
+
+dn: CN=kolabFolderType,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+cn: kolabFolderType
+distinguishedName: CN=kolabFolderType,{{ ad_schema_naming_context }}
+instanceType: 4
+attributeID: 1.3.6.1.4.1.19414.2.1.7
+attributeSyntax: 2.5.5.5
+isSingleValued: TRUE
+rangeUpper: 256
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabFolderType
+adminDescription: Type of a kolab folder
+oMSyntax: 22
+lDAPDisplayName: kolabFolderType
+name: kolabFolderType
+objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }}
+
+dn: CN=kolabTargetFolder,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+cn: kolabTargetFolder
+distinguishedName:
+ CN=kolabTargetFolder,{{ ad_schema_naming_context }}
+instanceType: 4
+attributeID: 1.3.6.1.4.1.19414.2.1.8
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeUpper: 512
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabTargetFolder
+adminDescription: Target for a Kolab Shared Folder delivery
+oMSyntax: 64
+lDAPDisplayName: kolabTargetFolder
+name: kolabTargetFolder
+objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }}
+
+dn: CN=kolabImapACL,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+cn: kolabImapACL
+distinguishedName: CN=kolabImapACL,{{ ad_schema_naming_context }}
+instanceType: 4
+attributeID: 1.3.6.1.4.1.19414.2.1.651
+attributeSyntax: 2.5.5.5
+isSingleValued: FALSE
+rangeUpper: 256
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabImapACL
+adminDescription: Cyrus IMAP access control lists
+oMSyntax: 22
+lDAPDisplayName: kolabImapACL
+name: kolabImapACL
+objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }}
+
+dn: CN=kolabDescAttribute,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+cn: kolabDescAttribute
+distinguishedName:
+ CN=kolabDescAttribute,{{ ad_schema_naming_context }}
+instanceType: 4
+attributeID: 1.3.6.1.4.1.19414.3.1.1
+attributeSyntax: 2.5.5.5
+isSingleValued: TRUE
+rangeUpper: 256
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabDescAttribute
+adminDescription: Descriptive attribute or parameter for a Resource
+oMSyntax: 22
+lDAPDisplayName: kolabDescAttribute
+name: kolabDescAttribute
+objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }}
+
+dn: CN=kolabMailHost,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+cn: kolabMailHost
+distinguishedName: CN=kolabMailHost,{{ ad_schema_naming_context }}
+instanceType: 4
+attributeID: 1.3.6.1.4.1.19414.3.1.2
+attributeSyntax: 2.5.5.4
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabMailHost
+adminDescription::
+ TWFpbCBob3N0IHdoZXJlIEtvbGFiIHVzZXIsIHNoYXJlZCBmb2xkZXIgLyByZXNvdXJjZSByZXNpZG
+ VzIG9uIA==
+oMSyntax: 20
+lDAPDisplayName: kolabMailHost
+name: kolabMailHost
+objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }}
+
+dn: CN=kolabMailQuota,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+cn: kolabMailQuota
+distinguishedName: CN=kolabMailQuota,{{ ad_schema_naming_context }}
+instanceType: 4
+attributeID: 1.3.6.1.4.1.19414.3.1.3
+attributeSyntax: 2.5.5.4
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabMailQuota
+adminDescription: Kolab mailbox quota limit.
+oMSyntax: 20
+lDAPDisplayName: kolabMailQuota
+name: kolabMailQuota
+objectCategory: CN=Attribute-Schema,{{ ad_schema_naming_context }}
+
+dn: CN=kolabSharedFolder,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: classSchema
+cn: kolabSharedFolder
+distinguishedName:
+ CN=kolabSharedFolder,{{ ad_schema_naming_context }}
+instanceType: 4
+subClassOf: top
+governsID: 1.3.6.1.4.1.19414.2.2.9
+mustContain: cn
+mayContain: owner
+mayContain: kolabImapAcl
+mayContain: kolabAlias
+mayContain: kolabAllowSMTPRecipient
+mayContain: kolabAllowSMTPSender
+mayContain: kolabDelegate
+mayContain: kolabFolderType
+mayContain: kolabMailHost
+mayContain: kolabTargetFolder
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabSharedFolder
+adminDescription: Kolab public shared folder
+objectClassCategory: 3
+lDAPDisplayName: kolabSharedFolder
+name: kolabSharedFolder
+systemOnly: FALSE
+defaultSecurityDescriptor:
+ D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)
+ (A;;RPLCLORC;;;AU)
+objectCategory: CN=Class-Schema,{{ ad_schema_naming_context }}
+defaultObjectCategory:
+ CN=kolabSharedFolder,{{ ad_schema_naming_context }}
+
+dn: CN=kolabInetOrgPerson,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: classSchema
+cn: kolabInetOrgPerson
+distinguishedName:
+ CN=kolabInetOrgPerson,{{ ad_schema_naming_context }}
+instanceType: 4
+subClassOf: top
+governsID: 1.3.6.1.4.1.19414.3.2.2
+mayContain: kolabAlias
+mayContain: kolabAllowSMTPRecipient
+mayContain: kolabAllowSMTPSender
+mayContain: kolabDelegate
+mayContain: kolabForwardUCE
+mayContain: kolabInvitationPolicy
+mayContain: kolabMailHost
+mayContain: kolabMailQuota
+mayContain: kolabVacationAddress
+mayContain: kolabVacationBeginDateTime
+mayContain: kolabVacationEndDateTime
+mayContain: kolabVacationResendInterval
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabInetOrgPerson
+adminDescription: Kolab Internet Organizational Person
+objectClassCategory: 3
+lDAPDisplayName: kolabInetOrgPerson
+name: kolabInetOrgPerson
+systemOnly: FALSE
+defaultSecurityDescriptor:
+ D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)
+ (A;;RPLCLORC;;;AU)
+objectCategory: CN=Class-Schema,{{ ad_schema_naming_context }}
+defaultObjectCategory:
+ CN=kolabInetOrgPerson,{{ ad_schema_naming_context }}
+
+dn: CN=kolabGroupOfUniqueNames,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: classSchema
+cn: kolabGroupOfUniqueNames
+distinguishedName:
+ CN=kolabGroupOfUniqueNames,{{ ad_schema_naming_context }}
+instanceType: 4
+subClassOf: top
+governsID: 1.3.6.1.4.1.19414.3.2.8
+mayContain: kolabAlias
+mayContain: kolabAllowSMTPRecipient
+mayContain: kolabAllowSMTPSender
+mayContain: kolabDelegate
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabGroupOfUniqueNames
+adminDescription: Kolab group of names (DNs) derived from RFC2256
+objectClassCategory: 3
+lDAPDisplayName: kolabGroupOfUniqueNames
+name: kolabGroupOfUniqueNames
+systemOnly: FALSE
+defaultSecurityDescriptor:
+ D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)
+ (A;;RPLCLORC;;;AU)
+objectCategory: CN=Class-Schema,{{ ad_schema_naming_context }}
+defaultObjectCategory:
+ CN=kolabGroupOfUniqueNames,{{ ad_schema_naming_context }}
+
+dn: CN=kolabResource,{{ ad_schema_naming_context }}
+changetype: add
+objectClass: top
+objectClass: classSchema
+cn: kolabResource
+distinguishedName: CN=kolabResource,{{ ad_schema_naming_context }}
+instanceType: 4
+subClassOf: top
+governsID: 1.3.6.1.4.1.19414.3.2.9
+mayContain: description
+mayContain: kolabDescAttribute
+mayContain: kolabInvitationPolicy
+mayContain: owner
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: kolabResource
+adminDescription: Kolab resource
+objectClassCategory: 3
+lDAPDisplayName: kolabResource
+name: kolabResource
+systemOnly: FALSE
+defaultSecurityDescriptor:
+ D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)
+ (A;;RPLCLORC;;;AU)
+objectCategory: CN=Class-Schema,{{ ad_schema_naming_context }}
+defaultObjectCategory:
+ CN=kolabResource,{{ ad_schema_naming_context }}
+-
diff --git a/templates/local_recipient_maps.cf.j2 b/templates/local_recipient_maps.cf.j2
new file mode 100644
--- /dev/null
+++ b/templates/local_recipient_maps.cf.j2
@@ -0,0 +1,26 @@
+server_host = {{ ldap.ldap_uri }}
+version = 3
+search_base = {{ ldap.base_dn }}
+scope = sub
+
+domain = ldap:/etc/postfix/ldap/mydestination.cf
+
+bind_dn = {{ ldap.service_bind_dn }}
+bind_pw = {{ ldap.service_bind_pw }}
+
+query_filter =
+ (&
+ (|
+ {% for item in ldap.mail_attributes.split(',') -%}
+ ({{ item | trim }}=%s)
+ {%- endfor %}
+ )
+ (|
+ {{ ldap.kolab_user_filter }}
+ {{ ldap.kolab_group_filter }}
+ {{ ldap.resource_filter }}
+ {{ ldap.sharedfolder_filter }}
+ )
+ )
+
+result_attribute = mail
diff --git a/templates/mailenabled_distgroups.cf.j2 b/templates/mailenabled_distgroups.cf.j2
new file mode 100644
--- /dev/null
+++ b/templates/mailenabled_distgroups.cf.j2
@@ -0,0 +1,30 @@
+server_host = {{ ldap.ldap_uri }}
+server_port = 389
+version = 3
+search_base = {{ ldap.group_base_dn }}
+scope = sub
+
+domain = ldap:/etc/postfix/ldap/mydestination.cf
+
+bind_dn = {{ ldap.service_bind_dn }}
+bind_pw = {{ ldap.service_bind_pw }}
+
+# This finds the mail enabled distribution group LDAP entry
+query_filter =
+ (&
+ (|
+ {% for item in ldap.mail_attributes.split(',') -%}
+ ({{ item | trim }}=%s)
+ {%- endfor %}
+ )
+ (objectClass=kolabgroupofuniquenames)
+ (objectclass={{ ldap.group_objectclass }})
+ (!(objectclass=groupofurls))
+ )
+
+# From this type of group, get all uniqueMember DNs
+special_result_attribute = {{ ldap.group_member_attr }}
+
+# Only from those DNs, get the mail
+result_attribute =
+leaf_result_attribute = mail
diff --git a/templates/mailenabled_dynamic_distgroups.cf.j2 b/templates/mailenabled_dynamic_distgroups.cf.j2
new file mode 100644
--- /dev/null
+++ b/templates/mailenabled_dynamic_distgroups.cf.j2
@@ -0,0 +1,28 @@
+server_host = {{ ldap.ldap_uri }}
+version = 3
+search_base = {{ ldap.group_base_dn }}
+scope = sub
+
+domain = ldap:/etc/postfix/ldap/mydestination.cf
+
+bind_dn = {{ ldap.service_bind_dn }}
+bind_pw = {{ ldap.service_bind_pw }}
+
+# This finds the mail enabled dynamic distribution group LDAP entry
+query_filter =
+ (&
+ (|
+ {% for item in ldap.mail_attributes.split(',') -%}
+ ({{ item | trim }}=%s)
+ {%- endfor %}
+ )
+ (objectClass=kolabgroupofuniquenames)
+ (objectClass=groupOfURLs)
+ )
+
+# From this type of group, get all memberURL searches/references
+special_result_attribute = memberURL
+
+# Only from those DNs, get the mail
+result_attribute =
+leaf_result_attribute = mail
diff --git a/templates/mydestination.cf.j2 b/templates/mydestination.cf.j2
new file mode 100644
--- /dev/null
+++ b/templates/mydestination.cf.j2
@@ -0,0 +1,10 @@
+server_host = {{ ldap.ldap_uri }}
+version = 3
+search_base = {{ ldap.domain_base_dn }}
+scope = sub
+
+bind_dn = {{ ldap.service_bind_dn }}
+bind_pw = {{ ldap.service_bind_pw }}
+
+query_filter = {{ ldap.domain_filter | replace('*', '%s') }}
+result_attribute = {{ ldap.domain_name_attribute }}
diff --git a/templates/transport_maps.cf.j2 b/templates/transport_maps.cf.j2
new file mode 100644
--- /dev/null
+++ b/templates/transport_maps.cf.j2
@@ -0,0 +1,23 @@
+server_host = {{ ldap.ldap_uri }}
+version = 3
+search_base = {{ ldap.base_dn }}
+scope = sub
+
+domain = ldap:/etc/postfix/ldap/mydestination.cf
+
+bind_dn = {{ ldap.service_bind_dn }}
+bind_pw = {{ ldap.service_bind_pw }}
+
+query_filter =
+ (&
+ (|
+ (mailAlternateAddress=%%s)
+ {% for item in ldap.mail_attributes.split(',') -%}
+ ({{ item | trim }}=%s)
+ {%- endfor %}
+ )
+ (objectclass=kolabinetorgperson)
+ )
+
+result_attribute = mail
+result_format = lmtp:unix:/var/lib/imap/socket/lmtp
diff --git a/templates/virtual_alias_maps.cf.j2 b/templates/virtual_alias_maps.cf.j2
new file mode 100644
--- /dev/null
+++ b/templates/virtual_alias_maps.cf.j2
@@ -0,0 +1,20 @@
+server_host = {{ ldap.ldap_uri }}
+version = 3
+search_base = {{ ldap.base_dn }}
+scope = sub
+
+domain = ldap:/etc/postfix/ldap/mydestination.cf
+
+bind_dn = {{ ldap.service_bind_dn }}
+bind_pw = {{ ldap.service_bind_pw }}
+
+query_filter =
+ (&
+ (|
+ {% for item in ldap.mail_attributes.split(',') -%}
+ ({{ item | trim }}=%s)
+ {%- endfor %}
+ )
+ (objectclass=kolabinetorgperson)
+ )
+result_attribute = mail
diff --git a/templates/virtual_alias_maps_mailforwarding.cf.j2 b/templates/virtual_alias_maps_mailforwarding.cf.j2
new file mode 100644
--- /dev/null
+++ b/templates/virtual_alias_maps_mailforwarding.cf.j2
@@ -0,0 +1,23 @@
+server_host = {{ ldap.ldap_uri }}
+version = 3
+search_base = {{ ldap.base_dn }}
+scope = sub
+
+domain = ldap:/etc/postfix/ldap/mydestination.cf
+
+bind_dn = {{ ldap.service_bind_dn }}
+bind_pw = {{ ldap.service_bind_pw }}
+
+query_filter =
+ (&
+ (|
+ {% for item in ldap.mail_attributes.split(',') -%}
+ ({{ item | trim }}=%s)
+ {%- endfor %}
+ )
+ (objectclass=mailRecipient)
+ (objectclass=inetOrgPerson)
+ (mailforwardingaddress=*)
+ )
+
+result_attribute = mailForwardingAddress
diff --git a/templates/virtual_alias_maps_sharedfolders.cf.j2 b/templates/virtual_alias_maps_sharedfolders.cf.j2
new file mode 100644
--- /dev/null
+++ b/templates/virtual_alias_maps_sharedfolders.cf.j2
@@ -0,0 +1,23 @@
+server_host = {{ ldap.ldap_uri }}
+version = 3
+search_base = {{ ldap.base_dn }}
+scope = sub
+
+domain = ldap:/etc/postfix/ldap/mydestination.cf
+
+bind_dn = {{ ldap.service_bind_dn }}
+bind_pw = {{ ldap.service_bind_pw }}
+
+query_filter =
+ (&
+ (|
+ {% for item in ldap.mail_attributes.split(',') -%}
+ ({{ item | trim }}=%s)
+ {%- endfor %}
+ )
+ (objectclass=kolabsharedfolder)
+ (kolabFolderType=mail)
+ )
+
+result_attribute = kolabtargetfolder
+result_format = "shared+%s"
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Apr 3, 9:38 PM (12 h, 32 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18826735
Default Alt Text
D732.1775252308.diff (146 KB)
Attached To
Mode
D732: This is first attempt to add a AD schema import / load to kolab setup tools. The idea is that when setup-kolab is run with --with-ad it should ask questions about where the AD server is and how to connect to it. Then it should compile a AD schema...
Attached
Detach File
Event Timeline