commit inicial
This commit is contained in:
commit
16c2f29f06
209
.gitignore
vendored
Normal file
209
.gitignore
vendored
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
# ---> Emacs
|
||||||
|
# -*- mode: gitignore; -*-
|
||||||
|
*~
|
||||||
|
\#*\#
|
||||||
|
/.emacs.desktop
|
||||||
|
/.emacs.desktop.lock
|
||||||
|
*.elc
|
||||||
|
auto-save-list
|
||||||
|
tramp
|
||||||
|
.\#*
|
||||||
|
|
||||||
|
# Org-mode
|
||||||
|
.org-id-locations
|
||||||
|
*_archive
|
||||||
|
|
||||||
|
# flymake-mode
|
||||||
|
*_flymake.*
|
||||||
|
|
||||||
|
# eshell files
|
||||||
|
/eshell/history
|
||||||
|
/eshell/lastdir
|
||||||
|
|
||||||
|
# elpa packages
|
||||||
|
/elpa/
|
||||||
|
|
||||||
|
# reftex files
|
||||||
|
*.rel
|
||||||
|
|
||||||
|
# AUCTeX auto folder
|
||||||
|
/auto/
|
||||||
|
|
||||||
|
# cask packages
|
||||||
|
.cask/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Flycheck
|
||||||
|
flycheck_*.el
|
||||||
|
|
||||||
|
# server auth directory
|
||||||
|
/server/
|
||||||
|
|
||||||
|
# projectiles files
|
||||||
|
.projectile
|
||||||
|
|
||||||
|
# directory configuration
|
||||||
|
.dir-locals.el
|
||||||
|
|
||||||
|
# ---> Python
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# celery beat schedule file
|
||||||
|
celerybeat-schedule
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# ---> Ansible
|
||||||
|
*.retry
|
||||||
|
|
||||||
|
# ---> Linux
|
||||||
|
*~
|
||||||
|
|
||||||
|
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||||
|
.fuse_hidden*
|
||||||
|
|
||||||
|
# KDE directory preferences
|
||||||
|
.directory
|
||||||
|
|
||||||
|
# Linux trash folder which might appear on any partition or disk
|
||||||
|
.Trash-*
|
||||||
|
|
||||||
|
# .nfs files are created when an open file is removed but is still being accessed
|
||||||
|
.nfs*
|
||||||
|
|
||||||
|
# ---> macOS
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
3
README.md
Normal file
3
README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Postfix Ansible role
|
||||||
|
|
||||||
|
Rol de Ansible para configurar el servidor SMTP Postfix.
|
369
action_plugins/postconf.py
Executable file
369
action_plugins/postconf.py
Executable file
@ -0,0 +1,369 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# (c) 2018, Mauro Torrez <maurotorrez@gmail.com>
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from __future__ import (absolute_import, division, print_function)
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
import re
|
||||||
|
from ansible.plugins.action import ActionBase
|
||||||
|
from ansible.utils.vars import merge_hash
|
||||||
|
from ansible.module_utils._text import to_text
|
||||||
|
|
||||||
|
class PfWorkflowError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ActionModule(ActionBase):
|
||||||
|
|
||||||
|
def getarg(self, key, default=True):
|
||||||
|
if isinstance(default, basestring):
|
||||||
|
if key == 'parameter':
|
||||||
|
return self._task.args.get(key, default)
|
||||||
|
return '{}'.format(self._task.args.get(key, default))
|
||||||
|
if default:
|
||||||
|
if key == 'state':
|
||||||
|
return '{}'.format(self._task.args.get(key, 'present'))
|
||||||
|
if key == 'parameter':
|
||||||
|
return self._task.args.get(key, '')
|
||||||
|
if key in ('service', 'command', 'value', 'type'):
|
||||||
|
return '{}'.format(self._task.args.get(key, ''))
|
||||||
|
if key in ('private', 'unprivileged', 'chroot', 'wakeup', 'process_limit'):
|
||||||
|
return '{}'.format(self._task.args.get(key, ''))
|
||||||
|
# no default value for key: return as-is
|
||||||
|
return self._task.args.get(key)
|
||||||
|
|
||||||
|
|
||||||
|
def runcmd(self, reg_name, cmd, param=None):
|
||||||
|
try:
|
||||||
|
if param:
|
||||||
|
self.reg[param][reg_name] = self._execute_module(
|
||||||
|
module_name='command',
|
||||||
|
module_args=dict(_raw_params=cmd)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
self.reg[reg_name] = self._execute_module(
|
||||||
|
module_name='command',
|
||||||
|
module_args=dict(_raw_params=cmd)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
raise PfWorkflowError(
|
||||||
|
"{}: {}; failed command line: {}".format(
|
||||||
|
type(e).__name__,
|
||||||
|
to_text(e),
|
||||||
|
cmd
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_arguments(self):
|
||||||
|
'''assert arguments are valid'''
|
||||||
|
|
||||||
|
# validate service name
|
||||||
|
self.state = self.getarg('state')
|
||||||
|
if self.state not in ('present', 'absent', 'prepend', 'append'):
|
||||||
|
raise PfWorkflowError(
|
||||||
|
'Invalid state requested: use "present", "absent", "prepend" or "append"'
|
||||||
|
)
|
||||||
|
|
||||||
|
# validate service name
|
||||||
|
self.service = self.getarg('service')
|
||||||
|
if re.search('[^a-zA-Z0-9_]',self.service):
|
||||||
|
raise PfWorkflowError(
|
||||||
|
'Invalid character found in service identifier'
|
||||||
|
)
|
||||||
|
|
||||||
|
# validate service type text
|
||||||
|
self.stype = self.getarg('type')
|
||||||
|
if re.search('[^a-zA-Z0-9_]',self.stype):
|
||||||
|
raise PfWorkflowError(
|
||||||
|
'Invalid character found in service type identifier'
|
||||||
|
)
|
||||||
|
|
||||||
|
# validate flags
|
||||||
|
for p in ('private', 'unprivileged', 'chroot'):
|
||||||
|
if re.search('[^yn-]',self.getarg(p)):
|
||||||
|
raise PfWorkflowError(
|
||||||
|
'Invalid value for boolean flag {}'.format(p)
|
||||||
|
)
|
||||||
|
|
||||||
|
# validate flags
|
||||||
|
if re.search('[^0-9?-]',self.getarg('wakeup')):
|
||||||
|
raise PfWorkflowError(
|
||||||
|
'Invalid value for wakeup flag: {}'.format(self.getarg('wakeup'))
|
||||||
|
)
|
||||||
|
|
||||||
|
# validate flags
|
||||||
|
if re.search('[^0-9-]',self.getarg('process_limit')):
|
||||||
|
raise PfWorkflowError(
|
||||||
|
'Invalid value for process_limit flag: {}'.format(self.getarg('process_limit'))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def set_service(self):
|
||||||
|
'''
|
||||||
|
Set service entry in master.cf
|
||||||
|
'''
|
||||||
|
if not self.service:
|
||||||
|
self.reg['service_skipped'] = True
|
||||||
|
return
|
||||||
|
|
||||||
|
state = self.getarg('state')
|
||||||
|
|
||||||
|
# guess existing entry ------------------------------------------------
|
||||||
|
|
||||||
|
# get current service entry (if exists)
|
||||||
|
cmdline = 'postconf -M {}'.format(self.service)
|
||||||
|
if self.stype:
|
||||||
|
# append service type to the command line
|
||||||
|
cmdline = '{}/{}'.format(cmdline,self.stype)
|
||||||
|
|
||||||
|
self.runcmd('cmd_initial_check',cmdline)
|
||||||
|
|
||||||
|
# check service is defined and unambiguous by counting stdout lines
|
||||||
|
if len(self.reg['cmd_initial_check']['stdout_lines']) > 1:
|
||||||
|
raise PfWorkflowError(
|
||||||
|
'Ambiguous service identifier, please provide service type'
|
||||||
|
)
|
||||||
|
|
||||||
|
elif len(self.reg['cmd_initial_check']['stdout_lines']) == 1:
|
||||||
|
if not self.stype:
|
||||||
|
self.stype = self.reg['cmd_initial_check']['stdout'].split()[1]
|
||||||
|
|
||||||
|
# fill service fields as given by postconf
|
||||||
|
cmdline = 'postconf -Fh \
|
||||||
|
{0}/{1}/private {0}/{1}/unprivileged {0}/{1}/chroot \
|
||||||
|
{0}/{1}/wakeup {0}/{1}/process_limit {0}/{1}/command'.format(
|
||||||
|
self.service, self.stype)
|
||||||
|
self.runcmd('cmd_fields_check',cmdline)
|
||||||
|
|
||||||
|
private = self.getarg(
|
||||||
|
'private',
|
||||||
|
self.reg['cmd_fields_check']['stdout_lines'][0]
|
||||||
|
)
|
||||||
|
unprivileged = self.getarg(
|
||||||
|
'unprivileged',
|
||||||
|
self.reg['cmd_fields_check']['stdout_lines'][1]
|
||||||
|
)
|
||||||
|
chroot = self.getarg(
|
||||||
|
'chroot',
|
||||||
|
self.reg['cmd_fields_check']['stdout_lines'][2]
|
||||||
|
)
|
||||||
|
wakeup = self.getarg(
|
||||||
|
'wakeup',
|
||||||
|
self.reg['cmd_fields_check']['stdout_lines'][3]
|
||||||
|
)
|
||||||
|
process_limit = self.getarg(
|
||||||
|
'process_limit',
|
||||||
|
self.reg['cmd_fields_check']['stdout_lines'][4]
|
||||||
|
)
|
||||||
|
|
||||||
|
# change the command field when set explicitly
|
||||||
|
command = self.reg['cmd_fields_check']['stdout_lines'][5]
|
||||||
|
if self.getarg('command') and command.split()[0] != self.getarg('command'):
|
||||||
|
command = self.getarg('command')
|
||||||
|
|
||||||
|
else:
|
||||||
|
# no entry for service, create it if state != absent
|
||||||
|
if state != 'absent':
|
||||||
|
private = self.getarg('private','-')
|
||||||
|
unprivileged = self.getarg('unprivileged','-')
|
||||||
|
chroot = self.getarg('chroot','-')
|
||||||
|
wakeup = self.getarg('wakeup','-')
|
||||||
|
process_limit = self.getarg('process_limit','-')
|
||||||
|
command = self.getarg('command')
|
||||||
|
|
||||||
|
cmdline = 'postconf -M {0}/{1}="{0} {1} {2} {3} {4} {5} {6} {7}"'.format(
|
||||||
|
self.service,
|
||||||
|
self.stype,
|
||||||
|
private,
|
||||||
|
unprivileged,
|
||||||
|
chroot,
|
||||||
|
wakeup,
|
||||||
|
process_limit,
|
||||||
|
command
|
||||||
|
)
|
||||||
|
self.runcmd('cmd_add_service_entry',cmdline)
|
||||||
|
|
||||||
|
|
||||||
|
# state = absent ------------------------------------------------------
|
||||||
|
if state == 'absent':
|
||||||
|
if self.getarg('parameter'):
|
||||||
|
# state=absent with parameter does not remove service entry
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# remove whole service definition
|
||||||
|
cmdline = 'postconf -M# {}/{}'.format(service,stype)
|
||||||
|
self.runcmd('cmd_remove_service',cmdline)
|
||||||
|
|
||||||
|
# non-absent states: set all fields -----------------------------------
|
||||||
|
else:
|
||||||
|
cmdline = 'postconf -F \
|
||||||
|
{0}/{1}/private={2} {0}/{1}/unprivileged={3} {0}/{1}/chroot={4} \
|
||||||
|
{0}/{1}/wakeup={5} {0}/{1}/process_limit={6} {0}/{1}/command={7}'.format(
|
||||||
|
self.service, self.stype, private, unprivileged, chroot, wakeup, process_limit,
|
||||||
|
'"' + command.replace(r'\\',r'\\\\').replace(r'"',r'\"') + '"'
|
||||||
|
)
|
||||||
|
self.runcmd('cmd_fields_set',cmdline)
|
||||||
|
|
||||||
|
|
||||||
|
# verify changes
|
||||||
|
cmdline = 'postconf -M {}/{}'.format(self.service,self.stype)
|
||||||
|
self.runcmd('cmd_final_check',cmdline)
|
||||||
|
|
||||||
|
self.reg['service_changed'] = True
|
||||||
|
if self.reg['cmd_initial_check']['stdout'] == self.reg['cmd_final_check']['stdout']:
|
||||||
|
self.reg['service_changed'] = False
|
||||||
|
|
||||||
|
|
||||||
|
def set_single_parameter(self, parameter='', value='', state=''):
|
||||||
|
'''
|
||||||
|
Set a single parameter value in main.cf (service=None) or master.cf
|
||||||
|
'''
|
||||||
|
|
||||||
|
# value should be string
|
||||||
|
if not isinstance(value, basestring):
|
||||||
|
try:
|
||||||
|
value = ', '.join(value)
|
||||||
|
except TypeError:
|
||||||
|
value = str(value)
|
||||||
|
|
||||||
|
# default command to execute for setting the parameter
|
||||||
|
postconf_cmd = 'postconf'
|
||||||
|
|
||||||
|
# validate parameter identifier
|
||||||
|
if not parameter or re.search('[^a-zA-Z0-9_-]', parameter):
|
||||||
|
raise PfWorkflowError(
|
||||||
|
'Invalid parameter identifier: «{}»'.format(parameter)
|
||||||
|
)
|
||||||
|
|
||||||
|
regkey = "param_{}".format(parameter)
|
||||||
|
self.reg[regkey] = {}
|
||||||
|
|
||||||
|
if self.service:
|
||||||
|
# change command to execute for setting the parameter
|
||||||
|
postconf_cmd = 'postconf -P'
|
||||||
|
# change parameter identifier to include service/type
|
||||||
|
parameter = '/'.join([self.service,self.stype,parameter])
|
||||||
|
|
||||||
|
#
|
||||||
|
# general logic for setting parameter
|
||||||
|
#
|
||||||
|
|
||||||
|
# 1: get current value
|
||||||
|
cmdline = "{} -h {}".format(postconf_cmd, parameter)
|
||||||
|
self.runcmd('cmd_initial_check',cmdline,regkey)
|
||||||
|
current_value = self.reg[regkey]['cmd_initial_check']['stdout']
|
||||||
|
|
||||||
|
# 2: act according to desired state
|
||||||
|
if state == 'absent':
|
||||||
|
cmdline = "{} -X {}".format(postconf_cmd, parameter)
|
||||||
|
self.runcmd('cmd_remove',cmdline,regkey)
|
||||||
|
|
||||||
|
elif state in ('append', 'prepend'):
|
||||||
|
# we assume the current value to be a comma-separated list,
|
||||||
|
# and check if desired value is contained in current value
|
||||||
|
|
||||||
|
if value not in current_value:
|
||||||
|
|
||||||
|
# only modify parameter if desired value not contained in current
|
||||||
|
if state == 'prepend':
|
||||||
|
new_value = ', '.join([value,current_value])
|
||||||
|
else:
|
||||||
|
new_value = ', '.join([current_value,value])
|
||||||
|
|
||||||
|
# escape value according to shlex posix mode
|
||||||
|
escaped_value= '"' + new_value.replace(
|
||||||
|
r'\\',r'\\\\').replace(r'"',r'\"')+'"'
|
||||||
|
cmdline = "{} -e {}={}".format(postconf_cmd, parameter, escaped_value)
|
||||||
|
self.runcmd('cmd_modify',cmdline,regkey)
|
||||||
|
|
||||||
|
elif state == 'present':
|
||||||
|
if value.strip() != current_value.strip():
|
||||||
|
# set value exactly as required
|
||||||
|
# escape value according to shlex posix mode
|
||||||
|
escaped_value= '"' + value.replace(
|
||||||
|
r'\\',r'\\\\').replace(r'"',r'\"')+'"'
|
||||||
|
cmdline = "{} -e {}={}".format(postconf_cmd, parameter, escaped_value)
|
||||||
|
self.runcmd('cmd_modify_exact',cmdline,regkey)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# unsupported state
|
||||||
|
raise PfWorkflowError(
|
||||||
|
"Unsupported value in «state»"
|
||||||
|
)
|
||||||
|
|
||||||
|
# finally check how the value has changed
|
||||||
|
cmdline = "{} -h {}".format(postconf_cmd, parameter)
|
||||||
|
self.runcmd('cmd_final_check',cmdline,regkey)
|
||||||
|
|
||||||
|
changed = self.reg.get('parameter_changed', False)
|
||||||
|
final_value = self.reg[regkey]['cmd_final_check']['stdout']
|
||||||
|
if final_value != current_value:
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
self.reg['parameter_changed'] = changed
|
||||||
|
|
||||||
|
|
||||||
|
def run(self, tmp=None, task_vars=None):
|
||||||
|
|
||||||
|
result = super(ActionModule, self).run(tmp, task_vars)
|
||||||
|
if result.get('skipped'):
|
||||||
|
return result
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.validate_arguments()
|
||||||
|
except PfWorkflowError as e:
|
||||||
|
return {
|
||||||
|
'failed': True,
|
||||||
|
'msg': e.message
|
||||||
|
}
|
||||||
|
|
||||||
|
self.reg = {}
|
||||||
|
|
||||||
|
# process service
|
||||||
|
if self.getarg('service',False):
|
||||||
|
self.set_service()
|
||||||
|
|
||||||
|
parameter = self.getarg('parameter')
|
||||||
|
state = self.getarg('state')
|
||||||
|
if parameter:
|
||||||
|
# build (parameter, value) pairs for iteration
|
||||||
|
if isinstance(parameter, basestring):
|
||||||
|
pv = [ (parameter, self.getarg('value')) ]
|
||||||
|
elif isinstance(parameter, dict):
|
||||||
|
# if dict ignore new_value
|
||||||
|
pv = parameter.items()
|
||||||
|
else:
|
||||||
|
# assume list --> only supported for 'absent'
|
||||||
|
pv = zip(parameter, parameter)
|
||||||
|
|
||||||
|
for p in pv:
|
||||||
|
# loop over parameters and exit if error
|
||||||
|
self.set_single_parameter(p[0], p[1], state=state)
|
||||||
|
else:
|
||||||
|
self.reg['parameter_skipped'] = True
|
||||||
|
|
||||||
|
if all((self.reg.get('service_skipped',False),
|
||||||
|
self.reg.get('parameter_skipped',False))):
|
||||||
|
return {
|
||||||
|
'failed': True,
|
||||||
|
'msg': 'FATAL: no action requested!'
|
||||||
|
}
|
||||||
|
|
||||||
|
result['changed'] = any((self.reg.get('service_changed'),
|
||||||
|
self.reg.get('parameter_changed')))
|
||||||
|
result['reg'] = self.reg
|
||||||
|
return result
|
271
defaults/main.yml
Normal file
271
defaults/main.yml
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
# Postfix config directory (changing this is probably a bad idea)
|
||||||
|
postfix_config_dir: "/etc/postfix"
|
||||||
|
|
||||||
|
# Where to save access lists
|
||||||
|
postfix_rules_dir: "{{ postfix_config_dir }}/rules"
|
||||||
|
|
||||||
|
# accepted email domains
|
||||||
|
postfix_mail_domains: "{{ mail_domains | default(['example.com']) }}"
|
||||||
|
|
||||||
|
# postfix server domain: used for identification of the server
|
||||||
|
postfix_server_domain: "{{ postfix_mail_domains | first }}"
|
||||||
|
|
||||||
|
# postfix server identification
|
||||||
|
postfix_server_name: "mail.{{ postfix_server_domain }}"
|
||||||
|
|
||||||
|
# domains considered as "local" unix domains (local server users)
|
||||||
|
# ansible_fqdn, localhost.localdomain, and localhost are always added regardless of this value
|
||||||
|
postfix_unix_domains: []
|
||||||
|
|
||||||
|
# networks considered "local"
|
||||||
|
# 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 are always added regardless of this value
|
||||||
|
postfix_local_networks: []
|
||||||
|
|
||||||
|
# domain-specific configuration
|
||||||
|
postfix_domain_config: {}
|
||||||
|
|
||||||
|
# LDAP ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Default LDAP connection parameters
|
||||||
|
postfix_ldap_server: "localhost"
|
||||||
|
postfix_ldap_port: 389
|
||||||
|
postfix_ldap_version: 3
|
||||||
|
postfix_ldap_scope: "sub"
|
||||||
|
postfix_ldap_bind: no
|
||||||
|
postfix_ldap_bind_dn: ''
|
||||||
|
postfix_ldap_bind_pw: ''
|
||||||
|
postfix_ldap_start_tls: no
|
||||||
|
postfix_ldap_tls_ca_cert_dir: ''
|
||||||
|
postfix_ldap_tls_ca_cert_file: ''
|
||||||
|
postfix_ldap_use_group_alias: yes
|
||||||
|
|
||||||
|
# SQLITE ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
postfix_sqlite_user_query: "SELECT name FROM users WHERE name = '%u'"
|
||||||
|
postfix_sqlite_alias_query: "SELECT dest FROM aliases WHERE alias = '%s'"
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# enable submission service?
|
||||||
|
postfix_submission_enable: yes
|
||||||
|
|
||||||
|
# TLS -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# TLS certificate/private key to use
|
||||||
|
postfix_tls_certificate: "{{ tls_certificate | default('') }}"
|
||||||
|
postfix_tls_private_key: "{{ tls_certificate_key | default('') }}"
|
||||||
|
|
||||||
|
# level of encryption to use for sending mail to the Internet
|
||||||
|
# only change this if you know what the implications are, see
|
||||||
|
# http://www.postfix.org/postconf.5.html#smtp_tls_security_level
|
||||||
|
# possible values: none|may|encrypt|dane|dane-only|fingerprint|verify|secure
|
||||||
|
postfix_outgoing_tls_security: 'may'
|
||||||
|
|
||||||
|
# level of encryption required for receiving mail from the Internet
|
||||||
|
# only change this if you know what the implications are, see
|
||||||
|
# http://www.postfix.org/postconf.5.html#smtpd_tls_security_level
|
||||||
|
postfix_incoming_tls_security: 'may'
|
||||||
|
|
||||||
|
# wether to allow insecure (plaintext) login from clients
|
||||||
|
postfix_allow_insecure_auth: no
|
||||||
|
|
||||||
|
# enable/disable tls session cache
|
||||||
|
postfix_tls_session_cache: yes
|
||||||
|
|
||||||
|
# SASL ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# wether to allow SASL authentication on smtpd (MX port 25)
|
||||||
|
postfix_enable_smtpd_auth: no
|
||||||
|
|
||||||
|
# creo que esto no sirve para nada
|
||||||
|
# smtpd_sasl_local_domain: $myhostname
|
||||||
|
|
||||||
|
# valor por defecto ya es noanonymous
|
||||||
|
# smtpd_sasl_security_options: noanonymous
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# MESSAGE SIZE LIMIT ----------------------------------------------------------
|
||||||
|
|
||||||
|
# This value should be fine for most people. Note that increasing this limit
|
||||||
|
# does not guarantee delivery of very big emails: remote server size
|
||||||
|
# restrictions still apply.
|
||||||
|
postfix_message_size_limit: 31457280
|
||||||
|
|
||||||
|
# HELO required ---------------------------------------------------------------
|
||||||
|
postfix_helo_required: yes
|
||||||
|
|
||||||
|
# biff enabled
|
||||||
|
postfix_biff: no
|
||||||
|
|
||||||
|
# postfix_bounce_queue_lifetime: 2d
|
||||||
|
# postfix_maximal_queue_lifetime: 2d
|
||||||
|
# postfix_compatibility_level = 2 DEFAULT 0
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# RESTRICTIONS AND ACCESS LISTS -----------------------------------------------
|
||||||
|
|
||||||
|
# Host-based access list: PCRE table, for wildcard support
|
||||||
|
# every item should contain a 'regex' field and a corresponding 'action' field
|
||||||
|
postfix_client_access_list: []
|
||||||
|
# - regex: '172\.16\.192\.0\/24'
|
||||||
|
# action: 'REJECT Please use alternative server'
|
||||||
|
# - regex: 'rude\.client\.com'
|
||||||
|
# action: 'REJECT Get out!'
|
||||||
|
|
||||||
|
# Restricciones aplicadas a los clientes SMTP
|
||||||
|
postfix_client_restrictions:
|
||||||
|
- check_client_access pcre:{{ postfix_rules_dir }}/client_access_list
|
||||||
|
- permit_sasl_authenticated
|
||||||
|
- permit_mynetworks
|
||||||
|
- reject_unknown_client_hostname
|
||||||
|
- reject_unauth_pipelining
|
||||||
|
- permit
|
||||||
|
|
||||||
|
# Restricciones aplicadas en SMTP DATA
|
||||||
|
postfix_data_restrictions:
|
||||||
|
- reject_unauth_pipelining
|
||||||
|
- permit
|
||||||
|
|
||||||
|
# HELO access list: reject/accept clients by their HELO hostname
|
||||||
|
# Hash table, list of items with "host" and corresponding "action" fields
|
||||||
|
postfix_helo_access_list: []
|
||||||
|
# - host: "{{ansible_ip}}"
|
||||||
|
# action: "REJECT You can't be me. Get out!"
|
||||||
|
# - host: "{{ansible_fqdn}}"
|
||||||
|
# action: "REJECT You can't be me. Get out!"
|
||||||
|
|
||||||
|
# Requerir HELO/EHLO y aplicarle restricciones
|
||||||
|
postfix_helo_restrictions:
|
||||||
|
- check_helo_access hash:{{ postfix_rules_dir }}/helo_access_list
|
||||||
|
- permit_mynetworks
|
||||||
|
- reject_non_fqdn_helo_hostname
|
||||||
|
- reject_invalid_helo_hostname
|
||||||
|
- warn_if_reject
|
||||||
|
- permit
|
||||||
|
|
||||||
|
# Restricciones de control de relay aplicadas en RCPT TO, antes de smtpd_recipient_restrictions
|
||||||
|
postfix_relay_restrictions:
|
||||||
|
- permit_mynetworks
|
||||||
|
- permit_sasl_authenticated
|
||||||
|
- reject_unauth_destination
|
||||||
|
- permit
|
||||||
|
|
||||||
|
# Recipient access list: reject/accept mail by RCPT TO recipients
|
||||||
|
postfix_recipient_access_list: []
|
||||||
|
# - rcpt: "emailvalidation.helpdesk01@gmail.com"
|
||||||
|
# action: "REJECT Forbidden recipient. Get out!"
|
||||||
|
|
||||||
|
# Restricciones al destinatario especificado en RCPT TO
|
||||||
|
postfix_recipient_restrictions:
|
||||||
|
- check_recipient_access hash:{{ postfix_rules_dir }}/recipient_access_list
|
||||||
|
- permit_mynetworks
|
||||||
|
- permit_sasl_authenticated
|
||||||
|
- reject_unknown_recipient_domain
|
||||||
|
- reject_non_fqdn_recipient
|
||||||
|
- reject_unauth_destination
|
||||||
|
- permit
|
||||||
|
|
||||||
|
# Recipient access list: reject/accept mail by MAIL FROM sender
|
||||||
|
postfix_sender_access_list: []
|
||||||
|
# - sender: "@addr.com"
|
||||||
|
# action: "REJECT We're fed up with your spam. Get out!"
|
||||||
|
|
||||||
|
# Restricciones aplicadas al remitente especificado en MAIL FROM
|
||||||
|
postfix_sender_restrictions:
|
||||||
|
- check_sender_access hash:{{ postfix_rules_dir }}/sender_access_list
|
||||||
|
- permit_sasl_authenticated
|
||||||
|
- permit_mynetworks
|
||||||
|
- reject_unknown_sender_domain
|
||||||
|
- reject_non_fqdn_sender
|
||||||
|
- permit
|
||||||
|
|
||||||
|
# configuracion postscreen ----------------------------------------------------
|
||||||
|
# ver http://www.postfix.org/POSTSCREEN_README.html
|
||||||
|
# y tambien http://www.postfix.org/postscreen.8.html
|
||||||
|
|
||||||
|
# habilitar postscreen?
|
||||||
|
postfix_postscreen_enable: yes
|
||||||
|
|
||||||
|
# lista blanca/negra de IPs (solo se permiten valores ip, ip/netmask)
|
||||||
|
postfix_postscreen_access_list: []
|
||||||
|
# action = (permit|dunno|reject). Ejemplos:
|
||||||
|
# - address: "127.0.0.0/8"
|
||||||
|
# action: dunno
|
||||||
|
# - address: "2001:db8::/32"
|
||||||
|
# action: reject
|
||||||
|
|
||||||
|
# lista de sitios y ponderacion a usar como criterio dnsbl
|
||||||
|
# cada item puede ser un string o un dict con atributos:
|
||||||
|
# .item, .score (opcional, default=1) y .mask (opcional, para
|
||||||
|
# ocultar la lista a clientes remotos)
|
||||||
|
postfix_postscreen_dnsbl_sites:
|
||||||
|
- site: zen.spamhaus.org
|
||||||
|
score: 3
|
||||||
|
- site: b.barracudacentral.org
|
||||||
|
score: 2
|
||||||
|
- site: bl.spameatingmonkey.net
|
||||||
|
score: 2
|
||||||
|
mask: spameatingmonkey.com
|
||||||
|
- site: bl.spamcop.net
|
||||||
|
- dnsbl.sorbs.net
|
||||||
|
- site: psbl.surriel.com
|
||||||
|
- bl.mailspike.net
|
||||||
|
- site: swl.spamhaus.org
|
||||||
|
score: -4 # whitelist
|
||||||
|
# example:
|
||||||
|
# - site: mypassword.bl.service.com
|
||||||
|
# score: 3
|
||||||
|
# mask: service.com
|
||||||
|
|
||||||
|
# acción a efectuar cuando el cliente está en la lista negra (access_list)
|
||||||
|
postfix_postscreen_blacklist_action: drop
|
||||||
|
|
||||||
|
# habilitar tests bare_newline? (no recomendado)
|
||||||
|
postfix_postscreen_bare_newline_enable: no
|
||||||
|
|
||||||
|
# acción a efectuar cuando el cliente no cumple el test bare_newline
|
||||||
|
postfix_postscreen_bare_newline_action: ignore
|
||||||
|
|
||||||
|
# acción a efectuar cuando el cliente está en una lista dnsbl (enforce|ignore|drop)
|
||||||
|
postfix_postscreen_dnsbl_action: enforce
|
||||||
|
|
||||||
|
# mapeo que determina cuáles dnsbls informar al cliente como razón del rechazo a la conexion
|
||||||
|
postfix_postscreen_dnsbl_reply_map: "pcre:$config_directory/reglas/postscreen_dnsbl_reply_map.pcre"
|
||||||
|
|
||||||
|
# umbral a superar para considerar al host remoto como spammer
|
||||||
|
postfix_postscreen_dnsbl_threshold: 3
|
||||||
|
|
||||||
|
# cuando esta bajo de este puntaje, no se hacen mas tests y se pasa a una whitelist
|
||||||
|
postfix_postscreen_dnsbl_whitelist_threshold: -1
|
||||||
|
|
||||||
|
# accion a efectuar cuando el cliente habla antes de su turno (enforce|ignore|drop)
|
||||||
|
postfix_postscreen_greet_action: enforce
|
||||||
|
|
||||||
|
# tiempo a esperar para detectar un cliente malo
|
||||||
|
postfix_postscreen_greet_wait: "${stress?{2}:{6}}s"
|
||||||
|
|
||||||
|
# habilitar deteccion de comandos no-smtp? (no recomendado)
|
||||||
|
postfix_postscreen_non_smtp_command_enable: no
|
||||||
|
|
||||||
|
# accion a efectuar cuando no se cumple el test non_smtp_command
|
||||||
|
postfix_postscreen_non_smtp_command_action: drop
|
||||||
|
|
||||||
|
# habilitar tests pipelining? (no recomendado)
|
||||||
|
postfix_postscreen_pipelining_enable: no
|
||||||
|
|
||||||
|
# accion a efectuar cuando no se cumple pipelining (enforce|ignore|drop)
|
||||||
|
postfix_postscreen_pipelining_action: enforce
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Hosts considerados como test, obligados a usar otro servidor
|
||||||
|
# El valor debe ser una regexp válida
|
||||||
|
# correo_hosts_test: []
|
||||||
|
|
||||||
|
# redes internas obligadas a utilizar servicio submission
|
||||||
|
# correo_forzar_submission: []
|
15
filter_plugins/belist.py
Normal file
15
filter_plugins/belist.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from collections import Sequence
|
||||||
|
def assert_list(arg):
|
||||||
|
''' Convert string argument to list-of-strings '''
|
||||||
|
if isinstance(arg, basestring):
|
||||||
|
return [ arg ]
|
||||||
|
if not isinstance(arg, Sequence):
|
||||||
|
return [ arg ]
|
||||||
|
return arg
|
||||||
|
|
||||||
|
class FilterModule(object):
|
||||||
|
def filters(self):
|
||||||
|
return {
|
||||||
|
'belist': assert_list
|
||||||
|
}
|
15
filter_plugins/binary.py
Normal file
15
filter_plugins/binary.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from collections import Sequence
|
||||||
|
def binary(arg,yes_value='yes',no_value='no'):
|
||||||
|
''' Convert boolean argument to 'yes' or 'no' string value '''
|
||||||
|
if isinstance(arg, bool):
|
||||||
|
if arg:
|
||||||
|
return yes_value
|
||||||
|
return no_value
|
||||||
|
return arg
|
||||||
|
|
||||||
|
class FilterModule(object):
|
||||||
|
def filters(self):
|
||||||
|
return {
|
||||||
|
'binary': binary
|
||||||
|
}
|
32
handlers/main.yml
Normal file
32
handlers/main.yml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
- name: restart postfix
|
||||||
|
service: name=postfix state=restarted
|
||||||
|
|
||||||
|
- name: reload postfix
|
||||||
|
service: name=postfix state=restarted
|
||||||
|
|
||||||
|
- name: newaliases
|
||||||
|
command: newaliases
|
||||||
|
|
||||||
|
- name: postmap hash aliases
|
||||||
|
command: "postmap hash:{{ dc[item]['alias_lookup']['file'] }}"
|
||||||
|
when:
|
||||||
|
- "dc[item]['alias_lookup']['provider'] == 'file'"
|
||||||
|
with_items: "{{ postfix_mail_domains|belist }}"
|
||||||
|
|
||||||
|
- name: postmap hash users
|
||||||
|
command: "postmap hash:{{ dc[item]['user_lookup']['file'] }}"
|
||||||
|
when:
|
||||||
|
- "dc[item]['user_lookup']['provider'] == 'file'"
|
||||||
|
with_items: "{{ postfix_mail_domains|belist }}"
|
||||||
|
|
||||||
|
- name: postmap no reply aliases
|
||||||
|
command: "postmap hash:{{ dc[item]['noreply_file'] }}"
|
||||||
|
with_items: "{{ postfix_mail_domains|belist }}"
|
||||||
|
|
||||||
|
- name: postmap access lists
|
||||||
|
command: postmap {{item}}
|
||||||
|
with_items:
|
||||||
|
- "{{ postfix_rules_dir }}/helo_access_list"
|
||||||
|
- "{{ postfix_rules_dir }}/recipient_access_list"
|
||||||
|
- "{{ postfix_rules_dir }}/sender_access_list"
|
255
tasks/main.yml
Normal file
255
tasks/main.yml
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
---
|
||||||
|
- name: "Load default config for domains"
|
||||||
|
set_fact:
|
||||||
|
dc: "{{ dc|default({})|combine( { item: {
|
||||||
|
'user_lookup': {
|
||||||
|
'provider': 'file',
|
||||||
|
'file': vmail_home +'/'+item+'_users',
|
||||||
|
'domain': item,
|
||||||
|
'server_host': postfix_ldap_server,
|
||||||
|
'server_port': postfix_ldap_port,
|
||||||
|
'version': postfix_ldap_version,
|
||||||
|
'scope': postfix_ldap_scope,
|
||||||
|
'bind': postfix_ldap_bind,
|
||||||
|
'bind_dn': postfix_ldap_bind_dn,
|
||||||
|
'bind_pw': postfix_ldap_bind_pw,
|
||||||
|
'start_tls': postfix_ldap_start_tls,
|
||||||
|
'tls_ca_cert_file': postfix_ldap_tls_ca_cert_file,
|
||||||
|
'tls_ca_cert_dir': postfix_ldap_tls_ca_cert_dir,
|
||||||
|
'search_base':
|
||||||
|
'ou=People,'+item.split('.')|map('regex_replace','^','dc=')|join(','),
|
||||||
|
'query_filter': '(&(objectClass=inetOrgPerson)(uid=%u))',
|
||||||
|
'result_attribute': 'uid',
|
||||||
|
'result_format': vmail_home+'/mail/'+item+'/%s/',
|
||||||
|
'dbpath': vmail_home+'/'+item+'_users.sqlite',
|
||||||
|
'query': postfix_sqlite_user_query
|
||||||
|
},
|
||||||
|
'users': [],
|
||||||
|
'alias_lookup': {
|
||||||
|
'provider': 'file',
|
||||||
|
'file': vmail_home +'/'+item+'_aliases',
|
||||||
|
'domain': item,
|
||||||
|
'server_host': postfix_ldap_server,
|
||||||
|
'server_port': postfix_ldap_port,
|
||||||
|
'version': postfix_ldap_version,
|
||||||
|
'scope': postfix_ldap_scope,
|
||||||
|
'bind': postfix_ldap_bind,
|
||||||
|
'bind_dn': postfix_ldap_bind_dn,
|
||||||
|
'bind_pw': postfix_ldap_bind_pw,
|
||||||
|
'start_tls': postfix_ldap_start_tls,
|
||||||
|
'tls_ca_cert_file': postfix_ldap_tls_ca_cert_file,
|
||||||
|
'tls_ca_cert_dir': postfix_ldap_tls_ca_cert_dir,
|
||||||
|
'search_base':
|
||||||
|
'ou=Alias,'+item.split('.')|map('regex_replace','^','dc=')|join(','),
|
||||||
|
'query_filter': '(&(objectClass=nisMailAlias)(cn=%u))',
|
||||||
|
'result_attribute': 'rfc822MailMember',
|
||||||
|
'result_format': '%s',
|
||||||
|
'dbpath': vmail_home+'/'+item+'_aliases.sqlite',
|
||||||
|
'query': postfix_sqlite_alias_query
|
||||||
|
},
|
||||||
|
'aliases': [],
|
||||||
|
'use_group_as_alias': postfix_ldap_use_group_alias,
|
||||||
|
'group_lookup': {
|
||||||
|
'provider': 'ldap',
|
||||||
|
'domain': item,
|
||||||
|
'server_host': postfix_ldap_server,
|
||||||
|
'server_port': postfix_ldap_port,
|
||||||
|
'version': postfix_ldap_version,
|
||||||
|
'scope': postfix_ldap_scope,
|
||||||
|
'bind': postfix_ldap_bind,
|
||||||
|
'bind_dn': postfix_ldap_bind_dn,
|
||||||
|
'bind_pw': postfix_ldap_bind_pw,
|
||||||
|
'start_tls': postfix_ldap_start_tls,
|
||||||
|
'tls_ca_cert_file': postfix_ldap_tls_ca_cert_file,
|
||||||
|
'tls_ca_cert_dir': postfix_ldap_tls_ca_cert_dir,
|
||||||
|
'search_base':
|
||||||
|
'ou=Group,'+item.split('.')|map('regex_replace','^','dc=')|join(','),
|
||||||
|
'query_filter': '(&(objectClass=posixGroup)(cn=%u))',
|
||||||
|
'result_attribute': 'memberUid',
|
||||||
|
'result_format': '%s@{{d}}',
|
||||||
|
},
|
||||||
|
'noreply_aliases': [ 'noreply' ],
|
||||||
|
'noreply_file': vmail_home +'/'+item+'_noreply',
|
||||||
|
} }, recursive=True) }}"
|
||||||
|
with_items: "{{ postfix_mail_domains|belist }}"
|
||||||
|
|
||||||
|
- name: "Override config for domains"
|
||||||
|
set_fact:
|
||||||
|
dc: '{{ dc | combine(postfix_domain_config, recursive=True) }}'
|
||||||
|
|
||||||
|
- apt: name=postfix update_cache=yes
|
||||||
|
- apt: name=postfix-pcre
|
||||||
|
notify: restart postfix
|
||||||
|
|
||||||
|
- apt: name=postfix-ldap
|
||||||
|
when:
|
||||||
|
# see http://jmespath.org/
|
||||||
|
- '"ldap" in dc|json_query("*.[ alias_lookup, user_lookup ][].provider")'
|
||||||
|
notify: restart postfix
|
||||||
|
- apt: name=postfix-sqlite
|
||||||
|
when:
|
||||||
|
# see http://jmespath.org/
|
||||||
|
- '"sqlite" in dc|json_query("*.[ alias_lookup, user_lookup ][].provider")'
|
||||||
|
notify: restart postfix
|
||||||
|
|
||||||
|
- name: "Template Dovecot delivery/auth service config for Postfix"
|
||||||
|
template:
|
||||||
|
src: 11-postfix.conf.j2
|
||||||
|
dest: /etc/dovecot/conf.d/11-postfix.conf
|
||||||
|
notify: restart dovecot
|
||||||
|
|
||||||
|
- name: "Configure lookup tables"
|
||||||
|
include_tasks: lookup_tables.yml
|
||||||
|
with_items: "{{postfix_mail_domains|belist}}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: "domain"
|
||||||
|
|
||||||
|
- name: "Configure no-reply local mail alias"
|
||||||
|
blockinfile:
|
||||||
|
block: |
|
||||||
|
_dev_null: /dev/null
|
||||||
|
marker: "# {mark} ANSIBLE-MANAGED ALIASES"
|
||||||
|
path: "/etc/aliases"
|
||||||
|
notify: newaliases
|
||||||
|
|
||||||
|
- name: "Create rules directory for access lists"
|
||||||
|
file:
|
||||||
|
name: "{{ postfix_rules_dir }}"
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: "Template client access list"
|
||||||
|
blockinfile:
|
||||||
|
path: "{{ postfix_rules_dir }}/client_access_list"
|
||||||
|
create: yes
|
||||||
|
block: |
|
||||||
|
# Edit host variable `postfix_client_access_list` to change these values
|
||||||
|
{% for entry in postfix_client_access_list -%}
|
||||||
|
{{ entry.regex }} {{ entry.action }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
- name: "Template helo access list"
|
||||||
|
blockinfile:
|
||||||
|
path: "{{ postfix_rules_dir }}/helo_access_list"
|
||||||
|
create: yes
|
||||||
|
block: |
|
||||||
|
# Edit host variable `postfix_helo_access_list` to change these values
|
||||||
|
{% for entry in postfix_helo_access_list -%}
|
||||||
|
{{ entry.host }} {{ entry.action }}
|
||||||
|
{% endfor %}
|
||||||
|
notify: postmap access lists
|
||||||
|
|
||||||
|
- name: "Template recipient access list"
|
||||||
|
blockinfile:
|
||||||
|
path: "{{ postfix_rules_dir }}/recipient_access_list"
|
||||||
|
create: yes
|
||||||
|
block: |
|
||||||
|
# Edit host variable `postfix_recipient_access_list` to change these values
|
||||||
|
{% for entry in postfix_recipient_access_list -%}
|
||||||
|
{{ entry.rcpt }} {{ entry.action }}
|
||||||
|
{% endfor %}
|
||||||
|
notify: postmap access lists
|
||||||
|
|
||||||
|
- name: "Template sender access list"
|
||||||
|
blockinfile:
|
||||||
|
path: "{{ postfix_rules_dir }}/sender_access_list"
|
||||||
|
create: yes
|
||||||
|
block: |
|
||||||
|
# Edit host variable `postfix_sender_access_list` to change these values
|
||||||
|
{% for entry in postfix_sender_access_list -%}
|
||||||
|
{{ entry.sender }} {{ entry.action }}
|
||||||
|
{% endfor %}
|
||||||
|
notify: postmap access lists
|
||||||
|
|
||||||
|
- name: "Set main.cf parameters"
|
||||||
|
postconf:
|
||||||
|
parameter:
|
||||||
|
mydestination: >-
|
||||||
|
{{ postfix_unix_domains | belist |
|
||||||
|
union( [ ansible_fqdn, 'localhost.localdomain', 'localhost'] ) |
|
||||||
|
difference( postfix_mail_domains|belist ) }}
|
||||||
|
myhostname: "{{ postfix_server_name }}"
|
||||||
|
mydomain: "{{ postfix_server_domain }}"
|
||||||
|
mynetworks: >-
|
||||||
|
{{ ['127.0.0.0/8', '[::ffff:127.0.0.0]/104', '[::1]/128'] |
|
||||||
|
union( postfix_local_networks ) }}
|
||||||
|
virtual_alias_maps: >-
|
||||||
|
{% for d in postfix_mail_domains|belist %}
|
||||||
|
{% set p = dc[d]['alias_lookup']['provider'] %}
|
||||||
|
{% if p == "ldap" %}
|
||||||
|
ldap:{{ postfix_config_dir }}/{{d}}_ldap_alias.cf
|
||||||
|
{% if dc[d]['use_group_as_alias'] %},
|
||||||
|
ldap:{{ postfix_config_dir }}/{{d}}_ldap_group.cf
|
||||||
|
{% endif %}
|
||||||
|
{% elif p == "sqlite" %}
|
||||||
|
sqlite:{{ postfix_config_dir }}/{{d}}_sqlite_alias.cf
|
||||||
|
{% elif p == "file" %}
|
||||||
|
hash:{{ vmail_home }}/{{d}}_aliases
|
||||||
|
{% endif %}{{ '' if loop.last else ',' }}{% endfor %},
|
||||||
|
hash:{{ postfix_config_dir }}/noreply_aliases
|
||||||
|
virtual_mailbox_maps: >-
|
||||||
|
{% for d in postfix_mail_domains|belist %}
|
||||||
|
{% set p = dc[d]['user_lookup']['provider'] %}
|
||||||
|
{% if p == "ldap" %}
|
||||||
|
ldap:{{ postfix_config_dir }}/{{d}}_ldap_user.cf
|
||||||
|
{% elif p == "sqlite" %}
|
||||||
|
sqlite:{{ postfix_config_dir }}/{{d}}_sqlite_user.cf
|
||||||
|
{% elif p == "file" %}
|
||||||
|
hash:{{ vmail_home }}/{{d}}_users
|
||||||
|
{% endif %}{{ '' if loop.last else ',' }}{% endfor %},
|
||||||
|
virtual_transport: "lmtp:unix:private/dovecot-lmtp"
|
||||||
|
virtual_mailbox_domains: "{{ postfix_mail_domains }}"
|
||||||
|
smtpd_sasl_path: private/auth
|
||||||
|
smtpd_sasl_type: dovecot
|
||||||
|
smtpd_sasl_auth_enable: "{{ 'yes' if postfix_enable_smtpd_auth else 'no' }}"
|
||||||
|
smtpd_tls_cert_file: "{{ postfix_tls_certificate }}"
|
||||||
|
smtpd_tls_key_file: "{{ postfix_tls_private_key }}"
|
||||||
|
smtp_tls_security_level: "{{postfix_incoming_tls_security}}"
|
||||||
|
smtpd_tls_security_level: "{{postfix_outgoing_tls_security}}"
|
||||||
|
smtpd_tls_auth_only: "{{ 'yes' if postfix_allow_insecure_auth else 'no'}}"
|
||||||
|
smtpd_tls_session_cache_database: "{{ 'btree:${data_directory}/smtpd_scache' if postfix_tls_session_cache else '' }}"
|
||||||
|
smtpd_client_restrictions: "{{ postfix_client_restrictions }}"
|
||||||
|
smtpd_data_restrictions: "{{ postfix_data_restrictions }}"
|
||||||
|
smtpd_helo_restrictions: "{{ postfix_helo_restrictions }}"
|
||||||
|
smtpd_relay_restrictions: "{{ postfix_relay_restrictions }}"
|
||||||
|
smtpd_recipient_restrictions: "{{ postfix_recipient_restrictions }}"
|
||||||
|
message_size_limit: "{{ postfix_message_size_limit }}"
|
||||||
|
smtpd_helo_required: "{{ 'yes' if postfix_helo_required else 'no' }}"
|
||||||
|
biff: "{{ 'yes' if postfix_biff else 'no' }}"
|
||||||
|
notify: reload postfix
|
||||||
|
|
||||||
|
- name: "Enable submission service"
|
||||||
|
postconf:
|
||||||
|
service: submission
|
||||||
|
type: inet
|
||||||
|
private: 'n'
|
||||||
|
command: smtpd
|
||||||
|
parameter:
|
||||||
|
milter_macro_daemon_name: ORIGINATING
|
||||||
|
smtpd_client_restrictions:
|
||||||
|
- permit_sasl_authenticated
|
||||||
|
- reject
|
||||||
|
smtpd_sasl_auth_enable: 'yes'
|
||||||
|
smtpd_tls_security_level: encrypt
|
||||||
|
syslog_name: postfix/submission
|
||||||
|
notify: reload postfix
|
||||||
|
when: "postfix_submission_enable == True"
|
||||||
|
|
||||||
|
- name: "Disable submission service"
|
||||||
|
postconf:
|
||||||
|
service: submission
|
||||||
|
type: inet
|
||||||
|
state: absent
|
||||||
|
notify: reload postfix
|
||||||
|
when: "postfix_submission_enable == False"
|
||||||
|
|
||||||
|
- name: "Enable postscreen"
|
||||||
|
include_tasks: postscreen.yml
|
||||||
|
when: "postfix_postscreen_enable == True"
|
||||||
|
|
||||||
|
- name: "Disable postscreen"
|
||||||
|
include_tasks: postscreen_disable.yml
|
||||||
|
when: "postfix_postscreen_enable == False"
|
||||||
|
|
||||||
|
# TODO: mensajes
|
||||||
|
|
||||||
|
# TODO: milter_header_checks
|
Loading…
x
Reference in New Issue
Block a user