1
0
mirror of https://github.com/Mailu/Mailu.git synced 2024-12-12 10:45:38 +02:00

Use dovecot-proxy where appropriate

This commit is contained in:
Florent Daigniere 2023-06-05 08:47:22 +02:00
parent 9299b68c62
commit f143aa3dc8
14 changed files with 143 additions and 140 deletions

View File

@ -56,7 +56,7 @@ def handle_authentication(headers):
method = headers["Auth-Method"].lower()
protocol = headers["Auth-Protocol"].lower()
# Incoming mail, no authentication
if method == "none" and protocol == "smtp":
if method in ['', 'none'] and protocol in ['smtp', 'lmtp']:
server, port = get_server(protocol, False)
if app.config["INBOUND_TLS_ENFORCE"]:
if "Auth-SSL" in headers and headers["Auth-SSL"] == "on":
@ -79,7 +79,7 @@ def handle_authentication(headers):
"Auth-Port": port
}
# Authenticated user
elif method == "plain":
elif method in ['plain', 'login']:
is_valid_user = False
# According to RFC2616 section 3.7.1 and PEP 3333, HTTP headers should
# be ASCII and are generally considered ISO8859-1. However when passing
@ -122,7 +122,7 @@ def handle_authentication(headers):
"Auth-Wait": 0
}
# Unexpected
raise Exception("SHOULD NOT HAPPEN")
raise Exception(f"SHOULD NOT HAPPEN {protocol} {method}")
def get_status(protocol, status):
@ -132,16 +132,20 @@ def get_status(protocol, status):
return status, codes[protocol]
def get_server(protocol, authenticated=False):
if protocol == "imap":
if protocol == 'imap':
hostname, port = app.config['IMAP_ADDRESS'], 143
elif protocol == "pop3":
elif protocol == 'pop3':
hostname, port = app.config['IMAP_ADDRESS'], 110
elif protocol == "smtp":
elif protocol == 'smtp':
if authenticated:
hostname, port = app.config['SMTP_ADDRESS'], 10025
else:
hostname, port = app.config['SMTP_ADDRESS'], 25
elif protocol == "sieve":
elif protocol == 'submission':
hostname, port = app.config['SMTP_ADDRESS'], 10025
elif protocol == 'lmtp':
hostname, port = app.config['IMAP_ADDRESS'], 2525
elif protocol == 'sieve':
hostname, port = app.config['IMAP_ADDRESS'], 4190
try:
# test if hostname is already resolved to an ip address

View File

@ -31,7 +31,7 @@ def nginx_authentication():
if int(flask.request.headers['Auth-Login-Attempt']) < 10:
response.headers['Auth-Wait'] = '3'
return response
raw_password = urllib.parse.unquote(headers["Auth-Pass"])
raw_password = urllib.parse.unquote(headers['Auth-Pass']) if 'Auth-Pass' in headers else ''
headers = nginx.handle_authentication(flask.request.headers)
response = flask.Response()
for key, value in headers.items():

View File

@ -421,7 +421,7 @@ class Email(object):
""" send an email to the address """
try:
f_addr = f'{app.config["POSTMASTER"]}@{idna.encode(app.config["DOMAIN"]).decode("ascii")}'
with smtplib.LMTP(host=app.config['IMAP_ADDRESS'], port=2525) as lmtp:
with smtplib.LMTP(host=app.config['FRONT_ADDRESS'], port=2525) as lmtp:
to_address = f'{self.localpart}@{idna.encode(self.domain_name).decode("ascii")}'
msg = text.MIMEText(body)
msg['Subject'] = subject

View File

@ -15,7 +15,7 @@ COPY start.py /
RUN echo $VERSION >/version
EXPOSE 110/tcp 143/tcp 993/tcp 4190/tcp 2525/tcp
# EXPOSE 110/tcp 143/tcp 993/tcp 4190/tcp 2525/tcp
HEALTHCHECK CMD kill -0 `cat /run/dovecot/master.pid`
VOLUME ["/mail"]

View File

