diff --git a/core/dovecot/Dockerfile b/core/dovecot/Dockerfile index cacfe354..484ab1d6 100644 --- a/core/dovecot/Dockerfile +++ b/core/dovecot/Dockerfile @@ -1,9 +1,9 @@ FROM alpine:edge -RUN echo "@testing http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \ - && apk add --no-cache \ - dovecot dovecot-sqlite dovecot-pigeonhole-plugin dovecot-pigeonhole-plugin-extdata \ - rspamd-client@testing python py-jinja2 +RUN apk add --no-cache \ + dovecot dovecot-pop3d dovecot-lmtpd dovecot-pigeonhole-plugin rspamd-client \ + python3 py3-pip \ + && pip3 install jinja2 podop COPY conf /conf COPY sieve /var/lib/dovecot diff --git a/core/dovecot/conf/auth.conf b/core/dovecot/conf/auth.conf new file mode 100644 index 00000000..44a874ba --- /dev/null +++ b/core/dovecot/conf/auth.conf @@ -0,0 +1,5 @@ +uri = proxy:/tmp/podop.socket:auth +iterate_disable = yes +default_pass_scheme = plain +password_key = passdb/%u +user_key = userdb/%u diff --git a/core/dovecot/conf/dovecot-sql.conf.ext b/core/dovecot/conf/dovecot-sql.conf.ext deleted file mode 100644 index b55a5557..00000000 --- a/core/dovecot/conf/dovecot-sql.conf.ext +++ /dev/null @@ -1,18 +0,0 @@ -driver = sqlite -connect = /data/main.db - -# Return the user hashed password -password_query = \ - SELECT NULL as password, 'Y' as nopassword, '{% if POD_ADDRESS_RANGE %}{{ POD_ADDRESS_RANGE }}{% else %}{{ FRONT_ADDRESS }}{% if WEBMAIL_ADDRESS %},{{ WEBMAIL_ADDRESS }}{% endif %}{% endif %}' as allow_nets \ - FROM user \ - WHERE user.email = '%u' - -# Mostly get the user quota -user_query = \ - SELECT '*:bytes=' || user.quota_bytes AS quota_rule \ - FROM user \ - WHERE user.email = '%u' - -# For using doveadm -A: -iterate_query = \ - SELECT user.email AS user FROM user diff --git a/core/dovecot/conf/dovecot.conf b/core/dovecot/conf/dovecot.conf index 94c43901..d6aee60c 100644 --- a/core/dovecot/conf/dovecot.conf +++ b/core/dovecot/conf/dovecot.conf @@ -7,17 +7,6 @@ postmaster_address = {{ POSTMASTER }}@{{ DOMAIN }} hostname = {{ HOSTNAMES.split(",")[0] }} submission_host = {{ FRONT_ADDRESS }} -service dict { - unix_listener dict { - group = mail - mode = 0660 - } -} - -dict { - sieve = sqlite:/etc/dovecot/pigeonhole-sieve.dict -} - ############### # Mailboxes ############### @@ -36,28 +25,18 @@ mail_plugins = $mail_plugins quota quota_clone namespace inbox { inbox = yes - mailbox Trash { + {% for mailbox in ("Trash", "Drafts", "Sent", "Junk") %} + mailbox {{ mailbox }} { auto = subscribe - special_use = \Trash - } - mailbox Drafts { - auto = subscribe - special_use = \Drafts - } - mailbox Sent { - auto = subscribe - special_use = \Sent - } - mailbox Junk { - auto = subscribe - special_use = \Junk + special_use = \{{ mailbox }} } + {% endfor %} } plugin { quota = count:User quota quota_vsizes = yes - quota_clone_dict = redis:host={{ REDIS_ADDRESS }}:port=6379:db=1 + quota_clone_dict = proxy:/tmp/podop.socket:quota } ############### @@ -65,16 +44,15 @@ plugin { ############### auth_mechanisms = plain login disable_plaintext_auth = no -ssl_protocols = !SSLv3 passdb { - driver = sql - args = /etc/dovecot/dovecot-sql.conf.ext + driver = dict + args = /etc/dovecot/auth.conf } userdb { - driver = sql - args = /etc/dovecot/dovecot-sql.conf.ext + driver = dict + args = /etc/dovecot/auth.conf } service auth { @@ -95,7 +73,6 @@ service auth-worker { ############### # IMAP & POP ############### - protocol imap { mail_plugins = $mail_plugins imap_quota imap_sieve } @@ -113,7 +90,6 @@ service imap-login { ############### # Delivery ############### - protocol lmtp { mail_plugins = $mail_plugins sieve recipient_delimiter = {{ RECIPIENT_DELIMITER }} @@ -125,11 +101,9 @@ service lmtp { } } - ############### # Filtering ############### - service managesieve-login { inet_listener sieve { port = 4190 @@ -140,13 +114,12 @@ service managesieve { } plugin { - sieve = file:~/sieve;active=~/.dovecot.sieve - sieve_plugins = sieve_extdata sieve_imapsieve sieve_extprograms - sieve_global_extensions = +vnd.dovecot.extdata +spamtest +spamtestplus +vnd.dovecot.execute +editheader + sieve = dict:proxy:/tmp/podop.socket:sieve + sieve_plugins = sieve_imapsieve sieve_extprograms + sieve_global_extensions = +spamtest +spamtestplus +vnd.dovecot.execute +editheader sieve_before = /var/lib/dovecot/before.sieve sieve_default = /var/lib/dovecot/default.sieve sieve_after = /var/lib/dovecot/after.sieve - sieve_extdata_dict_uri = proxy::sieve # Sieve execute sieve_execute_bin_dir = /var/lib/dovecot/bin diff --git a/core/dovecot/conf/pigeonhole-sieve.dict b/core/dovecot/conf/pigeonhole-sieve.dict deleted file mode 100644 index 917fce83..00000000 --- a/core/dovecot/conf/pigeonhole-sieve.dict +++ /dev/null @@ -1,43 +0,0 @@ -connect = /data/main.db - -map { - pattern = priv/spam_enabled - table = user - username_field = email - value_field = spam_enabled -} - -map { - pattern = priv/spam_threshold - table = user - username_field = email - value_field = spam_threshold -} - -map { - pattern = priv/reply_enabled - table = user - username_field = email - value_field = reply_enabled -} - -map { - pattern = priv/reply_subject - table = user - username_field = email - value_field = reply_subject -} - -map { - pattern = priv/reply_body - table = user - username_field = email - value_field = reply_body -} - -map { - pattern = priv/reply_enddate - table = user - username_field = email - value_field = reply_enddate -} diff --git a/core/dovecot/start.py b/core/dovecot/start.py index 83f91fab..48e9377c 100755 --- a/core/dovecot/start.py +++ b/core/dovecot/start.py @@ -1,21 +1,31 @@ -#!/usr/bin/python +#!/usr/bin/python3 import jinja2 import os import socket import glob +import multiprocessing + +from podop import run_server + + +def start_podop(): + os.setuid(8) + run_server(40, "dovecot", "/tmp/podop.socket", [ + ("quota", "url", "http://admin/internal/dovecot/quota/§"), + ("auth", "url", "http://admin/internal/dovecot/auth/§"), + ("sieve", "url", "http://admin/internal/dovecot/sieve/§"), + ]) convert = lambda src, dst: open(dst, "w").write(jinja2.Template(open(src).read()).render(**os.environ)) # Actual startup script os.environ["FRONT_ADDRESS"] = socket.gethostbyname(os.environ.get("FRONT_ADDRESS", "front")) -os.environ["REDIS_ADDRESS"] = socket.gethostbyname(os.environ.get("REDIS_ADDRESS", "redis")) -if os.environ["WEBMAIL"] != "none": - os.environ["WEBMAIL_ADDRESS"] = socket.gethostbyname(os.environ.get("WEBMAIL_ADDRESS", "webmail")) for dovecot_file in glob.glob("/conf/*"): convert(dovecot_file, os.path.join("/etc/dovecot", os.path.basename(dovecot_file))) -# Run postfix +# Run Podop, then postfix +multiprocessing.Process(target=start_podop).start() os.system("chown -R mail:mail /mail /var/lib/dovecot") os.execv("/usr/sbin/dovecot", ["dovecot", "-c", "/etc/dovecot/dovecot.conf", "-F"])