From e0b64a9e540b5f2dbea7d39c53a0e824bb51663d Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sat, 6 Apr 2024 17:28:38 +0200 Subject: [PATCH] simplify config with TLS, PORTS and PROXY_PROTOCOL --- core/dovecot/conf/dovecot.conf | 2 +- core/nginx/conf/nginx.conf | 34 +++++++++++------------ core/nginx/conf/proxy.conf | 2 +- core/nginx/config.py | 40 ++++++++++++++++++++++++++++ core/nginx/dovecot/proxy.conf | 34 ++++++++++++++--------- towncrier/newsfragments/3061.feature | 1 + 6 files changed, 81 insertions(+), 32 deletions(-) create mode 100644 towncrier/newsfragments/3061.feature diff --git a/core/dovecot/conf/dovecot.conf b/core/dovecot/conf/dovecot.conf index 4ea4fc43..75bd3a66 100644 --- a/core/dovecot/conf/dovecot.conf +++ b/core/dovecot/conf/dovecot.conf @@ -5,7 +5,7 @@ log_path = /dev/stderr protocols = imap pop3 lmtp sieve postmaster_address = {{ POSTMASTER }}@{{ DOMAIN }} hostname = {{ HOSTNAMES.split(",")[0] }} -{%- if PROXY_PROTOCOL in ['all', 'all-but-http', 'mail'] %} +{%- if PROXY_PROTOCOL_SMTP %} submission_host = {{ HOSTNAMES.split(",")[0] }} {% else %} submission_host = {{ FRONT_ADDRESS }} diff --git a/core/nginx/conf/nginx.conf b/core/nginx/conf/nginx.conf index 214744ee..a29c570b 100644 --- a/core/nginx/conf/nginx.conf +++ b/core/nginx/conf/nginx.conf @@ -22,7 +22,7 @@ http { {% if REAL_IP_HEADER %} real_ip_header {{ REAL_IP_HEADER }}; - {% elif PROXY_PROTOCOL in ['all', 'all-but-http', 'http'] %} + {% elif (PROXY_PROTOCOL_HTTPS or PROXY_PROTOCOL_HTTP) and REAL_IP_FROM %} real_ip_header proxy_protocol; {% endif %} @@ -54,14 +54,14 @@ http { gzip_min_length 1024; # TODO: figure out how to server pre-compressed assets from admin container - {% if not KUBERNETES_INGRESS and TLS_FLAVOR in [ 'letsencrypt', 'cert' ] %} + {% if PORT_80 and TLS_FLAVOR in [ 'letsencrypt', 'cert' ] %} # Enable the proxy for certbot if the flavor is letsencrypt and not on kubernetes # server { # Listen over HTTP - listen 80{% if PROXY_PROTOCOL in ['all', 'http'] %} proxy_protocol{% endif %}; + listen 80{% if PROXY_PROTOCOL_HTTP %} proxy_protocol{% endif %}; {% if SUBNET6 %} - listen [::]:80{% if PROXY_PROTOCOL in ['all', 'http'] %} proxy_protocol{% endif %}; + listen [::]:80{% if PROXY_PROTOCOL_HTTP %} proxy_protocol{% endif %}; {% endif %} {% if TLS_FLAVOR in ['letsencrypt', 'mail-letsencrypt'] %} location ^~ /.well-known/acme-challenge/ { @@ -95,18 +95,18 @@ http { client_max_body_size {{ MESSAGE_SIZE_LIMIT|int + 8388608 }}; # Listen on HTTP only in kubernetes or behind reverse proxy - {% if KUBERNETES_INGRESS or TLS_FLAVOR in [ 'mail-letsencrypt', 'notls', 'mail' ] %} - listen 80{% if PROXY_PROTOCOL in ['all', 'http'] %} proxy_protocol{% endif %}; + {% if TLS_FLAVOR in [ 'mail-letsencrypt', 'notls', 'mail' ] %} + listen 80{% if PROXY_PROTOCOL_HTTP %} proxy_protocol{% endif %}; {% if SUBNET6 %} - listen [::]:80{% if PROXY_PROTOCOL in ['all', 'http'] %} proxy_protocol{% endif %}; + listen [::]:80{% if PROXY_PROTOCOL_HTTP %} proxy_protocol{% endif %}; {% endif %} {% endif %} - # Only enable HTTPS if TLS is enabled with no error and not on kubernetes - {% if not KUBERNETES_INGRESS and TLS and not TLS_ERROR %} - listen 443 ssl http2{% if PROXY_PROTOCOL in ['all', 'all-but-http', 'http'] %} proxy_protocol{% endif %}; + # Only enable HTTPS if TLS is enabled with no error + {% if TLS_443 and not TLS_ERROR %} + listen 443 ssl http2{% if PROXY_PROTOCOL_HTTPS %} proxy_protocol{% endif %}; {% if SUBNET6 %} - listen [::]:443 ssl http2{% if PROXY_PROTOCOL in ['all', 'all-but-http', 'http'] %} proxy_protocol{% endif %}; + listen [::]:443 ssl http2{% if PROXY_PROTOCOL_HTTPS %} proxy_protocol{% endif %}; {% endif %} include /etc/nginx/tls.conf; @@ -162,7 +162,7 @@ http { {% endif %} # If TLS is failing, prevent access to anything except certbot - {% if not KUBERNETES_INGRESS and TLS_ERROR and not (TLS_FLAVOR in [ 'mail-letsencrypt', 'mail' ]) %} + {% if TLS_ERROR and not (TLS_FLAVOR in [ 'mail-letsencrypt', 'mail' ]) %} location / { return 403; } @@ -310,12 +310,12 @@ mail { resolver {{ RESOLVER }} valid=30s; error_log /dev/stderr info; - {% if TLS and not TLS_ERROR %} + {% if TLS_25 and not TLS_ERROR %} include /etc/nginx/tls.conf; ssl_session_cache shared:SSLMAIL:3m; {% endif %} - {% if PROXY_PROTOCOL in ['all', 'all-but-http', 'mail'] and REAL_IP_FROM %}{% for from_ip in REAL_IP_FROM.split(',') %} + {% if PROXY_PROTOCOL_SMTP and REAL_IP_FROM %}{% for from_ip in REAL_IP_FROM.split(',') %} set_real_ip_from {{ from_ip }}; {% endfor %}{% endif %} @@ -324,11 +324,11 @@ mail { # SMTP is always enabled, to avoid losing emails when TLS is failing server { - listen 25{% if PROXY_PROTOCOL in ['all', 'all-but-http', 'mail'] %} proxy_protocol{% endif %}; + listen 25{% if PROXY_PROTOCOL_SMTP %} proxy_protocol{% endif %}; {% if SUBNET6 %} - listen [::]:25{% if PROXY_PROTOCOL in ['all', 'all-but-http', 'mail'] %} proxy_protocol{% endif %}; + listen [::]:25{% if PROXY_PROTOCOL_SMTP %} proxy_protocol{% endif %}; {% endif %} - {% if TLS and not TLS_ERROR %} + {% if TLS_25 and not TLS_ERROR %} {% if TLS_FLAVOR in ['letsencrypt','mail-letsencrypt'] %} ssl_certificate /certs/letsencrypt/live/mailu/DANE-chain.pem; ssl_certificate /certs/letsencrypt/live/mailu-ecdsa/DANE-chain.pem; diff --git a/core/nginx/conf/proxy.conf b/core/nginx/conf/proxy.conf index ebbd30aa..9f6402cd 100644 --- a/core/nginx/conf/proxy.conf +++ b/core/nginx/conf/proxy.conf @@ -6,7 +6,7 @@ proxy_hide_header True-Client-IP; proxy_hide_header CF-Connecting-IP; proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; -{% if (REAL_IP_HEADER or (PROXY_PROTOCOL in ['http', 'all'])) and REAL_IP_FROM %} +{% if (REAL_IP_HEADER or (PROXY_PROTOCOL_HTTP or PROXY_PROTOCOL_HTTPS)) and REAL_IP_FROM %} proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-By $realip_remote_addr; {% else %} diff --git a/core/nginx/config.py b/core/nginx/config.py index 73cc085c..966a402c 100755 --- a/core/nginx/config.py +++ b/core/nginx/config.py @@ -70,6 +70,44 @@ with open("/etc/resolv.conf") as handle: resolver = content[content.index("nameserver") + 1] args["RESOLVER"] = f"[{resolver}]" if ":" in resolver else resolver +# Configure PROXY_PROTOCOL +PROTO_MAIL=['SMTP', 'POP3', 'POP3S', 'IMAP', 'IMAPS', 'SUBMISSION', 'SUBMISSIONS', 'MANAGESIEVE'] +PROTO_ALL_BUT_HTTP=PROTO_MAIL.copy() +PROTO_ALL_BUT_HTTP.extend(['HTTPS']) +PROTO_ALL=PROTO_ALL_BUT_HTTP.copy() +PROTO_ALL.extend(['HTTP']) +for item in args.get('PROXY_PROTOCOL', '').split(','): + match item: + case '25': args['PROXY_PROTOCOL_SMTP']=True; continue + case '80': args['PROXY_PROTOCOL_HTTP']=True; continue + case '110': args['PROXY_PROTOCOL_POP3']=True; continue + case '143': args['PROXY_PROTOCOL_IMAP']=True; continue + case '443': args['PROXY_PROTOCOL_HTTPS']=True; continue + case '465': args['PROXY_PROTOCOL_SUBMISSIONS']=True; continue + case '587': args['PROXY_PROTOCOL_SUBMISSION']=True; continue + case '993': args['PROXY_PROTOCOL_IMAPS']=True; continue + case '995': args['PROXY_PROTOCOL_POP3S']=True; continue + case '4190': args['PROXY_PROTOCOL_MANAGESIEVE']=True; continue + case 'mail': + for p in PROTO_MAIL: args[f'PROXY_PROTOCOL_{p}']=True; continue + case 'all-but-http': + for p in PROTO_ALL_BUT_HTTP: args[f'PROXY_PROTOCOL_{p}']=True; continue + case 'all': + for p in PROTO_ALL: args[f'PROXY_PROTOCOL_{p}']=True; continue + +PORTS_REQUIRING_TLS=['443', '465', '993', '995'] +ALL_PORTS='25,80,443,465,587,993,995,4190' +for item in args.get('PORTS', ALL_PORTS).split(','): + if item in PORTS_REQUIRING_TLS and args['TLS_FLAVOR'] == 'notls': + continue + args[f'PORT_{item}']=True + +for item in args.get('TLS', ALL_PORTS).split(','): + if item in PORTS_REQUIRING_TLS: + if args['TLS_FLAVOR'] == 'notls': + continue + args[f'TLS_{item}']=True + # TLS configuration cert_name = args.get("TLS_CERT_FILENAME", "cert.pem") keypair_name = args.get("TLS_KEYPAIR_FILENAME", "key.pem") @@ -129,6 +167,8 @@ 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" +args['TLS_PERMISSIVE'] = str(args.get('TLS_PERMISSIVE')).lower() not in ('false', 'no') + # Build final configuration paths conf.jinja("/conf/tls.conf", args, "/etc/nginx/tls.conf") conf.jinja("/conf/proxy.conf", args, "/etc/nginx/proxy.conf") diff --git a/core/nginx/dovecot/proxy.conf b/core/nginx/dovecot/proxy.conf index db5a5f03..40ed607a 100644 --- a/core/nginx/dovecot/proxy.conf +++ b/core/nginx/dovecot/proxy.conf @@ -75,11 +75,12 @@ service anvil { } } +{%- if PORT_4190 %} service managesieve-login { executable = managesieve-login inet_listener sieve { port = 4190 -{%- if PROXY_PROTOCOL in ['all', 'all-but-http', 'mail'] %} +{%- if PROXY_PROTOCOL_MANAGESIEVE %} haproxy = yes {% endif %} } @@ -87,6 +88,7 @@ service managesieve-login { port = 14190 } } +{% endif %} protocol imap { mail_max_userip_connections = 20 @@ -94,42 +96,46 @@ protocol imap { } service imap-login { +{%- if PORT_143 %} inet_listener imap { port = 143 -{%- if PROXY_PROTOCOL in ['all', 'all-but-http', 'mail'] %} +{%- if PROXY_PROTOCOL_IMAP %} haproxy = yes {% endif %} } +{% endif %} +{%- if TLS_993 and PORT_993 %} inet_listener imaps { port = 993 -{%- if TLS %} ssl = yes -{% endif %} -{%- if PROXY_PROTOCOL in ['all', 'all-but-http', 'mail'] %} +{%- if PROXY_PROTOCOL_IMAPS %} haproxy = yes {% endif %} } +{% endif %} inet_listener imap-webmail { port = 10143 } } service pop3-login { +{%- if PORT_110 %} inet_listener pop3 { port = 110 -{%- if PROXY_PROTOCOL in ['all', 'all-but-http', 'mail'] %} +{%- if PROXY_PROTOCOL_POP3 %} haproxy = yes {% endif %} } +{% endif %} +{%- if TLS_995 and PORT_995 %} inet_listener pop3s { port = 995 -{%- if TLS %} ssl = yes -{% endif %} -{%- if PROXY_PROTOCOL in ['all', 'all-but-http', 'mail'] %} +{%- if PROXY_PROTOCOL_POP3S %} haproxy = yes {% endif %} } +{% endif %} } recipient_delimiter = {{ RECIPIENT_DELIMITER }} @@ -141,21 +147,23 @@ service lmtp { } service submission-login { +{%- if PORT_587 %} inet_listener submission { port = 587 -{%- if PROXY_PROTOCOL in ['all', 'all-but-http', 'mail'] %} +{%- if PROXY_PROTOCOL_SUBMISSION %} haproxy = yes {% endif %} } +{% endif %} +{%- if TLS_465 and PORT_465 %} inet_listener submissions { port = 465 -{%- if TLS %} ssl = yes -{% endif %} -{%- if PROXY_PROTOCOL in ['all', 'all-but-http', 'mail'] %} +{%- if PROXY_PROTOCOL_SUBMISSIONS %} haproxy = yes {% endif %} } +{% endif %} inet_listener submission-webmail { port = 10025 } diff --git a/towncrier/newsfragments/3061.feature b/towncrier/newsfragments/3061.feature new file mode 100644 index 00000000..66b6e669 --- /dev/null +++ b/towncrier/newsfragments/3061.feature @@ -0,0 +1 @@ +Introduce new settings for configuring proxying and TLS. Drop TLS_FLAVOR=mail-letsencrypt