mirror of
https://github.com/Mailu/Mailu.git
synced 2024-12-04 10:24:41 +02:00
Implement managesieve support
This commit is contained in:
parent
152a2bf175
commit
107b0ab5ff
@ -20,7 +20,7 @@ Main features include:
|
||||
- **Standard email server**, IMAP and IMAP+, SMTP and Submission with autoconfiguration profiles for clients
|
||||
- **Advanced email features**, aliases, domain aliases, custom routing
|
||||
- **Web access**, multiple Webmails and administration interface
|
||||
- **User features**, aliases, auto-reply, auto-forward, fetched accounts
|
||||
- **User features**, aliases, auto-reply, auto-forward, fetched accounts, managesieve
|
||||
- **Admin features**, global admins, announcements, per-domain delegation, quotas
|
||||
- **Security**, enforced TLS, DANE, MTA-STS, Letsencrypt!, outgoing DKIM, anti-virus scanner, [Snuffleupagus](https://github.com/jvoisin/snuffleupagus/), block malicious attachments
|
||||
- **Antispam**, auto-learn, greylisting, DMARC and SPF, anti-spoofing
|
||||
|
@ -13,7 +13,8 @@ STATUSES = {
|
||||
"authentication": ("Authentication credentials invalid", {
|
||||
"imap": "AUTHENTICATIONFAILED",
|
||||
"smtp": "535 5.7.8",
|
||||
"pop3": "-ERR Authentication failed"
|
||||
"pop3": "-ERR Authentication failed",
|
||||
"sieve": "AuthFailed"
|
||||
}),
|
||||
"encryption": ("Must issue a STARTTLS command first", {
|
||||
"smtp": "530 5.7.0"
|
||||
@ -32,7 +33,7 @@ def check_credentials(user, password, ip, protocol=None, auth_port=None):
|
||||
return False
|
||||
is_ok = False
|
||||
# webmails
|
||||
if auth_port in WEBMAIL_PORTS and password.startswith('token-'):
|
||||
if auth_port in WEBMAIL_PORTS or auth_port == '4190' and password.startswith('token-'):
|
||||
if utils.verify_temp_token(user.get_id(), password):
|
||||
is_ok = True
|
||||
# All tokens are 32 characters hex lowercase
|
||||
@ -50,8 +51,8 @@ def handle_authentication(headers):
|
||||
""" Handle an HTTP nginx authentication request
|
||||
See: http://nginx.org/en/docs/mail/ngx_mail_auth_http_module.html#protocol
|
||||
"""
|
||||
method = headers["Auth-Method"]
|
||||
protocol = headers["Auth-Protocol"]
|
||||
method = headers["Auth-Method"].lower()
|
||||
protocol = headers["Auth-Protocol"].lower()
|
||||
# Incoming mail, no authentication
|
||||
if method == "none" and protocol == "smtp":
|
||||
server, port = get_server(protocol, False)
|
||||
@ -121,7 +122,7 @@ def handle_authentication(headers):
|
||||
"Auth-Wait": 0
|
||||
}
|
||||
# Unexpected
|
||||
return {}
|
||||
raise Exception("SHOULD NOT HAPPEN")
|
||||
|
||||
|
||||
def get_status(protocol, status):
|
||||
@ -140,6 +141,8 @@ def get_server(protocol, authenticated=False):
|
||||
hostname, port = app.config['SMTP_ADDRESS'], 10025
|
||||
else:
|
||||
hostname, port = app.config['SMTP_ADDRESS'], 25
|
||||
elif protocol == "sieve":
|
||||
hostname, port = app.config['IMAP_ADDRESS'], 4190
|
||||
try:
|
||||
# test if hostname is already resolved to an ip address
|
||||
ipaddress.ip_address(hostname)
|
||||
|
@ -12,6 +12,7 @@ default_login_user = mail
|
||||
default_internal_group = dovecot
|
||||
|
||||
haproxy_trusted_networks = {{ SUBNET }} {{ SUBNET6 }}
|
||||
login_trusted_networks = {{ SUBNET }} {{ SUBNET6 }}
|
||||
|
||||
###############
|
||||
# Mailboxes
|
||||
@ -149,7 +150,6 @@ service lmtp {
|
||||
service managesieve-login {
|
||||
inet_listener sieve {
|
||||
port = 4190
|
||||
haproxy = yes
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,17 +17,17 @@ ARG VERSION
|
||||
LABEL version=$VERSION
|
||||
|
||||
RUN set -euxo pipefail \
|
||||
; apk add --no-cache certbot nginx nginx-mod-http-brotli nginx-mod-stream nginx-mod-mail openssl \
|
||||
; rm /etc/nginx/conf.d/stream.conf
|
||||
; apk add --no-cache certbot nginx nginx-mod-http-brotli nginx-mod-mail openssl dovecot-lua dovecot-pigeonhole-plugin
|
||||
|
||||
COPY conf/ /conf/
|
||||
COPY --from=static /static/ /static/
|
||||
COPY *.py /
|
||||
COPY proxy.conf login.lua /
|
||||
|
||||
RUN echo $VERSION >/version
|
||||
|
||||
EXPOSE 80/tcp 443/tcp 110/tcp 143/tcp 465/tcp 587/tcp 993/tcp 995/tcp 25/tcp
|
||||
# EXPOSE 10025/tcp 10143/tcp 14190/tcp
|
||||
EXPOSE 80/tcp 443/tcp 110/tcp 143/tcp 465/tcp 587/tcp 993/tcp 995/tcp 25/tcp 4190/tcp
|
||||
# EXPOSE 10025/tcp 10143/tcp
|
||||
HEALTHCHECK --start-period=60s CMD curl -skfLo /dev/null http://127.0.0.1:10204/health
|
||||
|
||||
VOLUME ["/certs", "/overrides"]
|
||||
|
@ -5,7 +5,6 @@ pcre_jit on;
|
||||
error_log /dev/stderr notice;
|
||||
pid /var/run/nginx.pid;
|
||||
load_module "modules/ngx_mail_module.so";
|
||||
load_module "modules/ngx_stream_module.so";
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
@ -302,25 +301,6 @@ http {
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
|
||||
stream {
|
||||
log_format main '$remote_addr [$time_local] '
|
||||
'$protocol $status $bytes_sent $bytes_received '
|
||||
'$session_time "$upstream_addr" '
|
||||
'"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';
|
||||
access_log /dev/stdout main;
|
||||
|
||||
# managesieve
|
||||
server {
|
||||
listen 14190;
|
||||
resolver {{ RESOLVER }} valid=30s;
|
||||
|
||||
proxy_connect_timeout 1s;
|
||||
proxy_timeout 1m;
|
||||
proxy_protocol on;
|
||||
proxy_pass {{ IMAP_ADDRESS }}:4190;
|
||||
}
|
||||
}
|
||||
|
||||
mail {
|
||||
server_name {{ HOSTNAMES.split(",")[0] }};
|
||||
auth_http http://127.0.0.1:8000/auth/email;
|
||||
|
@ -55,3 +55,7 @@ conf.jinja("/conf/proxy.conf", args, "/etc/nginx/proxy.conf")
|
||||
conf.jinja("/conf/nginx.conf", args, "/etc/nginx/nginx.conf")
|
||||
if os.path.exists("/var/run/nginx.pid"):
|
||||
os.system("nginx -s reload")
|
||||
conf.jinja("/login.lua", args, "/etc/dovecot/login.lua")
|
||||
conf.jinja("/proxy.conf", args, "/etc/dovecot/proxy.conf")
|
||||
if os.path.exists("/run/dovecot/master.pid"):
|
||||
os.system("doveadm reload")
|
||||
|
38
core/nginx/login.lua
Normal file
38
core/nginx/login.lua
Normal file
@ -0,0 +1,38 @@
|
||||
function script_init()
|
||||
return 0
|
||||
end
|
||||
|
||||
function script_deinit()
|
||||
end
|
||||
|
||||
local http_client = dovecot.http.client {
|
||||
timeout = 2000;
|
||||
max_attempts = 3;
|
||||
}
|
||||
|
||||
function auth_passdb_lookup(req)
|
||||
local auth_request = http_client:request {
|
||||
url = "http://{{ ADMIN_ADDRESS }}/internal/auth/email";
|
||||
}
|
||||
auth_request:add_header('Auth-Port', req.local_port)
|
||||
auth_request:add_header('Auth-User', req.user)
|
||||
auth_request:add_header('Auth-Pass', req.password)
|
||||
auth_request:add_header('Auth-Protocol', 'sieve')
|
||||
auth_request:add_header('Client-IP', req.remote_ip)
|
||||
auth_request:add_header('Auth-SSL', req.secured)
|
||||
auth_request:add_header('Auth-Method', req.mechanism)
|
||||
local auth_response = auth_request:submit()
|
||||
local resp_status = auth_response:status()
|
||||
|
||||
if resp_status == 200
|
||||
then
|
||||
if auth_response:header('Auth-Status') == 'OK'
|
||||
then
|
||||
return dovecot.auth.PASSDB_RESULT_OK, "proxy=y host={{ IMAP_ADDRESS }} nopassword=Y"
|
||||
else
|
||||
return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, ""
|
||||
end
|
||||
else
|
||||
return dovecot.auth.PASSDB_RESULT_INTERNAL_FAILURE, ""
|
||||
end
|
||||
end
|
62
core/nginx/proxy.conf
Normal file
62
core/nginx/proxy.conf
Normal file
@ -0,0 +1,62 @@
|
||||
###############
|
||||
# General
|
||||
###############
|
||||
log_path = /dev/stderr
|
||||
protocols = sieve
|
||||
postmaster_address = {{ POSTMASTER }}@{{ DOMAIN }}
|
||||
hostname = {{ HOSTNAMES.split(",")[0] }}
|
||||
submission_host = {{ FRONT_ADDRESS }}
|
||||
#instance_name = managesieveproxy
|
||||
#base_dir = /run/dovecot2
|
||||
|
||||
default_internal_user = dovecot
|
||||
default_login_user = mail
|
||||
default_internal_group = dovecot
|
||||
|
||||
haproxy_trusted_networks = {% if REAL_IP_FROM %}{% for from_ip in REAL_IP_FROM.split(',') %}{{ from_ip }} {% endfor %}{% endif %}
|
||||
|
||||
###############
|
||||
# Authentication
|
||||
###############
|
||||
auth_username_chars =
|
||||
auth_mechanisms = plain login
|
||||
|
||||
{% if TLS %}
|
||||
ssl = required
|
||||
ssl_cert = <{{ TLS[0] }}
|
||||
ssl_key = <{{ TLS[1] }}
|
||||
{% if TLS_FLAVOR in ['letsencrypt','mail-letsencrypt'] %}
|
||||
ssl_alt_cert = <{{ TLS[2] }}
|
||||
ssl_alt_key = <{{ TLS[3] }}
|
||||
{% endif %}
|
||||
# intermediate configuration
|
||||
ssl_min_protocol = TLSv1.2
|
||||
ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
|
||||
ssl_prefer_server_ciphers = no
|
||||
ssl_dh = </conf/dhparam.pem
|
||||
{% else %}
|
||||
disable_plaintext_auth = no
|
||||
protocol sieve {
|
||||
ssl = no
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
passdb {
|
||||
driver = lua
|
||||
args = file=/etc/dovecot/login.lua blocking=yes
|
||||
}
|
||||
|
||||
service auth-worker {
|
||||
user = $default_internal_user
|
||||
group = $default_internal_group
|
||||
}
|
||||
|
||||
service managesieve-login {
|
||||
executable = managesieve-login
|
||||
inet_listener sieve {
|
||||
port = 4190
|
||||
{% if PROXY_PROTOCOL in ['all', 'mail'] %}
|
||||
haproxy = yes
|
||||
{% endif %}
|
||||
}
|
||||
}
|
@ -13,4 +13,5 @@ elif os.environ["TLS_FLAVOR"] in [ "mail", "cert" ]:
|
||||
subprocess.Popen(["/certwatcher.py"])
|
||||
|
||||
subprocess.call(["/config.py"])
|
||||
os.system("dovecot -c /etc/dovecot/proxy.conf")
|
||||
os.execv("/usr/sbin/nginx", ["nginx", "-g", "daemon off;"])
|
||||
|
@ -63,7 +63,7 @@ address for your mail server and that you have a dedicated hostname
|
||||
with forward and reverse DNS entries for this IP address.
|
||||
|
||||
Also, your host must not listen on ports ``25``, ``80``, ``110``, ``143``,
|
||||
``443``, ``465``, ``587``, ``993`` or ``995`` as these are used by Mailu
|
||||
``443``, ``465``, ``587``, ``993``, ``995`` nor ``4190`` as these are used by Mailu
|
||||
services. Therefore, you should disable or uninstall any program that is
|
||||
listening on these ports (or have them listen on a different port). For
|
||||
instance, on a default Debian install:
|
||||
|
@ -26,7 +26,7 @@ Main features include:
|
||||
- **Standard email server**, IMAP and IMAP+, SMTP and Submission with autoconfiguration profiles for clients
|
||||
- **Advanced email features**, aliases, domain aliases, custom routing
|
||||
- **Web access**, multiple Webmails and administration interface
|
||||
- **User features**, aliases, auto-reply, auto-forward, fetched accounts
|
||||
- **User features**, aliases, auto-reply, auto-forward, fetched accounts, managesieve
|
||||
- **Admin features**, global admins, announcements, per-domain delegation, quotas
|
||||
- **Security**, enforced TLS, DANE, MTA-STS, Letsencrypt!, outgoing DKIM, anti-virus scanner, `Snuffleupagus <https://github.com/jvoisin/snuffleupagus/>`_, block malicious attachments
|
||||
- **Antispam**, auto-learn, greylisting, DMARC and SPF, anti-spoofing
|
||||
|
@ -30,7 +30,7 @@ services:
|
||||
options:
|
||||
tag: mailu-front
|
||||
ports:
|
||||
{% for port in (80, 443, 25, 465, 587, 110, 995, 143, 993) %}
|
||||
{% for port in (80, 443, 25, 465, 587, 110, 995, 143, 993, 4190) %}
|
||||
{% if bind4 %}
|
||||
- "{{ bind4 }}:{{ port }}:{{ port }}"
|
||||
{% endif %}
|
||||
|
1
towncrier/newsfragments/81.feature
Normal file
1
towncrier/newsfragments/81.feature
Normal file
@ -0,0 +1 @@
|
||||
Add support for managesieve
|
@ -24,7 +24,14 @@ $config['smtp_user'] = '%u';
|
||||
$config['smtp_pass'] = '%p';
|
||||
|
||||
// Sieve script management
|
||||
$config['managesieve_host'] = '{{ FRONT_ADDRESS or "front" }}:14190';
|
||||
$config['managesieve_host'] = 'tls://{{ FRONT_ADDRESS or "front" }}:4190';
|
||||
$config['managesieve_conn_options'] = array(
|
||||
'ssl' => array(
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false,
|
||||
'allow_self_signed' => true,
|
||||
),
|
||||
);
|
||||
$config['managesieve_mbox_encoding'] = 'UTF8';
|
||||
|
||||
// roundcube customization
|
||||
|
@ -33,13 +33,13 @@
|
||||
},
|
||||
"Sieve": {
|
||||
"host": "{{ FRONT_ADDRESS }}",
|
||||
"port": 14190,
|
||||
"secure": 0,
|
||||
"port": 4190,
|
||||
"type": 2,
|
||||
"shortLogin": false,
|
||||
"ssl": {
|
||||
"verify_peer": false,
|
||||
"verify_peer_name": false,
|
||||
"allow_self_signed": false,
|
||||
"allow_self_signed": true,
|
||||
"SNI_enabled": true,
|
||||
"disable_compression": true,
|
||||
"security_level": 1
|
||||
|
Loading…
Reference in New Issue
Block a user