mirror of
https://github.com/Mailu/Mailu.git
synced 2024-12-12 10:45:38 +02:00
Merge #1922
1922: Harden postfix's configuration r=mergify[bot] a=nextgens ## What type of PR? enhancement ## What does this PR do? It hardens the default configuration: - disable AUTH commands on port 25 (nginx was not advertising the capability: normal clients wouldn't attempt it) - fix Forward Secrecy by ensuring that we don't use session tickets and don't cache on forensically carveable mediums - prevent clear-text credentials from being sent while authenticating to remote relays (this may break things if the relay doesn't support challenge-based authentication NOR STARTTLS - unlikely). - switch to default RSA keysizes (2048 bits and they get rekeyed every 3 months -modern clients will do ECC) - enable ECC certificates (much smaller than RSA keys, faster for better security margin) - configure nginx so that it doesn't send the legacy/root CA (clients that require it are unlikely to do TLS1.2 any ways) I don't think that any of those changes is impactful enough to warrant being documented. ### Related issue(s) - close #1804 ## Prerequistes Before we can consider review and merge, please make sure the following list is done and checked. If an entry in not applicable, you can check it or remove it from the list. - [x] In case of feature or enhancement: documentation updated accordingly - [x] Unless it's docs or a minor change: add [changelog](https://mailu.io/master/contributors/workflow.html#changelog) entry file. Co-authored-by: Florent Daigniere <nextgens@freenetproject.org> Co-authored-by: Jack Murray <github@c0rporation.com>
This commit is contained in:
commit
34b35ca9b7
@ -81,6 +81,13 @@ def handle_authentication(headers):
|
||||
raw_password = urllib.parse.unquote(headers["Auth-Pass"])
|
||||
password = raw_password.encode("iso8859-1").decode("utf8")
|
||||
ip = urllib.parse.unquote(headers["Client-Ip"])
|
||||
service_port = int(urllib.parse.unquote(headers["Auth-Port"]))
|
||||
if service_port == 25:
|
||||
return {
|
||||
"Auth-Status": "AUTH not supported",
|
||||
"Auth-Error-Code": "502 5.5.1",
|
||||
"Auth-Wait": 0
|
||||
}
|
||||
user = models.User.query.get(user_email)
|
||||
if check_credentials(user, password, ip, protocol):
|
||||
return {
|
||||
|
@ -1,13 +1,11 @@
|
||||
-----BEGIN DH PARAMETERS-----
|
||||
MIICCAKCAgEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
|
||||
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
|
||||
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
|
||||
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
|
||||
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
|
||||
ssbzSibBsu/6iGtCOGEfz9zeNVs7ZRkDW7w09N75nAI4YbRvydbmyQd62R0mkff3
|
||||
7lmMsPrBhtkcrv4TCYUTknC0EwyTvEN5RPT9RFLi103TZPLiHnH1S/9croKrnJ32
|
||||
nuhtK8UiNjoNq8Uhl5sN6todv5pC1cRITgq80Gv6U93vPBsg7j/VnXwl5B0rZp4e
|
||||
8W5vUsMWTfT7eTDp5OWIV7asfV9C1p9tGHdjzx1VA0AEh/VbpX4xzHpxNciG77Qx
|
||||
iu1qHgEtnmgyqQdgCpGBMMRtx3j5ca0AOAkpmaMzy4t6Gh25PXFAADwqTs6p+Y0K
|
||||
zAqCkc3OyX3Pjsm1Wn+IpGtNtahR9EGC4caKAH5eZV9q//////////8CAQI=
|
||||
MIIBiAKCAYEAtQlUSOKGjpdXJ154qmMEa1pEs+9CdSxWiZFkiXBJb0lTafOh8cfF
|
||||
2IkcWSwzxWwjW4Ad26UQQFh1poGf2QBzVk2vuKCekYzPAs/WqH8VwiXBiWR5R9lh
|
||||
v/+CkEBYuQOzAhXLN6ZGdPPa2sjdI49rlaIqyLJE4D0TI/VHYmC/vEwqkJUgaGrS
|
||||
19LhHZimnmouvrnyBPyf00czXlMow0RnmYeHVZ7W5hu7t9TH9o3QAN/GKiFfxFj+
|
||||
RkdLM7beQdS0He5YeTaElM5l1YT5d5gHFbOzEQyKHd10ux+bgVcgUeVbBnI1SAIC
|
||||
w53yc1PkDAiRijSP5j5aWq1djtJPheS13o35HyIf0cHzkNYhKfX5JWPj/cbgdM+C
|
||||
FL1bnRc8sL5oxmkDoGJhiNZIf4n2WtS8Zu28gUgat6S+vCm/4yavIc/T1g6UiNKE
|
||||
X41HPbsma/QWUwOL6S+b2qr+7rKqjI5TzVek8vBMellEV4mBvfQU3NDSQ4WvxbTq
|
||||
ZEOgLPA178nrAgEC
|
||||
-----END DH PARAMETERS-----
|
||||
|
@ -250,6 +250,7 @@ mail {
|
||||
listen 10025;
|
||||
protocol smtp;
|
||||
smtp_auth plain;
|
||||
auth_http_header Auth-Port 10025;
|
||||
}
|
||||
|
||||
# Default IMAP server for the webmail (no encryption, but authentication)
|
||||
@ -257,6 +258,7 @@ mail {
|
||||
listen 10143;
|
||||
protocol imap;
|
||||
smtp_auth plain;
|
||||
auth_http_header Auth-Port 10043;
|
||||
}
|
||||
|
||||
# SMTP is always enabled, to avoid losing emails when TLS is failing
|
||||
@ -271,6 +273,7 @@ mail {
|
||||
{% endif %}
|
||||
protocol smtp;
|
||||
smtp_auth none;
|
||||
auth_http_header Auth-Port 25;
|
||||
}
|
||||
|
||||
# All other protocols are disabled if TLS is failing
|
||||
@ -283,6 +286,7 @@ mail {
|
||||
{% endif %}
|
||||
protocol imap;
|
||||
imap_auth plain;
|
||||
auth_http_header Auth-Port 143;
|
||||
}
|
||||
|
||||
server {
|
||||
@ -293,6 +297,7 @@ mail {
|
||||
{% endif %}
|
||||
protocol pop3;
|
||||
pop3_auth plain;
|
||||
auth_http_header Auth-Port 110;
|
||||
}
|
||||
|
||||
server {
|
||||
@ -303,6 +308,7 @@ mail {
|
||||
{% endif %}
|
||||
protocol smtp;
|
||||
smtp_auth plain login;
|
||||
auth_http_header Auth-Port 587;
|
||||
}
|
||||
|
||||
{% if TLS %}
|
||||
@ -311,6 +317,7 @@ mail {
|
||||
listen [::]:465 ssl;
|
||||
protocol smtp;
|
||||
smtp_auth plain login;
|
||||
auth_http_header Auth-Port 465;
|
||||
}
|
||||
|
||||
server {
|
||||
@ -318,6 +325,7 @@ mail {
|
||||
listen [::]:993 ssl;
|
||||
protocol imap;
|
||||
imap_auth plain;
|
||||
auth_http_header Auth-Port 993;
|
||||
}
|
||||
|
||||
server {
|
||||
@ -325,6 +333,7 @@ mail {
|
||||
listen [::]:995 ssl;
|
||||
protocol pop3;
|
||||
pop3_auth plain;
|
||||
auth_http_header Auth-Port 995;
|
||||
}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
@ -1,5 +1,10 @@
|
||||
ssl_certificate {{ TLS[0] }};
|
||||
ssl_certificate_key {{ TLS[1] }};
|
||||
{% if TLS_FLAVOR in ['letsencrypt','mail-letsencrypt'] %}
|
||||
ssl_certificate {{ TLS[2] }};
|
||||
ssl_certificate_key {{ TLS[3] }};
|
||||
ssl_trusted_certificate /etc/ssl/certs/ca-cert-DST_Root_CA_X3.pem;
|
||||
{% endif %}
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_tickets off;
|
||||
ssl_dhparam /conf/dhparam.pem;
|
||||
|
@ -26,11 +26,11 @@ cert_name = os.getenv("TLS_CERT_FILENAME", default="cert.pem")
|
||||
keypair_name = os.getenv("TLS_KEYPAIR_FILENAME", default="key.pem")
|
||||
args["TLS"] = {
|
||||
"cert": ("/certs/%s" % cert_name, "/certs/%s" % keypair_name),
|
||||
"letsencrypt": ("/certs/letsencrypt/live/mailu/fullchain.pem",
|
||||
"/certs/letsencrypt/live/mailu/privkey.pem"),
|
||||
"letsencrypt": ("/certs/letsencrypt/live/mailu/nginx-chain.pem",
|
||||
"/certs/letsencrypt/live/mailu/privkey.pem", "/certs/letsencrypt/live/mailu-ecdsa/nginx-chain.pem", "/certs/letsencrypt/live/mailu-ecdsa/privkey.pem"),
|
||||
"mail": ("/certs/%s" % cert_name, "/certs/%s" % keypair_name),
|
||||
"mail-letsencrypt": ("/certs/letsencrypt/live/mailu/fullchain.pem",
|
||||
"/certs/letsencrypt/live/mailu/privkey.pem"),
|
||||
"mail-letsencrypt": ("/certs/letsencrypt/live/mailu/nginx-chain.pem",
|
||||
"/certs/letsencrypt/live/mailu/privkey.pem", "/certs/letsencrypt/live/mailu-ecdsa/nginx-chain.pem", "/certs/letsencrypt/live/mailu-ecdsa/privkey.pem"),
|
||||
"notls": None
|
||||
}[args["TLS_FLAVOR"]]
|
||||
|
||||
|
@ -4,7 +4,6 @@ import os
|
||||
import time
|
||||
import subprocess
|
||||
|
||||
|
||||
command = [
|
||||
"certbot",
|
||||
"-n", "--agree-tos", # non-interactive
|
||||
@ -14,10 +13,37 @@ command = [
|
||||
"--cert-name", "mailu",
|
||||
"--preferred-challenges", "http", "--http-01-port", "8008",
|
||||
"--keep-until-expiring",
|
||||
"--rsa-key-size", "4096",
|
||||
"--config-dir", "/certs/letsencrypt",
|
||||
"--post-hook", "/config.py"
|
||||
]
|
||||
command2 = [
|
||||
"certbot",
|
||||
"-n", "--agree-tos", # non-interactive
|
||||
"-d", os.environ["HOSTNAMES"],
|
||||
"-m", "{}@{}".format(os.environ["POSTMASTER"], os.environ["DOMAIN"]),
|
||||
"certonly", "--standalone",
|
||||
"--cert-name", "mailu-ecdsa",
|
||||
"--preferred-challenges", "http", "--http-01-port", "8008",
|
||||
"--keep-until-expiring",
|
||||
"--key-type", "ecdsa",
|
||||
"--config-dir", "/certs/letsencrypt",
|
||||
"--post-hook", "/config.py"
|
||||
]
|
||||
|
||||
def format_for_nginx(fullchain, output):
|
||||
""" We may want to strip ISRG Root X1 out
|
||||
"""
|
||||
certs = []
|
||||
with open(fullchain, 'r') as pem:
|
||||
cert = ''
|
||||
for line in pem:
|
||||
cert += line
|
||||
if '-----END CERTIFICATE-----' in line:
|
||||
certs += [cert]
|
||||
cert = ''
|
||||
with open(output, 'w') as pem:
|
||||
for cert in certs[:-1] if len(certs)>2 and os.getenv('LETSENCRYPT_SHORTCHAIN', default="False") else certs:
|
||||
pem.write(cert)
|
||||
|
||||
# Wait for nginx to start
|
||||
time.sleep(5)
|
||||
@ -25,5 +51,7 @@ time.sleep(5)
|
||||
# Run certbot every day
|
||||
while True:
|
||||
subprocess.call(command)
|
||||
format_for_nginx('/certs/letsencrypt/live/mailu/fullchain.pem', '/certs/letsencrypt/live/mailu/nginx-chain.pem')
|
||||
subprocess.call(command2)
|
||||
format_for_nginx('/certs/letsencrypt/live/mailu-ecdsa/fullchain.pem', '/certs/letsencrypt/live/mailu-ecdsa/nginx-chain.pem')
|
||||
time.sleep(86400)
|
||||
|
||||
|
@ -33,7 +33,8 @@ relayhost = {{ RELAYHOST }}
|
||||
{% if RELAYUSER %}
|
||||
smtp_sasl_auth_enable = yes
|
||||
smtp_sasl_password_maps = lmdb:/etc/postfix/sasl_passwd
|
||||
smtp_sasl_security_options = noanonymous
|
||||
smtp_sasl_security_options = noanonymous, noplaintext
|
||||
smtp_sasl_tls_security_options = noanonymous
|
||||
{% endif %}
|
||||
|
||||
# Recipient delimiter for extended addresses
|
||||
@ -50,7 +51,7 @@ smtpd_authorized_xclient_hosts={{ POD_ADDRESS_RANGE or SUBNET }}
|
||||
# General TLS configuration
|
||||
tls_high_cipherlist = EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA256:EECDH:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA
|
||||
tls_preempt_cipherlist = yes
|
||||
tls_ssl_options = NO_COMPRESSION
|
||||
tls_ssl_options = NO_COMPRESSION, NO_TICKET
|
||||
|
||||
# By default, outgoing TLS is more flexible because
|
||||
# 1. not all receiving servers will support TLS,
|
||||
@ -58,7 +59,8 @@ tls_ssl_options = NO_COMPRESSION
|
||||
smtp_tls_security_level = {{ OUTBOUND_TLS_LEVEL|default('may') }}
|
||||
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3
|
||||
smtp_tls_protocols =!SSLv2,!SSLv3
|
||||
smtp_tls_session_cache_database = lmdb:${data_directory}/smtp_scache
|
||||
smtp_tls_session_cache_database = lmdb:/dev/shm/postfix/smtp_scache
|
||||
smtpd_tls_session_cache_database = lmdb:/dev/shm/postfix/smtpd_scache
|
||||
|
||||
###############
|
||||
# Virtual
|
||||
|
@ -15,6 +15,7 @@ log.basicConfig(stream=sys.stderr, level=os.environ.get("LOG_LEVEL", "WARNING"))
|
||||
|
||||
def start_podop():
|
||||
os.setuid(getpwnam('postfix').pw_uid)
|
||||
os.mkdir('/dev/shm/postfix',mode=0o700)
|
||||
url = "http://" + os.environ["ADMIN_ADDRESS"] + "/internal/postfix/"
|
||||
# TODO: Remove verbosity setting from Podop?
|
||||
run_server(0, "postfix", "/tmp/podop.socket", [
|
||||
|
@ -163,6 +163,11 @@ See the `python docs`_ for more information.
|
||||
|
||||
.. _`python docs`: https://docs.python.org/3.6/library/logging.html#logging-levels
|
||||
|
||||
The ``LETSENCRYPT_SHORTCHAIN`` (default: False) setting controls whether we send the ISRG Root X1 certificate in TLS handshakes. This is required for `android handsets older than 7.1.1` but slows down the performance of modern devices.
|
||||
|
||||
.. _`android handsets older than 7.1.1`: https://community.letsencrypt.org/t/production-chain-changes/150739
|
||||
|
||||
|
||||
Antivirus settings
|
||||
------------------
|
||||
|
||||
|
5
towncrier/newsfragments/1922.enhancement
Normal file
5
towncrier/newsfragments/1922.enhancement
Normal file
@ -0,0 +1,5 @@
|
||||
Add support for ECDSA certificates when letsencrypt is used. This means dropping compatibility for android < 4.1.1
|
||||
Add LETSENCRYPT_SHORTCHAIN to your configuration to avoid sending ISRG Root X1 (this will break compatibility with android < 7.1.1)
|
||||
Disable AUTH command on port 25
|
||||
Disable TLS tickets, reconfigure the cache to improve Forward Secrecy
|
||||
Prevent clear-text credentials from being sent to relays
|
Loading…
Reference in New Issue
Block a user