From e4a32b55f5da3d8b4bde025f0964aab5b7744482 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sat, 19 Feb 2022 14:26:17 +0100 Subject: [PATCH 1/4] Send ISRG_X1 on port 25, make DANE pin that --- core/admin/mailu/models.py | 2 +- core/nginx/conf/nginx.conf | 4 ++++ core/nginx/letsencrypt.py | 11 +++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/core/admin/mailu/models.py b/core/admin/mailu/models.py index 920008fd..48b47ea5 100644 --- a/core/admin/mailu/models.py +++ b/core/admin/mailu/models.py @@ -276,7 +276,7 @@ class Domain(Base): hostname = app.config['HOSTNAME'] if app.config['TLS_FLAVOR'] in ('letsencrypt', 'mail-letsencrypt'): # current ISRG Root X1 (RSA 4096, O = Internet Security Research Group, CN = ISRG Root X1) @20210902 - return f'_25._tcp.{hostname}. 86400 IN TLSA 2 1 0 30820222300d06092a864886f70d01010105000382020f003082020a0282020100ade82473f41437f39b9e2b57281c87bedcb7df38908c6e3ce657a078f775c2a2fef56a6ef6004f28dbde68866c4493b6b163fd14126bbf1fd2ea319b217ed1333cba48f5dd79dfb3b8ff12f1219a4bc18a8671694a66666c8f7e3c70bfad292206f3e4c0e680aee24b8fb7997e94039fd347977c99482353e838ae4f0a6f832ed149578c8074b6da2fd0388d7b0370211b75f2303cfa8faeddda63abeb164fc28e114b7ecf0be8ffb5772ef4b27b4ae04c12250c708d0329a0e15324ec13d9ee19bf10b34a8c3f89a36151deac870794f46371ec2ee26f5b9881e1895c34796c76ef3b906279e6dba49a2f26c5d010e10eded9108e16fbb7f7a8f7c7e50207988f360895e7e237960d36759efb0e72b11d9bbc03f94905d881dd05b42ad641e9ac0176950a0fd8dfd5bd121f352f28176cd298c1a80964776e4737baceac595e689d7f72d689c50641293e593edd26f524c911a75aa34c401f46a199b5a73a516e863b9e7d72a712057859ed3e5178150b038f8dd02f05b23e7b4a1c4b730512fcc6eae050137c439374b3ca74e78e1f0108d030d45b7136b407bac130305c48b7823b98a67d608aa2a32982ccbabd83041ba2830341a1d605f11bc2b6f0a87c863b46a8482a88dc769a76bf1f6aa53d198feb38f364dec82b0d0a28fff7dbe21542d422d0275de179fe18e77088ad4ee6d98b3ac6dd27516effbc64f533434f0203010001' + return f'_25._tcp.{hostname}. 86400 IN TLSA 2 1 1 0b9fa5a59eed715c26c1020c711b4f6ec42d58b0015e14337a39dad301c5afc3' @property def dkim_key(self): diff --git a/core/nginx/conf/nginx.conf b/core/nginx/conf/nginx.conf index 8fee8989..60cdc1ea 100644 --- a/core/nginx/conf/nginx.conf +++ b/core/nginx/conf/nginx.conf @@ -288,6 +288,10 @@ mail { listen 25; listen [::]:25; {% if TLS and not TLS_ERROR %} + {% if TLS_FLAVOR in ['letsencrypt','mail-letsencrypt'] %} + ssl_certificate /certs/letsencrypt/live/mailu/nginx-chain-DANE.pem; + ssl_certificate /certs/letsencrypt/live/mailu-ecdsa/nginx-chain-DANE.pem; + {% endif %} ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers 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:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA; ssl_prefer_server_ciphers on; diff --git a/core/nginx/letsencrypt.py b/core/nginx/letsencrypt.py index e3db05f1..c9048c8c 100755 --- a/core/nginx/letsencrypt.py +++ b/core/nginx/letsencrypt.py @@ -47,6 +47,15 @@ def format_for_nginx(fullchain, output): for cert in certs[:-1] if len(certs)>2 and os.getenv('LETSENCRYPT_SHORTCHAIN', default="False") else certs: pem.write(cert) +def add_DANE_pin(chain, output): + with open(output, 'w') as pem: + with open(chain, 'r') as chain: + for line in chain: + pem.write(line) + with open('/etc/ssl/certs/ca-cert-ISRG_Root_X1.pem', 'r') as isrgx1: + for line in isrgx1: + pem.write(line) + # Wait for nginx to start time.sleep(5) @@ -54,6 +63,8 @@ time.sleep(5) while True: subprocess.call(command) format_for_nginx('/certs/letsencrypt/live/mailu/fullchain.pem', '/certs/letsencrypt/live/mailu/nginx-chain.pem') + add_DANE_pin('/certs/letsencrypt/live/mailu/chain.pem', '/certs/letsencrypt/live/mailu/nginx-chain-DANE.pem') subprocess.call(command2) format_for_nginx('/certs/letsencrypt/live/mailu-ecdsa/fullchain.pem', '/certs/letsencrypt/live/mailu-ecdsa/nginx-chain.pem') + add_DANE_pin('/certs/letsencrypt/live/mailu-ecdsa/chain.pem', '/certs/letsencrypt/live/mailu-ecdsa/nginx-chain-DANE.pem') time.sleep(86400) From 0816cb9497f31ca4ee71aebdee97b87e71870cc8 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sun, 20 Feb 2022 11:56:21 +0100 Subject: [PATCH 2/4] simplify as per ghostwheel42's suggestion --- core/nginx/conf/nginx.conf | 4 ++-- core/nginx/config.py | 18 ++++++++++++++++++ core/nginx/letsencrypt.py | 28 ---------------------------- 3 files changed, 20 insertions(+), 30 deletions(-) diff --git a/core/nginx/conf/nginx.conf b/core/nginx/conf/nginx.conf index 60cdc1ea..b9bb20b7 100644 --- a/core/nginx/conf/nginx.conf +++ b/core/nginx/conf/nginx.conf @@ -289,8 +289,8 @@ mail { listen [::]:25; {% if TLS and not TLS_ERROR %} {% if TLS_FLAVOR in ['letsencrypt','mail-letsencrypt'] %} - ssl_certificate /certs/letsencrypt/live/mailu/nginx-chain-DANE.pem; - ssl_certificate /certs/letsencrypt/live/mailu-ecdsa/nginx-chain-DANE.pem; + ssl_certificate /certs/letsencrypt/live/mailu/fullchain.pem; + ssl_certificate /certs/letsencrypt/live/mailu-ecdsa/fullchain.pem; {% endif %} ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers 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:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA; diff --git a/core/nginx/config.py b/core/nginx/config.py index 9fa75877..bd6967b9 100755 --- a/core/nginx/config.py +++ b/core/nginx/config.py @@ -34,6 +34,24 @@ args["TLS"] = { "notls": None }[args["TLS_FLAVOR"]] +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) + +if args['TLS_FLAVOR'] in ['letsencrypt', 'mail-letsencrypt']: + format_for_nginx('/certs/letsencrypt/live/mailu/fullchain.pem', '/certs/letsencrypt/live/mailu/nginx-chain.pem') + format_for_nginx('/certs/letsencrypt/live/mailu-ecdsa/fullchain.pem', '/certs/letsencrypt/live/mailu-ecdsa/nginx-chain.pem') + if args["TLS"] and not all(os.path.exists(file_path) for file_path in args["TLS"]): print("Missing cert or key file, disabling TLS") args["TLS_ERROR"] = "yes" diff --git a/core/nginx/letsencrypt.py b/core/nginx/letsencrypt.py index c9048c8c..48316c1c 100755 --- a/core/nginx/letsencrypt.py +++ b/core/nginx/letsencrypt.py @@ -32,39 +32,11 @@ command2 = [ "--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) - -def add_DANE_pin(chain, output): - with open(output, 'w') as pem: - with open(chain, 'r') as chain: - for line in chain: - pem.write(line) - with open('/etc/ssl/certs/ca-cert-ISRG_Root_X1.pem', 'r') as isrgx1: - for line in isrgx1: - pem.write(line) - # Wait for nginx to start 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') - add_DANE_pin('/certs/letsencrypt/live/mailu/chain.pem', '/certs/letsencrypt/live/mailu/nginx-chain-DANE.pem') subprocess.call(command2) - format_for_nginx('/certs/letsencrypt/live/mailu-ecdsa/fullchain.pem', '/certs/letsencrypt/live/mailu-ecdsa/nginx-chain.pem') - add_DANE_pin('/certs/letsencrypt/live/mailu-ecdsa/chain.pem', '/certs/letsencrypt/live/mailu-ecdsa/nginx-chain-DANE.pem') time.sleep(86400) From ab35492589ea5f0fead61e90e3b3a95927a608ed Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sun, 20 Feb 2022 12:02:30 +0100 Subject: [PATCH 3/4] the first time the loop runs we don't have the second cert --- core/nginx/config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/nginx/config.py b/core/nginx/config.py index bd6967b9..d9cfe6a5 100755 --- a/core/nginx/config.py +++ b/core/nginx/config.py @@ -36,6 +36,8 @@ args["TLS"] = { def format_for_nginx(fullchain, output): """ We may want to strip ISRG Root X1 out """ + if not os.path.exists(fullchain): + return certs = [] with open(fullchain, 'r') as pem: cert = '' From f9869b1d796c3f69a929c769ffa6ecd9cdeb1d51 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Thu, 24 Feb 2022 12:45:30 +0100 Subject: [PATCH 4/4] ghostwheel42's suggestions --- core/nginx/config.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/core/nginx/config.py b/core/nginx/config.py index d9cfe6a5..77cb3dcd 100755 --- a/core/nginx/config.py +++ b/core/nginx/config.py @@ -38,17 +38,13 @@ def format_for_nginx(fullchain, output): """ We may want to strip ISRG Root X1 out """ if not os.path.exists(fullchain): return - certs = [] + split = '-----END CERTIFICATE-----\n' with open(fullchain, 'r') as pem: - cert = '' - for line in pem: - cert += line - if '-----END CERTIFICATE-----' in line: - certs += [cert] - cert = '' + certs = [f'{cert}{split}' for cert in pem.read().split(split) if cert] + if len(certs)>2 and os.getenv('LETSENCRYPT_SHORTCHAIN'): + del certs[-1] 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) + pem.write(''.join(certs)) if args['TLS_FLAVOR'] in ['letsencrypt', 'mail-letsencrypt']: format_for_nginx('/certs/letsencrypt/live/mailu/fullchain.pem', '/certs/letsencrypt/live/mailu/nginx-chain.pem')