@ -11,7 +11,6 @@ default_internal_user = dovecot
default_login_user = mail
default_internal_group = dovecot
haproxy_trusted_networks = {{ SUBNET }} {{ SUBNET6 }}
login_trusted_networks = {{ SUBNET }} {{ SUBNET6 }}
###############
@ -112,14 +111,12 @@ protocol pop3 {
service imap-login {
inet_listener imap {
port = 143
haproxy = yes
}
}
service pop3-login {
inet_listener pop3 {
port = 110
haproxy = yes
}
}
@ -191,6 +188,12 @@ plugin {
imapsieve_mailbox2_before = file:/conf/report-ham.sieve
}
service anvil {
unix_listener anvil-auth-penalty {
mode = 0
}
}
###############
# Extensions
###############

View File

@ -17,7 +17,7 @@ ARG VERSION
LABEL version=$VERSION
RUN set -euxo pipefail \
; apk add --no-cache certbot nginx nginx-mod-http-brotli nginx-mod-mail openssl dovecot-lua dovecot-pigeonhole-plugin
; apk add --no-cache certbot nginx nginx-mod-http-brotli nginx-mod-mail openssl dovecot-lua dovecot-pigeonhole-plugin dovecot-lmtpd dovecot-pop3d dovecot-submissiond
COPY conf/ /conf/
COPY --from=static /static/ /static/

View File

@ -321,28 +321,6 @@ mail {
# Advertise real capabilities of backends (postfix/dovecot)
smtp_capabilities PIPELINING "SIZE {{ MESSAGE_SIZE_LIMIT }}" ETRN ENHANCEDSTATUSCODES 8BITMIME DSN;
pop3_capabilities TOP UIDL RESP-CODES PIPELINING AUTH-RESP-CODE USER;
imap_capabilities IMAP4 IMAP4rev1 UIDPLUS SASL-IR LOGIN-REFERRALS ID ENABLE IDLE LITERAL+;
# Default SMTP server for the webmail (no encryption, but authentication)
server {
listen 10025;
protocol smtp;
smtp_auth plain;
auth_http_header Auth-Port 10025;
auth_http_header Client-Port $remote_port;
}
# Default IMAP server for the webmail (no encryption, but authentication)
server {
listen 10143;
protocol imap;
smtp_auth plain;
auth_http_header Auth-Port 10143;
auth_http_header Client-Port $remote_port;
# ensure we talk HAPROXY protocol to the backends
proxy_protocol on;
}
# SMTP is always enabled, to avoid losing emails when TLS is failing
server {
@ -367,92 +345,4 @@ mail {
auth_http_header Auth-Port 25;
auth_http_header Client-Port $remote_port;
}
# All other protocols are disabled if TLS is failing
{% if not TLS_ERROR %}
server {
listen 143{% if PROXY_PROTOCOL in ['all', 'mail'] %} proxy_protocol{% endif %};
{% if SUBNET6 %}
listen [::]:143{% if PROXY_PROTOCOL in ['all', 'mail'] %} proxy_protocol{% endif %};
{% endif %}
{% if TLS %}
starttls only;
{% endif %}
protocol imap;
imap_auth plain;
auth_http_header Auth-Port 143;
auth_http_header Client-Port $remote_port;
# ensure we talk HAPROXY protocol to the backends
proxy_protocol on;
}
server {
listen 110{% if PROXY_PROTOCOL in ['all', 'mail'] %} proxy_protocol{% endif %};
{% if SUBNET6 %}
listen [::]:110{% if PROXY_PROTOCOL in ['all', 'mail'] %} proxy_protocol{% endif %};
{% endif %}
{% if TLS %}
starttls only;
{% endif %}
protocol pop3;
pop3_auth plain;
auth_http_header Auth-Port 110;
auth_http_header Client-Port $remote_port;
# ensure we talk HAPROXY protocol to the backends
proxy_protocol on;
}
server {
listen 587{% if PROXY_PROTOCOL in ['all', 'mail'] %} proxy_protocol{% endif %};
{% if SUBNET6 %}
listen [::]:587{% if PROXY_PROTOCOL in ['all', 'mail'] %} proxy_protocol{% endif %};
{% endif %}
{% if TLS %}
starttls only;
{% endif %}
protocol smtp;
smtp_auth plain login;
auth_http_header Auth-Port 587;
auth_http_header Client-Port $remote_port;
}
{% if TLS %}
server {
listen 465 ssl{% if PROXY_PROTOCOL in ['all', 'mail'] %} proxy_protocol{% endif %};
{% if SUBNET6 %}
listen [::]:465 ssl{% if PROXY_PROTOCOL in ['all', 'mail'] %} proxy_protocol{% endif %};
{% endif %}
protocol smtp;
smtp_auth plain login;
auth_http_header Auth-Port 465;
auth_http_header Client-Port $remote_port;
}
server {
listen 993 ssl{% if PROXY_PROTOCOL in ['all', 'mail'] %} proxy_protocol{% endif %};
{% if SUBNET6 %}
listen [::]:993 ssl{% if PROXY_PROTOCOL in ['all', 'mail'] %} proxy_protocol{% endif %};
{% endif %}
protocol imap;
imap_auth plain;
auth_http_header Auth-Port 993;
auth_http_header Client-Port $remote_port;
# ensure we talk HAPROXY protocol to the backends
proxy_protocol on;
}
server {
listen 995 ssl{% if PROXY_PROTOCOL in ['all', 'mail'] %} proxy_protocol{% endif %};
{% if SUBNET6 %}
listen [::]:995 ssl{% if PROXY_PROTOCOL in ['all', 'mail'] %} proxy_protocol{% endif %};
{% endif %}
protocol pop3;
pop3_auth plain;
auth_http_header Auth-Port 995;
auth_http_header Client-Port $remote_port;
# ensure we talk HAPROXY protocol to the backends
proxy_protocol on;
}
{% endif %}
{% endif %}
}

View File

@ -16,7 +16,10 @@ function auth_passdb_lookup(req)
}
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)
if req.password ~= nil
then
auth_request:add_header('Auth-Pass', req.password)
end
auth_request:add_header('Auth-Protocol', req.service)
auth_request:add_header('Client-IP', req.remote_ip)
auth_request:add_header('Client-Port', req.remote_port)
@ -31,7 +34,7 @@ function auth_passdb_lookup(req)
then
local server = auth_response:header('Auth-Server')
local port = auth_response:header('Auth-Port')
return dovecot.auth.PASSDB_RESULT_OK, "proxy=y host=" .. server .. " port=" .. port .. " nopassword=Y"
return dovecot.auth.PASSDB_RESULT_OK, "proxy=y host=" .. server .. " port=" .. port .. " nopassword=Y proxy_noauth=Y"
else
return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, ""
end

