4 Commits

Author SHA1 Message Date
Spitap
af9d8c2849 Apply fixes by @tomas-kucera
Co-Authored-By: Katzman <tku@centrum.cz>
2022-12-27 20:03:16 +01:00
Spitap
6b4302b566 Update from master
commit 5c22600d98
Merge: bc12ca7 bcdbb4a
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Tue Nov 29 16:54:28 2022 +0100

    Merge pull request #462 from Spitfireap/randomize-api-call-time

    randomize api call time

commit bcdbb4a2ce
Author: Spitap <dev@asdrip.fr>
Date:   Tue Nov 29 14:53:05 2022 +0100

    fix typo

commit bd1ddcef21
Author: Spitap <dev@asdrip.fr>
Date:   Tue Nov 29 13:45:31 2022 +0100

    randomize api call time

commit bc12ca7327
Merge: d364239 bd0ecd0
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Mon Nov 14 15:49:41 2022 +0100

    Merge pull request #458 from Spitfireap/fix-include_try

    fix typo in dovecot configuration file

commit bd0ecd0949
Author: Spitap <dev@asdrip.fr>
Date:   Thu Nov 10 14:57:43 2022 +0100

    fix typo in dovecot configuration file

commit d364239348
Merge: 61838db 3763300
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Wed Nov 9 10:51:30 2022 +0100

    Merge pull request #456 from modoboa/feature/improved_backup_restore

    WIP: Improved backup/restore system.

commit 37633008cb
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Wed Nov 9 10:30:44 2022 +0100

    Fixed restore mode

commit d6f9a5b913
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Tue Nov 8 17:20:25 2022 +0100

    Few fixes.

commit 8b1d60ee59
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Tue Nov 8 17:19:23 2022 +0100

    Few fixes

commit 2b5edae5d5
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Sun Nov 6 10:30:24 2022 +0100

    WIP: Improved backup/restore system.

commit 61838dbe4d
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Sat Nov 5 09:30:50 2022 +0100

    Check if restore is defined before doing anything else.

    fix #453

commit 962cac3ad9
Merge: 1b192c5 ef2359a
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Fri Nov 4 09:41:20 2022 +0100

    Merge pull request #450 from Spitfireap/fixed-super-call

    fixed super call in modoboa's script

commit ef2359a2a8
Author: Spitap <dev@asdrip.fr>
Date:   Thu Nov 3 23:10:21 2022 +0100

    fixed super call

commit 1b192c5fd5
Merge: 754d652 b0b0146
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Thu Nov 3 15:34:48 2022 +0100

    Merge pull request #449 from Spitfireap/fixed-import-typo

    fixed constants import

commit b0b01465d9
Author: Spitap <dev@asdrip.fr>
Date:   Thu Nov 3 15:00:07 2022 +0100

    fixed constants import

commit 754d652fc2
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Thu Nov 3 12:27:04 2022 +0100

    Few fixes

commit cb5fa75693
Merge: 1afb8e6 e01265a
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Thu Nov 3 12:20:25 2022 +0100

    Merge pull request #444 from Spitfireap/tighter-config-file-perm

    tighter config file permission

commit 1afb8e61fc
Merge: 15c1779 8dd0b7d
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Thu Nov 3 12:17:16 2022 +0100

    Merge pull request #424 from Spitfireap/restore

    Backup & restore system

commit 8dd0b7d497
Author: Spitap <dev@asdrip.fr>
Date:   Thu Nov 3 10:57:03 2022 +0100

    Last camelCase

commit 554611b366
Author: Spitap <dev@asdrip.fr>
Date:   Thu Nov 3 10:54:06 2022 +0100

    review fix

commit 15c17796f2
Merge: ce8e7e6 84d1363
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Fri Oct 28 09:43:30 2022 +0200

    Merge pull request #446 from Spitfireap/fix-ssl-min-protocol

    fixed ssl_min_protocol setting

commit 84d13633a1
Author: Spitap <dev@asdrip.fr>
Date:   Thu Oct 27 22:37:47 2022 +0200

    fixed ssl_min_protocol setting

commit ce8e7e6027
Merge: 8e8ae5f fe7df27
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Thu Oct 27 17:56:37 2022 +0200

    Merge pull request #445 from Spitfireap/dovecot-fixes

    Fixes ssl permission error, updated ssl_protocol parameter

commit e01265a4ee
Merge: a5fba03 235ef3b
Author: Spitap <dev@asdrip.fr>
Date:   Thu Oct 27 17:44:37 2022 +0200

    Merge branch 'tighter-config-file-perm' of https://github.com/Spitfireap/modoboa-installer into tighter-config-file-perm

commit a5fba03264
Author: Spitap <dev@asdrip.fr>
Date:   Thu Oct 27 11:13:47 2022 +0200

    tighter config file permission

commit fe7df276fc
Author: Spitap <dev@asdrip.fr>
Date:   Thu Oct 27 17:25:39 2022 +0200

    Check dovecot version greater

commit 8f34f0af6f
Author: Spitap <dev@asdrip.fr>
Date:   Thu Oct 27 17:00:58 2022 +0200

    Fixes ssl permission error, updated ssl_protocol parameter

commit 8e8ae5fb9c
Merge: 67f6cee fefbf54
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Thu Oct 27 16:49:20 2022 +0200

    Merge pull request #439 from stefaweb/master

    Update config_dict_template.py for default max_servers value

commit 235ef3befb
Author: Spitap <dev@asdrip.fr>
Date:   Thu Oct 27 11:13:47 2022 +0200

    thighter config file permission

commit 67f6cee8ea
Merge: b84abbb 53f7f8e
Author: Antoine Nguyen <tonio@ngyn.org>
Date:   Tue Oct 25 19:32:37 2022 +0200

    Merge pull request #442 from Spitfireap/patch-1

    Set $max_server to 2 to avoid amavis crash

commit 5c9d5c9a03
Author: Spitap <dev@asdrip.fr>
Date:   Tue Oct 25 16:58:57 2022 +0200

    DKIM keys restore, Radicale backup/restore, fixes

commit 4c1f8710b5
Author: Spitap <dev@asdrip.fr>
Date:   Tue Oct 25 16:04:55 2022 +0200

    Added dkim key backup

commit e34eb4b337
Author: Spitap <dev@asdrip.fr>
Date:   Tue Oct 25 13:59:28 2022 +0200

    fix database path

commit 53f7f8ef9d
Author: Spitfireap <45575529+Spitfireap@users.noreply.github.com>
Date:   Wed Oct 19 08:19:40 2022 +0000

    Update config_dict_template.py

commit 35778cd614
Merge: 6726f5b b84abbb
Author: Spitfireap <45575529+Spitfireap@users.noreply.github.com>
Date:   Tue Oct 18 17:17:48 2022 +0200

    Merge branch 'modoboa:master' into restore

commit fefbf549a4
Author: Stephane Leclerc <sleclerc@actionweb.fr>
Date:   Thu Oct 6 13:36:13 2022 +0200

    Update config_dict_template.py for default max_server value

commit 6726f5b1a2
Author: Spitap <dev@asdrip.fr>
Date:   Mon Sep 26 13:39:28 2022 +0200

    Improved path generation, path mistake proofing

commit a192cbcbd0
Author: Spitap <dev@asdrip.fr>
Date:   Mon Sep 19 16:40:25 2022 +0200

    Updated doc, default path on conf file

commit 5bed9655ea
Author: Spitap <dev@asdrip.fr>
Date:   Mon Sep 19 15:53:19 2022 +0200

    fixed typo

commit 6b096a7470
Author: Spitap <dev@asdrip.fr>
Date:   Mon Sep 19 15:50:03 2022 +0200

    Simplified db dumps restore

commit e30add03fd
Merge: d75d83f 1f8dd1b
Author: Spitap <dev@asdrip.fr>
Date:   Mon Sep 19 15:39:05 2022 +0200

    Update from master

commit d75d83f202
Author: Spitap <dev@asdrip.fr>
Date:   Mon Sep 19 15:13:44 2022 +0200

    more refactoring

commit f3811b4b39
Author: Spitap <dev@asdrip.fr>
Date:   Mon Sep 19 14:59:43 2022 +0200

    refactoring

commit b0d56b3989
Author: Spitap <dev@asdrip.fr>
Date:   Thu Sep 15 11:32:57 2022 +0200

    PEP formating

commit 53e3e3ec58
Author: Spitap <dev@asdrip.fr>
Date:   Fri Aug 5 15:20:11 2022 +0200

    Better UX, use of os to concatenate path

commit e546d2cb23
Author: Spitap <dev@asdrip.fr>
Date:   Wed Jul 27 16:32:59 2022 +0200

    Better UX

commit 70faa1c5cb
Author: Spitap <dev@asdrip.fr>
Date:   Wed Jul 27 15:58:41 2022 +0200

    Fixed backupdir index

commit 563979a7dd
Author: Spitap <dev@asdrip.fr>
Date:   Wed Jul 27 15:51:22 2022 +0200

    fixed mail backup/restore

commit ee2ccf0647
Author: Spitap <dev@asdrip.fr>
Date:   Wed Jul 27 14:35:48 2022 +0200

    Fixed postfix install, added restore to readme

commit 2077c94b52
Author: Spitap <dev@asdrip.fr>
Date:   Tue Jul 26 17:05:00 2022 +0200

    Fix amavis config file not copied to right location

commit 4a7222bd24
Author: Spitap <dev@asdrip.fr>
Date:   Tue Jul 26 16:53:24 2022 +0200

    Fixed nginx call to uwsgi

commit e7b6104195
Author: Spitap <dev@asdrip.fr>
Date:   Tue Jul 26 16:39:41 2022 +0200

    fixed install within class

commit 4a00590354
Author: Spitap <dev@asdrip.fr>
Date:   Tue Jul 26 16:20:03 2022 +0200

    fixed restore disclamer

commit 15768c429e
Author: Spitap <dev@asdrip.fr>
Date:   Tue Jul 26 12:07:42 2022 +0200

    Restore workflow done

commit 439ffb94c4
Author: Spitap <dev@asdrip.fr>
Date:   Mon Jul 25 18:54:47 2022 +0200

    initial commit

commit 37bc21dfd3
Author: Spitap <dev@asdrip.fr>
Date:   Tue Jul 26 10:36:08 2022 +0200

    Backup postewhite.conf instead of custom whitelist

    Postwhite.conf contains a custom host list

commit 26204143af
Merge: 2097055 d495afd
Author: Spitap <dev@asdrip.fr>
Date:   Mon Jul 25 22:10:26 2022 +0200

    Merge branch 'master' into backup

commit 20970557de
Author: Spitap <dev@asdrip.fr>
Date:   Mon Jul 25 22:05:35 2022 +0200

    Allow to disable mail backup

commit 632c26596e
Author: Spitap <dev@asdrip.fr>
Date:   Mon Jul 25 21:52:15 2022 +0200

    Update backup readme

commit 9e1c18cd6b
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 19:09:53 2022 +0200

    Fix argument passed as list instead of string

commit db6457c5f5
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 19:07:18 2022 +0200

    better path handling

commit 579faccfa5
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 19:00:32 2022 +0200

    added an automatic bash option (no path provided) or a path provided bash (for cron job)

commit 5318fa279b
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 18:00:50 2022 +0200

    bash option

commit 74de6a9bb1
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 17:31:56 2022 +0200

    Reset pgpass before trying to backup secondary dbs

commit 54185a7c5a
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 17:26:40 2022 +0200

    Fix database backup logic issue

commit 1f9d69c37c
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 17:21:59 2022 +0200

    Fix copy issue

commit 8d02d2a9fb
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 17:09:23 2022 +0200

    added safe mkdir in utils, use utils.mkdir_safe() in backup

commit 6f604a5fec
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 16:53:56 2022 +0200

    Fix loop logic

commit 568c4a65a0
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 16:51:32 2022 +0200

    fix none-type passed to os.path

commit dc84a79528
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 14:12:35 2022 +0200

    Note : capitalize affects only first letter

commit 304e25fa3c
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 14:10:57 2022 +0200

    Fix getattr

commit 070efd61c4
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 14:08:39 2022 +0200

    Fix import

commit 9917d8023e
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 14:02:41 2022 +0200

    Edited README, fix backup run process

commit 27b9de6755
Author: Spitap <dev@asdrip.fr>
Date:   Thu Jul 21 13:48:44 2022 +0200

    database backup

commit 56ed214fb5
Author: Spitap <dev@asdrip.fr>
Date:   Tue Jul 19 19:06:53 2022 +0200

    Starting work on backup system
2022-12-22 18:47:55 +01:00
Spitap
d9ced63e99 centos9 fixes 2022-10-18 10:19:32 +02:00
Spitap
b13cdbf0ba Centos9 support and removed centos7 support 2022-10-14 20:10:39 +02:00
109 changed files with 749 additions and 4022 deletions

3
.github/FUNDING.yml vendored
View File

@@ -1,3 +0,0 @@
# These are supported funding model platforms
github: [modoboa]

View File

@@ -1,7 +1,7 @@
# Impacted versions # Impacted versions
* Distribution: Debian / Ubuntu / Centos * Distribution: Debian / Ubuntu / Centos
* Codename: Jessie / Trusty / Centos 7 / ... * Codename: Jessie / Trusty / Centos 9 Stream / ...
* Arch: 32 Bits / 64 Bits * Arch: 32 Bits / 64 Bits
* Database: PostgreSQL / MySQL * Database: PostgreSQL / MySQL

View File

@@ -11,48 +11,47 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
python-version: [3.9, '3.10', '3.11', '3.12'] python-version: [3.7, 3.8, 3.9]
fail-fast: false fail-fast: false
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5 uses: actions/setup-python@v2
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Install dependencies - name: Install dependencies
run: | run: |
pip install -r test-requirements.txt pip install -r test-requirements.txt
- name: Run tests - name: Run tests
if: ${{ matrix.python-version != '3.12' }} if: ${{ matrix.python-version != '3.9' }}
run: | run: |
python tests.py python tests.py
- name: Run tests and coverage - name: Run tests and coverage
if: ${{ matrix.python-version == '3.12' }} if: ${{ matrix.python-version == '3.9' }}
run: | run: |
coverage run tests.py coverage run tests.py
- name: Upload coverage result - name: Upload coverage result
if: ${{ matrix.python-version == '3.12' }} if: ${{ matrix.python-version == '3.9' }}
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v2
with: with:
name: coverage-results name: coverage-results
path: .coverage path: .coverage
include-hidden-files: true
coverage: coverage:
needs: test needs: test
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v2
with: with:
python-version: '3.12' python-version: '3.9'
- name: Install dependencies - name: Install dependencies
run: | run: |
pip install codecov pip install codecov
- name: Download coverage results - name: Download coverage results
uses: actions/download-artifact@v4 uses: actions/download-artifact@v2
with: with:
name: coverage-results name: coverage-results
- name: Report coverage - name: Report coverage

View File

@@ -1,32 +0,0 @@
name: Update version file
on:
workflow_run:
branches: [ master ]
workflows: [Modoboa installer]
types:
- completed
jobs:
update-version:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository.
ref: ${{ github.head_ref }}
- name: Overwrite file
uses: "DamianReeves/write-file-action@master"
with:
path: version.txt
write-mode: overwrite
contents: ${{ github.sha }}
- name: Commit & Push
uses: Andro999b/push@v1.3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ github.ref_name }}
force: true
message: '[GitHub Action] Updated version file'

5
.gitignore vendored
View File

@@ -58,8 +58,3 @@ target/
# PyCharm # PyCharm
.idea/ .idea/
#KDE
*.kdev4
installer.cfg

View File

@@ -1,20 +1,22 @@
**modoboa-installer** modoboa-installer
===================== =================
|workflow| |codecov| |workflow| |codecov|
An installer which deploys a complete mail server based on Modoboa. An installer which deploy a complete mail server based on Modoboa.
.. warning:: .. warning::
This tool is still in beta, it has been tested on: This tool is still in beta stage, it has been tested on:
* Debian 12 and upper * Debian Buster (10) / Bullseye (11)
* Ubuntu Focal Fossa (20.04) and upper * Ubuntu Bionic Beaver (18.04) and upper
* CentOS 9 Stream
.. warning:: .. warning::
``/tmp`` partition must be mounted without the ``noexec`` option. ``/tmp`` partition must be mounted without the ``noexec`` option.
Centos 7 support has been depreceated since modoboa requires python 3.7>=.
.. note:: .. note::
@@ -43,7 +45,8 @@ The following components are installed by the installer:
* Nginx and uWSGI * Nginx and uWSGI
* Postfix * Postfix
* Dovecot * Dovecot
* Amavis (with SpamAssassin and ClamAV) or Rspamd * Amavis (with SpamAssassin and ClamAV)
* automx (autoconfiguration service)
* OpenDKIM * OpenDKIM
* Radicale (CalDAV and CardDAV server) * Radicale (CalDAV and CardDAV server)
@@ -75,7 +78,7 @@ If you want more information about the installation process, add the
``--debug`` option to your command line. ``--debug`` option to your command line.
Upgrade mode Upgrade mode
============ ------------
An experimental upgrade mode is available. An experimental upgrade mode is available.
@@ -91,7 +94,7 @@ You can activate it as follows::
It will automatically install latest versions of modoboa and its plugins. It will automatically install latest versions of modoboa and its plugins.
Backup mode Backup mode
=========== ------------
An experimental backup mode is available. An experimental backup mode is available.
@@ -106,19 +109,7 @@ You can start the process as follows::
Then follow the step on the console. Then follow the step on the console.
There is also a non-interactive mode:: There is also a non-interactive mode:
$ sudo ./run.py --silent-backup <your domain>
You can also add a path, else it will be saved in ./modoboa_backup/Backup_M_Y_d_H_M::
$ sudo ./run.py --silent-backup --backup-path "/My_Backup_Path" <your domain>
if you want to disable mail backup::
$ sudo ./run.py --backup --no-mail <your domain>
This can be useful for larger instance
1. Silent mode 1. Silent mode
@@ -140,7 +131,7 @@ configuration file (set enabled to False).
This can be useful for larger instance. This can be useful for larger instance.
Restore mode Restore mode
============ ------------
An experimental restore mode is available. An experimental restore mode is available.
@@ -151,7 +142,7 @@ You can start the process as follows::
Then wait for the process to finish. Then wait for the process to finish.
Change the generated hostname Change the generated hostname
============================= -----------------------------
By default, the installer will setup your email server using the By default, the installer will setup your email server using the
following hostname: ``mail.<your domain>``. If you want a different following hostname: ``mail.<your domain>``. If you want a different
@@ -170,24 +161,12 @@ modifications.
Finally, run the installer without the Finally, run the installer without the
``--stop-after-configfile-check`` option. ``--stop-after-configfile-check`` option.
Certificate Let's Encrypt certificate
=========== -------------------------
Self-signed
-----------
It is the default type of certificate the installer will generate, it
is however not recommended for production use.
Letsencrypt
-----------
.. warning:: .. warning::
Please note that by using this option, you agree to the `ToS Please note this option requires the hostname you're using to be
<https://community.letsencrypt.org/tos>`_ of
letsencrypt and that your IP will be logged (see ToS).
Please also note this option requires the hostname you're using to be
valid (ie. it can be resolved with a DNS query) and to match the valid (ie. it can be resolved with a DNS query) and to match the
server you're installing Modoboa on. server you're installing Modoboa on.
@@ -198,8 +177,6 @@ modify the following settings::
[certificate] [certificate]
generate = true generate = true
type = letsencrypt type = letsencrypt
tls_cert_file_path =
tls_key_file_path =
[letsencrypt] [letsencrypt]
email = admin@example.com email = admin@example.com
@@ -207,43 +184,6 @@ modify the following settings::
Change the ``email`` setting to a valid value since it will be used Change the ``email`` setting to a valid value since it will be used
for account recovery. for account recovery.
Manual
------
.. warning::
It is not possible to configure manual certs interactively, so
you'll have to do it in 2 steps. Please run ``run.py`` with
`--stop-after-configfile-check` first, configure your file as
desired and apply the configuration as written bellow. Then run
``run.py`` again but without `--stop-after-configfile-check` or
`--interactive`.
If you want to use already generated certs, simply edit the
``installer.cfg`` file and modify the following settings::
[certificate]
generate = true
type = manual
tls_cert_file_path = *path to tls fullchain file*
tls_key_file_path = *path to tls key file*
Antispam
========
You have 3 options regarding antispam : disabled, Amavis, Rspamd
Amavis
------
Amavis
Rspamd
------
Rspamd
.. |workflow| image:: https://github.com/modoboa/modoboa-installer/workflows/Modoboa%20installer/badge.svg .. |workflow| image:: https://github.com/modoboa/modoboa-installer/workflows/Modoboa%20installer/badge.svg
.. |codecov| image:: https://codecov.io/gh/modoboa/modoboa-installer/graph/badge.svg?token=Fo2o1GdHZq .. |codecov| image:: http://codecov.io/github/modoboa/modoboa-installer/coverage.svg?branch=master
:target: https://codecov.io/gh/modoboa/modoboa-installer :target: http://codecov.io/github/modoboa/modoboa-installer?branch=master

View File

@@ -1,37 +0,0 @@
"""Checks to be performed before any install or upgrade"""
import sys
from urllib.request import urlopen
from modoboa_installer import utils
def check_version():
local_version = ""
with open("version.txt", "r") as version:
local_version = version.readline()
remote_version = ""
with urlopen("https://raw.githubusercontent.com/modoboa/modoboa-installer/master/version.txt") as r_version:
remote_version = r_version.read().decode()
if local_version == "" or remote_version == "":
utils.printcolor(
"Could not check that your installer is up-to-date: "
f"local version: {local_version}, "
f"remote version: {remote_version}",
utils.YELLOW
)
if remote_version != local_version:
utils.error(
"Your installer seems outdated.\n"
"Check README file for instructions about how to update.\n"
"No support will be provided without an up-to-date installer!"
)
answer = utils.user_input("Continue anyway? (y/N) ")
if not answer.lower().startswith("y"):
sys.exit(0)
else:
utils.success("Installer seems up to date!")
def handle():
check_version()

View File

@@ -20,27 +20,9 @@ COMPATIBILITY_MATRIX = {
"modoboa-pdfcredentials": ">=1.1.1", "modoboa-pdfcredentials": ">=1.1.1",
"modoboa-sievefilters": ">=1.1.1", "modoboa-sievefilters": ">=1.1.1",
"modoboa-webmail": ">=1.2.0", "modoboa-webmail": ">=1.2.0",
}, }
} }
EXTENSIONS_AVAILABILITY = { EXTENSIONS_AVAILABILITY = {
"modoboa-contacts": "1.7.4", "modoboa-contacts": "1.7.4",
} }
REMOVED_EXTENSIONS = {
"modoboa-pdfcredentials": "2.1.0",
"modoboa-dmarc": "2.1.0",
"modoboa-imap-migration": "2.1.0",
"modoboa-sievefilters": "2.3.0",
"modoboa-postfix-autoreply": "2.3.0",
"modoboa-contacts": "2.4.0",
"modoboa-radicale": "2.4.0",
"modoboa-webmail": "2.4.0",
}
APP_INCOMPATIBILITY = {
"opendkim": ["rspamd"],
"amavis": ["rspamd"],
"postwhite": ["rspamd"],
"spamassassin": ["rspamd"]
}

View File