View File

@ -5,10 +5,21 @@ log_path = /dev/stderr
auth_verbose=yes
mail_debug=yes
login_log_format_elements = user=<%u> method=%m rip=%r rport=%b lip=%l lport=%a mpid=%e %c
protocols = sieve
protocols = sieve imap pop3 lmtp submission
postmaster_address = {{ POSTMASTER }}@{{ DOMAIN }}
hostname = {{ HOSTNAMES.split(",")[0] }}
submission_host = {{ FRONT_ADDRESS }}
submission_host = {{ SMTP_ADDRESS }}
submission_relay_host = {{ SMTP_ADDRESS }}
submission_relay_port = 10025
submission_relay_trusted = yes
submission_relay_ssl = no
submission_max_mail_size = {{ MESSAGE_SIZE_LIMIT }}
submission_backend_capabilities = 8BITMIME DSN VRFY
submission_client_workarounds = mailbox-for-path whitespace-before-path
# disable BURL
imap_urlauth_host=
lmtp_proxy = yes
lmtp_client_workarounds = whitespace-before-path mailbox-for-path
default_internal_user = dovecot
default_login_user = mail
@ -32,15 +43,12 @@ 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_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
ssl_prefer_server_ciphers = no
ssl_dh = </conf/dhparam.pem
ssl_options = no_compression no_ticket
{% else %}
disable_plaintext_auth = no
protocol sieve {
ssl = no
}
ssl = no
{% endif %}
passdb {
@ -51,6 +59,14 @@ passdb {
service auth-worker {
user = dovenull
group = dovenull
unix_listener auth-worker {
}
}
service anvil {
unix_listener anvil-auth-penalty {
mode = 0
}
}
service managesieve-login {
@ -65,3 +81,76 @@ service managesieve-login {
port = 14190
}
}
protocol imap {
mail_max_userip_connections = 20
imap_idle_notify_interval = 29mins
}
service imap-login {
inet_listener imap {
port = 143
{%- if PROXY_PROTOCOL in ['all', 'mail'] %}
haproxy = yes
{% endif %}
}
inet_listener imaps {
port = 993
{%- if TLS %}
ssl = yes
{% endif %}
{%- if PROXY_PROTOCOL in ['all', 'mail'] %}
haproxy = yes
{% endif %}
}
inet_listener imap-webmail {
port = 10143
}
}
service pop3-login {
inet_listener pop3 {
port = 110
{%- if PROXY_PROTOCOL in ['all', 'mail'] %}
haproxy = yes
{% endif %}
}
inet_listener pop3s {
port = 995
{%- if TLS %}
ssl = yes
{% endif %}
{%- if PROXY_PROTOCOL in ['all', 'mail'] %}
haproxy = yes
{% endif %}
}
}
recipient_delimiter = {{ RECIPIENT_DELIMITER }}
service lmtp {
user = $default_internal_user
inet_listener lmtp {
port = 2525
}
}
service submission-login {
inet_listener submission {
port = 587
{%- if PROXY_PROTOCOL in ['all', 'mail'] %}
haproxy = yes
{% endif %}
}
inet_listener submissions {
port = 465
{%- if TLS %}
ssl = yes
{% endif %}
{%- if PROXY_PROTOCOL in ['all', 'mail'] %}
haproxy = yes
{% endif %}
}
inet_listener submission-webmail {
port = 10025
}
}

View File

@ -81,7 +81,7 @@ virtual_mailbox_maps = ${podop}mailbox
# Mails are transported if required, then forwarded to Dovecot for delivery
relay_domains = ${podop}transport
transport_maps = lmdb:/etc/postfix/transport.map, ${podop}transport
virtual_transport = lmtp:inet:{{ IMAP_ADDRESS }}:2525
virtual_transport = lmtp:inet:{{ FRONT_ADDRESS }}:2525
# Sender and recipient canonical maps, mostly for SRS
sender_canonical_maps = ${podop}sendermap

View File

@ -7,7 +7,7 @@ smtp inet n - n - 1 smtpd
# Internal SMTP service
10025 inet n - n - 1 smtpd
-o smtpd_sasl_auth_enable=yes
-o smtpd_discard_ehlo_keywords=pipelining
-o smtpd_discard_ehlo_keywords=pipelining,silent-discard
-o smtpd_client_restrictions=$check_ratelimit,reject_unlisted_sender,reject_authenticated_sender_login_mismatch,permit
-o smtpd_reject_unlisted_recipient={% if REJECT_UNLISTED_RECIPIENT %}{{ REJECT_UNLISTED_RECIPIENT }}{% else %}no{% endif %}
-o cleanup_service_name=outclean

View File

@ -60,7 +60,7 @@ def run(debug):
protocol=fetch["protocol"],
host=escape_rc_string(fetch["host"]),
port=fetch["port"],
smtphost=f'{os.environ["SMTP_ADDRESS"]}' if fetch['scan'] else f'{os.environ["IMAP_ADDRESS"]}/2525',
smtphost=f'{os.environ["FRONT_ADDRESS"]}' if fetch['scan'] else f'{os.environ["FRONT_ADDRESS"]}/2525',
username=escape_rc_string(fetch["username"]),
password=escape_rc_string(fetch["password"]),
options=options,

View File

@ -19,10 +19,24 @@ $config['request_path'] = '{{ WEB_WEBMAIL or "none" }}';
$config['trusted_host_patterns'] = [ {{ HOSTNAMES.split(",") | map("tojson") | join(',') }}];
// Mail servers
$config['imap_host'] = '{{ FRONT_ADDRESS or "front" }}:10143';
$config['smtp_host'] = '{{ FRONT_ADDRESS or "front" }}:10025';
$config['imap_host'] = 'tls://{{ FRONT_ADDRESS or "front" }}:10143';
$config['imap_conn_options'] = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true,
),
);
$config['smtp_host'] = 'tls://{{ FRONT_ADDRESS or "front" }}:10025';
$config['smtp_user'] = '%u';
$config['smtp_pass'] = '%p';
$config['smtp_conn_options'] = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true,
),
);
// Sieve script management
$config['managesieve_host'] = 'tls://{{ FRONT_ADDRESS or "front" }}:14190';

View File

@ -3,7 +3,7 @@
"IMAP": {
"host": "{{ FRONT_ADDRESS }}",
"port": 10143,
"secure": 0,
"secure": 2,
"shortLogin": false,
"ssl": {
"verify_peer": false,
@ -17,7 +17,7 @@
"SMTP": {
"host": "{{ FRONT_ADDRESS }}",
"port": 10025,
"secure": 0,
"secure": 2,
"shortLogin": false,
"ssl": {
"verify_peer": false,