@@ -27,50 +27,25 @@ ConfigDictTemplate = [
} }
] ]
}, },
{
"name": "antispam",
"values": [
{
"option": "enabled",
"default": "true",
"customizable": True,
"values": ["true", "false"],
"question": "Do you want to setup an antispam utility?"
},
{
"option": "type",
"default": "amavis",
"customizable": True,
"question": "Please select your antispam utility",
"values": ["rspamd", "amavis"],
"if": ["antispam.enabled=true"]
}
]
},
{ {
"name": "certificate", "name": "certificate",
"values": [ "values": [
{
"option": "generate",
"default": "true",
},
{ {
"option": "type", "option": "type",
"default": "self-signed", "default": "self-signed",
"customizable": True, "customizable": True,
"question": "Please choose your certificate type", "question": "Please choose your certificate type",
"values": ["self-signed", "letsencrypt", "manual"], "values": ["self-signed", "letsencrypt"],
"non_interactive_values": ["manual"],
},
{
"option": "tls_cert_file_path",
"default": ""
},
{
"option": "tls_key_file_path",
"default": ""
} }
], ],
}, },
{ {
"name": "letsencrypt", "name": "letsencrypt",
"if": ["certificate.type=letsencrypt"], "if": "certificate.type=letsencrypt",
"values": [ "values": [
{ {
"option": "email", "option": "email",
@@ -105,7 +80,7 @@ ConfigDictTemplate = [
}, },
{ {
"name": "postgres", "name": "postgres",
"if": ["database.engine=postgres"], "if": "database.engine=postgres",
"values": [ "values": [
{ {
"option": "user", "option": "user",
@@ -121,7 +96,7 @@ ConfigDictTemplate = [
}, },
{ {
"name": "mysql", "name": "mysql",
"if": ["database.engine=mysql"], "if": "database.engine=mysql",
"values": [ "values": [
{ {
"option": "user", "option": "user",
@@ -143,31 +118,6 @@ ConfigDictTemplate = [
} }
] ]
}, },
{
"name": "fail2ban",
"values": [
{
"option": "enabled",
"default": "true",
},
{
"option": "config_dir",
"default": "/etc/fail2ban"
},
{
"option": "max_retry",
"default": "20"
},
{
"option": "ban_time",
"default": "3600"
},
{
"option": "find_time",
"default": "30"
},
]
},
{ {
"name": "modoboa", "name": "modoboa",
"values": [ "values": [
@@ -205,16 +155,14 @@ ConfigDictTemplate = [
"customizable": True, "customizable": True,
"question": "Please enter Modoboa db password", "question": "Please enter Modoboa db password",
}, },
{
"option": "cron_error_recipient",
"default": "root",
"customizable": True,
"question":
"Please enter a mail recipient for cron error reports"
},
{ {
"option": "extensions", "option": "extensions",
"default": "" "default": (
"modoboa-amavis modoboa-pdfcredentials "
"modoboa-postfix-autoreply modoboa-sievefilters "
"modoboa-webmail modoboa-contacts "
"modoboa-radicale"
),
}, },
{ {
"option": "devmode", "option": "devmode",
@@ -223,59 +171,40 @@ ConfigDictTemplate = [
] ]
}, },
{ {
"name": "rspamd", "name": "automx",
"if": ["antispam.enabled=true", "antispam.type=rspamd"],
"values": [ "values": [
{ {
"option": "enabled", "option": "enabled",
"default": ["antispam.enabled=true", "antispam.type=rspamd"],
},
{
"option": "user",
"default": "_rspamd",
},
{
"option": "password",
"default": make_password,
"customizable": True,
"question": "Please enter Rspamd interface password",
},
{
"option": "dnsbl",
"default": "true", "default": "true",
}, },
{ {
"option": "dkim_keys_storage_dir", "option": "user",
"default": "/var/lib/dkim" "default": "automx",
}, },
{ {
"option": "key_map_path", "option": "config_dir",
"default": "/var/lib/dkim/keys.path.map" "default": "/etc",
}, },
{ {
"option": "selector_map_path", "option": "home_dir",
"default": "/var/lib/dkim/selectors.path.map" "default": "/srv/automx",
}, },
{ {
"option": "greylisting", "option": "venv_path",
"default": "true" "default": "%(home_dir)s/env",
}, },
{ {
"option": "whitelist_auth", "option": "instance_path",
"default": "true" "default": "%(home_dir)s/instance",
}, },
{ ]
"option": "whitelist_auth_weigth",
"default": "-5"
}
],
}, },
{ {
"name": "amavis", "name": "amavis",
"values": [ "values": [
{ {
"option": "enabled", "option": "enabled",
"default": ["antispam.enabled=true", "antispam.type=amavis"], "default": "true",
}, },
{ {
"option": "user", "option": "user",
@@ -296,6 +225,8 @@ ConfigDictTemplate = [
{ {
"option": "dbpassword", "option": "dbpassword",
"default": make_password, "default": make_password,
"customizable": True,
"question": "Please enter amavis db password"
}, },
], ],
}, },
@@ -325,7 +256,7 @@ ConfigDictTemplate = [
}, },
{ {
"option": "user", "option": "user",
"default": "dovecot", "default": "vmail",
}, },
{ {
"option": "home_dir", "option": "home_dir",
@@ -345,11 +276,7 @@ ConfigDictTemplate = [
}, },
{ {
"option": "radicale_auth_socket_path", "option": "radicale_auth_socket_path",
"default": "/var/run/dovecot/auth-radicale", "default": "/var/run/dovecot/auth-radicale"
},
{
"option": "move_spam_to_junk",
"default": "true",
}, },
] ]
}, },
@@ -371,7 +298,7 @@ ConfigDictTemplate = [
"values": [ "values": [
{ {
"option": "enabled", "option": "enabled",
"default": "false", "default": "true",
}, },
{ {
"option": "config_dir", "option": "config_dir",
@@ -394,10 +321,6 @@ ConfigDictTemplate = [
"option": "message_size_limit", "option": "message_size_limit",
"default": "11534336", "default": "11534336",
}, },
{
"option": "dhe_group",
"default": "4096"
}
] ]
}, },
{ {
@@ -405,7 +328,7 @@ ConfigDictTemplate = [
"values": [ "values": [
{ {
"option": "enabled", "option": "enabled",
"default": ["antispam.enabled=true", "antispam.type=amavis"], "default": "true",
}, },
{ {
"option": "config_dir", "option": "config_dir",
@@ -415,11 +338,10 @@ ConfigDictTemplate = [
}, },
{ {
"name": "spamassassin", "name": "spamassassin",
"if": ["antispam.enabled=true", "antispam.type=amavis"],
"values": [ "values": [
{ {
"option": "enabled", "option": "enabled",
"default": ["antispam.enabled=true", "antispam.type=amavis"], "default": "true",
}, },
{ {
"option": "config_dir", "option": "config_dir",
@@ -454,7 +376,7 @@ ConfigDictTemplate = [
}, },
{ {
"option": "nb_processes", "option": "nb_processes",
"default": "4", "default": "2",
}, },
] ]
}, },
@@ -485,11 +407,10 @@ ConfigDictTemplate = [
}, },
{ {
"name": "opendkim", "name": "opendkim",
"if": ["antispam.enabled=true", "antispam.type=amavis"],
"values": [ "values": [
{ {
"option": "enabled", "option": "enabled",
"default": ["antispam.enabled=true", "antispam.type=amavis"], "default": "true",
}, },
{ {
"option": "user", "option": "user",

View File

@@ -3,19 +3,19 @@
import os import os
import pwd import pwd
import stat import stat
from typing import Optional
from . import package from . import package
from . import system from . import system
from . import utils from . import utils
class Database: class Database(object):
"""Common database backend.""" """Common database backend."""
default_port: Optional[int] = None default_port = None
packages: Optional[dict[str, list[str]]] = None packages = None
service: Optional[str] = None service = None
def __init__(self, config): def __init__(self, config):
"""Install if necessary.""" """Install if necessary."""
@@ -36,12 +36,13 @@ class Database:
class PostgreSQL(Database): class PostgreSQL(Database):
"""Postgres.""" """Postgres."""
default_port = 5432 default_port = 5432
packages = { packages = {
"deb": ["postgresql", "postgresql-server-dev-all"], "deb": ["postgresql", "postgresql-server-dev-all"],
"rpm": ["postgresql-server", "postgresql-devel"] "rpm": ["postgresql-server", "postgresql-server-devel", "postgresql"]
} }
service = "postgresql" service = "postgresql"
@@ -53,19 +54,7 @@ class PostgreSQL(Database):
"""Install database if required.""" """Install database if required."""
name, version = utils.dist_info() name, version = utils.dist_info()
if "CentOS" in name: if "CentOS" in name:
if version.startswith("7"): initdb_cmd = "postgresql-setup --initdb"
# Install newer version of postgres in this case
package.backend.install(
"https://download.postgresql.org/pub/repos/yum/"
"reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm"
)
self.packages["rpm"] = [
"postgresql10-server", "postgresql10-devel"]
self.service = "postgresql-10"
initdb_cmd = "/usr/pgsql-10/bin/postgresql-10-setup initdb"
cfgfile = "/var/lib/pgsql/10/data/pg_hba.conf"
else:
initdb_cmd = "postgresql-setup initdb"
cfgfile = "/var/lib/pgsql/data/pg_hba.conf" cfgfile = "/var/lib/pgsql/data/pg_hba.conf"
package.backend.install_many(self.packages[package.backend.FORMAT]) package.backend.install_many(self.packages[package.backend.FORMAT])
utils.exec_cmd(initdb_cmd) utils.exec_cmd(initdb_cmd)
@@ -102,7 +91,7 @@ class PostgreSQL(Database):
def create_database(self, name, owner): def create_database(self, name, owner):
"""Create a database.""" """Create a database."""
code, output = utils.exec_cmd( code, output = utils.exec_cmd(
"psql -lqt | cut -d \\| -f 1 | grep -w {} | wc -l" "psql -lqt | cut -d \| -f 1 | grep -w {} | wc -l"
.format(name), sudo_user=self.dbuser) .format(name), sudo_user=self.dbuser)
if code: if code:
return return
@@ -156,6 +145,7 @@ class PostgreSQL(Database):
class MySQL(Database): class MySQL(Database):
"""MySQL backend.""" """MySQL backend."""
default_port = 3306 default_port = 3306
@@ -176,17 +166,13 @@ class MySQL(Database):
if name.startswith("debian"): if name.startswith("debian"):
if version.startswith("8"): if version.startswith("8"):
self.packages["deb"].append("libmysqlclient-dev") self.packages["deb"].append("libmysqlclient-dev")
elif int(version[:2]) >= 11: elif version.startswith("11"):
self.packages["deb"].append("libmariadb-dev") self.packages["deb"].append("libmariadb-dev")
else: else:
self.packages["deb"].append("libmariadbclient-dev") self.packages["deb"].append("libmariadbclient-dev")
elif name == "ubuntu": elif name == "ubuntu":
if version.startswith("2"):
# Works for Ubuntu 20, 22, and 24.
self.packages["deb"].append("libmariadb-dev")
else:
self.packages["deb"].append("libmysqlclient-dev") self.packages["deb"].append("libmysqlclient-dev")
super().install_package() super(MySQL, self).install_package()
queries = [] queries = []
if name.startswith("debian"): if name.startswith("debian"):
if version.startswith("8"): if version.startswith("8"):
@@ -197,10 +183,7 @@ class MySQL(Database):
"mariadb-server", "root_password_again", "password", "mariadb-server", "root_password_again", "password",
self.dbpassword) self.dbpassword)
return return
if ( if version.startswith("11"):
(name.startswith("debian") and int(version[:2]) >= 11) or
(name.startswith("ubuntu") and int(version[:2]) >= 22)
):
queries = [ queries = [
"SET PASSWORD FOR 'root'@'localhost' = PASSWORD('{}')" "SET PASSWORD FOR 'root'@'localhost' = PASSWORD('{}')"
.format(self.dbpassword), .format(self.dbpassword),

View File

@@ -1,51 +0,0 @@
from . import utils
def installation_disclaimer(args, config):
"""Display installation disclaimer."""
hostname = config.get("general", "hostname")
utils.printcolor(
"Notice:\n"
"It is recommanded to run this installer on a FRESHLY installed server.\n"
"(ie. with nothing special already installed on it)\n",
utils.CYAN
)
utils.printcolor(
"Warning:\n"
"Before you start the installation, please make sure the following "
"DNS records exist for domain '{}':\n"
" {} IN A <IP ADDRESS OF YOUR SERVER>\n"
" @ IN MX {}.\n".format(
args.domain,
hostname.replace(".{}".format(args.domain), ""),
hostname
),
utils.YELLOW
)
utils.printcolor(
"Your mail server will be installed with the following components:",
utils.BLUE)
def upgrade_disclaimer(config):
"""Display upgrade disclaimer."""
utils.printcolor(
"Your mail server is about to be upgraded and the following components"
" will be impacted:", utils.BLUE
)
def backup_disclaimer():
"""Display backup disclamer. """
utils.printcolor(
"Your mail server will be backed up locally.\n"
" !! You should really transfer the backup somewhere else...\n"
" !! Custom configuration (like for postfix) won't be saved.", utils.BLUE)
def restore_disclaimer():
"""Display restore disclamer. """
utils.printcolor(
"You are about to restore a previous installation of Modoboa.\n"
"If a new version has been released in between, please update your database!",
utils.BLUE)

View File

@@ -2,12 +2,10 @@
import re import re
from os.path import isfile as file_exists
from . import utils from . import utils
class Package: class Package(object):
"""Base classe.""" """Base classe."""
def __init__(self, dist_name): def __init__(self, dist_name):
@@ -31,17 +29,10 @@ class DEBPackage(Package):
FORMAT = "deb" FORMAT = "deb"
def __init__(self, dist_name): def __init__(self, dist_name):
super().__init__(dist_name) super(DEBPackage, self).__init__(dist_name)
self.index_updated = False self.index_updated = False
self.policy_file = "/usr/sbin/policy-rc.d" self.policy_file = "/usr/sbin/policy-rc.d"
def enable_backports(self, codename):
code, output = utils.exec_cmd(f"grep {codename}-backports /etc/apt/sources.list")
if code:
with open(f"/etc/apt/sources.list.d/backports.list", "w") as fp:
fp.write(f"deb http://deb.debian.org/debian {codename}-backports main\n")
self.update(force=True)
def prepare_system(self): def prepare_system(self):
"""Make sure services don't start at installation.""" """Make sure services don't start at installation."""
with open(self.policy_file, "w") as fp: with open(self.policy_file, "w") as fp:
@@ -51,34 +42,11 @@ class DEBPackage(Package):
def restore_system(self): def restore_system(self):
utils.exec_cmd("rm -f {}".format(self.policy_file)) utils.exec_cmd("rm -f {}".format(self.policy_file))
def add_custom_repository(self, def update(self):
name: str,
url: str,
key_url: str,
codename: str,
with_source: bool = True):
key_file = f"/etc/apt/keyrings/{name}.gpg"
utils.exec_cmd(
f"wget -O - {key_url} | gpg --dearmor | tee {key_file} > /dev/null"
)
line_types = ["deb"]
if with_source:
line_types.append("deb-src")
for line_type in line_types:
line = (
f"{line_type} [arch=amd64 signed-by={key_file}] "
f"{url} {codename} main"
)
target_file = f"/etc/apt/sources.list.d/{name}.list"
tee_option = "-a" if file_exists(target_file) else ""
utils.exec_cmd(f'echo "{line}" | tee {tee_option} {target_file}')
self.index_updated = False
def update(self, force=False):
"""Update local cache.""" """Update local cache."""
if self.index_updated and not force: if self.index_updated:
return return
utils.exec_cmd("apt-get -o Dpkg::Progress-Fancy=0 update --quiet") utils.exec_cmd("apt-get update --quiet")
self.index_updated = True self.index_updated = True
def preconfigure(self, name, question, qtype, answer): def preconfigure(self, name, question, qtype, answer):
@@ -89,18 +57,18 @@ class DEBPackage(Package):
def install(self, name): def install(self, name):
"""Install a package.""" """Install a package."""
self.update() self.update()
utils.exec_cmd("apt-get -o Dpkg::Progress-Fancy=0 install --quiet --assume-yes -o DPkg::options::=--force-confold {}".format(name)) utils.exec_cmd("apt-get install --quiet --assume-yes {}".format(name))
def install_many(self, names): def install_many(self, names):
"""Install many packages.""" """Install many packages."""
self.update() self.update()
return utils.exec_cmd("apt-get -o Dpkg::Progress-Fancy=0 install --quiet --assume-yes -o DPkg::options::=--force-confold {}".format( return utils.exec_cmd("apt-get install --quiet --assume-yes {}".format(
" ".join(names))) " ".join(names)))
def get_installed_version(self, name): def get_installed_version(self, name):
"""Get installed package version.""" """Get installed package version."""
code, output = utils.exec_cmd( code, output = utils.exec_cmd(
"dpkg -s {} | grep Version".format(name)) "dpkg -s {} | grep Version".format(name), capture_output=True)
match = re.match(r"Version: (\d:)?(.+)-\d", output.decode()) match = re.match(r"Version: (\d:)?(.+)-\d", output.decode())
if match: if match:
return match.group(2) return match.group(2)
@@ -114,22 +82,32 @@ class RPMPackage(Package):
def __init__(self, dist_name): def __init__(self, dist_name):
"""Initialize backend.""" """Initialize backend."""
super().__init__(dist_name) self.dist_name = dist_name
if "centos" in dist_name: super(RPMPackage, self).__init__(dist_name)
def prepare_system(self):
if "centos" in self.dist_name:
utils.exec_cmd("dnf config-manager --set-enabled crb")
self.install("epel-release") self.install("epel-release")
self.update()
def update(self):
"""Update the database repo."""
utils.exec_cmd("dnf update -y --quiet")
def install(self, name): def install(self, name):
"""Install a package.""" """Install a package."""
utils.exec_cmd("yum install -y --quiet {}".format(name)) """Need to add check for rrdtool, sendmail-milter, libmemcached and --enablerepo=crb"""
utils.exec_cmd("dnf install -y --quiet {}".format(name))
def install_many(self, names): def install_many(self, names):
"""Install many packages.""" """Install many packages."""
return utils.exec_cmd("yum install -y --quiet {}".format(" ".join(names))) return utils.exec_cmd("dnf install -y --quiet {}".format(" ".join(names)))
def get_installed_version(self, name): def get_installed_version(self, name):
"""Get installed package version.""" """Get installed package version."""
code, output = utils.exec_cmd( code, output = utils.exec_cmd(
"rpm -qi {} | grep Version".format(name)) "rpm -qi {} | grep Version".format(name), capture_output=True)
match = re.match(r"Version\s+: (.+)", output.decode()) match = re.match(r"Version\s+: (.+)", output.decode())
if match: if match:
return match.group(1) return match.group(1)
@@ -140,7 +118,7 @@ def get_backend():
"""Return the appropriate package backend.""" """Return the appropriate package backend."""
distname = utils.dist_name() distname = utils.dist_name()
backend = None backend = None
if distname in ["debian", "debian gnu/linux", "ubuntu", "linuxmint"]: if distname in ["debian", "debian gnu/linux", "ubuntu"]:
backend = DEBPackage backend = DEBPackage
elif "centos" in distname: elif "centos" in distname:
backend = RPMPackage backend = RPMPackage

View File

@@ -1,7 +1,6 @@
"""Python related tools.""" """Python related tools."""
import os import os
import sys
from . import package from . import package
from . import utils from . import utils
@@ -46,40 +45,6 @@ def install_packages(names, venv=None, upgrade=False, **kwargs):
utils.exec_cmd(cmd, **kwargs) utils.exec_cmd(cmd, **kwargs)
def get_package_version(name, venv=None, **kwargs):
"""Returns the version of an installed package."""
cmd = "{} show {}".format(
get_pip_path(venv),
name
)
exit_code, output = utils.exec_cmd(cmd, **kwargs)
if exit_code != 0:
utils.error(f"Failed to get version of {name}. "
f"Output is: {output}")
sys.exit(1)
version_list_clean = []
for line in output.decode().split("\n"):
if not line.startswith("Version:"):
continue
version_item_list = line.split(":")
version_list = version_item_list[1].split(".")
for element in version_list:
try:
version_list_clean.append(int(element))
except ValueError:
utils.printcolor(
f"Failed to decode some part of the version of {name}",
utils.YELLOW)
version_list_clean.append(element)
if len(version_list_clean) == 0:
utils.printcolor(
f"Failed to find the version of {name}",
utils.RED)
sys.exit(1)
return version_list_clean
def install_package_from_repository(name, url, vcs="git", venv=None, **kwargs): def install_package_from_repository(name, url, vcs="git", venv=None, **kwargs):
"""Install a Python package from its repository.""" """Install a Python package from its repository."""
if vcs == "git": if vcs == "git":
@@ -89,10 +54,16 @@ def install_package_from_repository(name, url, vcs="git", venv=None, **kwargs):
utils.exec_cmd(cmd, **kwargs) utils.exec_cmd(cmd, **kwargs)
def setup_virtualenv(path, sudo_user=None): def setup_virtualenv(path, sudo_user=None, python_version=2):
"""Install a virtualenv if needed.""" """Install a virtualenv if needed."""
if os.path.exists(path): if os.path.exists(path):
return return
if python_version == 2:
python_binary = "python"
packages = ["python-virtualenv"]
if utils.dist_name() == "debian":
packages.append("virtualenv")
else:
if utils.dist_name().startswith("centos"): if utils.dist_name().startswith("centos"):
python_binary = "python3" python_binary = "python3"
packages = ["python3"] packages = ["python3"]
@@ -101,5 +72,8 @@ def setup_virtualenv(path, sudo_user=None):
packages = ["python3-venv"] packages = ["python3-venv"]
package.backend.install_many(packages) package.backend.install_many(packages)
with utils.settings(sudo_user=sudo_user): with utils.settings(sudo_user=sudo_user):
if python_version == 2:
utils.exec_cmd("virtualenv {}".format(path))
else:
utils.exec_cmd("{} -m venv {}".format(python_binary, path)) utils.exec_cmd("{} -m venv {}".format(python_binary, path))
install_packages(["pip", "setuptools"], venv=path, upgrade=True) install_packages(["pip", "setuptools\<58.0.0"], venv=path, upgrade=True)

View File

@@ -21,7 +21,7 @@ class Amavis(base.Installer):
"unrar-free", "unrar-free",
], ],
"rpm": [ "rpm": [
"amavisd-new", "arj", "lz4", "lzop", "p7zip", "amavis", "arj", "lz4", "lzop", "p7zip",
], ],
} }
with_db = True with_db = True
@@ -52,17 +52,7 @@ class Amavis(base.Installer):
packages = super(Amavis, self).get_packages() packages = super(Amavis, self).get_packages()
if package.backend.FORMAT == "deb": if package.backend.FORMAT == "deb":
db_driver = "pg" if self.db_driver == "pgsql" else self.db_driver db_driver = "pg" if self.db_driver == "pgsql" else self.db_driver
packages += ["libdbd-{}-perl".format(db_driver)] return packages + ["libdbd-{}-perl".format(db_driver)]
name, version = utils.dist_info()
try:
major_version = int(version.split(".")[0])
except ValueError:
major_version = 0
if major_version >= 13:
packages = [p if p != "liblz4-tool" else "lz4" for p in packages]
return packages
if self.db_driver == "pgsql": if self.db_driver == "pgsql":
db_driver = "Pg" db_driver = "Pg"
elif self.db_driver == "mysql": elif self.db_driver == "mysql":

View File

@@ -0,0 +1,102 @@
"""Automx related tasks."""
import os
import pwd
import shutil
import stat
from .. import python
from .. import system
from .. import utils
from . import base
class Automx(base.Installer):
"""Automx installation."""
appname = "automx"
config_files = ["automx.conf"]
no_daemon = True
packages = {
"deb": ["memcached", "unzip"],
"rpm": ["memcached", "unzip"]
}
with_user = True
def __init__(self, *args, **kwargs):
"""Get configuration."""
super(Automx, self).__init__(*args, **kwargs)
self.venv_path = self.config.get("automx", "venv_path")
self.instance_path = self.config.get("automx", "instance_path")
def get_template_context(self):
"""Additional variables."""
context = super(Automx, self).get_template_context()
sql_dsn = "{}://{}:{}@{}:{}/{}".format(
"postgresql" if self.dbengine == "postgres" else self.dbengine,
self.config.get("modoboa", "dbuser"),
self.config.get("modoboa", "dbpassword"),
self.dbhost,
self.dbport,
self.config.get("modoboa", "dbname"))
if self.db_driver == "pgsql":
sql_query = (
"SELECT first_name || ' ' || last_name AS display_name, email"
", SPLIT_PART(email, '@', 2) AS domain "
"FROM core_user WHERE email='%s' AND is_active")
else:
sql_query = (
"SELECT concat(first_name, ' ', last_name) AS display_name, "
"email, SUBSTRING_INDEX(email, '@', -1) AS domain "
"FROM core_user WHERE email='%s' AND is_active=1"
)
context.update({"sql_dsn": sql_dsn, "sql_query": sql_query})
return context
def _setup_venv(self):
"""Prepare a python virtualenv."""
python.setup_virtualenv(
self.venv_path, sudo_user=self.user, python_version=3)
packages = [
"future", "lxml", "ipaddress", "sqlalchemy", "python-memcached",
"python-dateutil", "configparser"
]
if self.dbengine == "postgres":
packages.append("psycopg2-binary")
else:
packages.append("mysqlclient")
python.install_packages(packages, self.venv_path, sudo_user=self.user)
target = "{}/master.zip".format(self.home_dir)
if os.path.exists(target):
os.unlink(target)
utils.exec_cmd(
"wget https://github.com/sys4/automx/archive/master.zip",
sudo_user=self.user, cwd=self.home_dir)
self.repo_dir = "{}/automx-master".format(self.home_dir)
if os.path.exists(self.repo_dir):
shutil.rmtree(self.repo_dir)
utils.exec_cmd(
"unzip master.zip", sudo_user=self.user, cwd=self.home_dir)
utils.exec_cmd(
"{} setup.py install".format(
python.get_path("python", self.venv_path)),
cwd=self.repo_dir)
def _deploy_instance(self):
"""Copy files to instance dir."""
if not os.path.exists(self.instance_path):
pw = pwd.getpwnam(self.user)
mode = (
stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP |
stat.S_IROTH | stat.S_IXOTH)
utils.mkdir(self.instance_path, mode, pw[2], pw[3])
path = "{}/src/automx_wsgi.py".format(self.repo_dir)
utils.exec_cmd("cp {} {}".format(path, self.instance_path),
sudo_user=self.user, cwd=self.home_dir)
def post_run(self):
"""Additional tasks."""
self._setup_venv()
self._deploy_instance()
system.enable_and_start_service("memcached")

View File

@@ -44,7 +44,8 @@ class Backup:
path_exists = os.path.exists(path) path_exists = os.path.exists(path)
if path_exists and os.path.isfile(path): if path_exists and os.path.isfile(path):
utils.error("Error, you provided a file instead of a directory!") utils.printcolor(
"Error, you provided a file instead of a directory!", utils.RED)
return False return False
if not path_exists: if not path_exists:
@@ -57,7 +58,9 @@ class Backup:
utils.mkdir_safe(path, stat.S_IRWXU | utils.mkdir_safe(path, stat.S_IRWXU |
stat.S_IRWXG, pw[2], pw[3]) stat.S_IRWXG, pw[2], pw[3])
else: else:
utils.error("Error, backup directory not present.") utils.printcolor(
"Error, backup directory not present.", utils.RED
)
return False return False
if len(os.listdir(path)) != 0: if len(os.listdir(path)) != 0:
@@ -77,7 +80,9 @@ class Backup:
shutil.rmtree(os.path.join(path, "databases"), shutil.rmtree(os.path.join(path, "databases"),
ignore_errors=False) ignore_errors=False)
else: else:
utils.error("Error: backup directory not clean.") utils.printcolor(
"Error: backup directory not clean.", utils.RED
)
return False return False
self.backup_path = path self.backup_path = path
@@ -126,8 +131,8 @@ class Backup:
home_path = self.config.get("dovecot", "home_dir") home_path = self.config.get("dovecot", "home_dir")
if not os.path.exists(home_path) or os.path.isfile(home_path): if not os.path.exists(home_path) or os.path.isfile(home_path):
utils.error("Error backing up Email, provided path " utils.printcolor("Error backing up Email, provided path "
f" ({home_path}) seems not right...") f" ({home_path}) seems not right...", utils.RED)
else: else:
dst = os.path.join(self.backup_path, "mails/") dst = os.path.join(self.backup_path, "mails/")
@@ -142,7 +147,7 @@ class Backup:
""" """
Custom config : Custom config :
- DKIM keys: {{keys_storage_dir}} - DKIM keys: {{keys_storage_dir}}
- Radicale collection (calendars, contacts): {{home_dir}} - Radicale collection (calendat, contacts): {{home_dir}}
- Amavis : /etc/amavis/conf.d/99-custom - Amavis : /etc/amavis/conf.d/99-custom
- Postwhite : /etc/postwhite.conf - Postwhite : /etc/postwhite.conf
Feel free to suggest to add others! Feel free to suggest to add others!

View File

@@ -2,27 +2,25 @@
import os import os
import sys import sys
from typing import Optional
from .. import database from .. import database
from .. import package from .. import package
from .. import python
from .. import system from .. import system
from .. import utils from .. import utils
class Installer: class Installer(object):
"""Simple installer for one application.""" """Simple installer for one application."""
appname: str appname = None
no_daemon: bool = False no_daemon = False
daemon_name: Optional[str] = None daemon_name = None
packages: dict[str, list[str]] = {} packages = {}
with_user: bool = False with_user = False
with_db: bool = False with_db = False
config_files: list[str] = [] config_files = []
def __init__(self, config, upgrade: bool, archive_path: str) -> None: def __init__(self, config, upgrade: bool, archive_path: str):
"""Get configuration.""" """Get configuration."""
self.config = config self.config = config
self.upgrade = upgrade self.upgrade = upgrade
@@ -44,20 +42,6 @@ class Installer:
self.dbuser = self.config.get(self.appname, "dbuser") self.dbuser = self.config.get(self.appname, "dbuser")
self.dbpasswd = self.config.get(self.appname, "dbpassword") self.dbpasswd = self.config.get(self.appname, "dbpassword")
@property
def modoboa_2_2_or_greater(self) -> bool:
# Check if modoboa version > 2.2
modoboa_version = python.get_package_version(
"modoboa",
self.config.get("modoboa", "venv_path"),
sudo_user=self.config.get("modoboa", "user")
)
condition = (
(int(modoboa_version[0]) == 2 and int(modoboa_version[1]) >= 2) or
int(modoboa_version[0]) > 2
)
return condition
@property @property
def config_dir(self): def config_dir(self):
"""Return main configuration directory.""" """Return main configuration directory."""
@@ -147,7 +131,7 @@ class Installer:
return return
exitcode, output = package.backend.install_many(packages) exitcode, output = package.backend.install_many(packages)
if exitcode: if exitcode:
utils.error("Failed to install dependencies") utils.printcolor("Failed to install dependencies", utils.RED)
sys.exit(1) sys.exit(1)
def get_config_files(self): def get_config_files(self):

View File

@@ -15,7 +15,7 @@ class Clamav(base.Installer):
packages = { packages = {
"deb": ["clamav-daemon"], "deb": ["clamav-daemon"],
"rpm": [ "rpm": [
"clamav", "clamav-update", "clamav-server", "clamav-server-systemd" "clamav", "clamav-update", "clamd"
], ],
} }
@@ -42,7 +42,6 @@ class Clamav(base.Installer):
"""Additional tasks.""" """Additional tasks."""
if package.backend.FORMAT == "deb": if package.backend.FORMAT == "deb":
user = self.config.get(self.appname, "user") user = self.config.get(self.appname, "user")
if self.config.getboolean("amavis", "enabled"):
system.add_user_to_group( system.add_user_to_group(
user, self.config.get("amavis", "user") user, self.config.get("amavis", "user")
) )
@@ -58,7 +57,7 @@ class Clamav(base.Installer):
# Check if not present before # Check if not present before
path = "/usr/lib/systemd/system/clamd@.service" path = "/usr/lib/systemd/system/clamd@.service"
code, output = utils.exec_cmd( code, output = utils.exec_cmd(
r"grep 'WantedBy\s*=\s*multi-user.target' {}".format(path)) "grep 'WantedBy=multi-user.target' {}".format(path))
if code: if code:
utils.exec_cmd( utils.exec_cmd(
"""cat <<EOM >> {} """cat <<EOM >> {}

View File

@@ -4,7 +4,6 @@ import glob
import os import os
import pwd import pwd
import shutil import shutil
import stat
from .. import database from .. import database
from .. import package from .. import package
@@ -15,135 +14,61 @@ from . import base
class Dovecot(base.Installer): class Dovecot(base.Installer):
"""Dovecot installer.""" """Dovecot installer."""
appname = "dovecot" appname = "dovecot"
packages = { packages = {
"deb": [ "deb": [
"dovecot-imapd", "dovecot-imapd", "dovecot-lmtpd", "dovecot-managesieved",
"dovecot-lmtpd", "dovecot-sieve"],
"dovecot-managesieved", "rpm": [
"dovecot-sieve", "dovecot", "dovecot-pigeonhole"]
],
"rpm": ["dovecot", "dovecot-pigeonhole"],
}
per_version_config_files = {
"2.3": [
"dovecot.conf",
"dovecot-dict-sql.conf.ext",
"conf.d/10-ssl.conf",
"conf.d/10-master.conf",
"conf.d/20-lmtp.conf",
"conf.d/10-ssl-keys.try",
"conf.d/dovecot-oauth2.conf.ext",
],
"2.4": [
"dovecot.conf",
"conf.d/10-mail.conf",
"conf.d/10-master.conf",
"conf.d/10-ssl.conf",
"conf.d/10-ssl-keys.try",
"conf.d/15-mailboxes.conf",
"conf.d/20-lmtp.conf",
"conf.d/auth-oauth2.conf.ext",
],
} }
config_files = [
"dovecot.conf", "dovecot-dict-sql.conf.ext", "conf.d/10-ssl.conf",
"conf.d/10-master.conf", "conf.d/20-lmtp.conf", "conf.d/10-ssl-keys.try"]
with_user = True with_user = True
@property def get_config_files(self):
def version(self) -> str:
if not hasattr(self, "_version"):
self._version = package.backend.get_installed_version("dovecot-core")[:3]
return self._version
def setup_user(self):
"""Setup mailbox user."""
super().setup_user()
self.mailboxes_owner = self.app_config["mailboxes_owner"]
system.create_user(self.mailboxes_owner, self.home_dir)
def _get_config_files_for_version(self, version: str) -> list[str]:
files = self.per_version_config_files[version]
if version == "2.4":
files += [
f"conf.d/auth-sql-{self.dbengine}.conf.ext=conf.d/auth-sql.conf.ext",
f"conf.d/auth-master-{self.dbengine}.conf.ext=conf.d/auth-master.conf.ext",
f"conf.d/30-dict-server-{self.dbengine}.conf=conf.d/30-dict-server.conf",
]
else:
files += [
f"dovecot-sql-{self.dbengine}.conf.ext=dovecot-sql.conf.ext",
f"dovecot-sql-master-{self.dbengine}.conf.ext=dovecot-sql-master.conf.ext",
]
result = []
for path in files:
if "=" not in path:
result.append(f"{version}/{path}={path}")
else:
src, dst = path.split("=")
result.append(f"{version}/{src}={dst}")
return result
def get_config_files(self) -> list[str]:
"""Additional config files.""" """Additional config files."""
_config_files = self._get_config_files_for_version(self.version) return self.config_files + [
_config_files.append( "dovecot-sql-{}.conf.ext=dovecot-sql.conf.ext"
f"postlogin-{self.dbengine}.sh=/usr/local/bin/postlogin.sh" .format(self.dbengine),
) "dovecot-sql-master-{}.conf.ext=dovecot-sql-master.conf.ext"
if self.app_config["move_spam_to_junk"]: .format(self.dbengine),
_config_files += [ "postlogin-{}.sh=/usr/local/bin/postlogin.sh"
"custom_after_sieve/spam-to-junk.sieve=conf.d/custom_after_sieve/spam-to-junk.sieve", .format(self.dbengine),
f"{self.version}/conf.d/90-sieve.conf=conf.d/90-sieve.conf",
] ]
return _config_files
def get_packages(self): def get_packages(self):
"""Additional packages.""" """Additional packages."""
packages = ["dovecot-{}".format(self.db_driver)] packages = ["dovecot-{}".format(self.db_driver)]
if package.backend.FORMAT == "deb": if package.backend.FORMAT == "deb":
if "pop3" in self.config.get("dovecot", "extra_protocols"): if "pop3" in self.config.get("dovecot", "extra_protocols"):
packages += ["dovecot-pop3d"] packages += ["dovecot-pop3d"]
packages += super().get_packages() return super(Dovecot, self).get_packages() + packages
backports_codename = getattr(self, "backports_codename", None)
if backports_codename:
packages = [
f"{package}/{backports_codename}-backports" for package in packages
]
return packages
def install_packages(self): def install_packages(self):
"""Preconfigure Dovecot if needed.""" """Preconfigure Dovecot if needed."""
name, version = utils.dist_info()
name = name.lower()
if name.startswith("debian") and version.startswith("12"):
package.backend.enable_backports("bookworm")
self.backports_codename = "bookworm"
package.backend.preconfigure( package.backend.preconfigure(
"dovecot-core", "create-ssl-cert", "boolean", "false" "dovecot-core", "create-ssl-cert", "boolean", "false")
) super(Dovecot, self).install_packages()
super().install_packages()
def get_template_context(self): def get_template_context(self):
"""Additional variables.""" """Additional variables."""
context = super().get_template_context() context = super(Dovecot, self).get_template_context()
pw_mailbox = pwd.getpwnam(self.mailboxes_owner) pw = pwd.getpwnam(self.user)
dovecot_package = {"deb": "dovecot-core", "rpm": "dovecot"} dovecot_package = {"deb": "dovecot-core", "rpm": "dovecot"}
ssl_protocol_parameter = "ssl_protocols" ssl_protocol_parameter = "ssl_protocols"
if ( if package.backend.get_installed_version(dovecot_package[package.backend.FORMAT]) > "2.3":
package.backend.get_installed_version(
dovecot_package[package.backend.FORMAT]
)
> "2.3"
):
ssl_protocol_parameter = "ssl_min_protocol" ssl_protocol_parameter = "ssl_min_protocol"
ssl_protocols = "!SSLv2 !SSLv3" ssl_protocols = "!SSLv2 !SSLv3"
if package.backend.get_installed_version("openssl").startswith( if package.backend.get_installed_version("openssl").startswith("1.1") \
"1.1" or package.backend.get_installed_version("openssl").startswith("3"):
) or package.backend.get_installed_version("openssl").startswith("3"):
ssl_protocols = "!SSLv3" ssl_protocols = "!SSLv3"
if ssl_protocol_parameter == "ssl_min_protocol": if ssl_protocol_parameter == "ssl_min_protocol":
ssl_protocols = "TLSv1.2" ssl_protocols = "TLSv1"
if "centos" in utils.dist_name(): if "centos" in utils.dist_name():
protocols = "protocols = imap lmtp sieve" protocols = "protocols = imap lmtp sieve"
extra_protocols = self.config.get("dovecot", "extra_protocols") extra_protocols = self.config.get("dovecot", "extra_protocols")
@@ -152,22 +77,10 @@ class Dovecot(base.Installer):
else: else:
# Protocols are automatically guessed on debian/ubuntu # Protocols are automatically guessed on debian/ubuntu
protocols = "" protocols = ""
context.update({
oauth2_client_id, oauth2_client_secret = utils.create_oauth2_app(
"Dovecot", "dovecot", self.config
)
hostname = self.config.get("general", "hostname")
oauth2_introspection_url = (
f"https://{oauth2_client_id}:{oauth2_client_secret}"
f"@{hostname}/api/o/introspect/"
)
context.update(
{
"db_driver": self.db_driver, "db_driver": self.db_driver,
"mailboxes_owner_uid": pw_mailbox[2], "mailboxes_owner_uid": pw[2],
"mailboxes_owner_gid": pw_mailbox[3], "mailboxes_owner_gid": pw[3],
"mailbox_owner": self.mailboxes_owner,
"modoboa_user": self.config.get("modoboa", "user"), "modoboa_user": self.config.get("modoboa", "user"),
"modoboa_dbname": self.config.get("modoboa", "dbname"), "modoboa_dbname": self.config.get("modoboa", "dbname"),
"modoboa_dbuser": self.config.get("modoboa", "dbuser"), "modoboa_dbuser": self.config.get("modoboa", "dbuser"),
@@ -175,70 +88,37 @@ class Dovecot(base.Installer):
"protocols": protocols, "protocols": protocols,
"ssl_protocols": ssl_protocols, "ssl_protocols": ssl_protocols,
"ssl_protocol_parameter": ssl_protocol_parameter, "ssl_protocol_parameter": ssl_protocol_parameter,
"modoboa_2_2_or_greater": "" if self.modoboa_2_2_or_greater else "#",
"not_modoboa_2_2_or_greater": (
"" if not self.modoboa_2_2_or_greater else "#"
),
"do_move_spam_to_junk": (
"" if self.app_config["move_spam_to_junk"] else "#"
),
"oauth2_introspection_url": oauth2_introspection_url,
"radicale_user": self.config.get("radicale", "user"), "radicale_user": self.config.get("radicale", "user"),
} "radicale_auth_socket_path": os.path.basename(
) self.config.get("dovecot", "radicale_auth_socket_path"))
})
return context return context
def install_config_files(self):
"""Create sieve dir if needed."""
if self.app_config["move_spam_to_junk"]:
utils.mkdir_safe(
f"{self.config_dir}/conf.d/custom_after_sieve",
stat.S_IRWXU
| stat.S_IRGRP
| stat.S_IXGRP
| stat.S_IROTH
| stat.S_IXOTH,
0,
0,
)
super().install_config_files()
def post_run(self): def post_run(self):
"""Additional tasks.""" """Additional tasks."""
if self.version == "2.3" and self.dbengine == "postgres": if self.dbengine == "postgres":
dbname = self.config.get("modoboa", "dbname") dbname = self.config.get("modoboa", "dbname")
dbuser = self.config.get("modoboa", "dbuser") dbuser = self.config.get("modoboa", "dbuser")
dbpassword = self.config.get("modoboa", "dbpassword") dbpassword = self.config.get("modoboa", "dbpassword")
backend = database.get_backend(self.config) backend = database.get_backend(self.config)
backend.load_sql_file( backend.load_sql_file(
dbname, dbname, dbuser, dbpassword,
dbuser, self.get_file_path("install_modoboa_postgres_trigger.sql")
dbpassword,
self.get_file_path("install_modoboa_postgres_trigger.sql"),
) )
backend.load_sql_file( backend.load_sql_file(
dbname, dbname, dbuser, dbpassword,
dbuser, self.get_file_path("fix_modoboa_postgres_schema.sql")
dbpassword,
self.get_file_path("fix_modoboa_postgres_schema.sql"),
) )
for f in glob.glob(f"{self.get_file_path(f'{self.version}/conf.d')}/*"): for f in glob.glob("{}/*".format(self.get_file_path("conf.d"))):
if os.path.isfile(f):
utils.copy_file(f, "{}/conf.d".format(self.config_dir)) utils.copy_file(f, "{}/conf.d".format(self.config_dir))
# Make postlogin script executable # Make postlogin script executable
utils.exec_cmd("chmod +x /usr/local/bin/postlogin.sh") utils.exec_cmd("chmod +x /usr/local/bin/postlogin.sh")
# Only root should have read access to the 10-ssl-keys.try
# See https://github.com/modoboa/modoboa/issues/2570
utils.exec_cmd("chmod 600 /etc/dovecot/conf.d/10-ssl-keys.try")
# Add mailboxes user to dovecot group for modoboa mailbox commands. # Add mailboxes user to dovecot group for modoboa mailbox commands.
# See https://github.com/modoboa/modoboa/issues/2157. # See https://github.com/modoboa/modoboa/issues/2157.
if self.app_config["move_spam_to_junk"]: system.add_user_to_group(
# Compile sieve script self.config.get("dovecot", "mailboxes_owner"),
sieve_file = ( 'dovecot'
f"{self.config_dir}/conf.d/custom_after_sieve/spam-to-junk.sieve"
) )
utils.exec_cmd(f"/usr/bin/sievec {sieve_file}")
system.add_user_to_group(self.mailboxes_owner, "dovecot")
def restart_daemon(self): def restart_daemon(self):
"""Restart daemon process. """Restart daemon process.
@@ -252,8 +132,7 @@ class Dovecot(base.Installer):
action = "start" if code else "restart" action = "start" if code else "restart"
utils.exec_cmd( utils.exec_cmd(
"service {} {} > /dev/null 2>&1".format(self.appname, action), "service {} {} > /dev/null 2>&1".format(self.appname, action),
capture_output=False, capture_output=False)
)
system.enable_service(self.get_daemon_name()) system.enable_service(self.get_daemon_name())
def backup(self, path): def backup(self, path):
@@ -261,10 +140,8 @@ class Dovecot(base.Installer):
home_dir = self.config.get("dovecot", "home_dir") home_dir = self.config.get("dovecot", "home_dir")
utils.printcolor("Backing up mails", utils.MAGENTA) utils.printcolor("Backing up mails", utils.MAGENTA)
if not os.path.exists(home_dir) or os.path.isfile(home_dir): if not os.path.exists(home_dir) or os.path.isfile(home_dir):
utils.error( utils.error("Error backing up emails, provided path "
"Error backing up emails, provided path " f" ({home_dir}) seems not right...")
f" ({home_dir}) seems not right..."
)
return return
dst = os.path.join(path, "mails/") dst = os.path.join(path, "mails/")
@@ -284,15 +161,12 @@ class Dovecot(base.Installer):
shutil.copytree(mail_dir, home_dir) shutil.copytree(mail_dir, home_dir)
# Resetting permission for vmail # Resetting permission for vmail
for dirpath, dirnames, filenames in os.walk(home_dir): for dirpath, dirnames, filenames in os.walk(home_dir):
shutil.chown(dirpath, self.mailboxes_owner, self.mailboxes_owner) shutil.chown(dirpath, self.user, self.user)
for filename in filenames: for filename in filenames:
shutil.chown( shutil.chown(os.path.join(dirpath, filename),
os.path.join(dirpath, filename), self.user, self.user)
self.mailboxes_owner,
self.mailboxes_owner,
)
else: else:
utils.printcolor( utils.printcolor(
"It seems that emails were not backed up, skipping restoration.", "It seems that emails were not backed up, skipping restoration.",
utils.MAGENTA, utils.MAGENTA
) )

View File

@@ -1,17 +0,0 @@
"""fail2ban related functions."""
from . import base
class Fail2ban(base.Installer):
"""Fail2ban installer."""
appname = "fail2ban"
packages = {
"deb": ["fail2ban"],
"rpm": ["fail2ban"]
}
config_files = [
"jail.d/modoboa.conf",
"filter.d/modoboa-auth.conf",
]

View File

@@ -1,213 +0,0 @@
-- Amavis 2.11.0 MySQL schema
-- Provided by Modoboa
-- Warning: foreign key creations are enabled
-- local users
CREATE TABLE users (
id int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY, -- unique id
priority integer NOT NULL DEFAULT '7', -- sort field, 0 is low prior.
policy_id integer unsigned NOT NULL DEFAULT '1', -- JOINs with policy.id
email varbinary(255) NOT NULL UNIQUE,
fullname varchar(255) DEFAULT NULL -- not used by amavisd-new
-- local char(1) -- Y/N (optional field, see note further down)
);
-- any e-mail address (non- rfc2822-quoted), external or local,
-- used as senders in wblist
CREATE TABLE mailaddr (
id int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
priority integer NOT NULL DEFAULT '7', -- 0 is low priority
email varbinary(255) NOT NULL UNIQUE
);
-- per-recipient whitelist and/or blacklist,
-- puts sender and recipient in relation wb (white or blacklisted sender)
CREATE TABLE wblist (
rid integer unsigned NOT NULL, -- recipient: users.id
sid integer unsigned NOT NULL, -- sender: mailaddr.id
wb varchar(10) NOT NULL, -- W or Y / B or N / space=neutral / score
PRIMARY KEY (rid,sid)
);
CREATE TABLE policy (
id int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
-- 'id' this is the _only_ required field
policy_name varchar(32), -- not used by amavisd-new, a comment
virus_lover char(1) default NULL, -- Y/N
spam_lover char(1) default NULL, -- Y/N
unchecked_lover char(1) default NULL, -- Y/N
banned_files_lover char(1) default NULL, -- Y/N
bad_header_lover char(1) default NULL, -- Y/N
bypass_virus_checks char(1) default NULL, -- Y/N
bypass_spam_checks char(1) default NULL, -- Y/N
bypass_banned_checks char(1) default NULL, -- Y/N
bypass_header_checks char(1) default NULL, -- Y/N
virus_quarantine_to varchar(64) default NULL,
spam_quarantine_to varchar(64) default NULL,
banned_quarantine_to varchar(64) default NULL,
unchecked_quarantine_to varchar(64) default NULL,
bad_header_quarantine_to varchar(64) default NULL,
clean_quarantine_to varchar(64) default NULL,
archive_quarantine_to varchar(64) default NULL,
spam_tag_level float default NULL, -- higher score inserts spam info headers
spam_tag2_level float default NULL, -- inserts 'declared spam' header fields
spam_tag3_level float default NULL, -- inserts 'blatant spam' header fields
spam_kill_level float default NULL, -- higher score triggers evasive actions
-- e.g. reject/drop, quarantine, ...
-- (subject to final_spam_destiny setting)
spam_dsn_cutoff_level float default NULL,
spam_quarantine_cutoff_level float default NULL,
addr_extension_virus varchar(64) default NULL,
addr_extension_spam varchar(64) default NULL,
addr_extension_banned varchar(64) default NULL,
addr_extension_bad_header varchar(64) default NULL,
warnvirusrecip char(1) default NULL, -- Y/N
warnbannedrecip char(1) default NULL, -- Y/N
warnbadhrecip char(1) default NULL, -- Y/N
newvirus_admin varchar(64) default NULL,
virus_admin varchar(64) default NULL,
banned_admin varchar(64) default NULL,
bad_header_admin varchar(64) default NULL,
spam_admin varchar(64) default NULL,
spam_subject_tag varchar(64) default NULL,
spam_subject_tag2 varchar(64) default NULL,
spam_subject_tag3 varchar(64) default NULL,
message_size_limit integer default NULL, -- max size in bytes, 0 disable
banned_rulenames varchar(64) default NULL, -- comma-separated list of ...
-- names mapped through %banned_rules to actual banned_filename tables
disclaimer_options varchar(64) default NULL,
forward_method varchar(64) default NULL,
sa_userconf varchar(64) default NULL,
sa_username varchar(64) default NULL
);
-- R/W part of the dataset (optional)
-- May reside in the same or in a separate database as lookups database;
-- REQUIRES SUPPORT FOR TRANSACTIONS; specified in @storage_sql_dsn
--
-- MySQL note ( http://dev.mysql.com/doc/mysql/en/storage-engines.html ):
-- ENGINE is the preferred term, but cannot be used before MySQL 4.0.18.
-- TYPE is available beginning with MySQL 3.23.0, the first version of
-- MySQL for which multiple storage engines were available. If you omit
-- the ENGINE or TYPE option, the default storage engine is used.
-- By default this is MyISAM.
--
-- Please create additional indexes on keys when needed, or drop suggested
-- ones as appropriate to optimize queries needed by a management application.
-- See your database documentation for further optimization hints. With MySQL
-- see Chapter 15 of the reference manual. For example the chapter 15.17 says:
-- InnoDB does not keep an internal count of rows in a table. To process a
-- SELECT COUNT(*) FROM T statement, InnoDB must scan an index of the table,
-- which takes some time if the index is not entirely in the buffer pool.
--
-- Wayne Smith adds: When using MySQL with InnoDB one might want to
-- increase buffer size for both pool and log, and might also want
-- to change flush settings for a little better performance. Example:
-- innodb_buffer_pool_size = 384M
-- innodb_log_buffer_size = 8M
-- innodb_flush_log_at_trx_commit = 0
-- The big performance increase is the first two, the third just helps with
-- lowering disk activity. Consider also adjusting the key_buffer_size.
-- provide unique id for each e-mail address, avoids storing copies
CREATE TABLE maddr (
partition_tag integer DEFAULT 0, -- see $partition_tag
id bigint unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
email varbinary(255) NOT NULL, -- full mail address
domain varchar(255) NOT NULL, -- only domain part of the email address
-- with subdomain fields in reverse
CONSTRAINT part_email UNIQUE (partition_tag,email)
) ENGINE=InnoDB;
-- information pertaining to each processed message as a whole;
-- NOTE: records with NULL msgs.content should be ignored by utilities,
-- as such records correspond to messages just being processes, or were lost
-- NOTE: instead of a character field time_iso, one might prefer:
-- time_iso TIMESTAMP NOT NULL DEFAULT 0,
-- but the following MUST then be set in amavisd.conf: $timestamp_fmt_mysql=1
CREATE TABLE msgs (
partition_tag integer DEFAULT 0, -- see $partition_tag
mail_id varbinary(16) NOT NULL, -- long-term unique mail id, dflt 12 ch
secret_id varbinary(16) DEFAULT '', -- authorizes release of mail_id, 12 ch
am_id varchar(20) NOT NULL, -- id used in the log
time_num integer unsigned NOT NULL, -- rx_time: seconds since Unix epoch
time_iso char(16) NOT NULL, -- rx_time: ISO8601 UTC ascii time
sid bigint unsigned NOT NULL, -- sender: maddr.id
policy varchar(255) DEFAULT '', -- policy bank path (like macro %p)
client_addr varchar(255) DEFAULT '', -- SMTP client IP address (IPv4 or v6)
size integer unsigned NOT NULL, -- message size in bytes
originating char(1) DEFAULT ' ' NOT NULL, -- sender from inside or auth'd
content char(1), -- content type: V/B/U/S/Y/M/H/O/T/C
-- virus/banned/unchecked/spam(kill)/spammy(tag2)/
-- /bad-mime/bad-header/oversized/mta-err/clean
-- is NULL on partially processed mail
-- (prior to 2.7.0 the CC_SPAMMY was logged as 's', now 'Y' is used;
-- to avoid a need for case-insenstivity in queries)
quar_type char(1), -- quarantined as: ' '/F/Z/B/Q/M/L
-- none/file/zipfile/bsmtp/sql/
-- /mailbox(smtp)/mailbox(lmtp)
quar_loc varbinary(255) DEFAULT '', -- quarantine location (e.g. file)
dsn_sent char(1), -- was DSN sent? Y/N/q (q=quenched)
spam_level float, -- SA spam level (no boosts)
message_id varchar(255) DEFAULT '', -- mail Message-ID header field
from_addr varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT '',
-- mail From header field, UTF8
subject varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT '',
-- mail Subject header field, UTF8
host varchar(255) NOT NULL, -- hostname where amavisd is running
PRIMARY KEY (partition_tag,mail_id),
FOREIGN KEY (sid) REFERENCES maddr(id) ON DELETE RESTRICT
) ENGINE=InnoDB;
CREATE INDEX msgs_idx_sid ON msgs (sid);
CREATE INDEX msgs_idx_mess_id ON msgs (message_id); -- useful with pen pals
CREATE INDEX msgs_idx_time_num ON msgs (time_num);
-- alternatively when purging based on time_iso (instead of msgs_idx_time_num):
CREATE INDEX msgs_idx_time_iso ON msgs (time_iso);
-- When using FOREIGN KEY contraints, InnoDB requires index on a field
-- (an the field must be the first field in the index). Hence create it:
CREATE INDEX msgs_idx_mail_id ON msgs (mail_id);
-- per-recipient information related to each processed message;
-- NOTE: records in msgrcpt without corresponding msgs.mail_id record are
-- orphaned and should be ignored and eventually deleted by external utilities
CREATE TABLE msgrcpt (
partition_tag integer DEFAULT 0, -- see $partition_tag
mail_id varbinary(16) NOT NULL, -- (must allow duplicates)
rseqnum integer DEFAULT 0 NOT NULL, -- recip's enumeration within msg
rid bigint unsigned NOT NULL, -- recipient: maddr.id (dupl. allowed)
is_local char(1) DEFAULT ' ' NOT NULL, -- recip is: Y=local, N=foreign
content char(1) DEFAULT ' ' NOT NULL, -- content type V/B/U/S/Y/M/H/O/T/C
ds char(1) NOT NULL, -- delivery status: P/R/B/D/T
-- pass/reject/bounce/discard/tempfail
rs char(1) NOT NULL, -- release status: initialized to ' '
bl char(1) DEFAULT ' ', -- sender blacklisted by this recip
wl char(1) DEFAULT ' ', -- sender whitelisted by this recip
bspam_level float, -- per-recipient (total) spam level
smtp_resp varchar(255) DEFAULT '', -- SMTP response given to MTA
PRIMARY KEY (partition_tag,mail_id,rseqnum),
FOREIGN KEY (rid) REFERENCES maddr(id) ON DELETE RESTRICT,
FOREIGN KEY (mail_id) REFERENCES msgs(mail_id) ON DELETE CASCADE
) ENGINE=InnoDB;
CREATE INDEX msgrcpt_idx_mail_id ON msgrcpt (mail_id);
CREATE INDEX msgrcpt_idx_rid ON msgrcpt (rid);
-- Additional index on rs since Modoboa uses it to filter its quarantine
CREATE INDEX msgrcpt_idx_rs ON msgrcpt (rs);
-- mail quarantine in SQL, enabled by $*_quarantine_method='sql:'
-- NOTE: records in quarantine without corresponding msgs.mail_id record are
-- orphaned and should be ignored and eventually deleted by external utilities
CREATE TABLE quarantine (
partition_tag integer DEFAULT 0, -- see $partition_tag
mail_id varbinary(16) NOT NULL, -- long-term unique mail id
chunk_ind integer unsigned NOT NULL, -- chunk number, starting with 1
mail_text blob NOT NULL, -- store mail as chunks of octets
PRIMARY KEY (partition_tag,mail_id,chunk_ind),
FOREIGN KEY (mail_id) REFERENCES msgs(mail_id) ON DELETE CASCADE
) ENGINE=InnoDB;

View File

@@ -1,189 +0,0 @@
CREATE TABLE policy (
id serial PRIMARY KEY, -- 'id' is the _only_ required field
policy_name varchar(32), -- not used by amavisd-new, a comment
virus_lover char(1) default NULL, -- Y/N
spam_lover char(1) default NULL, -- Y/N
unchecked_lover char(1) default NULL, -- Y/N
banned_files_lover char(1) default NULL, -- Y/N
bad_header_lover char(1) default NULL, -- Y/N
bypass_virus_checks char(1) default NULL, -- Y/N
bypass_spam_checks char(1) default NULL, -- Y/N
bypass_banned_checks char(1) default NULL, -- Y/N
bypass_header_checks char(1) default NULL, -- Y/N
virus_quarantine_to varchar(64) default NULL,
spam_quarantine_to varchar(64) default NULL,
banned_quarantine_to varchar(64) default NULL,
unchecked_quarantine_to varchar(64) default NULL,
bad_header_quarantine_to varchar(64) default NULL,
clean_quarantine_to varchar(64) default NULL,
archive_quarantine_to varchar(64) default NULL,
spam_tag_level real default NULL, -- higher score inserts spam info headers
spam_tag2_level real default NULL, -- inserts 'declared spam' header fields
spam_tag3_level real default NULL, -- inserts 'blatant spam' header fields
spam_kill_level real default NULL, -- higher score triggers evasive actions
-- e.g. reject/drop, quarantine, ...
-- (subject to final_spam_destiny setting)
spam_dsn_cutoff_level real default NULL,
spam_quarantine_cutoff_level real default NULL,
addr_extension_virus varchar(64) default NULL,
addr_extension_spam varchar(64) default NULL,
addr_extension_banned varchar(64) default NULL,
addr_extension_bad_header varchar(64) default NULL,
warnvirusrecip char(1) default NULL, -- Y/N
warnbannedrecip char(1) default NULL, -- Y/N
warnbadhrecip char(1) default NULL, -- Y/N
newvirus_admin varchar(64) default NULL,
virus_admin varchar(64) default NULL,
banned_admin varchar(64) default NULL,
bad_header_admin varchar(64) default NULL,
spam_admin varchar(64) default NULL,
spam_subject_tag varchar(64) default NULL,
spam_subject_tag2 varchar(64) default NULL,
spam_subject_tag3 varchar(64) default NULL,
message_size_limit integer default NULL, -- max size in bytes, 0 disable
banned_rulenames varchar(64) default NULL, -- comma-separated list of ...
-- names mapped through %banned_rules to actual banned_filename tables
disclaimer_options varchar(64) default NULL,
forward_method varchar(64) default NULL,
sa_userconf varchar(64) default NULL,
sa_username varchar(64) default NULL
);
-- local users
CREATE TABLE users (
id serial PRIMARY KEY, -- unique id
priority integer NOT NULL DEFAULT 7, -- sort field, 0 is low prior.
policy_id integer NOT NULL DEFAULT 1 CHECK (policy_id >= 0) REFERENCES policy(id),
email bytea NOT NULL UNIQUE, -- email address, non-rfc2822-quoted
fullname varchar(255) DEFAULT NULL -- not used by amavisd-new
-- local char(1) -- Y/N (optional, see SQL section in README.lookups)
);
-- any e-mail address (non- rfc2822-quoted), external or local,
-- used as senders in wblist
CREATE TABLE mailaddr (
id serial PRIMARY KEY,
priority integer NOT NULL DEFAULT 9, -- 0 is low priority
email bytea NOT NULL UNIQUE
);
-- per-recipient whitelist and/or blacklist,
-- puts sender and recipient in relation wb (white or blacklisted sender)
CREATE TABLE wblist (
rid integer NOT NULL CHECK (rid >= 0) REFERENCES users(id),
sid integer NOT NULL CHECK (sid >= 0) REFERENCES mailaddr(id),
wb varchar(10) NOT NULL, -- W or Y / B or N / space=neutral / score
PRIMARY KEY (rid,sid)
);
-- grant usage rights:
GRANT select ON policy TO amavis;
GRANT select ON users TO amavis;
GRANT select ON mailaddr TO amavis;
GRANT select ON wblist TO amavis;
-- R/W part of the dataset (optional)
-- May reside in the same or in a separate database as lookups database;
-- REQUIRES SUPPORT FOR TRANSACTIONS; specified in @storage_sql_dsn
--
-- Please create additional indexes on keys when needed, or drop suggested
-- ones as appropriate to optimize queries needed by a management application.
-- See your database documentation for further optimization hints.
-- provide unique id for each e-mail address, avoids storing copies
CREATE TABLE maddr (
id serial PRIMARY KEY,
partition_tag integer DEFAULT 0, -- see $partition_tag
email bytea NOT NULL, -- full e-mail address
domain varchar(255) NOT NULL, -- only domain part of the email address
-- with subdomain fields in reverse
CONSTRAINT part_email UNIQUE (partition_tag,email)
);
-- information pertaining to each processed message as a whole;
-- NOTE: records with a NULL msgs.content should be ignored by utilities,
-- as such records correspond to messages just being processed, or were lost
CREATE TABLE msgs (
partition_tag integer DEFAULT 0, -- see $partition_tag
mail_id bytea NOT NULL, -- long-term unique mail id, dflt 12 ch
secret_id bytea DEFAULT '', -- authorizes release of mail_id, 12 ch
am_id varchar(20) NOT NULL, -- id used in the log
time_num integer NOT NULL CHECK (time_num >= 0),
-- rx_time: seconds since Unix epoch
time_iso timestamp WITH TIME ZONE NOT NULL,-- rx_time: ISO8601 UTC ascii time
sid integer NOT NULL CHECK (sid >= 0), -- sender: maddr.id
policy varchar(255) DEFAULT '', -- policy bank path (like macro %p)
client_addr varchar(255) DEFAULT '', -- SMTP client IP address (IPv4 or v6)
size integer NOT NULL CHECK (size >= 0), -- message size in bytes
originating char(1) DEFAULT ' ' NOT NULL, -- sender from inside or auth'd
content char(1), -- content type: V/B/U/S/Y/M/H/O/T/C
-- virus/banned/unchecked/spam(kill)/spammy(tag2)/
-- /bad-mime/bad-header/oversized/mta-err/clean
-- is NULL on partially processed mail
-- (prior to 2.7.0 the CC_SPAMMY was logged as 's', now 'Y' is used;
--- to avoid a need for case-insenstivity in queries)
quar_type char(1), -- quarantined as: ' '/F/Z/B/Q/M/L
-- none/file/zipfile/bsmtp/sql/
-- /mailbox(smtp)/mailbox(lmtp)
quar_loc varchar(255) DEFAULT '', -- quarantine location (e.g. file)
dsn_sent char(1), -- was DSN sent? Y/N/q (q=quenched)
spam_level real, -- SA spam level (no boosts)
message_id varchar(255) DEFAULT '', -- mail Message-ID header field
from_addr varchar(255) DEFAULT '', -- mail From header field, UTF8
subject varchar(255) DEFAULT '', -- mail Subject header field, UTF8
host varchar(255) NOT NULL, -- hostname where amavisd is running
CONSTRAINT msgs_partition_mail UNIQUE (partition_tag,mail_id),
PRIMARY KEY (partition_tag,mail_id)
--FOREIGN KEY (sid) REFERENCES maddr(id) ON DELETE RESTRICT
);
CREATE INDEX msgs_idx_sid ON msgs (sid);
CREATE INDEX msgs_idx_mess_id ON msgs (message_id); -- useful with pen pals
CREATE INDEX msgs_idx_time_iso ON msgs (time_iso);
CREATE INDEX msgs_idx_time_num ON msgs (time_num); -- optional
-- per-recipient information related to each processed message;
-- NOTE: records in msgrcpt without corresponding msgs.mail_id record are
-- orphaned and should be ignored and eventually deleted by external utilities
CREATE TABLE msgrcpt (
partition_tag integer DEFAULT 0, -- see $partition_tag
mail_id bytea NOT NULL, -- (must allow duplicates)
rseqnum integer DEFAULT 0 NOT NULL, -- recip's enumeration within msg
rid integer NOT NULL, -- recipient: maddr.id (duplicates allowed)
is_local char(1) DEFAULT ' ' NOT NULL, -- recip is: Y=local, N=foreign
content char(1) DEFAULT ' ' NOT NULL, -- content type V/B/U/S/Y/M/H/O/T/C
ds char(1) NOT NULL, -- delivery status: P/R/B/D/T
-- pass/reject/bounce/discard/tempfail
rs char(1) NOT NULL, -- release status: initialized to ' '
bl char(1) DEFAULT ' ', -- sender blacklisted by this recip
wl char(1) DEFAULT ' ', -- sender whitelisted by this recip
bspam_level real, -- per-recipient (total) spam level
smtp_resp varchar(255) DEFAULT '', -- SMTP response given to MTA
CONSTRAINT msgrcpt_partition_mail_rseq UNIQUE (partition_tag,mail_id,rseqnum),
PRIMARY KEY (partition_tag,mail_id,rseqnum)
--FOREIGN KEY (rid) REFERENCES maddr(id) ON DELETE RESTRICT,
--FOREIGN KEY (mail_id) REFERENCES msgs(mail_id) ON DELETE CASCADE
);
CREATE INDEX msgrcpt_idx_mail_id ON msgrcpt (mail_id);
CREATE INDEX msgrcpt_idx_rid ON msgrcpt (rid);
-- Additional index on rs since Modoboa uses it to filter its quarantine
CREATE INDEX msgrcpt_idx_rs ON msgrcpt (rs);
-- mail quarantine in SQL, enabled by $*_quarantine_method='sql:'
-- NOTE: records in quarantine without corresponding msgs.mail_id record are
-- orphaned and should be ignored and eventually deleted by external utilities
CREATE TABLE quarantine (
partition_tag integer DEFAULT 0, -- see $partition_tag
mail_id bytea NOT NULL, -- long-term unique mail id
chunk_ind integer NOT NULL CHECK (chunk_ind >= 0), -- chunk number, 1..
mail_text bytea NOT NULL, -- store mail as chunks of octects
PRIMARY KEY (partition_tag,mail_id,chunk_ind)
--FOREIGN KEY (mail_id) REFERENCES msgs(mail_id) ON DELETE CASCADE
);

View File

@@ -0,0 +1,41 @@
[automx]
provider = %domain
domains = *
# Protect against DoS
memcache = 127.0.0.1:11211
memcache_ttl = 600
client_error_limit = 20
rate_limit_exception_networks = 127.0.0.0/8, ::1/128
[global]
backend = sql
action = settings
account_type = email
host = %sql_dsn
query = %sql_query
result_attrs = display_name, email
smtp = yes
smtp_server = %hostname
smtp_port = 587
smtp_encryption = starttls
smtp_auth = plaintext
smtp_auth_identity = ${email}
smtp_refresh_ttl = 6
smtp_default = yes
imap = yes
imap_server = %hostname
imap_port = 143
imap_encryption = starttls
imap_auth = plaintext
imap_auth_identity = ${email}
imap_refresh_ttl = 6
pop = yes
pop_server = %hostname
pop_port = 110
pop_encryption = starttls
pop_auth = plaintext
pop_auth_identity = ${email}

View File

@@ -1,5 +0,0 @@
passdb {
driver = oauth2
mechanisms = xoauth2 oauthbearer
args = /etc/dovecot/conf.d/dovecot-oauth2.conf.ext
}

View File

@@ -1,6 +0,0 @@
introspection_mode = post
introspection_url = %{oauth2_introspection_url}
username_attribute = username
tls_ca_cert_file = /etc/ssl/certs/ca-certificates.crt
active_attribute = active
active_value = true

View File

@@ -1,122 +0,0 @@
#log_debug=category=auth
#auth_debug_passwords = yes
##
## Authentication processes
##
# Enable LOGIN command and all other plaintext authentications even if
# SSL/TLS is not used (LOGINDISABLED capability). Note that if the remote IP
# matches the local IP (ie. you're connecting from the same computer), the
# connection is considered secure and plaintext authentication is allowed,
# unless ssl = required.
#auth_allow_cleartext = yes
# Authentication cache size (e.g. 10M). 0 means it's disabled. Note that
# bsdauth, PAM and vpopmail require cache_key to be set for caching to be used.
#auth_cache_size = 0
# Time to live for cached data. After TTL expires the cached record is no
# longer used, *except* if the main database lookup returns internal failure.
# We also try to handle password changes automatically: If user's previous
# authentication was successful, but this one wasn't, the cache isn't used.
# For now this works only with plaintext authentication.
#auth_cache_ttl = 1 hour
# TTL for negative hits (user not found, password mismatch).
# 0 disables caching them completely.
#auth_cache_negative_ttl = 1 hour
# Space separated list of realms for SASL authentication mechanisms that need
# them. You can leave it empty if you don't want to support multiple realms.
# Many clients simply use the first one listed here, so keep the default realm
# first.
#auth_realms =
#
# Default realm/domain to use if none was specified. This is used for both
# SASL realms and appending @domain to username in plaintext logins.
#auth_default_domain =
# List of allowed characters in username. If the user-given username contains
# a character not listed in here, the login automatically fails. This is just
# an extra check to make sure user can't exploit any potential quote escaping
# vulnerabilities with SQL/LDAP databases. If you want to allow all characters,
# set this value to empty.
#auth_username_chars = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@
# Username character translations before it's looked up from databases. The
# value contains series of from -> to characters. For example "#@/@" means
# that '#' and '/' characters are translated to '@'.
#auth_username_translation =
# Username formatting before it's looked up from databases.
auth_username_format = %{user|lower}
#auth_username_format = %{user|username|lower}
# If you want to allow master users to log in by specifying the master
# username within the normal username string (ie. not using SASL mechanism's
# support for it), you can specify the separator character here. The format
# is then <username><separator><master username>. UW-IMAP uses "*" as the
# separator, so that could be a good choice.
auth_master_user_separator = *
# Username to use for users logging in with ANONYMOUS SASL mechanism
#auth_anonymous_username = anonymous
# Host name to use in GSSAPI principal names. The default is to use the
# name returned by gethostname(). Use "$ALL" (with quotes) to allow all keytab
# entries.
#auth_gssapi_hostname =
# Kerberos keytab to use for the GSSAPI mechanism. Will use the system
# default (usually /etc/krb5.keytab) if not specified. You may need to change
# the auth service to run as root to be able to read this file.
#auth_krb5_keytab =
# Do NTLM and GSS-SPNEGO authentication using Samba's winbind daemon and
# ntlm_auth helper. <https://doc.dovecot.org/latest/core/config/auth/mechanisms/winbind.html>
#auth_use_winbind = no
# Path for Samba's ntlm_auth helper binary.
#auth_winbind_helper_path = /usr/bin/ntlm_auth
# Time to delay before replying to failed authentications.
#auth_failure_delay = 2 secs
# Require a valid SSL client certificate or the authentication fails.
#auth_ssl_require_client_cert = no
# Take the username from client's SSL certificate, using
# X509_NAME_get_text_by_NID() which returns the subject's DN's
# CommonName.
#auth_ssl_username_from_cert = no
# Space separated list of wanted authentication mechanisms:
# plain login digest-md5 cram-md5 ntlm anonymous gssapi
# gss-spnego xoauth2 oauthbearer
# NOTE: See also auth_allow_cleartext setting.
auth_mechanisms = plain login oauthbearer xoauth2
##
## Password and user databases
##
#
# Password database is used to verify user's password (and nothing more).
# You can have multiple passdbs and userdbs. This is useful if you want to
# allow both system users (/etc/passwd) and virtual users to login without
# duplicating the system users into virtual database.
#
# <https://doc.dovecot.org/latest/core/config/auth/passdb.html>
#
# User database specifies where mails are located and what user/group IDs
# own them. For single-UID configuration use "static" userdb.
#
# <https://doc.dovecot.org/latest/core/config/auth/userdb.html>
#!include auth-deny.conf.ext
!include auth-master.conf.ext
!include auth-oauth2.conf.ext
#!include auth-system.conf.ext
!include auth-sql.conf.ext
#!include auth-ldap.conf.ext
#!include auth-passwdfile.conf.ext
#!include auth-static.conf.ext

View File

@@ -1,417 +0,0 @@
##
## Mailbox locations and namespaces
##
# Location for users' mailboxes. The default is empty, which means that Dovecot
# tries to find the mailboxes automatically. This won't work if the user
# doesn't yet have any mail, so you should explicitly tell Dovecot the full
# location.
#
# If you're using mbox, giving a path to the INBOX file (eg. /var/mail/%%u)
# isn't enough. You'll also need to tell Dovecot where the other mailboxes are
# kept. This is called the "root mail directory", and it must be the first
# path given in the mail_location setting.
#
# There are a few special variables you can use, eg.:
#
# %%{user} - username
# %%{user|username} - user part in user@domain, same as %%u if there's no domain
# %%{user|domain} - domain part in user@domain, empty if there's no domain
# %%{home} - home directory
#
# See https://doc.dovecot.org/latest/core/settings/variables.html for full list
# of variables.
#
# Example:
# mail_driver = maildir
# mail_path = ~/Maildir
# mail_inbox_path = ~/Maildir/.INBOX
#
# Debian defaults
# Note that upstream considers mbox deprecated and strongly recommends
# against its use in production environments. See further information
# at
# https://doc.dovecot.org/2.4.0/core/config/mailbox/formats/mbox.html
# mail_driver = mbox
# mail_home = /home/%%{user|username}
# mail_path = %%{home}/mail
# mail_inbox_path = /var/mail/%%{user}
mail_driver = maildir
mail_home = %{home_dir}/%%{user|domain}/%%{user|username}
mail_path = %%{home}/Maildir
# If you need to set multiple mailbox locations or want to change default
# namespace settings, you can do it by defining namespace sections.
#
# You can have private, shared and public namespaces. Private namespaces
# are for user's personal mails. Shared namespaces are for accessing other
# users' mailboxes that have been shared. Public namespaces are for shared
# mailboxes that are managed by sysadmin. If you create any shared or public
# namespaces you'll typically want to enable ACL plugin also, otherwise all
# users can access all the shared mailboxes, assuming they have permissions
# on filesystem level to do so.
namespace inbox {
# Namespace type: private, shared or public
#type = private
# Hierarchy separator to use. You should use the same separator for all
# namespaces or some clients get confused. '/' is usually a good one.
# The default however depends on the underlying mail storage format.
#separator =
# Prefix required to access this namespace. This needs to be different for
# all namespaces. For example "Public/".
#prefix =
# Physical location of the mailbox. This is in same format as
# mail location, which is also the default for it.
# mail_driver =
# mail_path =
#
# There can be only one INBOX, and this setting defines which namespace
# has it.
inbox = yes
# If namespace is hidden, it's not advertised to clients via NAMESPACE
# extension. You'll most likely also want to set list=no. This is mostly
# useful when converting from another server with different namespaces which
# you want to deprecate but still keep working. For example you can create
# hidden namespaces with prefixes "~/mail/", "~%%u/mail/" and "mail/".
#hidden = no
# Show the mailboxes under this namespace with LIST command. This makes the
# namespace visible for clients that don't support NAMESPACE extension.
# "children" value lists child mailboxes, but hides the namespace prefix.
#list = yes
# Namespace handles its own subscriptions. If set to "no", the parent
# namespace handles them (empty prefix should always have this as "yes")
#subscriptions = yes
# See 15-mailboxes.conf for definitions of special mailboxes.
}
# Example shared namespace configuration
#namespace shared {
#type = shared
#separator = /
# Mailboxes are visible under "shared/user@domain/"
# $user, $domain and $username are expanded to the destination user.
#prefix = shared/$user/
# Mail location for other users' mailboxes. Note that %%{variables} and ~/
# expands to the logged in user's data. %%{owner_user} and %%{owner_home}
# destination user's data.
#mail_driver = maildir
#mail_path = %%{owner_home}/Maildir
#mail_index_path = ~/Maildir/shared/%%{owner_user}
# Use the default namespace for saving subscriptions.
#subscriptions = no
# List the shared/ namespace only if there are visible shared mailboxes.
#list = children
#}
# Should shared INBOX be visible as "shared/user" or "shared/user/INBOX"?
#mail_shared_explicit_inbox = no
# System user and group used to access mails. If you use multiple, userdb
# can override these by returning uid or gid fields. You can use either numbers
# or names. <https://doc.dovecot.org/latest/core/config/system_users.html#uids>
#mail_uid =
#mail_gid =
# Group to enable temporarily for privileged operations. Currently this is
# used only with INBOX when either its initial creation or dotlocking fails.
# Typically this is set to "mail" to give access to /var/mail.
mail_privileged_group = mail
# Grant access to these supplementary groups for mail processes. Typically
# these are used to set up access to shared mailboxes. Note that it may be
# dangerous to set these if users can create symlinks (e.g. if "mail" group is
# set here, ln -s /var/mail ~/mail/var could allow a user to delete others'
# mailboxes, or ln -s /secret/shared/box ~/mail/mybox would allow reading it).
#mail_access_groups =
# Allow full filesystem access to clients. There's no access checks other than
# what the operating system does for the active UID/GID. It works with both
# maildir and mboxes, allowing you to prefix mailboxes names with eg. /path/
# or ~user/.
#mail_full_filesystem_access = no
# Dictionary for key=value mailbox attributes. This is used for example by
# URLAUTH and METADATA extensions.
#mail_attribute {
# dict file {
# path = %%{home}/Maildir/dovecot-attributes
# }
#}
# A comment or note that is associated with the server. This value is
# accessible for authenticated users through the IMAP METADATA server
# entry "/shared/comment".
#mail_server_comment = ""
# Indicates a method for contacting the server administrator. According to
# RFC 5464, this value MUST be a URI (e.g., a mailto: or tel: URL), but that
# is currently not enforced. Use for example mailto:admin@example.com. This
# value is accessible for authenticated users through the IMAP METADATA server
# entry "/shared/admin".
#mail_server_admin =
##
## Mail processes
##
# Don't use mmap() at all. This is required if you store indexes to shared
# filesystems (NFS or clustered filesystem).
#mmap_disable = no
# Rely on O_EXCL to work when creating dotlock files. NFS supports O_EXCL
# since version 3, so this should be safe to use nowadays by default.
#dotlock_use_excl = yes
# When to use fsync() or fdatasync() calls:
# optimized (default): Whenever necessary to avoid losing important data
# always: Useful with e.g. NFS when write()s are delayed
# never: Never use it (best performance, but crashes can lose data)
#mail_fsync = optimized
# Locking method for index files. Alternatives are fcntl, flock and dotlock.
# Dotlocking uses some tricks which may create more disk I/O than other locking
# methods. NFS users: flock doesn't work, remember to change mmap_disable.
#lock_method = fcntl
# Directory where mails can be temporarily stored. Usually it's used only for
# mails larger than >= 128 kB. It's used by various parts of Dovecot, for
# example LDA/LMTP while delivering large mails or zlib plugin for keeping
# uncompressed mails.
#mail_temp_dir = /tmp
# Valid UID range for users, defaults to 500 and above. This is mostly
# to make sure that users can't log in as daemons or other system users.
# Note that denying root logins is hardcoded to dovecot binary and can't
# be done even if first_valid_uid is set to 0.
#first_valid_uid = 500
#last_valid_uid = 0
# Valid GID range for users, defaults to non-root/wheel. Users having
# non-valid GID as primary group ID aren't allowed to log in. If user
# belongs to supplementary groups with non-valid GIDs, those groups are
# not set.
#first_valid_gid = 1
#last_valid_gid = 0
# Maximum allowed length for mail keyword name. It's only forced when trying
# to create new keywords.
#mail_max_keyword_length = 50
# ':' separated list of directories under which chrooting is allowed for mail
# processes (ie. /var/mail will allow chrooting to /var/mail/foo/bar too).
# This setting doesn't affect login_chroot, mail_chroot or auth chroot
# settings. If this setting is empty, "/./" in home dirs are ignored.
# WARNING: Never add directories here which local users can modify, that
# may lead to root exploit. Usually this should be done only if you don't
# allow shell access for users. <doc/wiki/Chrooting.txt>
#valid_chroot_dirs =
# Default chroot directory for mail processes. This can be overridden for
# specific users in user database by giving /./ in user's home directory
# (eg. /home/./user chroots into /home). Note that usually there is no real
# need to do chrooting, Dovecot doesn't allow users to access files outside
# their mail directory anyway. If your home directories are prefixed with
# the chroot directory, append "/." to mail_chroot. <doc/wiki/Chrooting.txt>
#mail_chroot =
# UNIX socket path to master authentication server to find users.
# This is used by imap (for shared users) and lda.
#auth_socket_path = /var/run/dovecot/auth-userdb
# Directory where to look up mail plugins.
#mail_plugin_dir = /usr/lib/dovecot
# Space separated list of plugins to load for all services. Plugins specific to
# IMAP, LDA, etc. are added to this list in their own .conf files.
#mail_plugins =
#
# To add plugins, use
#mail_plugins {
# plugin = yes
#}
mail_plugins {
quota = yes
quota_clone = yes
}
##
## Mailbox handling optimizations
##
# Mailbox list indexes can be used to optimize IMAP STATUS commands. They are
# also required for IMAP NOTIFY extension to be enabled.
#mailbox_list_index = yes
# Trust mailbox list index to be up-to-date. This reduces disk I/O at the cost
# of potentially returning out-of-date results after e.g. server crashes.
# The results will be automatically fixed once the folders are opened.
#mailbox_list_index_very_dirty_syncs = yes
# Should INBOX be kept up-to-date in the mailbox list index? By default it's
# not, because most of the mailbox accesses will open INBOX anyway.
#mailbox_list_index_include_inbox = no
# The minimum number of mails in a mailbox before updates are done to cache
# file. This allows optimizing Dovecot's behavior to do less disk writes at
# the cost of more disk reads.
#mail_cache_min_mail_count = 0
# When IDLE command is running, mailbox is checked once in a while to see if
# there are any new mails or other changes. This setting defines the minimum
# time to wait between those checks. Dovecot can also use inotify and
# kqueue to find out immediately when changes occur.
#mailbox_idle_check_interval = 30 secs
# Save mails with CR+LF instead of plain LF. This makes sending those mails
# take less CPU, especially with sendfile() syscall with Linux and FreeBSD.
# But it also creates a bit more disk I/O which may just make it slower.
# Also note that if other software reads the mboxes/maildirs, they may handle
# the extra CRs wrong and cause problems.
#mail_save_crlf = no
# Max number of mails to keep open and prefetch to memory. This only works with
# some mailbox formats and/or operating systems.
#mail_prefetch_count = 0
# How often to scan for stale temporary files and delete them (0 = never).
# These should exist only after Dovecot dies in the middle of saving mails.
#mail_temp_scan_interval = 1w
# How many slow mail accesses sorting can perform before it returns failure.
# With IMAP the reply is: NO [LIMIT] Requested sort would have taken too long.
# The untagged SORT reply is still returned, but it's likely not correct.
#mail_sort_max_read_count = 0
protocol !indexer-worker {
# If folder vsize calculation requires opening more than this many mails from
# disk (i.e. mail sizes aren't in cache already), return failure and finish
# the calculation via indexer process. Disabled by default. This setting must
# be 0 for indexer-worker processes.
#mail_vsize_bg_after_count = 0
}
##
## Maildir-specific settings
##
# By default LIST command returns all entries in maildir beginning with a dot.
# Enabling this option makes Dovecot return only entries which are directories.
# This is done by stat()ing each entry, so it causes more disk I/O.
# (For systems setting struct dirent->d_type, this check is free and it's
# done always regardless of this setting)
#maildir_stat_dirs = no
# When copying a message, do it with hard links whenever possible. This makes
# the performance much better, and it's unlikely to have any side effects.
#maildir_copy_with_hardlinks = yes
# Assume Dovecot is the only MUA accessing Maildir: Scan cur/ directory only
# when its mtime changes unexpectedly or when we can't find the mail otherwise.
#maildir_very_dirty_syncs = no
# If enabled, Dovecot doesn't use the S=<size> in the Maildir filenames for
# getting the mail's physical size, except when recalculating Maildir++ quota.
# This can be useful in systems where a lot of the Maildir filenames have a
# broken size. The performance hit for enabling this is very small.
#maildir_broken_filename_sizes = no
# Always move mails from new/ directory to cur/, even when the \Recent flags
# aren't being reset.
#maildir_empty_new = no
##
## mbox-specific settings
##
# Which locking methods to use for locking mbox. There are four available:
# dotlock: Create <mailbox>.lock file. This is the oldest and most NFS-safe
# solution. If you want to use /var/mail/ like directory, the users
# will need write access to that directory.
# dotlock_try: Same as dotlock, but if it fails because of permissions or
# because there isn't enough disk space, just skip it.
# fcntl : Use this if possible. Works with NFS too if lockd is used.
# flock : May not exist in all systems. Doesn't work with NFS.
# lockf : May not exist in all systems. Doesn't work with NFS.
#
# You can use multiple locking methods; if you do the order they're declared
# in is important to avoid deadlocks if other MTAs/MUAs are using multiple
# locking methods as well. Some operating systems don't allow using some of
# them simultaneously.
#mbox_read_locks = fcntl
#mbox_write_locks = dotlock fcntl
# Maximum time to wait for lock (all of them) before aborting.
#mbox_lock_timeout = 5 mins
# If dotlock exists but the mailbox isn't modified in any way, override the
# lock file after this much time.
#mbox_dotlock_change_timeout = 2 mins
# When mbox changes unexpectedly we have to fully read it to find out what
# changed. If the mbox is large this can take a long time. Since the change
# is usually just a newly appended mail, it'd be faster to simply read the
# new mails. If this setting is enabled, Dovecot does this but still safely
# fallbacks to re-reading the whole mbox file whenever something in mbox isn't
# how it's expected to be. The only real downside to this setting is that if
# some other MUA changes message flags, Dovecot doesn't notice it immediately.
# Note that a full sync is done with SELECT, EXAMINE, EXPUNGE and CHECK
# commands.
#mbox_dirty_syncs = yes
# Like mbox_dirty_syncs, but don't do full syncs even with SELECT, EXAMINE,
# EXPUNGE or CHECK commands. If this is set, mbox_dirty_syncs is ignored.
#mbox_very_dirty_syncs = no
# Delay writing mbox headers until doing a full write sync (EXPUNGE and CHECK
# commands and when closing the mailbox). This is especially useful for POP3
# where clients often delete all mails. The downside is that our changes
# aren't immediately visible to other MUAs.
#mbox_lazy_writes = yes
# If mbox size is smaller than this (e.g. 100k), don't write index files.
# If an index file already exists it's still read, just not updated.
#mbox_min_index_size = 0
# Mail header selection algorithm to use for MD5 POP3 UIDLs when
# pop3_uidl_format=%%m. For backwards compatibility we use apop3d inspired
# algorithm, but it fails if the first Received: header isn't unique in all
# mails. An alternative algorithm is "all" that selects all headers.
#mbox_md5 = apop3d
##
## mdbox-specific settings
##
# Maximum dbox file size until it's rotated.
#mdbox_rotate_size = 10M
# Maximum dbox file age until it's rotated. Typically in days. Day begins
# from midnight, so 1d = today, 2d = yesterday, etc. 0 = check disabled.
#mdbox_rotate_interval = 0
# When creating new mdbox files, immediately preallocate their size to
# mdbox_rotate_size. This setting currently works only in Linux with some
# filesystems (ext4, xfs).
#mdbox_preallocate_space = no
# Settings to control adding $HasAttachment or $HasNoAttachment keywords.
# By default, all MIME parts with Content-Disposition=attachment, or inlines
# with filename parameter are consired attachments.
# add-flags - Add the keywords when saving new mails or when fetching can
# do it efficiently.
# content-type=type or !type - Include/exclude content type. Excluding will
# never consider the matched MIME part as attachment. Including will only
# negate an exclusion (e.g. content-type=!foo/* content-type=foo/bar).
# exclude-inlined - Exclude any Content-Disposition=inline MIME part.
#mail_attachment_detection_options =

View File

@@ -1,174 +0,0 @@
#default_process_limit = 100
#default_client_limit = 1000
# Default VSZ (virtual memory size) limit for service processes. This is mainly
# intended to catch and kill processes that leak memory before they eat up
# everything.
#default_vsz_limit = 256M
# Login user is internally used by login processes. This is the most untrusted
# user in Dovecot system. It shouldn't have access to anything at all.
#default_login_user = dovenull
# Internal user is used by unprivileged processes. It should be separate from
# login user, so that login processes can't disturb other processes.
#default_internal_user = dovecot
service imap-login {
inet_listener imap {
#port = 143
}
inet_listener imaps {
#port = 993
#ssl = yes
}
# Number of connections to handle before starting a new process. Typically
# the only useful values are 0 (unlimited) or 1. 1 is more secure, but 0
# is faster. <d>
#service_restart_request_count = 1
# Number of processes to always keep waiting for more connections.
#process_min_avail = 0
# If you set service_restart_request_count=0, you probably need to grow this.
#vsz_limit = 256M # default
}
service pop3-login {
inet_listener pop3 {
#port = 110
}
inet_listener pop3s {
#port = 995
#ssl = yes
}
}
service submission-login {
inet_listener submission {
#port = 587
}
inet_listener submissions {
#port = 465
}
}
service lmtp {
unix_listener lmtp {
#mode = 0666
}
# Create inet listener only if you can't use the above UNIX socket
#inet_listener lmtp {
# Avoid making LMTP visible for the entire internet
#listen = 127.0.0.1
#port = 24
#}
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0600
user = postfix
group = postfix
}
}
service imap {
# Most of the memory goes to mmap()ing files. You may need to increase this
# limit if you have huge mailboxes.
#vsz_limit = 256M # default
# Max. number of IMAP processes (connections)
#process_limit = 1024
executable = imap postlogin
}
service pop3 {
# Max. number of POP3 processes (connections)
#process_limit = 1024
executable = imap postlogin
}
service submission {
# Max. number of SMTP Submission processes (connections)
#process_limit = 1024
}
service postlogin {
executable = script-login /usr/local/bin/postlogin.sh
user = %modoboa_user
unix_listener postlogin {
}
}
service stats {
# To allow modoboa to access available cipher list.
unix_listener stats-reader {
user = %{mailboxes_owner}
group = %{mailboxes_owner}
mode = 0660
}
unix_listener stats-writer {
user = %{mailboxes_owner}
group = %{mailboxes_owner}
mode = 0660
}
}
service auth {
# auth_socket_path points to this userdb socket by default. It's typically
# used by dovecot-lda, doveadm, possibly imap process, etc. Users that have
# full permissions to this socket are able to get a list of all usernames and
# get the results of everyone's userdb lookups.
#
# The default 0666 mode allows anyone to connect to the socket, but the
# userdb lookups will succeed only if the userdb returns an "uid" field that
# matches the caller process's UID. Also if caller's uid or gid matches the
# socket's uid or gid the lookup succeeds. Anything else causes a failure.
#
# To give the caller full permissions to lookup all users, set the mode to
# something else than 0666 and Dovecot lets the kernel enforce the
# permissions (e.g. 0777 allows everyone full permissions).
unix_listener auth-userdb {
#mode = 0666
user = %{mailboxes_owner}
#group =
}
# Postfix smtp-auth
unix_listener /var/spool/postfix/private/auth {
mode = 0666
user = postfix
group = postfix
}
%{radicale_enabled}unix_listener auth-radicale {
%{radicale_enabled} mode = 0666
%{radicale_enabled} user = %{radicale_user}
%{radicale_enabled} group = %{radicale_user}
%{radicale_enabled} type = auth-legacy
%{radicale_enabled}}
# Auth process is run as this user.
#user = $SET:default_internal_user
}
service auth-worker {
# Auth worker process is run as root by default, so that it can access
# /etc/shadow. If this isn't necessary, the user should be changed to
# $SET:default_internal_user.
#user = root
}
service dict {
# If dict proxy is used, mail processes should have access to its socket.
# For example: mode=0660, group=vmail and global mail_access_groups=vmail
unix_listener dict {
mode = 0600
user = %{mailboxes_owner}
#group =
}
}

View File

@@ -1,6 +0,0 @@
# PEM encoded X.509 SSL/TLS certificate and private key. They're opened before
# dropping root privileges, so keep the key file unreadable by anyone but
# root. Included doc/mkcert.sh can be used to easily generate self-signed
# certificate, just make sure to update the domains in dovecot-openssl.cnf
ssl_server_cert_file = %tls_cert_file
ssl_server_key_file = %tls_key_file

View File

@@ -1,59 +0,0 @@
##
## SSL settings
##
# SSL/TLS support: yes, no, required. <https://doc.dovecot.org/latest/core/config/ssl.html>
ssl = yes
# PEM encoded X.509 SSL/TLS certificate and private key. By default, Debian
# installs a self-signed certificate. This is useful for testing, but you
# should obtain a real certificate from a recognized certificate authority.
#
# These files are opened before dropping root privileges, so keep the key file
# unreadable by anyone but root. Included /usr/share/dovecot/mkcert.sh can be
# used to easily generate self-signed certificate, just make sure to update the
# domains in dovecot-openssl.cnf
#
# Preferred permissions: root:root 0444
# ssl_server_cert_file = /etc/dovecot/private/dovecot.pem
# Preferred permissions: root:root 0400
# ssl_server_key_file = /etc/dovecot/private/dovecot.key
!include_try /etc/dovecot/conf.d/10-ssl-keys.try
# If key file is password protected, give the password here. Alternatively
# give it when starting dovecot with -p parameter. Since this file is often
# world-readable, you may want to place this setting instead to a different
# root owned 0600 file by using ssl_key_password = <path.
#ssl_server_key_password =
# PEM encoded trusted certificate authority. Set this only if you intend to use
# ssl_request_client_cert=yes. The file should contain the CA certificate(s)
# followed by the matching CRL(s). (e.g. ssl_server_ca_file = /etc/ssl/certs/ca.pem)
#ssl_server_ca_file =
# Require that CRL check succeeds for client certificates.
#ssl_server_require_crl = yes
# Request client to send a certificate. If you also want to require it, set
# auth_ssl_require_client_cert=yes in auth section.
#ssl_server_request_client_cert = no
# Which field from certificate to use for username. commonName and
# x500UniqueIdentifier are the usual choices. You'll also need to set
# auth_ssl_username_from_cert=yes.
#ssl_server_cert_username_field = commonName
# SSL protocols to use. Debian systems specify TLSv1.2 by default, which should
# be reasonbly secure and compatible with existing clients.
%ssl_protocol_parameter = %ssl_protocols
# Diffie-Hellman parameters are no longer required and should be phased out.
# They do not work with ECDH(E) and require DH(E) ciphers.
#ssl_server_dh_file = /etc/dovecot/dh.pem
# SSL ciphers to use
#ssl_cipher_list = ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH
ssl_cipher_list = EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH+aRSA+RC4:EECDH:EDH+aRSA:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4
# SSL crypto device to use, for valid values run "openssl engine"
#ssl_crypto_device = /dev/crypto

View File

@@ -1,90 +0,0 @@
##
## Mailbox definitions
##
# Each mailbox is specified in a separate mailbox section. The section name
# specifies the mailbox name. If it has spaces, you can put the name
# "in quotes". These sections can contain the following mailbox settings:
#
# auto:
# Indicates whether the mailbox with this name is automatically created
# implicitly when it is first accessed. The user can also be automatically
# subscribed to the mailbox after creation. The following values are
# defined for this setting:
#
# no - Never created automatically.
# create - Automatically created, but no automatic subscription.
# subscribe - Automatically created and subscribed.
#
# special_use:
# A space-separated list of SPECIAL-USE flags (RFC 6154) to use for the
# mailbox. There are no validity checks, so you could specify anything
# you want in here, but it's not a good idea to use flags other than the
# standard ones specified in the RFC:
#
# \All - This (virtual) mailbox presents all messages in the
# user's message store.
# \Archive - This mailbox is used to archive messages.
# \Drafts - This mailbox is used to hold draft messages.
# \Flagged - This (virtual) mailbox presents all messages in the
# user's message store marked with the IMAP \Flagged flag.
# \Important - This (virtual) mailbox presents all messages in the
# user's message store deemed important to user.
# \Junk - This mailbox is where messages deemed to be junk mail
# are held.
# \Sent - This mailbox is used to hold copies of messages that
# have been sent.
# \Trash - This mailbox is used to hold messages that have been
# deleted.
#
# comment:
# Defines a default comment or note associated with the mailbox. This
# value is accessible through the IMAP METADATA mailbox entries
# "/shared/comment" and "/private/comment". Users with sufficient
# privileges can override the default value for entries with a custom
# value.
# NOTE: Assumes "namespace inbox" has been defined in 10-mail.conf.
namespace inbox {
# These mailboxes are widely used and could perhaps be created automatically:
mailbox Drafts {
auto = subscribe
special_use = \Drafts
}
mailbox Junk {
auto = subscribe
special_use = \Junk
}
mailbox Trash {
auto = subscribe
special_use = \Trash
}
# For \Sent mailboxes there are two widely used names. We'll mark both of
# them as \Sent. User typically deletes one of them if duplicates are created.
mailbox Sent {
auto = subscribe
special_use = \Sent
}
# mailbox "Sent Messages" {
# special_use = \Sent
# }
# If you have a virtual "All messages" mailbox:
#mailbox virtual/All {
# special_use = \All
# comment = All my messages
#}
# If you have a virtual "Flagged" mailbox:
#mailbox virtual/Flagged {
# special_use = \Flagged
# comment = All my flagged messages
#}
# If you have a virtual "Important" mailbox:
#mailbox virtual/Important {
# special_use = \Important
# comment = All my important messages
#}
}

View File

@@ -1,108 +0,0 @@
##
## IMAP specific settings
##
# If nothing happens for this long while client is IDLEing, move the connection
# to imap-hibernate process and close the old imap process. This saves memory,
# because connections use very little memory in imap-hibernate process. The
# downside is that recreating the imap process back uses some resources.
#imap_hibernate_timeout = 0
# Maximum IMAP command line length. Some clients generate very long command
# lines with huge mailboxes, so you may need to raise this if you get
# "Too long argument" or "IMAP command line too large" errors often.
#imap_max_line_length = 64k
# IMAP logout format string:
# %{input} - total number of bytes read from client
# %{output} - total number of bytes sent to client
# %{fetch_hdr_count} - Number of mails with mail header data sent to client
# %{fetch_hdr_bytes} - Number of bytes with mail header data sent to client
# %{fetch_body_count} - Number of mails with mail body data sent to client
# %{fetch_body_bytes} - Number of bytes with mail body data sent to client
# %{deleted} - Number of mails where client added \Deleted flag
# %{expunged} - Number of mails that client expunged, which does not
# include automatically expunged mails
# %{autoexpunged} - Number of mails that were automatically expunged after
# client disconnected
# %{trashed} - Number of mails that client copied/moved to the
# special_use=\Trash mailbox.
# %{appended} - Number of mails saved during the session
#imap_logout_format = in=%i out=%o deleted=%{deleted} expunged=%{expunged} \
# trashed=%{trashed} hdr_count=%{fetch_hdr_count} \
# hdr_bytes=%{fetch_hdr_bytes} body_count=%{fetch_body_count} \
# body_bytes=%{fetch_body_bytes}
# Amend or override the IMAP capability response. To override, set the value
# with imap_capability =
#
# To amend, you can use a boolean list to specify which capabilities to turn
# on and off
#imap_capability {
# SPECIAL-USE = yes
# "LITERAL+" = no
#}
# How long to wait between "OK Still here" notifications when client is
# IDLEing.
#imap_idle_notify_interval = 2 mins
# ID field names and values to send to clients. Using * as the value makes
# Dovecot use the default value. The following fields have default values
# currently: name, version, os, os-version, support-url, support-email,
# revision.
#imap_id_send =
# Use imap_id_received event to log IMAP id
# Workarounds for various client bugs:
# delay-newmail:
# Send EXISTS/RECENT new mail notifications only when replying to NOOP
# and CHECK commands. Some clients ignore them otherwise, for example OSX
# Mail (<v2.1). Outlook Express breaks more badly though, without this it
# may show user "Message no longer in server" errors. Note that OE6 still
# breaks even with this workaround if synchronization is set to
# "Headers Only".
# tb-extra-mailbox-sep:
# Thunderbird gets somehow confused with LAYOUT=fs (mbox and dbox) and
# adds extra '/' suffixes to mailbox names. This option causes Dovecot to
# ignore the extra '/' instead of treating it as invalid mailbox name.
# tb-lsub-flags:
# Show \Noselect flags for LSUB replies with LAYOUT=fs (e.g. mbox).
# This makes Thunderbird realize they aren't selectable and show them
# greyed out, instead of only later giving "not selectable" popup error.
#
# This is a boolean list
#imap_client_workarounds {
# delay-newmail = yes
#}
# Host allowed in URLAUTH URLs sent by client. "*" allows all.
#imap_urlauth_host =
# Enable IMAP LITERAL- extension (replaces LITERAL+)
#imap_literal_minus = no
# What happens when FETCH fails due to some internal error:
# disconnect-immediately:
# The FETCH is aborted immediately and the IMAP client is disconnected.
# disconnect-after:
# The FETCH runs for all the requested mails returning as much data as
# possible. The client is finally disconnected without a tagged reply.
# no-after:
# Same as disconnect-after, but tagged NO reply is sent instead of
# disconnecting the client. If the client attempts to FETCH the same failed
# mail more than once, the client is disconnected. This is to avoid clients
# from going into infinite loops trying to FETCH a broken mail.
#imap_fetch_failure = disconnect-immediately
protocol imap {
# Space separated list of plugins to load (default is global mail_plugins).
mail_plugins {
quota = yes
}
# Maximum number of IMAP connections allowed for a user from each IP address.
# NOTE: The username is compared case-sensitively.
#mail_max_userip_connections = 10
}

View File

@@ -1,53 +0,0 @@
##
#i
## LMTP specific settings
##
# Support proxying to other LMTP/SMTP servers by performing passdb lookups.
#lmtp_proxy = no
# When recipient address includes the detail (e.g. user+detail), try to save
# the mail to the detail mailbox. See also recipient_delimiter and
# lda_mailbox_autocreate settings.
#lmtp_save_to_detail_mailbox = no
# Verify quota before replying to RCPT TO. This adds a small overhead.
lmtp_rcpt_check_quota = yes
# Add "Received:" header to mails delivered.
#lmtp_add_received_header = yes
# Which recipient address to use for Delivered-To: header and Received:
# header. The default is "final", which is the same as the one given to
# RCPT TO command. "original" uses the address given in RCPT TO's ORCPT
# parameter, "none" uses nothing. Note that "none" is currently always used
# when a mail has multiple recipients.
#lmtp_hdr_delivery_address = final
# Workarounds for various client bugs:
# whitespace-before-path:
# Allow one or more spaces or tabs between `MAIL FROM:' and path and between
# `RCPT TO:' and path.
# mailbox-for-path:
# Allow using bare Mailbox syntax (i.e., without <...>) instead of full path
# syntax.
#
#lmtp_client_workarounds {
# whitespace-before-path = yes
#}
protocol lmtp {
mail_plugins {
quota = yes
sieve = yes
}
postmaster_address = %postmaster_address
# This strips the domain name before delivery, since the default
# userdb in Debian is /etc/passwd, which doesn't include domain
# names in the user. If you're using a different userdb backend
# that does include domain names, you may wish to remove this. See
# https://doc.dovecot.org/2.4.0/howto/lmtp/exim.html and
# https://doc.dovecot.org/2.4.0/core/summaries/settings.html#auth_username_format
# auth_username_format = %%{user | username}
}

View File

@@ -1,45 +0,0 @@
##
## Dictionary server settings
##
# Dictionary can be used to store key=value lists. This is used by several
# plugins. The dictionary can be accessed either directly or though a
# dictionary server. The following dict block maps dictionary names to URIs
# when the server is used. These can then be referenced using URIs in format
# "proxy::<name>".
dict_server {
mysql %dbhost {
port = %dbport
dbname = %modoboa_dbname
user = %modoboa_dbuser
password = %modoboa_dbpassword
}
dict quota {
driver = sql
sql_driver = %db_driver
hostname = %dbhost
dict_map priv/quota/storage {
sql_table = admin_quota
username_field = username
value_field bytes {
type = uint
}
}
dict_map priv/quota/messages {
sql_table = admin_quota
username_field = username
value_field messages {
type = uint
}
}
}
}
quota_clone {
dict proxy {
name = quota
}
}

View File

@@ -1,47 +0,0 @@
##
## Dictionary server settings
##
# Dictionary can be used to store key=value lists. This is used by several
# plugins. The dictionary can be accessed either directly or though a
# dictionary server. The following dict block maps dictionary names to URIs
# when the server is used. These can then be referenced using URIs in format
# "proxy::<name>".
dict_server {
pgsql %dbhost {
parameters {
port = %dbport
dbname = %modoboa_dbname
user = %modoboa_dbuser
password = %modoboa_dbpassword
}
}
dict quota {
driver = sql
sql_driver = %db_driver
hostname = %dbhost
dict_map priv/quota/storage {
sql_table = admin_quota
username_field = username
value_field bytes {
type = uint
}
}
dict_map priv/quota/messages {
sql_table = admin_quota
username_field = username
value_field messages {
type = uint
}
}
}
}
quota_clone {
dict proxy {
name = quota
}
}

View File

@@ -1,79 +0,0 @@
##
## Quota configuration.
##
# Note that you also have to enable quota plugin in mail_plugins setting.
## <https://doc.dovecot.org/latest/core/plugins/quota.html>
##
## Quota limits
##
# Quota limits are set using "quota_rule" parameters. To get per-user quota
# limits, you can set/override them by returning "quota_rule" extra field
# from userdb. It's also possible to give mailbox-specific limits, for example
# to give additional 100 MB when saving to Trash:
#mail_plugins {
# quota = yes
#}
quota "User quota" {
# storage_size = 1G
}
#
#namespace inbox {
# mailbox Trash {
# quota_storage_extra = 100M
# }
#}
##
## Quota warnings
##
# You can execute a given command when user exceeds a specified quota limit.
# Each quota root has separate limits. Only the command for the first
# exceeded limit is excecuted, so put the highest limit first.
# The commands are executed via script service by connecting to the named
# UNIX socket (quota-warning below).
# Note that % needs to be escaped as %%, otherwise "% " expands to empty.
#quota "User quota" {
# warning warn-95 {
# quota_storage_percentage = 95
# execute quota-warning {
# args = 95 %{user}
# }
# }
# warning warn-80 {
# quota_storage_percentage = 80
# execute quota-warning {
# args = 80 %{user}
# }
# }
#}
# Example quota-warning service. The unix listener's permissions should be
# set in a way that mail processes can connect to it. Below example assumes
# that mail processes run as vmail user. If you use mode=0666, all system users
# can generate quota warnings to anyone.
#service quota-warning {
# executable = script /usr/local/bin/quota-warning.sh
# user = dovecot
# unix_listener quota-warning {
# user = vmail
# }
#}
##
## Quota backends
##
# Multiple backends are supported:
# count: Default and recommended, quota driver tracks the quota internally within Dovecot's index files.
# maildir: Maildir++ quota
# fs: Read-only support for filesystem quota
#quota "User quota" {
# driver = count
#}

View File

@@ -1,118 +0,0 @@
##
## Settings for the Sieve interpreter
##
# Do not forget to enable the Sieve plugin in 15-lda.conf and 20-lmtp.conf
# by adding it to the respective mail_plugins { sieve = yes } settings.
# See https://doc.dovecot.org/latest/core/plugins/sieve.html
# Personal sieve script location
#sieve_script personal {
# driver = file
# path = ~/sieve
# active_path = ~/.dovecot.sieve
#}
# Default sieve script location
#sieve_script default {
# type = default
# name = default
# driver = file
# path = /etc/dovecot/sieve/default/
#}
%{do_move_spam_to_junk}sieve_script after {
%{do_move_spam_to_junk} type = after
%{do_move_spam_to_junk} path = /etc/dovecot/conf.d/custom_after_sieve
%{do_move_spam_to_junk}}
# Which Sieve language extensions are available to users. By default, all
# supported extensions are available, except for deprecated extensions or
# those that are still under development. Some system administrators may want
# to disable certain Sieve extensions or enable those that are not available
# by default. This setting can use 'yes' and 'no' to specify differences relative
# to the default. For example `imapflags = yes' will enable the
# deprecated imapflags extension in addition to all extensions were already
# enabled by default.
#sieve_extensions {
# mboxmetadata = yes
# vnd.dovecot.debug = yes
#}
# Which Sieve language extensions are ONLY available in global scripts. This
# can be used to restrict the use of certain Sieve extensions to administrator
# control, for instance when these extensions can cause security concerns.
# This setting has higher precedence than the `sieve_extensions' setting
# (above), meaning that the extensions enabled with this setting are never
# available to the user's personal script no matter what is specified for the
# `sieve_extensions' setting. The syntax of this setting is similar to the
# `sieve_extensions' setting, with the difference that extensions are
# enabled or disabled for exclusive use in global scripts. Currently, no
# extensions are marked as such by default.
#sieve_global_extensions =
# The Pigeonhole Sieve interpreter can have plugins of its own. Using this
# setting, the used plugins can be specified. Check the Dovecot documentation
# https://doc.dovecot.org/latest/core/plugins/sieve.html
#sieve_plugins = sieve_imapsieve sieve_extprograms
#sieve_pipe_bin_dir = /usr/share/dovecot-pigeonhole/sieve
#sieve_execute_bin_dir = /usr/share/dovecot-pigeonhole/sieve
#sieve_global_extensions {
# vnd.dovecot.pipe = yes
# vnd.dovecot.execute = yes
#}
#imapsieve_url =
# The separator that is expected between the :user and :detail
# address parts introduced by the subaddress extension. This may
# also be a sequence of characters (e.g. '--'). The current
# implementation looks for the separator from the left of the
# localpart and uses the first one encountered. The :user part is
# left of the separator and the :detail part is right. This setting
# is also used by Dovecot's LMTP service.
#recipient_delimiter = +-_
# The maximum size of a Sieve script. The compiler will refuse to compile any
# script larger than this limit. If set to 0, no limit on the script size is
# enforced.
#sieve_max_script_size = 1M
# The maximum number of actions that can be performed during a single script
# execution. If set to 0, no limit on the total number of actions is enforced.
#sieve_max_actions = 32
# The maximum number of redirect actions that can be performed during a single
# script execution. If set to 0, no redirect actions are allowed.
#sieve_max_redirects = 4
# The maximum number of personal Sieve scripts a single user can have. If set
# to 0, no limit on the number of scripts is enforced.
# (Currently only relevant for ManageSieve)
#sieve_quota_script_count = 0
# The maximum amount of disk storage a single user's scripts may occupy. If
# set to 0, no limit on the used amount of disk storage is enforced.
# (Currently only relevant for ManageSieve)
#sieve_quota_storage_size = 0
#mailbox Spam {
## From elsewhere to Spam folder
# sieve_script report-spam {
# type = before
# cause = copy
# path = /etc/dovecot/report-spam.sieve
# }
#}
## From Spam folder to elsewhere
#imapsieve_from Spam {
# sieve_script report-ham {
# type = before
# cause = copy
# path = /etc/dovecot/report-ham.sieve
# }
#}

View File

@@ -1,28 +0,0 @@
# Authentication for master users. Included from auth.conf.
# By adding master=yes setting inside a passdb you make the passdb a list
# of "master users", who can log in as anyone else.
# <https://doc.dovecot.org/latest/core/config/auth/master_users.html>
# Example master user passdb using passwd-file. You can use any passdb though.
#passdb master-passwd-file {
# driver = passwd-file
# master = yes
# passwd_file_path = /etc/dovecot/master-users
#}
sql_driver = %db_driver
mysql %dbhost {
port = %dbport
dbname = %modoboa_dbname
user = %modoboa_dbuser
password = %modoboa_dbpassword
}
passdb db1 {
driver = sql
sql_query = SELECT email AS user, password FROM core_user WHERE email='%%{user}' and is_active=1 and master_user=1
master = yes
result_success = continue
}

View File

@@ -1,30 +0,0 @@
# Authentication for master users. Included from auth.conf.
# By adding master=yes setting inside a passdb you make the passdb a list
# of "master users", who can log in as anyone else.
# <https://doc.dovecot.org/latest/core/config/auth/master_users.html>
# Example master user passdb using passwd-file. You can use any passdb though.
#passdb master-passwd-file {
# driver = passwd-file
# master = yes
# passwd_file_path = /etc/dovecot/master-users
#}
sql_driver = %db_driver
pgsql %dbhost {
parameters {
port = %dbport
dbname = %modoboa_dbname
user = %modoboa_dbuser
password = %modoboa_dbpassword
}
}
passdb db1 {
driver = sql
sql_query = SELECT email AS user, password FROM core_user WHERE email='%%{user}' and is_active and master_user
master = yes
result_success = continue
}

View File

@@ -1,24 +0,0 @@
auth_mechanisms {
xoauth2 = yes
oauthbearer = yes
}
oauth2 {
introspection_mode = post
introspection_url = %{oauth2_introspection_url}
#force_introspection = yes
username_attribute = username
}
# with local validation
#oauth2 {
# introspection_mode = local
# username_attribute = email
# oauth2_local_validation {
# dict fs {
# fs posix {
# prefix = /etc/dovecot/oauth2-keys/
# }
# }
# }
#}

View File

@@ -1,195 +0,0 @@
# Authentication for SQL users. Included from auth.conf.
#
# <https://doc.dovecot.org/latest/core/config/auth/databases/sql.html>
# For the sql passdb module, you'll need a database with a table that
# contains fields for at least the username and password. If you want to
# use the user@domain syntax, you might want to have a separate domain
# field as well.
#
# If your users all have the same uig/gid, and have predictable home
# directories, you can use the static userdb module to generate the home
# dir based on the username and domain. In this case, you won't need fields
# for home, uid, or gid in the database.
#
# If you prefer to use the sql userdb module, you'll want to add fields
# for home, uid, and gid. Here is an example table:
#
# CREATE TABLE users (
# username VARCHAR(128) NOT NULL,
# domain VARCHAR(128) NOT NULL,
# password VARCHAR(64) NOT NULL,
# home VARCHAR(255) NOT NULL,
# uid INTEGER NOT NULL,
# gid INTEGER NOT NULL,
# active CHAR(1) DEFAULT 'Y' NOT NULL
# );
# Database driver: mysql, pgsql, sqlite
sql_driver = %db_driver
# Database connection string. This is driver-specific setting.
#
# HA / round-robin load-balancing is supported by giving multiple host
# settings, like: host=sql1.host.org host=sql2.host.org
#
# pgsql:
# For available options, see the PostgreSQL documention for the
# PQconnectdb function of libpq.
# Use maxconns=n (default 5) to change how many connections Dovecot can
# create to pgsql.
#
# mysql:
# Basic options emulate PostgreSQL option names:
# host, port, user, password, dbname
#
# But also adds some new settings:
# client_flags - See MySQL manual
# ssl_ca, ssl_ca_path - Set either one or both to enable SSL
# ssl_cert, ssl_key - For sending client-side certificates to server
# ssl_cipher - Set minimum allowed cipher security (default: HIGH)
# option_file - Read options from the given file instead of
# the default my.cnf location
# option_group - Read options from the given group (default: client)
#
# You can connect to UNIX sockets by using host: host=/var/run/mysql.sock
# Note that currently you can't use spaces in parameters.
#
# sqlite:
# The path to the database file.
#
# Examples:
# mysql 192.168.1.1 {
# dbname = users
# }
# mysql sql.example.com {
# ssl = yes
# user = virtual
# password = blarg
# dbname = virtual
# }
# sqlite /etc/dovecot/authdb.sqlite {
# }
#
#mysql /var/run/mysqld/mysqld.sock {
# user = dovecot
# password = dvmail
# dbname = dovecot
#}
#mysql localhost {
# ...
#}
mysql %dbhost {
port = %dbport
dbname = %modoboa_dbname
user = %modoboa_dbuser
password = %modoboa_dbpassword
}
#passdb sql {
# default_password_scheme = SHA256
# passdb query to retrieve the password. It can return fields:
# password - The user's password. This field must be returned.
# user - user@domain from the database. Needed with case-insensitive lookups.
# username and domain - An alternative way to represent the "user" field.
#
# The "user" field is often necessary with case-insensitive lookups to avoid
# e.g. "name" and "nAme" logins creating two different mail directories. If
# your user and domain names are in separate fields, you can return "username"
# and "domain" fields instead of "user".
#
# The query can also return other fields which have a special meaning, see
# https://doc.dovecot.org/latest/core/config/auth/passdb.html#extra-fields
#
# Commonly used available substitutions (see https://doc.dovecot.org/latest/core/settings/variables.html
# for full list):
# %%{user} = entire user@domain
# %%{user|username} = user part of user@domain
# %%{user|domain} = domain part of user@domain
#
# Note that these can be used only as input to SQL query. If the query outputs
# any of these substitutions, they're not touched. Otherwise it would be
# difficult to have eg. usernames containing '%%' characters.
#
# Example:
# query = SELECT userid AS user, pw AS password \
# FROM users WHERE userid = '%%u' AND active = 'Y'
#
# query = \
# SELECT userid as username, domain, password \
# FROM users WHERE userid = '%%{user|username}' AND domain = '%%{user|domain}'
#}
passdb sql {
query = SELECT email AS user, password FROM core_user u INNER JOIN admin_mailbox mb ON u.id=mb.user_id INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE (mb.is_send_only=0 OR '%%{protocol}' NOT IN ('imap', 'pop3')) AND u.email='%%{user}' AND u.is_active=1 AND dom.enabled=1
}
#userdb sql {
# userdb query to retrieve the user information. It can return fields:
# uid - System UID (overrides mail_uid setting)
# gid - System GID (overrides mail_gid setting)
# home - Home directory
# mail_driver - Mail driver
# mail_path - Mail storage path
#
# None of these are strictly required. If you use a single UID and GID, and
# home or mail directory fits to a template string, you could use userdb static
# instead. For a list of all fields that can be returned, see
# Examples:
# query = SELECT home, uid, gid FROM users WHERE userid = '%%{user}'
# query = SELECT dir AS home, user AS uid, group AS gid FROM users where userid = '%%{user}'
# query = SELECT home, 501 AS uid, 501 AS gid FROM users WHERE userid = '%%{user}'
#
# query = \
# SELECT home, uid, gid \
# FROM users WHERE userid = '%%{user|username}' AND domain = '%%{user|domain}'
# Query to get a list of all usernames.
# iterate_query = SELECT username AS user,domain FROM users
# userdb_ldap {
# iterate_fields {
# home = /var/vmail/%%{home}
# }
# }
#}
userdb sql {
query = SELECT '%{home_dir}/%%{user|domain}/%%{user|username}' AS home, %mailboxes_owner_uid as uid, %mailboxes_owner_gid as gid, CONCAT(mb.quota, 'M') AS quota_storage_size FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN core_user u ON u.id=mb.user_id WHERE (mb.is_send_only=0 OR '%%{protocol}' NOT IN ('imap', 'pop3', 'lmtp')) AND mb.address='%%{user|username}' AND dom.name='%%{user|domain}'
iterate_query = SELECT email AS user FROM core_user
}
#passdb static {
# fields {
# user=%%{user|username|lower}
# noauthenticate=yes
# }
## you can remove next line if you want to always normalize your usernames
# skip = authenticated
#}
# "prefetch" user database means that the passdb already provided the
# needed information and there's no need to do a separate userdb lookup.
# <https://doc.dovecot.org/latest/core/config/auth/databases/prefetch.html>
#userdb prefetch {
#}
#userdb static {
# fields {
# user=%%{user|lower}
# }
# you can remove next line if you want to always normalize your usernames
# skip = found
#}
# If you don't have any user-specific settings, you can avoid the user_query
# by using userdb static instead of userdb sql, for example:
# <https://doc.dovecot.org/latest/core/config/auth/databases/static.html>
#userdb static {
#fields {
# uid = vmail
# gid = vmail
# home = /var/vmail/%%{user}
#}
#}

View File

@@ -1,195 +0,0 @@
# Authentication for SQL users. Included from auth.conf.
#
# <https://doc.dovecot.org/latest/core/config/auth/databases/sql.html>
# For the sql passdb module, you'll need a database with a table that
# contains fields for at least the username and password. If you want to
# use the user@domain syntax, you might want to have a separate domain
# field as well.
#
# If your users all have the same uig/gid, and have predictable home
# directories, you can use the static userdb module to generate the home
# dir based on the username and domain. In this case, you won't need fields
# for home, uid, or gid in the database.
#
# If you prefer to use the sql userdb module, you'll want to add fields
# for home, uid, and gid. Here is an example table:
#
# CREATE TABLE users (
# username VARCHAR(128) NOT NULL,
# domain VARCHAR(128) NOT NULL,
# password VARCHAR(64) NOT NULL,
# home VARCHAR(255) NOT NULL,
# uid INTEGER NOT NULL,
# gid INTEGER NOT NULL,
# active CHAR(1) DEFAULT 'Y' NOT NULL
# );
# Database driver: mysql, pgsql, sqlite
sql_driver = %db_driver
# Database connection string. This is driver-specific setting.
#
# HA / round-robin load-balancing is supported by giving multiple host
# settings, like: host=sql1.host.org host=sql2.host.org
#
# pgsql:
# For available options, see the PostgreSQL documention for the
# PQconnectdb function of libpq.
# Use maxconns=n (default 5) to change how many connections Dovecot can
# create to pgsql.
#
# mysql:
# Basic options emulate PostgreSQL option names:
# host, port, user, password, dbname
#
# But also adds some new settings:
# client_flags - See MySQL manual
# ssl_ca, ssl_ca_path - Set either one or both to enable SSL
# ssl_cert, ssl_key - For sending client-side certificates to server
# ssl_cipher - Set minimum allowed cipher security (default: HIGH)
# option_file - Read options from the given file instead of
# the default my.cnf location
# option_group - Read options from the given group (default: client)
#
# You can connect to UNIX sockets by using host: host=/var/run/mysql.sock
# Note that currently you can't use spaces in parameters.
#
# sqlite:
# The path to the database file.
#
# Examples:
# mysql 192.168.1.1 {
# dbname = users
# }
# mysql sql.example.com {
# ssl = yes
# user = virtual
# password = blarg
# dbname = virtual
# }
# sqlite /etc/dovecot/authdb.sqlite {
# }
#
#mysql /var/run/mysqld/mysqld.sock {
# user = dovecot
# password = dvmail
# dbname = dovecot
#}
#mysql localhost {
# ...
#}
pgsql %dbhost {
parameters {
port = %dbport
dbname = %modoboa_dbname
user = %modoboa_dbuser
password = %modoboa_dbpassword
}
}
#passdb sql {
# default_password_scheme = SHA256
# passdb query to retrieve the password. It can return fields:
# password - The user's password. This field must be returned.
# user - user@domain from the database. Needed with case-insensitive lookups.
# username and domain - An alternative way to represent the "user" field.
#
# The "user" field is often necessary with case-insensitive lookups to avoid
# e.g. "name" and "nAme" logins creating two different mail directories. If
# your user and domain names are in separate fields, you can return "username"
# and "domain" fields instead of "user".
#
# The query can also return other fields which have a special meaning, see
# https://doc.dovecot.org/latest/core/config/auth/passdb.html#extra-fields
#
# Commonly used available substitutions (see https://doc.dovecot.org/latest/core/settings/variables.html
# for full list):
# %%{user} = entire user@domain
# %%{user|username} = user part of user@domain
# %%{user|domain} = domain part of user@domain
#
# Note that these can be used only as input to SQL query. If the query outputs
# any of these substitutions, they're not touched. Otherwise it would be
# difficult to have eg. usernames containing '%%' characters.
#
# Example:
# query = SELECT userid AS user, pw AS password \
# FROM users WHERE userid = '%%u' AND active = 'Y'
#
# query = \
# SELECT userid as username, domain, password \
# FROM users WHERE userid = '%%{user|username}' AND domain = '%%{user|domain}'
#}
passdb sql {
query = SELECT email AS user, password FROM core_user u INNER JOIN admin_mailbox mb ON u.id=mb.user_id INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE (mb.is_send_only IS NOT TRUE OR '%%{protocol}' NOT IN ('imap', 'pop3')) AND email='%%{user}' AND is_active AND dom.enabled
}
#userdb sql {
# userdb query to retrieve the user information. It can return fields:
# uid - System UID (overrides mail_uid setting)
# gid - System GID (overrides mail_gid setting)
# home - Home directory
# mail_driver - Mail driver
# mail_path - Mail storage path
#
# None of these are strictly required. If you use a single UID and GID, and
# home or mail directory fits to a template string, you could use userdb static
# instead. For a list of all fields that can be returned, see
# Examples:
# query = SELECT home, uid, gid FROM users WHERE userid = '%%{user}'
# query = SELECT dir AS home, user AS uid, group AS gid FROM users where userid = '%%{user}'
# query = SELECT home, 501 AS uid, 501 AS gid FROM users WHERE userid = '%%{user}'
#
# query = \
# SELECT home, uid, gid \
# FROM users WHERE userid = '%%{user|username}' AND domain = '%%{user|domain}'
# Query to get a list of all usernames.
# iterate_query = SELECT username AS user,domain FROM users
# userdb_ldap {
# iterate_fields {
# home = /var/vmail/%%{home}
# }
# }
#}
userdb sql {
query = SELECT '%{home_dir}/%%{user|domain}/%%{user|username}' AS home, %mailboxes_owner_uid as uid, %mailboxes_owner_gid as gid, mb.quota || 'M' AS quota_storage_size FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN core_user u ON u.id=mb.user_id WHERE (mb.is_send_only IS NOT TRUE OR '%%{protocol}' NOT IN ('imap', 'pop3', 'lmtp')) AND mb.address='%%{user|username}' AND dom.name='%%{user|domain}'
iterate_query = SELECT email AS user FROM core_user
}
#passdb static {
# fields {
# user=%%{user|username|lower}
# noauthenticate=yes
# }
## you can remove next line if you want to always normalize your usernames
# skip = authenticated
#}
# "prefetch" user database means that the passdb already provided the
# needed information and there's no need to do a separate userdb lookup.
# <https://doc.dovecot.org/latest/core/config/auth/databases/prefetch.html>
#userdb prefetch {
#}
#userdb static {
# fields {
# user=%%{user|lower}
# }
# you can remove next line if you want to always normalize your usernames
# skip = found
#}
# If you don't have any user-specific settings, you can avoid the user_query
# by using userdb static instead of userdb sql, for example:
# <https://doc.dovecot.org/latest/core/config/auth/databases/static.html>
#userdb static {
#fields {
# uid = vmail
# gid = vmail
# home = /var/vmail/%%{user}
#}
#}

View File

@@ -1,87 +0,0 @@
## Dovecot configuration file
# If you're in a hurry, see https://doc.dovecot.org/latest/core/config/guides/quick.html
# "doveconf -n" command gives a clean output of the changed settings. Use it
# instead of copy&pasting files when posting to the Dovecot mailing list.
# '#' character and everything after it is treated as comments. Extra spaces
# and tabs are ignored. If you want to use either of these explicitly, put the
# value inside quotes, eg.: key = "# char and trailing whitespace "
# Default values are shown for each setting, it's not required to uncomment
# those. These are exceptions to this though: No sections (e.g. namespace {})
# or plugin settings are added by default, they're listed only as examples.
# Paths are also just examples with the real defaults being based on configure
# options. The paths listed here are for configure --prefix=/usr/local
# --sysconfdir=/usr/local/etc --localstatedir=/var
dovecot_config_version = 2.4.0
dovecot_storage_version = 2.4.0
# Protocols we want to be serving.
%protocols
!include_try /usr/share/dovecot/protocols.d/*.protocol
# A comma separated list of IPs or hosts where to listen in for connections.
# "*" listens in all IPv4 interfaces, "::" listens in all IPv6 interfaces.
# If you want to specify non-default ports or anything more complex,
# edit conf.d/master.conf.
#listen = *, ::
# Base directory where to store runtime data.
#base_dir = /var/run/dovecot/
# Name of this instance. In multi-instance setup doveadm and other commands
# can use -i <instance_name> to select which instance is used (an alternative
# to -c <config_path>). The instance name is also added to Dovecot processes
# in ps output.
#instance_name = dovecot
# Greeting message for clients.
#login_greeting = Dovecot ready.
# Space separated list of trusted network ranges. Connections from these
# IPs are allowed to override their IP addresses and ports (for logging and
# for authentication checks). disable_plaintext_auth is also ignored for
# these networks, unless ssl=required.
# Typically you'd specify your IMAP proxy servers here.
#login_trusted_networks =
# With proxy_maybe=yes if proxy destination matches any of these IPs, don't do
# proxying. This isn't necessary normally, but may be useful if the destination
# IP is e.g. a load balancer's IP.
#auth_proxy_self =
# Show more verbose process titles (in ps). Currently shows user name and
# IP address. Useful for seeing who are actually using the IMAP processes
# (eg. shared mailboxes or if same uid is used for multiple accounts).
#verbose_proctitle = yes
# Should all processes be killed when Dovecot master process shuts down.
# Setting this to "no" means that Dovecot can be upgraded without
# forcing existing client connections to close (although that could also be
# a problem if the upgrade is e.g. because of a security fix).
#shutdown_clients = yes
# If non-zero, run mail commands via this many connections to doveadm server,
# instead of running them directly in the same process.
#doveadm_worker_count = 0
# UNIX socket or host:port used for connecting to doveadm server
#doveadm_socket_path = doveadm-server
# Space separated list of environment variables that are preserved on Dovecot
# startup and passed down to all of its child processes. You can also give
# key=value pairs to always set specific settings.
#import_environment {
# TZ=%%{env:TZ}
#}
# Most of the actual configuration gets included below. The filenames are
# first sorted by their ASCII value and parsed in that order. The 00-prefixes
# in filenames are intended to make it easier to understand the ordering.
!include conf.d/*.conf
# A config file can also tried to be included without giving an error if
# it's not found:
!include_try local.conf

View File

@@ -96,7 +96,7 @@ auth_master_user_separator = *
# plain login digest-md5 cram-md5 ntlm rpa apop anonymous gssapi otp skey # plain login digest-md5 cram-md5 ntlm rpa apop anonymous gssapi otp skey
# gss-spnego # gss-spnego
# NOTE: See also disable_plaintext_auth setting. # NOTE: See also disable_plaintext_auth setting.
auth_mechanisms = plain login oauthbearer xoauth2 auth_mechanisms = plain login
## ##
## Password and user databases ## Password and user databases
@@ -120,7 +120,6 @@ auth_mechanisms = plain login oauthbearer xoauth2
#!include auth-system.conf.ext #!include auth-system.conf.ext
!include auth-sql.conf.ext !include auth-sql.conf.ext
!include auth-oauth2.conf.ext
#!include auth-ldap.conf.ext #!include auth-ldap.conf.ext
#!include auth-passwdfile.conf.ext #!include auth-passwdfile.conf.ext
#!include auth-checkpassword.conf.ext #!include auth-checkpassword.conf.ext

View File

@@ -92,14 +92,14 @@ service postlogin {
service stats { service stats {
# To allow modoboa to access available cipher list. # To allow modoboa to access available cipher list.
unix_listener stats-reader { unix_listener stats-reader {
user = %{mailboxes_owner} user = vmail
group = %{mailboxes_owner} group = vmail
mode = 0660 mode = 0660
} }
unix_listener stats-writer { unix_listener stats-writer {
user = %{mailboxes_owner} user = vmail
group = %{mailboxes_owner} group = vmail
mode = 0660 mode = 0660
} }
} }
@@ -120,7 +120,7 @@ service auth {
# permissions (e.g. 0777 allows everyone full permissions). # permissions (e.g. 0777 allows everyone full permissions).
unix_listener auth-userdb { unix_listener auth-userdb {
#mode = 0666 #mode = 0666
user = %{mailboxes_owner} user = vmail
#group = #group =
} }
@@ -131,6 +131,13 @@ service auth {
group = postfix group = postfix
} }
# Radicale auth
%{radicale_enabled}unix_listener %{radicale_auth_socket_path} {
%{radicale_enabled} mode = 0666
%{radicale_enabled} user = %{radicale_user}
%{radicale_enabled} group = %{radicale_user}
%{radicale_enabled}}
# Auth process is run as this user. # Auth process is run as this user.
#user = $default_internal_user #user = $default_internal_user
} }
@@ -147,7 +154,7 @@ service dict {
# For example: mode=0660, group=vmail and global mail_access_groups=vmail # For example: mode=0660, group=vmail and global mail_access_groups=vmail
unix_listener dict { unix_listener dict {
mode = 0600 mode = 0600
user = %{mailboxes_owner} user = vmail
#group = #group =
} }
} }

View File

@@ -38,7 +38,7 @@ plugin {
# Identical to sieve_before, only the specified scripts are executed after the # Identical to sieve_before, only the specified scripts are executed after the
# user's script (only when keep is still in effect!). Multiple script file or # user's script (only when keep is still in effect!). Multiple script file or
# directory paths can be specified by appending an increasing number. # directory paths can be specified by appending an increasing number.
%{do_move_spam_to_junk}sieve_after = /etc/dovecot/conf.d/custom_after_sieve #sieve_after =
#sieve_after2 = #sieve_after2 =
#sieve_after2 = (etc...) #sieve_after2 = (etc...)

View File

@@ -1,4 +0,0 @@
require "fileinto";
if header :contains "X-Spam-Status" "Yes" {
fileinto "Junk";
}

View File

@@ -123,8 +123,7 @@ connect = host=%dbhost port=%dbport dbname=%modoboa_dbname user=%modoboa_dbuser
#user_query = \ #user_query = \
# SELECT home, uid, gid \ # SELECT home, uid, gid \
# FROM users WHERE username = '%%n' AND domain = '%%d' # FROM users WHERE username = '%%n' AND domain = '%%d'
%{not_modoboa_2_2_or_greater}user_query = SELECT '%{home_dir}/%%d/%%n' AS home, %mailboxes_owner_uid as uid, %mailboxes_owner_gid as gid, CONCAT('*:bytes=', mb.quota, 'M') AS quota_rule FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN core_user u ON u.id=mb.user_id WHERE mb.address='%%n' AND dom.name='%%d' user_query = SELECT '%{home_dir}/%%d/%%n' AS home, %mailboxes_owner_uid as uid, %mailboxes_owner_gid as gid, CONCAT('*:bytes=', mb.quota, 'M') AS quota_rule FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN core_user u ON u.id=mb.user_id WHERE mb.address='%%n' AND dom.name='%%d'
%{modoboa_2_2_or_greater}user_query = SELECT '%{home_dir}/%%d/%%n' AS home, %mailboxes_owner_uid as uid, %mailboxes_owner_gid as gid, CONCAT('*:bytes=', mb.quota, 'M') AS quota_rule FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN core_user u ON u.id=mb.user_id WHERE (mb.is_send_only=0 OR '%%s' NOT IN ('imap', 'pop3', 'lmtp')) AND mb.address='%%n' AND dom.name='%%d'
# If you wish to avoid two SQL lookups (passdb + userdb), you can use # If you wish to avoid two SQL lookups (passdb + userdb), you can use
# userdb prefetch instead of userdb sql in dovecot.conf. In that case you'll # userdb prefetch instead of userdb sql in dovecot.conf. In that case you'll
@@ -134,8 +133,7 @@ connect = host=%dbhost port=%dbport dbname=%modoboa_dbname user=%modoboa_dbuser
# SELECT userid AS user, password, \ # SELECT userid AS user, password, \
# home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \ # home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \
# FROM users WHERE userid = '%%u' # FROM users WHERE userid = '%%u'
%{not_modoboa_2_2_or_greater}password_query = SELECT email AS user, password, '%{home_dir}/%%d/%%n' AS userdb_home, %mailboxes_owner_uid AS userdb_uid, %mailboxes_owner_gid AS userdb_gid, CONCAT('*:bytes=', mb.quota, 'M') AS userdb_quota_rule FROM core_user u INNER JOIN admin_mailbox mb ON u.id=mb.user_id INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE u.email='%%u' AND u.is_active=1 AND dom.enabled=1 password_query = SELECT email AS user, password, '%{home_dir}/%%d/%%n' AS userdb_home, %mailboxes_owner_uid AS userdb_uid, %mailboxes_owner_gid AS userdb_gid, CONCAT('*:bytes=', mb.quota, 'M') AS userdb_quota_rule FROM core_user u INNER JOIN admin_mailbox mb ON u.id=mb.user_id INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE u.email='%%u' AND u.is_active=1 AND dom.enabled=1
%{modoboa_2_2_or_greater}password_query = SELECT email AS user, password, '%{home_dir}/%%d/%%n' AS userdb_home, %mailboxes_owner_uid AS userdb_uid, %mailboxes_owner_gid AS userdb_gid, CONCAT('*:bytes=', mb.quota, 'M') AS userdb_quota_rule FROM core_user u INNER JOIN admin_mailbox mb ON u.id=mb.user_id INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE (mb.is_send_only=0 OR '%%s' NOT IN ('imap', 'pop3')) AND u.email='%%u' AND u.is_active=1 AND dom.enabled=1
# Query to get a list of all usernames. # Query to get a list of all usernames.
#iterate_query = SELECT username AS user FROM users #iterate_query = SELECT username AS user FROM users

View File

@@ -123,8 +123,7 @@ connect = host=%dbhost port=%dbport dbname=%modoboa_dbname user=%modoboa_dbuser
#user_query = \ #user_query = \
# SELECT home, uid, gid \ # SELECT home, uid, gid \
# FROM users WHERE username = '%%n' AND domain = '%%d' # FROM users WHERE username = '%%n' AND domain = '%%d'
%{not_modoboa_2_2_or_greater}user_query = SELECT '%{home_dir}/%%d/%%n' AS home, %mailboxes_owner_uid as uid, %mailboxes_owner_gid as gid, '*:bytes=' || mb.quota || 'M' AS quota_rule FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN core_user u ON u.id=mb.user_id WHERE mb.address='%%n' AND dom.name='%%d' user_query = SELECT '%{home_dir}/%%d/%%n' AS home, %mailboxes_owner_uid as uid, %mailboxes_owner_gid as gid, '*:bytes=' || mb.quota || 'M' AS quota_rule FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN core_user u ON u.id=mb.user_id WHERE mb.address='%%n' AND dom.name='%%d'
%{modoboa_2_2_or_greater}user_query = SELECT '%{home_dir}/%%d/%%n' AS home, %mailboxes_owner_uid as uid, %mailboxes_owner_gid as gid, '*:bytes=' || mb.quota || 'M' AS quota_rule FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN core_user u ON u.id=mb.user_id WHERE (mb.is_send_only IS NOT TRUE OR '%%s' NOT IN ('imap', 'pop3', 'lmtp')) AND mb.address='%%n' AND dom.name='%%d'
# If you wish to avoid two SQL lookups (passdb + userdb), you can use # If you wish to avoid two SQL lookups (passdb + userdb), you can use
# userdb prefetch instead of userdb sql in dovecot.conf. In that case you'll # userdb prefetch instead of userdb sql in dovecot.conf. In that case you'll
@@ -134,8 +133,7 @@ connect = host=%dbhost port=%dbport dbname=%modoboa_dbname user=%modoboa_dbuser
# SELECT userid AS user, password, \ # SELECT userid AS user, password, \
# home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \ # home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \
# FROM users WHERE userid = '%%u' # FROM users WHERE userid = '%%u'
%{not_modoboa_2_2_or_greater}password_query = SELECT email AS user, password, '%{home_dir}/%%d/%%n' AS userdb_home, %mailboxes_owner_uid AS userdb_uid, %mailboxes_owner_gid AS userdb_gid, CONCAT('*:bytes=', mb.quota, 'M') AS userdb_quota_rule FROM core_user u INNER JOIN admin_mailbox mb ON u.id=mb.user_id INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE email='%%u' AND is_active AND dom.enabled password_query = SELECT email AS user, password, '%{home_dir}/%%d/%%n' AS userdb_home, %mailboxes_owner_uid AS userdb_uid, %mailboxes_owner_gid AS userdb_gid, CONCAT('*:bytes=', mb.quota, 'M') AS userdb_quota_rule FROM core_user u INNER JOIN admin_mailbox mb ON u.id=mb.user_id INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE email='%%u' AND is_active AND dom.enabled
%{modoboa_2_2_or_greater}password_query = SELECT email AS user, password, '%{home_dir}/%%d/%%n' AS userdb_home, %mailboxes_owner_uid AS userdb_uid, %mailboxes_owner_gid AS userdb_gid, CONCAT('*:bytes=', mb.quota, 'M') AS userdb_quota_rule FROM core_user u INNER JOIN admin_mailbox mb ON u.id=mb.user_id INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE (mb.is_send_only IS NOT TRUE OR '%%s' NOT IN ('imap', 'pop3')) AND email='%%u' AND is_active AND dom.enabled
# Query to get a list of all usernames. # Query to get a list of all usernames.
#iterate_query = SELECT username AS user FROM users #iterate_query = SELECT username AS user FROM users

View File

@@ -1,9 +0,0 @@
# Fail2Ban filter Modoboa authentication
[INCLUDES]
before = common.conf
[Definition]
failregex = modoboa\.auth: WARNING Failed connection attempt from \'<HOST>\' as user \'.*?\'$

View File

@@ -1,9 +0,0 @@
[modoboa]
enabled = true
port = http,https
protocol = tcp
filter = modoboa-auth
maxretry = %max_retry
bantime = %ban_time
findtime = %find_time
logpath = /var/log/auth.log

View File

@@ -3,7 +3,6 @@
# #
PYTHON=%{venv_path}/bin/python PYTHON=%{venv_path}/bin/python
INSTANCE=%{instance_path} INSTANCE=%{instance_path}
MAILTO=%{cron_error_recipient}
# Operations on mailboxes # Operations on mailboxes
%{dovecot_enabled}* * * * * %{dovecot_mailboxes_owner} $PYTHON $INSTANCE/manage.py handle_mailbox_operations %{dovecot_enabled}* * * * * %{dovecot_mailboxes_owner} $PYTHON $INSTANCE/manage.py handle_mailbox_operations
@@ -34,4 +33,4 @@ MAILTO=%{cron_error_recipient}
%{minutes} %{hours} * * * root $PYTHON $INSTANCE/manage.py communicate_with_public_api %{minutes} %{hours} * * * root $PYTHON $INSTANCE/manage.py communicate_with_public_api
# Generate DKIM keys (they will belong to the user running this job) # Generate DKIM keys (they will belong to the user running this job)
%{dkim_cron_enabled}* * * * * %{opendkim_user} umask 077 && $PYTHON $INSTANCE/manage.py modo manage_dkim_keys %{opendkim_enabled}* * * * * %{opendkim_user} umask 077 && $PYTHON $INSTANCE/manage.py modo manage_dkim_keys

View File

@@ -1,9 +0,0 @@
[program:modoboa-base-worker]
autostart=true
autorestart=true
command=%{venv_path}/bin/python %{home_dir}/instance/manage.py rqworker modoboa
directory=%{home_dir}
user=%{user}
redirect_stderr=true
numprocs=1
stopsignal=TERM

View File

@@ -1,9 +0,0 @@
[program:modoboa-dkim-worker]
autostart=true
autorestart=true
command=%{venv_path}/bin/python %{home_dir}/instance/manage.py rqworker dkim
directory=%{home_dir}
user=%{dkim_user}
redirect_stderr=true
numprocs=1
stopsignal=TERM

View File

@@ -6,4 +6,3 @@ directory=%{home_dir}
redirect_stderr=true redirect_stderr=true
user=%{user} user=%{user}
numprocs=1 numprocs=1

View File

@@ -1,14 +1,18 @@
upstream automx {
server unix:%uwsgi_socket_path fail_timeout=0;
}
server { server {
listen 80; listen 80;
listen [::]:80; listen [::]:80;
server_name %hostname; server_name %hostname;
root %app_instance_path; root /srv/automx/instance;
access_log /var/log/nginx/%{hostname}-access.log; access_log /var/log/nginx/%{hostname}-access.log;
error_log /var/log/nginx/%{hostname}-error.log; error_log /var/log/nginx/%{hostname}-error.log;
location ~ ^/(mail/config-v1.1.xml|mobileconfig) { location /mail/config-v1.1.xml {
include uwsgi_params; include uwsgi_params;
uwsgi_pass modoboa; uwsgi_pass automx;
} }
} }

View File

@@ -10,8 +10,8 @@ server {
} }
server { server {
listen 443 ssl http2; listen 443 ssl;
listen [::]:443 ssl http2; listen [::]:443 ssl;
server_name %hostname; server_name %hostname;
root %app_instance_path; root %app_instance_path;
@@ -37,20 +37,7 @@ server {
try_files $uri $uri/ =404; try_files $uri $uri/ =404;
} }
%{rspamd_enabled} location /rspamd/ { location ^~ /new-admin {
%{rspamd_enabled} proxy_pass http://localhost:11334/;
%{rspamd_enabled}
%{rspamd_enabled} proxy_set_header Host $host;
%{rspamd_enabled} proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
%{rspamd_enabled} }
location ~ ^/(api|accounts) {
include uwsgi_params;
uwsgi_param UWSGI_SCRIPT instance.wsgi:application;
uwsgi_pass modoboa;
}
location / {
alias %{app_instance_path}/frontend/; alias %{app_instance_path}/frontend/;
index index.html; index index.html;
@@ -61,5 +48,10 @@ server {
try_files $uri $uri/ /index.html = 404; try_files $uri $uri/ /index.html = 404;
} }
location / {
include uwsgi_params;
uwsgi_param UWSGI_SCRIPT instance.wsgi:application;
uwsgi_pass modoboa;
}
%{extra_config} %{extra_config}
} }

View File

@@ -1,11 +0,0 @@
if /^\s*Received:.*Authenticated sender.*\(Postfix\)/
/^Received: from .*? \([\w\-.]* \[.*?\]\)(.*|\n.*)\(Authenticated sender: (.+)\)\s+by.+\(Postfix\) with (.*)/
REPLACE Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with $3
endif
if /^\s*Received: from .*rspamd.localhost .*\(Postfix\)/
/^Received: from.* (.*|\n.*)\((.+) (.+)\)\s+by (.+) \(Postfix\) with (.*)/
REPLACE Received: from rspamd (rspamd $3) by $4 (Postfix) with $5
endif
/^\s*X-Enigmail/ IGNORE
/^\s*X-Originating-IP/ IGNORE
/^\s*X-Forward/ IGNORE

View File

@@ -41,29 +41,22 @@ smtpd_tls_auth_only = no
smtpd_tls_CApath = /etc/ssl/certs smtpd_tls_CApath = /etc/ssl/certs
smtpd_tls_key_file = %tls_key_file smtpd_tls_key_file = %tls_key_file
smtpd_tls_cert_file = %tls_cert_file smtpd_tls_cert_file = %tls_cert_file
smtpd_tls_dh1024_param_file = ${config_directory}/ffdhe%{dhe_group}.pem smtpd_tls_dh1024_param_file = ${config_directory}/dh2048.pem
smtpd_tls_loglevel = 1 smtpd_tls_loglevel = 1
smtpd_tls_session_cache_database = btree:$data_directory/smtpd_tls_session_cache smtpd_tls_session_cache_database = btree:$data_directory/smtpd_tls_session_cache
smtpd_tls_security_level = may smtpd_tls_security_level = may
smtpd_tls_received_header = yes smtpd_tls_received_header = yes
# Disallow SSLv2 and SSLv3, only accept secure ciphers # Disallow SSLv2 and SSLv3, only accept secure ciphers
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 smtpd_tls_protocols = !SSLv2, !SSLv3
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
smtpd_tls_mandatory_ciphers = high smtpd_tls_mandatory_ciphers = high
smtpd_tls_mandatory_exclude_ciphers = aNULL, eNULL, EXPORT, DES, RC4, MD5, PSK, aECDH, EDH-DSS-DES-CBC3-SHA, EDH-RSA-DES-CBC3-SHA, KRB5-DES, CBC3-SHA, CAMELLIA, SEED-SHA, AES256-SHA, AES256-SHA256, AES256-GCM-SHA384, AES128-SHA, AES128-SHA256, AES128-GCM-SHA256, DHE-RSA-AES128-GCM-SHA256, DHE-RSA-AES128-SHA, DHE-RSA-AES128-SHA256, DHE-RSA-AES256-GCM-SHA384, DHE-RSA-AES256-SHA, DHE-RSA-AES256-SHA256, DHE-RSA-CHACHA20-POLY1305, ECDHE-RSA-AES128-SHA, ECDHE-RSA-AES256-SHA smtpd_tls_mandatory_exclude_ciphers = aNULL, MD5 , DES, ADH, RC4, PSD, SRP, 3DES, eNULL
smtpd_tls_exclude_ciphers = aNULL, eNULL, EXPORT, DES, RC4, MD5, PSK, aECDH, EDH-DSS-DES-CBC3-SHA, EDH-RSA-DES-CBC3-SHA, KRB5-DES, CBC3-SHA, CAMELLIA, SEED-SHA, AES256-SHA, AES256-SHA256, AES256-GCM-SHA384, AES128-SHA, AES128-SHA256, AES128-GCM-SHA256, DHE-RSA-AES128-GCM-SHA256, DHE-RSA-AES128-SHA, DHE-RSA-AES128-SHA256, DHE-RSA-AES256-GCM-SHA384, DHE-RSA-AES256-SHA, DHE-RSA-AES256-SHA256, DHE-RSA-CHACHA20-POLY1305, ECDHE-RSA-AES128-SHA, ECDHE-RSA-AES256-SHA smtpd_tls_exclude_ciphers = aNULL, MD5 , DES, ADH, RC4, PSD, SRP, 3DES, eNULL
tls_preempt_cipherlist = yes
tls_ssl_options = NO_COMPRESSION
# Enable elliptic curve cryptography # Enable elliptic curve cryptography
smtpd_tls_eecdh_grade = strong smtpd_tls_eecdh_grade = strong
# SMTP Smuggling prevention
# See https://www.postfix.org/smtp-smuggling.html
smtpd_data_restrictions = reject_unauth_pipelining
smtpd_forbid_unauth_pipelining = yes
# Use TLS if this is supported by the remote SMTP server, otherwise use plaintext. # Use TLS if this is supported by the remote SMTP server, otherwise use plaintext.
smtp_tls_CApath = /etc/ssl/certs smtp_tls_CApath = /etc/ssl/certs
smtp_tls_security_level = may smtp_tls_security_level = may
@@ -74,10 +67,10 @@ smtp_tls_exclude_ciphers = EXPORT, LOW
# #
%{dovecot_enabled}virtual_transport = lmtp:unix:private/dovecot-lmtp %{dovecot_enabled}virtual_transport = lmtp:unix:private/dovecot-lmtp
%{dovecot_enabled}virtual_mailbox_domains = proxy:%{db_driver}:/etc/postfix/sql-domains.cf virtual_mailbox_domains = proxy:%{db_driver}:/etc/postfix/sql-domains.cf
%{dovecot_enabled}virtual_alias_domains = proxy:%{db_driver}:/etc/postfix/sql-domain-aliases.cf virtual_alias_domains = proxy:%{db_driver}:/etc/postfix/sql-domain-aliases.cf
%{dovecot_enabled}virtual_alias_maps = virtual_alias_maps =
%{dovecot_enabled} proxy:%{db_driver}:/etc/postfix/sql-aliases.cf proxy:%{db_driver}:/etc/postfix/sql-aliases.cf
## Relay domains ## Relay domains
# #
@@ -122,19 +115,10 @@ strict_rfc821_envelopes = yes
%{opendkim_enabled}milter_default_action = accept %{opendkim_enabled}milter_default_action = accept
%{opendkim_enabled}milter_content_timeout = 30s %{opendkim_enabled}milter_content_timeout = 30s
# Rspamd setup
%{rspamd_enabled}smtpd_milters = inet:localhost:11332
%{rspamd_enabled}non_smtpd_milters = inet:localhost:11332
%{rspamd_enabled}milter_default_action = accept
%{rspamd_enabled}milter_protocol = 6
# List of authorized senders # List of authorized senders
smtpd_sender_login_maps = smtpd_sender_login_maps =
proxy:%{db_driver}:/etc/postfix/sql-sender-login-map.cf proxy:%{db_driver}:/etc/postfix/sql-sender-login-map.cf
# Add authenticated header to hide public client IP
smtpd_sasl_authenticated_header = yes
# Recipient restriction rules # Recipient restriction rules
smtpd_recipient_restrictions = smtpd_recipient_restrictions =
check_policy_service inet:127.0.0.1:9999 check_policy_service inet:127.0.0.1:9999
@@ -151,27 +135,28 @@ smtpd_recipient_restrictions =
## Postcreen settings ## Postcreen settings
# #
%{rspamd_disabled}postscreen_access_list = postscreen_access_list =
%{rspamd_disabled} permit_mynetworks permit_mynetworks
%{rspamd_disabled} cidr:/etc/postfix/postscreen_spf_whitelist.cidr cidr:/etc/postfix/postscreen_spf_whitelist.cidr
%{rspamd_disabled}postscreen_blacklist_action = enforce postscreen_blacklist_action = enforce
# Use some DNSBL # Use some DNSBL
%{rspamd_disabled}postscreen_dnsbl_sites = postscreen_dnsbl_sites =
%{rspamd_disabled} zen.spamhaus.org=127.0.0.[2..11]*3 zen.spamhaus.org=127.0.0.[2..11]*3
%{rspamd_disabled} bl.spameatingmonkey.net=127.0.0.2*2 bl.spameatingmonkey.net=127.0.0.2*2
%{rspamd_disabled} bl.spamcop.net=127.0.0.2 bl.spamcop.net=127.0.0.2
%{rspamd_disabled}postscreen_dnsbl_threshold = 3 dnsbl.sorbs.net=127.0.0.[2..15]
%{rspamd_disabled}postscreen_dnsbl_action = enforce postscreen_dnsbl_threshold = 3
postscreen_dnsbl_action = enforce
%{rspamd_disabled}postscreen_greet_banner = Welcome, please wait... postscreen_greet_banner = Welcome, please wait...
%{rspamd_disabled}postscreen_greet_action = enforce postscreen_greet_action = enforce
%{rspamd_disabled}postscreen_pipelining_enable = yes postscreen_pipelining_enable = yes
%{rspamd_disabled}postscreen_pipelining_action = enforce postscreen_pipelining_action = enforce
%{rspamd_disabled}postscreen_non_smtp_command_enable = yes postscreen_non_smtp_command_enable = yes
%{rspamd_disabled}postscreen_non_smtp_command_action = enforce postscreen_non_smtp_command_action = enforce
%{rspamd_disabled}postscreen_bare_newline_enable = yes postscreen_bare_newline_enable = yes
%{rspamd_disabled}postscreen_bare_newline_action = enforce postscreen_bare_newline_action = enforce

View File

@@ -9,8 +9,7 @@
# service type private unpriv chroot wakeup maxproc command + args # service type private unpriv chroot wakeup maxproc command + args
# (yes) (yes) (yes) (never) (100) # (yes) (yes) (yes) (never) (100)
# ========================================================================== # ==========================================================================
%{rspamd_disabled}smtp inet n - - - 1 postscreen smtp inet n - - - 1 postscreen
%{rspamd_enabled}smtp inet n - - - - smtpd
smtpd pass - - - - - smtpd smtpd pass - - - - - smtpd
%{amavis_enabled} -o smtpd_proxy_filter=inet:[127.0.0.1]:10024 %{amavis_enabled} -o smtpd_proxy_filter=inet:[127.0.0.1]:10024
%{amavis_enabled} -o smtpd_proxy_options=speed_adjust %{amavis_enabled} -o smtpd_proxy_options=speed_adjust
@@ -27,7 +26,6 @@ submission inet n - - - - smtpd
-o smtpd_helo_restrictions= -o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=reject_sender_login_mismatch -o smtpd_sender_restrictions=reject_sender_login_mismatch
-o milter_macro_daemon_name=ORIGINATING -o milter_macro_daemon_name=ORIGINATING
-o cleanup_service_name=ascleanup
%{amavis_enabled} -o smtpd_proxy_filter=inet:[127.0.0.1]:10026 %{amavis_enabled} -o smtpd_proxy_filter=inet:[127.0.0.1]:10026
#smtps inet n - - - - smtpd #smtps inet n - - - - smtpd
# -o syslog_name=postfix/smtps # -o syslog_name=postfix/smtps
@@ -43,8 +41,6 @@ submission inet n - - - - smtpd
#628 inet n - - - - qmqpd #628 inet n - - - - qmqpd
pickup unix n - - 60 1 pickup pickup unix n - - 60 1 pickup
cleanup unix n - - - 0 cleanup cleanup unix n - - - 0 cleanup
ascleanup unix n - - - 0 cleanup
-o header_checks=pcre:/etc/postfix/anonymize_headers.pcre
qmgr unix n - n 300 1 qmgr qmgr unix n - n 300 1 qmgr
#qmgr unix n - n 300 1 oqmgr #qmgr unix n - n 300 1 oqmgr
tlsmgr unix - - - 1000? 1 tlsmgr tlsmgr unix - - - 1000? 1 tlsmgr
@@ -82,7 +78,7 @@ scache unix - - - - 1 scache
# Also specify in main.cf: maildrop_destination_recipient_limit=1 # Also specify in main.cf: maildrop_destination_recipient_limit=1
# #
maildrop unix - n n - - pipe maildrop unix - n n - - pipe
flags=DRhu user=%{dovecot_mailboxes_owner} argv=/usr/bin/maildrop -d ${recipient} flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
# #
# ==================================================================== # ====================================================================
# #
@@ -128,6 +124,11 @@ mailman unix - n n - - pipe
flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
${nexthop} ${user} ${nexthop} ${user}
# Modoboa autoreply service
#
autoreply unix - n n - - pipe
flags= user=%{dovecot_mailboxes_owner}:%{dovecot_mailboxes_owner} argv=%{modoboa_venv_path}/bin/python %{modoboa_instance_path}/manage.py autoreply $sender $mailbox
# Amavis return path # Amavis return path
# #
%{amavis_enabled}127.0.0.1:10025 inet n - n - - smtpd %{amavis_enabled}127.0.0.1:10025 inet n - n - - smtpd
@@ -148,4 +149,4 @@ mailman unix - n n - - pipe
%{amavis_enabled} -o smtpd_client_connection_count_limit=0 %{amavis_enabled} -o smtpd_client_connection_count_limit=0
%{amavis_enabled} -o smtpd_client_connection_rate_limit=0 %{amavis_enabled} -o smtpd_client_connection_rate_limit=0
%{amavis_enabled} -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks %{amavis_enabled} -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks
%{amavis_enabled} -o local_header_rewrite_clients=permit_mynetworks,permit_sasl_authenticated %{amavis_enabled} -o local_header_rewrite_clients=

View File

@@ -71,7 +71,7 @@
# Authentication method # Authentication method
# Value: none | htpasswd | remote_user | http_x_remote_user # Value: none | htpasswd | remote_user | http_x_remote_user
type = radicale_modoboa_auth_oauth2 type = radicale_dovecot_auth
# Htpasswd filename # Htpasswd filename
# htpasswd_filename = users # htpasswd_filename = users
@@ -85,7 +85,7 @@ type = radicale_modoboa_auth_oauth2
# Incorrect authentication delay (seconds) # Incorrect authentication delay (seconds)
#delay = 1 #delay = 1
oauth2_introspection_endpoint = %{oauth2_introspection_url} auth_socket = %{auth_socket_path}
[rights] [rights]
@@ -102,6 +102,8 @@ file = %{config_dir}/rights
# Storage backend # Storage backend
# Value: multifilesystem # Value: multifilesystem
type = radicale_storage_by_index
radicale_storage_by_index_fields = dtstart, dtend, uid, summary
# Folder for storing local collections, created if not present # Folder for storing local collections, created if not present
filesystem_folder = %{home_dir}/collections filesystem_folder = %{home_dir}/collections

View File

@@ -1,14 +0,0 @@
clamav {
scan_mime_parts = true;
scan_text_mime = true;
scan_image_mime = true;
retransmits = 2;
timeout = 30;
symbol = "CLAM_VIRUS";
type = "clamav";
servers = "127.0.0.1:3310"
patterns {
# symbol_name = "pattern";
JUST_EICAR = "Test.EICAR";
}
}

View File

@@ -1,3 +0,0 @@
try_fallback = false;
selector_map = "%selector_map_path";
path_map = "%key_map_path";

View File

@@ -1,3 +0,0 @@
try_fallback = false;
selector_map = "%selector_map_path";
path_map = "%key_map_path";

View File

@@ -1,21 +0,0 @@
reporting {
# Required attributes
enabled = true; # Enable reports in general
email = 'postmaster@%hostname'; # Source of DMARC reports
domain = '%hostname'; # Domain to serve
org_name = '%hostname'; # Organisation
# Optional parameters
#bcc_addrs = ["postmaster@example.com"]; # additional addresses to copy on reports
report_local_controller = false; # Store reports for local/controller scans (for testing only)
#helo = 'rspamd.localhost'; # Helo used in SMTP dialog
#smtp = '127.0.0.1'; # SMTP server IP
#smtp_port = 25; # SMTP server port
from_name = '%hostname DMARC REPORT'; # SMTP FROM
msgid_from = 'rspamd'; # Msgid format
#max_entries = 1k; # Maxiumum amount of entries per domain
#keys_expire = 2d; # Expire date for Redis keys
#only_domains = '/path/to/map'; # Only store reports from domains or eSLDs listed in this map
# Available from 3.3
#exclude_domains = '/path/to/map'; # Exclude reports from domains or eSLDs listed in this map
#exclude_domains = ["example.com", "another.com"]; # Alternative, use array to exclude reports from domains or eSLDs
}

View File

@@ -1,5 +0,0 @@
rules {
DMARC_POLICY_QUARANTINE {
action = "add header";
}
}

View File

@@ -1,2 +0,0 @@
%{greylisting_disabled}enabled = false;
servers = "127.0.0.1:6379";

View File

@@ -1,5 +0,0 @@
symbols {
"WHITELIST_AUTHENTICATED" {
weight = %whitelist_auth_weigth;
}
}

View File

@@ -1,20 +0,0 @@
actions {
reject = 15; # normal value is 15, 150 so it will never be rejected
add_header = 6; # set to 0.1 for testing, 6 for normal operation.
rewrite_subject = 8; # Default: 8
greylist = 4; # Default: 4
}
group "antivirus" {
symbol "JUST_EICAR" {
weight = 10;
description = "Eicar test signature";
}
symbol "CLAM_VIRUS_FAIL" {
weight = 0;
}
symbol "CLAM_VIRUS" {
weight = 10;
description = "ClamAV found a Virus";
}
}

View File

@@ -1,16 +0,0 @@
use = ["x-spam-status","x-virus","authentication-results" ];
extended_spam_headers = false;
skip_local = false;
skip_authenticated = false;
routines {
x-virus {
header = "X-Virus";
remove = 1;
symbols = ["CLAM_VIRUS", "JUST_EICAR"];
}
}

View File

@@ -1 +0,0 @@
enabled = true;

View File

@@ -1,6 +0,0 @@
# to disable all predefined rules if the user doesn't want dnsbl
url_whitelist = [];
rbls {
}

View File

@@ -1,2 +0,0 @@
write_servers = "localhost";
read_servers = "localhost";

View File

@@ -1,8 +0,0 @@
authenticated {
priority = high;
authenticated = yes;
apply {
groups_disabled = ["rbl", "spf"];
}
%{whitelist_auth_enabled} symbols ["WHITELIST_AUTHENTICATED"];
}

View File

@@ -1,6 +0,0 @@
spf_cache_size = 1k;
spf_cache_expire = 1d;
max_dns_nesting = 10;
max_dns_requests = 30;
min_cache_ttl = 5m;
disable_ipv6 = false;

View File

@@ -1 +0,0 @@
enable_password = %controller_password

View File

@@ -1 +0,0 @@
enabled = false;

View File

@@ -1,3 +0,0 @@
upstream "local" {
self_scan = yes;
}

View File

@@ -0,0 +1,14 @@
[uwsgi]
uid = %app_user
gid = %app_user
plugins = %uwsgi_plugin
home = %app_venv_path
chdir = %app_instance_path
module = automx_wsgi
master = true
vhost = true
harakiri = 60
processes = %nb_processes
socket = %uwsgi_socket_path
chmod-socket = 660
vacuum = true

View File

@@ -13,5 +13,3 @@ socket = %uwsgi_socket_path
chmod-socket = 660 chmod-socket = 660
vacuum = true vacuum = true
single-interpreter = True single-interpreter = True
max-requests = 5000
buffer-size = 8192

View File

@@ -26,8 +26,7 @@ class Modoboa(base.Installer):
"deb": [ "deb": [
"build-essential", "python3-dev", "libxml2-dev", "libxslt-dev", "build-essential", "python3-dev", "libxml2-dev", "libxslt-dev",
"libjpeg-dev", "librrd-dev", "rrdtool", "libffi-dev", "cron", "libjpeg-dev", "librrd-dev", "rrdtool", "libffi-dev", "cron",
"libssl-dev", "redis-server", "supervisor", "pkg-config", "libssl-dev", "redis-server", "supervisor"
"libcairo2-dev", "libmagic-dev"
], ],
"rpm": [ "rpm": [
"gcc", "gcc-c++", "python3-devel", "libxml2-devel", "libxslt-devel", "gcc", "gcc-c++", "python3-devel", "libxml2-devel", "libxslt-devel",
@@ -44,74 +43,80 @@ class Modoboa(base.Installer):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Get configuration.""" """Get configuration."""
super().__init__(*args, **kwargs) super(Modoboa, self).__init__(*args, **kwargs)
self.venv_path = self.config.get("modoboa", "venv_path") self.venv_path = self.config.get("modoboa", "venv_path")
self.instance_path = self.config.get("modoboa", "instance_path") self.instance_path = self.config.get("modoboa", "instance_path")
self.extensions = self.config.get("modoboa", "extensions").split() self.extensions = self.config.get("modoboa", "extensions").split()
self.devmode = self.config.getboolean("modoboa", "devmode") self.devmode = self.config.getboolean("modoboa", "devmode")
self.amavis_enabled = self.config.getboolean("amavis", "enabled") # Sanity check for amavis
self.rspamd_enabled = self.config.getboolean("rspamd", "enabled") self.amavis_enabled = False
if "modoboa-amavis" in self.extensions:
if self.config.getboolean("amavis", "enabled"):
self.amavis_enabled = True
else:
self.extensions.remove("modoboa-amavis")
if "modoboa-radicale" in self.extensions:
if not self.config.getboolean("radicale", "enabled"):
self.extensions.remove("modoboa-radicale")
self.dovecot_enabled = self.config.getboolean("dovecot", "enabled") self.dovecot_enabled = self.config.getboolean("dovecot", "enabled")
self.opendkim_enabled = self.config.getboolean("opendkim", "enabled") self.opendkim_enabled = self.config.getboolean("opendkim", "enabled")
self.dkim_cron_enabled = False
def is_extension_ok_for_version(self, extension, version): def is_extension_ok_for_version(self, extension, version):
"""Check if extension can be installed with this modo version.""" """Check if extension can be installed with this modo version."""
if extension not in compatibility_matrix.EXTENSIONS_AVAILABILITY:
return True
version = utils.convert_version_to_int(version) version = utils.convert_version_to_int(version)
if extension in compatibility_matrix.EXTENSIONS_AVAILABILITY:
min_version = compatibility_matrix.EXTENSIONS_AVAILABILITY[extension] min_version = compatibility_matrix.EXTENSIONS_AVAILABILITY[extension]
min_version = utils.convert_version_to_int(min_version) min_version = utils.convert_version_to_int(min_version)
return version >= min_version return version >= min_version
if extension in compatibility_matrix.REMOVED_EXTENSIONS:
max_version = compatibility_matrix.REMOVED_EXTENSIONS[extension]
max_version = utils.convert_version_to_int(max_version)
return version < max_version
return True
def _setup_venv(self): def _setup_venv(self):
"""Prepare a dedicated virtualenv.""" """Prepare a dedicated virtualenv."""
python.setup_virtualenv(self.venv_path, sudo_user=self.user) python.setup_virtualenv(
packages = [] self.venv_path, sudo_user=self.user, python_version=3)
packages = ["rrdtool"]
version = self.config.get("modoboa", "version") version = self.config.get("modoboa", "version")
extras = "postgresql"
if self.dbengine != "postgres":
extras = "mysql"
if self.devmode:
extras += ",dev"
if version == "latest": if version == "latest":
packages += [f"modoboa[{extras}]"] + self.extensions packages += ["modoboa"] + self.extensions
for extension in list(self.extensions):
if extension in compatibility_matrix.REMOVED_EXTENSIONS.keys():
self.extensions.remove(extension)
self.extensions = [
extension for extension in self.extensions
if extension not in compatibility_matrix.REMOVED_EXTENSIONS
]
else: else:
matrix = compatibility_matrix.COMPATIBILITY_MATRIX[version] matrix = compatibility_matrix.COMPATIBILITY_MATRIX[version]
packages.append(f"modoboa[{extras}]=={version}") packages.append("modoboa=={}".format(version))
for extension in list(self.extensions): for extension in list(self.extensions):
if not self.is_extension_ok_for_version(extension, version): if not self.is_extension_ok_for_version(extension, version):
self.extensions.remove(extension) self.extensions.remove(extension)
continue continue
if extension in matrix: if extension in matrix:
req_version = matrix[extension] req_version = matrix[extension]
if req_version is None: req_version = req_version.replace("<", "\<")
continue req_version = req_version.replace(">", "\>")
req_version = req_version.replace("<", "\\<")
req_version = req_version.replace(">", "\\>")
packages.append("{}{}".format(extension, req_version)) packages.append("{}{}".format(extension, req_version))
else: else:
packages.append(extension) packages.append(extension)
# Temp fix for django-braces
python.install_package(
"django-braces", self.venv_path, upgrade=self.upgrade,
sudo_user=self.user
)
if self.dbengine == "postgres":
packages.append("psycopg2-binary\<2.9")
else:
packages.append("mysqlclient")
if sys.version_info.major == 2 and sys.version_info.micro < 9: if sys.version_info.major == 2 and sys.version_info.micro < 9:
# Add extra packages to fix the SNI issue # Add extra packages to fix the SNI issue
packages += ["pyOpenSSL"] packages += ["pyOpenSSL"]
# Temp fix for https://github.com/modoboa/modoboa/issues/2247
packages.append("django-webpack-loader==0.7.0")
python.install_packages( python.install_packages(
packages, self.venv_path, packages, self.venv_path,
upgrade=self.upgrade, upgrade=self.upgrade,
sudo_user=self.user, sudo_user=self.user,
beta=self.config.getboolean("modoboa", "install_beta") beta=self.config.getboolean("modoboa", "install_beta")
) )
if self.devmode:
# FIXME: use dev-requirements instead
python.install_packages(
["django-bower", "django-debug-toolbar"], self.venv_path,
upgrade=self.upgrade, sudo_user=self.user)
def _deploy_instance(self): def _deploy_instance(self):
"""Deploy Modoboa.""" """Deploy Modoboa."""
@@ -135,10 +140,6 @@ class Modoboa(base.Installer):
prefix = ". {}; ".format( prefix = ". {}; ".format(
os.path.join(self.venv_path, "bin", "activate")) os.path.join(self.venv_path, "bin", "activate"))
if self.amavis_enabled:
self.extensions += ["modoboa.amavis"]
if self.rspamd_enabled:
self.extensions += ["modoboa.rspamd"]
args = [ args = [
"--collectstatic", "--collectstatic",
"--timezone", self.config.get("modoboa", "timezone"), "--timezone", self.config.get("modoboa", "timezone"),
@@ -176,7 +177,7 @@ class Modoboa(base.Installer):
if self.upgrade and self.opendkim_enabled and self.dbengine == "postgres": if self.upgrade and self.opendkim_enabled and self.dbengine == "postgres":
# Restore view previously deleted # Restore view previously deleted
self.backend.load_sql_file( self.backend.load_sql_file(
self.dbname, self.dbuser, self.dbpasswd, self.dbname, self.dbuser, self.dbpassword,
self.get_file_path("dkim_view_{}.sql".format(self.dbengine)) self.get_file_path("dkim_view_{}.sql".format(self.dbengine))
) )
self.backend.grant_right_on_table( self.backend.grant_right_on_table(
@@ -186,7 +187,7 @@ class Modoboa(base.Installer):
def setup_database(self): def setup_database(self):
"""Additional config.""" """Additional config."""
super().setup_database() super(Modoboa, self).setup_database()
if not self.amavis_enabled: if not self.amavis_enabled:
return return
self.backend.grant_access( self.backend.grant_access(
@@ -194,7 +195,7 @@ class Modoboa(base.Installer):
def get_packages(self): def get_packages(self):
"""Include extra packages if needed.""" """Include extra packages if needed."""
packages = super().get_packages() packages = super(Modoboa, self).get_packages()
condition = ( condition = (
package.backend.FORMAT == "rpm" and package.backend.FORMAT == "rpm" and
sys.version_info.major == 2 and sys.version_info.major == 2 and
@@ -204,10 +205,6 @@ class Modoboa(base.Installer):
packages += ["openssl-devel"] packages += ["openssl-devel"]
return packages return packages
def setup_user(self):
super().setup_user()
self._setup_venv()
def get_config_files(self): def get_config_files(self):
"""Return appropriate path.""" """Return appropriate path."""
config_files = super().get_config_files() config_files = super().get_config_files()
@@ -216,13 +213,6 @@ class Modoboa(base.Installer):
else: else:
path = "supervisor=/etc/supervisord.d/policyd.ini" path = "supervisor=/etc/supervisord.d/policyd.ini"
config_files.append(path) config_files.append(path)
# Add worker for dkim if needed
if self.modoboa_2_2_or_greater:
config_files.append(
"supervisor-rq-dkim=/etc/supervisor/conf.d/modoboa-dkim-worker.conf")
config_files.append(
"supervisor-rq-base=/etc/supervisor/conf.d/modoboa-base-worker.conf")
return config_files return config_files
def get_template_context(self): def get_template_context(self):
@@ -231,20 +221,17 @@ class Modoboa(base.Installer):
extensions = self.config.get("modoboa", "extensions") extensions = self.config.get("modoboa", "extensions")
extensions = extensions.split() extensions = extensions.split()
random_hour = random.randint(0, 6) random_hour = random.randint(0, 6)
self.dkim_cron_enabled = (not self.modoboa_2_2_or_greater and
self.opendkim_enabled)
context.update({ context.update({
"sudo_user": ( "sudo_user": (
"uwsgi" if package.backend.FORMAT == "rpm" else context["user"] "uwsgi" if package.backend.FORMAT == "rpm" else context["user"]
), ),
"dovecot_mailboxes_owner": ( "dovecot_mailboxes_owner": (
self.config.get("dovecot", "mailboxes_owner")), self.config.get("dovecot", "mailboxes_owner")),
"radicale_enabled": (
"" if "modoboa-radicale" in extensions else "#"),
"opendkim_user": self.config.get("opendkim", "user"), "opendkim_user": self.config.get("opendkim", "user"),
"dkim_user": "_rspamd" if self.rspamd_enabled else self.config.get("opendkim", "user"),
"minutes": random.randint(1, 59), "minutes": random.randint(1, 59),
"hours": f"{random_hour},{random_hour+12}", "hours" : f"{random_hour},{random_hour+12}"
"modoboa_2_2_or_greater": "" if self.modoboa_2_2_or_greater else "#",
"dkim_cron_enabled": "" if self.dkim_cron_enabled else "#"
}) })
return context return context
@@ -256,19 +243,22 @@ class Modoboa(base.Installer):
self.instance_path, "media", "webmail") self.instance_path, "media", "webmail")
pw = pwd.getpwnam(self.user) pw = pwd.getpwnam(self.user)
for d in [rrd_root_dir, pdf_storage_dir, webmail_media_dir]: for d in [rrd_root_dir, pdf_storage_dir, webmail_media_dir]:
utils.mkdir_safe(d, stat.S_IRWXU | stat.S_IRWXG, pw[2], pw[3]) utils.mkdir(d, stat.S_IRWXU | stat.S_IRWXG, pw[2], pw[3])
settings = { settings = {
"admin": { "admin": {
"handle_mailboxes": True, "handle_mailboxes": True,
"account_auto_removal": True "account_auto_removal": True
}, },
"modoboa_amavis": {
"am_pdp_mode": "inet",
},
"maillog": { "maillog": {
"rrd_rootdir": rrd_root_dir, "rrd_rootdir": rrd_root_dir,
}, },
"pdfcredentials": { "modoboa_pdfcredentials": {
"storage_dir": pdf_storage_dir "storage_dir": pdf_storage_dir
}, },
"calendars": { "modoboa_radicale": {
"server_location": "https://{}/radicale/".format( "server_location": "https://{}/radicale/".format(
self.config.get("general", "hostname")), self.config.get("general", "hostname")),
"rights_file_path": "{}/rights".format( "rights_file_path": "{}/rights".format(
@@ -281,19 +271,6 @@ class Modoboa(base.Installer):
if self.config.getboolean("opendkim", "enabled"): if self.config.getboolean("opendkim", "enabled"):
settings["admin"]["dkim_keys_storage_dir"] = ( settings["admin"]["dkim_keys_storage_dir"] = (
self.config.get("opendkim", "keys_storage_dir")) self.config.get("opendkim", "keys_storage_dir"))
if self.rspamd_enabled:
settings["admin"]["dkim_keys_storage_dir"] = (
self.config.get("rspamd", "dkim_keys_storage_dir"))
settings["rspamd"] = {
"key_map_path": self.config.get("rspamd", "key_map_path"),
"selector_map_path": self.config.get("rspamd", "selector_map_path")
}
if self.config.getboolean("amavis", "enabled"):
settings["amavis"] = {
"am_pdp_mode": "inet",
}
settings = json.dumps(settings) settings = json.dumps(settings)
query = ( query = (
"UPDATE core_localconfig SET _parameters='{}'" "UPDATE core_localconfig SET _parameters='{}'"
@@ -304,18 +281,17 @@ class Modoboa(base.Installer):
def post_run(self): def post_run(self):
"""Additional tasks.""" """Additional tasks."""
if 'centos' in utils.dist_name(): self._setup_venv()
system.enable_and_start_service("redis")
else:
system.enable_and_start_service("redis-server")
self._deploy_instance() self._deploy_instance()
if not self.upgrade: if not self.upgrade:
self.apply_settings() self.apply_settings()
if 'centos' in utils.dist_name(): if 'centos' in utils.dist_name():
supervisor = "supervisord" supervisor = "supervisord"
system.enable_and_start_service("redis")
else: else:
supervisor = "supervisor" supervisor = "supervisor"
system.enable_and_start_service("redis-server")
# Restart supervisor # Restart supervisor
system.enable_service(supervisor) system.enable_service(supervisor)
utils.exec_cmd("service {} stop".format(supervisor)) utils.exec_cmd("service {} stop".format(supervisor))

View File

@@ -19,15 +19,14 @@ class Nginx(base.Installer):
"rpm": ["nginx"] "rpm": ["nginx"]
} }
def get_template_context(self): def get_template_context(self, app):
"""Additionnal variables.""" """Additionnal variables."""
context = super().get_template_context() context = super(Nginx, self).get_template_context()
context.update({ context.update({
"app_instance_path": ( "app_instance_path": (
self.config.get("modoboa", "instance_path")), self.config.get(app, "instance_path")),
"uwsgi_socket_path": ( "uwsgi_socket_path": (
Uwsgi(self.config, self.upgrade, self.restore).get_socket_path("modoboa") Uwsgi(self.config, self.upgrade, self.restore).get_socket_path(app))
)
}) })
return context return context
@@ -35,10 +34,9 @@ class Nginx(base.Installer):
"""Custom app configuration.""" """Custom app configuration."""
if hostname is None: if hostname is None:
hostname = self.config.get("general", "hostname") hostname = self.config.get("general", "hostname")
context = self.get_template_context() context = self.get_template_context(app)
context.update({"hostname": hostname, "extra_config": extra_config}) context.update({"hostname": hostname, "extra_config": extra_config})
src = self.get_file_path("{}.conf.tpl".format(app)) src = self.get_file_path("{}.conf.tpl".format(app))
group = None
if package.backend.FORMAT == "deb": if package.backend.FORMAT == "deb":
dst = os.path.join( dst = os.path.join(
self.config_dir, "sites-available", "{}.conf".format(hostname)) self.config_dir, "sites-available", "{}.conf".format(hostname))
@@ -48,7 +46,6 @@ class Nginx(base.Installer):
if os.path.exists(link): if os.path.exists(link):
return return
os.symlink(dst, link) os.symlink(dst, link)
if self.config.has_section(app):
group = self.config.get(app, "user") group = self.config.get(app, "user")
user = "www-data" user = "www-data"
else: else:
@@ -57,17 +54,25 @@ class Nginx(base.Installer):
utils.copy_from_template(src, dst, context) utils.copy_from_template(src, dst, context)
group = "uwsgi" group = "uwsgi"
user = "nginx" user = "nginx"
if user and group:
system.add_user_to_group(user, group) system.add_user_to_group(user, group)
def post_run(self): def post_run(self):
"""Additionnal tasks.""" """Additionnal tasks."""
extra_modoboa_config = "" extra_modoboa_config = ""
if self.config.getboolean("automx", "enabled"):
hostname = "autoconfig.{}".format( hostname = "autoconfig.{}".format(
self.config.get("general", "domain")) self.config.get("general", "domain"))
self._setup_config("autoconfig", hostname) self._setup_config("automx", hostname)
extra_modoboa_config = """
location ~* ^/autodiscover/autodiscover.xml {
include uwsgi_params;
uwsgi_pass automx;
}
location /mobileconfig {
include uwsgi_params;
uwsgi_pass automx;
}
"""
if self.config.get("radicale", "enabled"): if self.config.get("radicale", "enabled"):
extra_modoboa_config += """ extra_modoboa_config += """
location /radicale/ { location /radicale/ {

View File

@@ -82,13 +82,11 @@ class Opendkim(base.Installer):
"""Additional tasks. """Additional tasks.
Check linux distribution (package deb, rpm), to adapt Check linux distribution (package deb, rpm), to adapt
to config file location and syntax. to config file location and syntax.
- update opendkim isocket port config - update opendkim isocket port config for Debian based distro
- make sure opendkim starts after db service started - make sure opendkim starts after db service started
""" """
if package.backend.FORMAT == "deb": if package.backend.FORMAT == "deb":
params_file = "/etc/default/opendkim" params_file = "/etc/default/opendkim"
else:
params_file = "/etc/opendkim.conf"
pattern = r"s/^(SOCKET=.*)/#\1/" pattern = r"s/^(SOCKET=.*)/#\1/"
utils.exec_cmd( utils.exec_cmd(
"perl -pi -e '{}' {}".format(pattern, params_file)) "perl -pi -e '{}' {}".format(pattern, params_file))
@@ -115,20 +113,19 @@ class Opendkim(base.Installer):
"""Restore keys.""" """Restore keys."""
dkim_keys_backup = os.path.join( dkim_keys_backup = os.path.join(
self.archive_path, "custom/dkim") self.archive_path, "custom/dkim")
keys_storage_dir = self.app_config["keys_storage_dir"]
if os.path.isdir(dkim_keys_backup): if os.path.isdir(dkim_keys_backup):
for file in os.listdir(dkim_keys_backup): for file in os.listdir(dkim_keys_backup):
file_path = os.path.join(dkim_keys_backup, file) file_path = os.path.join(dkim_keys_backup, file)
if os.path.isfile(file_path): if os.path.isfile(file_path):
utils.copy_file(file_path, keys_storage_dir) utils.copy_file(file_path, self.config.get(
"opendkim", "keys_storage_dir", fallback="/var/lib/dkim"))
utils.success("DKIM keys restored from backup") utils.success("DKIM keys restored from backup")
# Setup permissions
user = self.config.get("opendkim", "user")
utils.exec_cmd(f"chown -R {user}:{user} {keys_storage_dir}")
def custom_backup(self, path): def custom_backup(self, path):
"""Backup DKIM keys.""" """Backup DKIM keys."""
if os.path.isdir(self.app_config["keys_storage_dir"]): storage_dir = self.config.get(
shutil.copytree(self.app_config["keys_storage_dir"], os.path.join(path, "dkim")) "opendkim", "keys_storage_dir", fallback="/var/lib/dkim")
if os.path.isdir(storage_dir):
shutil.copytree(storage_dir, os.path.join(path, "dkim"))
utils.printcolor( utils.printcolor(
"DKIM keys saved!", utils.GREEN) "DKIM keys saved!", utils.GREEN)

View File

@@ -14,13 +14,15 @@ from . import backup, install
class Postfix(base.Installer): class Postfix(base.Installer):
"""Postfix installer.""" """Postfix installer."""
appname = "postfix" appname = "postfix"
packages = { packages = {
"deb": ["postfix", "postfix-pcre"], "deb": ["postfix"],
"rpm": ["postfix"],
} }
config_files = ["main.cf", "master.cf", "anonymize_headers.pcre"] config_files = ["main.cf", "master.cf"]
def get_packages(self): def get_packages(self):
"""Additional packages.""" """Additional packages."""
@@ -28,28 +30,17 @@ class Postfix(base.Installer):
packages = ["postfix-{}".format(self.db_driver)] packages = ["postfix-{}".format(self.db_driver)]
else: else:
packages = [] packages = []
return super().get_packages() + packages return super(Postfix, self).get_packages() + packages
def install_packages(self): def install_packages(self):
"""Preconfigure postfix package installation.""" """Preconfigure postfix package installation."""
if "centos" in utils.dist_name():
config = configparser.ConfigParser()
with open("/etc/yum.repos.d/CentOS-Base.repo") as fp:
config.read_file(fp)
config.set("centosplus", "enabled", "1")
config.set("centosplus", "includepkgs", "postfix-*")
config.set("base", "exclude", "postfix-*")
config.set("updates", "exclude", "postfix-*")
with open("/etc/yum.repos.d/CentOS-Base.repo", "w") as fp:
config.write(fp)
package.backend.preconfigure( package.backend.preconfigure(
"postfix", "main_mailer_type", "select", "No configuration") "postfix", "main_mailer_type", "select", "No configuration")
super().install_packages() super(Postfix, self).install_packages()
def get_template_context(self): def get_template_context(self):
"""Additional variables.""" """Additional variables."""
context = super().get_template_context() context = super(Postfix, self).get_template_context()
context.update({ context.update({
"db_driver": self.db_driver, "db_driver": self.db_driver,
"dovecot_mailboxes_owner": self.config.get( "dovecot_mailboxes_owner": self.config.get(
@@ -59,19 +50,10 @@ class Postfix(base.Installer):
"modoboa_instance_path": self.config.get( "modoboa_instance_path": self.config.get(
"modoboa", "instance_path"), "modoboa", "instance_path"),
"opendkim_port": self.config.get( "opendkim_port": self.config.get(
"opendkim", "port"), "opendkim", "port")
"rspamd_disabled": "" if not self.config.getboolean(
"rspamd", "enabled") else "#"
}) })
return context return context
def check_dhe_group_file(self):
group = self.config.get(self.appname, "dhe_group")
file_name = f"ffdhe{group}.pem"
if not os.path.exists(f"{self.config_dir}/{file_name}"):
url = f"https://raw.githubusercontent.com/internetstandards/dhe_groups/main/{file_name}"
utils.exec_cmd(f"wget {url}", cwd=self.config_dir)
def post_run(self): def post_run(self):
"""Additional tasks.""" """Additional tasks."""
venv_path = self.config.get("modoboa", "venv_path") venv_path = self.config.get("modoboa", "venv_path")
@@ -93,8 +75,10 @@ class Postfix(base.Installer):
if not os.path.exists(path): if not os.path.exists(path):
utils.copy_file(os.path.join("/etc", f), path) utils.copy_file(os.path.join("/etc", f), path)
# Generate DHE group # Generate EDH parameters
self.check_dhe_group_file() if not os.path.exists("{}/dh2048.pem".format(self.config_dir)):
cmd = "openssl dhparam -dsaparam -out dh2048.pem 2048"
utils.exec_cmd(cmd, cwd=self.config_dir)
# Generate /etc/aliases.db file to avoid warnings # Generate /etc/aliases.db file to avoid warnings
aliases_file = "/etc/aliases" aliases_file = "/etc/aliases"
@@ -102,18 +86,8 @@ class Postfix(base.Installer):
utils.exec_cmd("postalias {}".format(aliases_file)) utils.exec_cmd("postalias {}".format(aliases_file))
# Postwhite # Postwhite
condition = (
not self.config.getboolean("rspamd", "enabled") and
self.config.getboolean("postwhite", "enabled")
)
if condition:
install("postwhite", self.config, self.upgrade, self.archive_path) install("postwhite", self.config, self.upgrade, self.archive_path)
def backup(self, path): def backup(self, path):
"""Launch postwhite backup.""" """Launch postwhite backup."""
condition = (
not self.config.getboolean("rspamd", "enabled") and
self.config.getboolean("postwhite", "enabled")
)
if condition:
backup("postwhite", self.config, path) backup("postwhite", self.config, path)

View File

@@ -20,8 +20,8 @@ class Postwhite(base.Installer):
] ]
no_daemon = True no_daemon = True
packages = { packages = {
"deb": ["bind9-host", "unzip"], "deb": ["bind9-host"],
"rpm": ["bind-utils", "unzip"] "rpm": ["bind-utils"]
} }
def install_from_archive(self, repository, target_dir): def install_from_archive(self, repository, target_dir):
@@ -47,10 +47,8 @@ class Postwhite(base.Installer):
self.install_from_archive(SPF_TOOLS_REPOSITORY, install_dir) self.install_from_archive(SPF_TOOLS_REPOSITORY, install_dir)
self.postw_dir = self.install_from_archive( self.postw_dir = self.install_from_archive(
POSTWHITE_REPOSITORY, install_dir) POSTWHITE_REPOSITORY, install_dir)
utils.copy_file( postw_bin = os.path.join(self.postw_dir, "postwhite")
os.path.join(self.postw_dir, "postwhite.conf"), self.config_dir) utils.exec_cmd("{} /etc/postwhite.conf".format(postw_bin))
self.postw_bin = os.path.join(self.postw_dir, "postwhite")
utils.exec_cmd("{} /etc/postwhite.conf".format(self.postw_bin))
def custom_backup(self, path): def custom_backup(self, path):
"""Backup custom configuration if any.""" """Backup custom configuration if any."""
@@ -67,3 +65,6 @@ class Postwhite(base.Installer):
if os.path.isfile(postwhite_backup_configuration): if os.path.isfile(postwhite_backup_configuration):
utils.copy_file(postwhite_backup_configuration, self.config_dir) utils.copy_file(postwhite_backup_configuration, self.config_dir)
utils.success("postwhite.conf restored from backup") utils.success("postwhite.conf restored from backup")
else:
utils.copy_file(
os.path.join(self.postw_dir, "postwhite.conf"), self.config_dir)

View File

@@ -31,30 +31,30 @@ class Radicale(base.Installer):
def _setup_venv(self): def _setup_venv(self):
"""Prepare a dedicated virtualenv.""" """Prepare a dedicated virtualenv."""
python.setup_virtualenv(self.venv_path, sudo_user=self.user) python.setup_virtualenv(
self.venv_path, sudo_user=self.user, python_version=3)
packages = [ packages = [
"Radicale", "pytz", "radicale-modoboa-auth-oauth2" "Radicale", "radicale-dovecot-auth", "pytz"
] ]
python.install_packages(packages, self.venv_path, sudo_user=self.user) python.install_packages(packages, self.venv_path, sudo_user=self.user)
python.install_package_from_repository(
"radicale-storage-by-index",
"https://github.com/tonioo/RadicaleStorageByIndex",
venv=self.venv_path, sudo_user=self.user)
def get_template_context(self): def get_template_context(self):
"""Additional variables.""" """Additional variables."""
context = super().get_template_context() context = super(Radicale, self).get_template_context()
oauth2_client_id, oauth2_client_secret = utils.create_oauth2_app( radicale_auth_socket_path = self.config.get(
"Radicale", "radicale", self.config) "dovecot", "radicale_auth_socket_path")
hostname = self.config.get("general", "hostname")
oauth2_introspection_url = (
f"https://{oauth2_client_id}:{oauth2_client_secret}"
f"@{hostname}/api/o/introspect/"
)
context.update({ context.update({
"oauth2_introspection_url": oauth2_introspection_url, "auth_socket_path": radicale_auth_socket_path
}) })
return context return context
def get_config_files(self): def get_config_files(self):
"""Return appropriate path.""" """Return appropriate path."""
config_files = super().get_config_files() config_files = super(Radicale, self).get_config_files()
if package.backend.FORMAT == "deb": if package.backend.FORMAT == "deb":
path = "supervisor=/etc/supervisor/conf.d/radicale.conf" path = "supervisor=/etc/supervisor/conf.d/radicale.conf"
else: else:

View File

@@ -13,14 +13,14 @@ class Restore:
""" """
if not os.path.isdir(restore): if not os.path.isdir(restore):
utils.error( utils.printcolor(
"Provided path is not a directory !") "Provided path is not a directory !", utils.RED)
sys.exit(1) sys.exit(1)
modoba_sql_file = os.path.join(restore, "databases/modoboa.sql") modoba_sql_file = os.path.join(restore, "databases/modoboa.sql")
if not os.path.isfile(modoba_sql_file): if not os.path.isfile(modoba_sql_file):
utils.error( utils.printcolor(
modoba_sql_file + " not found, please check your backup") modoba_sql_file + " not found, please check your backup", utils.RED)
sys.exit(1) sys.exit(1)
# Everything seems alright here, proceeding... # Everything seems alright here, proceeding...

Some files were not shown because too many files have changed in this diff Show More