Publish MTA-STS policy for incoming mail (#1731)
Co-authored-by: Daniel Mabbett <triumph_2500@hotmail.com>
This commit is contained in:
parent
7de8fc9bc0
commit
afc9f9686a
|
@ -24,6 +24,12 @@ Web:
|
||||||
|
|
||||||
* Add a new hidden feature to set nginx alias in www/custom.yaml.
|
* Add a new hidden feature to set nginx alias in www/custom.yaml.
|
||||||
|
|
||||||
|
MTA-STS:
|
||||||
|
|
||||||
|
* Added support for client side MTA-STS when there is a valid SSL Certificate on the primary domain
|
||||||
|
* Automatically adds reporting when alias "tlsrpt@<primary-domain>" is added.
|
||||||
|
* Starts default on 'testing', but changes will be kept between MiaB Upgrades.
|
||||||
|
|
||||||
Setup:
|
Setup:
|
||||||
|
|
||||||
* Improved error handling.
|
* Improved error handling.
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
version: STSv1
|
||||||
|
mode: MODE
|
||||||
|
mx: PRIMARY_HOSTNAME
|
||||||
|
max_age: 86400
|
|
@ -21,6 +21,9 @@
|
||||||
location = /mail/config-v1.1.xml {
|
location = /mail/config-v1.1.xml {
|
||||||
alias /var/lib/mailinabox/mozilla-autoconfig.xml;
|
alias /var/lib/mailinabox/mozilla-autoconfig.xml;
|
||||||
}
|
}
|
||||||
|
location = /.well-known/mta-sts.txt {
|
||||||
|
alias /var/lib/mailinabox/mta-sts.txt;
|
||||||
|
}
|
||||||
|
|
||||||
# Roundcube Webmail configuration.
|
# Roundcube Webmail configuration.
|
||||||
rewrite ^/mail$ /mail/ redirect;
|
rewrite ^/mail$ /mail/ redirect;
|
||||||
|
|
|
@ -9,8 +9,9 @@ import ipaddress
|
||||||
import rtyaml
|
import rtyaml
|
||||||
import dns.resolver
|
import dns.resolver
|
||||||
|
|
||||||
from mailconfig import get_mail_domains
|
from mailconfig import get_mail_domains, get_mail_aliases
|
||||||
from utils import shell, load_env_vars_from_file, safe_domain_name, sort_domains
|
from utils import shell, load_env_vars_from_file, safe_domain_name, sort_domains
|
||||||
|
from ssl_certificates import get_ssl_certificates, check_certificate
|
||||||
|
|
||||||
# From https://stackoverflow.com/questions/3026957/how-to-validate-a-domain-name-using-regex-php/16491074#16491074
|
# From https://stackoverflow.com/questions/3026957/how-to-validate-a-domain-name-using-regex-php/16491074#16491074
|
||||||
# This regular expression matches domain names according to RFCs, it also accepts fqdn with an leading dot,
|
# This regular expression matches domain names according to RFCs, it also accepts fqdn with an leading dot,
|
||||||
|
@ -303,6 +304,42 @@ def build_zone(domain, all_domains, additional_records, www_redirect_domains, en
|
||||||
if not has_rec(qname, rtype):
|
if not has_rec(qname, rtype):
|
||||||
records.append((qname, rtype, value, explanation))
|
records.append((qname, rtype, value, explanation))
|
||||||
|
|
||||||
|
# If this is a domain name that there are email addresses configured for, i.e. "something@"
|
||||||
|
# this domain name, then the domain name is a MTA-STS (https://tools.ietf.org/html/rfc8461)
|
||||||
|
# Policy Domain.
|
||||||
|
#
|
||||||
|
# A "_mta-sts" TXT record signals the presence of a MTA-STS policy, and an effectively random policy
|
||||||
|
# ID is used to signal that a new policy may (or may not) be deployed any time the DNS is
|
||||||
|
# updated.
|
||||||
|
#
|
||||||
|
# The policy itself is served at the "mta-sts" (no underscore) subdomain over HTTPS. The
|
||||||
|
# TLS certificate used by Postfix for STARTTLS must be a valid certificate for the MX
|
||||||
|
# name (PRIMARY_HOSTNAME), so we do not set an MTA-STS policy if the certificate is not
|
||||||
|
# valid (e.g. because it is self-signed and a valid certificate has not yet been provisioned).
|
||||||
|
get_prim_cert = get_ssl_certificates(env)[env['PRIMARY_HOSTNAME']]
|
||||||
|
response = check_certificate(env['PRIMARY_HOSTNAME'], get_prim_cert['certificate'],get_prim_cert['private-key'])
|
||||||
|
|
||||||
|
if response[0] == 'OK' and domain in get_mail_domains(env):
|
||||||
|
mta_sts_records = [
|
||||||
|
("mta-sts", "A", env["PUBLIC_IP"], "Provides MTA-STS support"),
|
||||||
|
("mta-sts", "AAAA", env.get('PUBLIC_IPV6'), "Provides MTA-STS support"),
|
||||||
|
("_mta-sts", "TXT", "v=STSv1; id=%sZ" % datetime.datetime.now().strftime("%Y%m%d%H%M%S"), "Enables MTA-STS support")
|
||||||
|
]
|
||||||
|
# Rules can be custom configured accoring to https://tools.ietf.org/html/rfc8460.
|
||||||
|
# Skip if the rules below if the user has set a custom _smtp._tls record.
|
||||||
|
if not has_rec("_smtp._tls", "TXT", prefix="v=TLSRPTv1;"):
|
||||||
|
# if the alias 'tlsrpt@PRIMARY_HOSTNAME' is configured, automaticly, reporting will be enabled to this email address
|
||||||
|
tls_rpt_email = "tlsrpt@%s" % env['PRIMARY_HOSTNAME']
|
||||||
|
tls_rpt_string = ""
|
||||||
|
for alias in get_mail_aliases(env):
|
||||||
|
if alias[0] == tls_rpt_email:
|
||||||
|
tls_rpt_string = " rua=mailto:%s" % tls_rpt_email
|
||||||
|
mta_sts_records.append(("_smtp._tls", "TXT", "v=TLSRPTv1;%s" % tls_rpt_string, "For reporting, add an email alias: 'tlsrpt@%s' or add a custom TXT record like 'v=TLSRPTv1; rua=mailto:[youremail]@%s' for reporting" % (env["PRIMARY_HOSTNAME"], env["PRIMARY_HOSTNAME"])))
|
||||||
|
for qname, rtype, value, explanation in mta_sts_records:
|
||||||
|
if value is None or value.strip() == "": continue # skip IPV6 if not set
|
||||||
|
if not has_rec(qname, rtype):
|
||||||
|
records.append((qname, rtype, value, explanation))
|
||||||
|
|
||||||
# Sort the records. The None records *must* go first in the nsd zone file. Otherwise it doesn't matter.
|
# Sort the records. The None records *must* go first in the nsd zone file. Otherwise it doesn't matter.
|
||||||
records.sort(key = lambda rec : list(reversed(rec[0].split(".")) if rec[0] is not None else ""))
|
records.sort(key = lambda rec : list(reversed(rec[0].split(".")) if rec[0] is not None else ""))
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,9 @@ def get_web_domains(env, include_www_redirects=True, exclude_dns_elsewhere=True)
|
||||||
domains |= set('autoconfig.' + maildomain for maildomain in get_mail_domains(env))
|
domains |= set('autoconfig.' + maildomain for maildomain in get_mail_domains(env))
|
||||||
domains |= set('autodiscover.' + maildomain for maildomain in get_mail_domains(env))
|
domains |= set('autodiscover.' + maildomain for maildomain in get_mail_domains(env))
|
||||||
|
|
||||||
|
# 'mta-sts.' for MTA-STS support.
|
||||||
|
domains |= set('mta-sts.' + maildomain for maildomain in get_mail_domains(env))
|
||||||
|
|
||||||
if exclude_dns_elsewhere:
|
if exclude_dns_elsewhere:
|
||||||
# ...Unless the domain has an A/AAAA record that maps it to a different
|
# ...Unless the domain has an A/AAAA record that maps it to a different
|
||||||
# IP address than this box. Remove those domains from our list.
|
# IP address than this box. Remove those domains from our list.
|
||||||
|
|
|
@ -109,6 +109,12 @@ As discussed above, there is no way to require on-the-wire encryption of mail. W
|
||||||
|
|
||||||
When DNSSEC is enabled at the box's domain name's registrar, [DANE TLSA](https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities) records are automatically published in DNS. Senders supporting DANE will enforce encryption on-the-wire between them and the box --- see the section on DANE for outgoing mail above. ([source](management/dns_update.py))
|
When DNSSEC is enabled at the box's domain name's registrar, [DANE TLSA](https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities) records are automatically published in DNS. Senders supporting DANE will enforce encryption on-the-wire between them and the box --- see the section on DANE for outgoing mail above. ([source](management/dns_update.py))
|
||||||
|
|
||||||
|
### MTA-STS
|
||||||
|
|
||||||
|
SMTP MTA Strict Transport Security ([SMTP MTA-STS for short](https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol#SMTP_MTA_Strict_Transport_Security)).
|
||||||
|
|
||||||
|
MTA-STS is a mechanism that instructs an SMTP server that the communication with the other SMTP server MUST be encrypted and that the domain name on the certificate should match the domain in the policy. It uses a combination of DNS and HTTPS to publish a policy that tells the sending party what to do when an encrypted channel can not be negotiated.
|
||||||
|
|
||||||
### Filters
|
### Filters
|
||||||
|
|
||||||
Incoming mail is run through several filters. Email is bounced if the sender's IP address is listed in the [Spamhaus Zen blacklist](http://www.spamhaus.org/zen/) or if the sender's domain is listed in the [Spamhaus Domain Block List](http://www.spamhaus.org/dbl/). Greylisting (with [postgrey](http://postgrey.schweikert.ch/)) is also used to cut down on spam. ([source](setup/mail-postfix.sh))
|
Incoming mail is run through several filters. Email is bounced if the sender's IP address is listed in the [Spamhaus Zen blacklist](http://www.spamhaus.org/zen/) or if the sender's domain is listed in the [Spamhaus Domain Block List](http://www.spamhaus.org/dbl/). Greylisting (with [postgrey](http://postgrey.schweikert.ch/)) is also used to cut down on spam. ([source](setup/mail-postfix.sh))
|
||||||
|
|
|
@ -82,6 +82,11 @@ if [ ! -f $STORAGE_ROOT/mailinabox.version ]; then
|
||||||
chown $STORAGE_USER.$STORAGE_USER $STORAGE_ROOT/mailinabox.version
|
chown $STORAGE_USER.$STORAGE_USER $STORAGE_ROOT/mailinabox.version
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Default policy (initial) for MTA_STS = testing in the current state of inclusion.
|
||||||
|
# it can be changed to "none", "testing" or "enforce". With this extention, this is preserved by
|
||||||
|
# future upgrades
|
||||||
|
|
||||||
|
MTA_STS="${DEFAULT_MTA_STS:-testing}"
|
||||||
|
|
||||||
# Save the global options in /etc/mailinabox.conf so that standalone
|
# Save the global options in /etc/mailinabox.conf so that standalone
|
||||||
# tools know where to look for data.
|
# tools know where to look for data.
|
||||||
|
@ -93,6 +98,7 @@ PUBLIC_IP=$PUBLIC_IP
|
||||||
PUBLIC_IPV6=$PUBLIC_IPV6
|
PUBLIC_IPV6=$PUBLIC_IPV6
|
||||||
PRIVATE_IP=$PRIVATE_IP
|
PRIVATE_IP=$PRIVATE_IP
|
||||||
PRIVATE_IPV6=$PRIVATE_IPV6
|
PRIVATE_IPV6=$PRIVATE_IPV6
|
||||||
|
MTA_STS=$MTA_STS
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Start service configuration.
|
# Start service configuration.
|
||||||
|
|
17
setup/web.sh
17
setup/web.sh
|
@ -19,7 +19,7 @@ fi
|
||||||
|
|
||||||
echo "Installing Nginx (web server)..."
|
echo "Installing Nginx (web server)..."
|
||||||
|
|
||||||
apt_install nginx php-cli php-fpm
|
apt_install nginx php-cli php-fpm idn2
|
||||||
|
|
||||||
rm -f /etc/nginx/sites-enabled/default
|
rm -f /etc/nginx/sites-enabled/default
|
||||||
|
|
||||||
|
@ -122,6 +122,20 @@ cat conf/mozilla-autoconfig.xml \
|
||||||
> /var/lib/mailinabox/mozilla-autoconfig.xml
|
> /var/lib/mailinabox/mozilla-autoconfig.xml
|
||||||
chmod a+r /var/lib/mailinabox/mozilla-autoconfig.xml
|
chmod a+r /var/lib/mailinabox/mozilla-autoconfig.xml
|
||||||
|
|
||||||
|
# Create a generic mta-sts.txt file which is exposed via the
|
||||||
|
# nginx configuration at /.well-known/mta-sts.txt
|
||||||
|
# more documentation is available on:
|
||||||
|
# https://www.uriports.com/blog/mta-sts-explained/
|
||||||
|
# default mode is "testing", which means: "Messages will be delivered as
|
||||||
|
# though there was no failure but a report will be sent if TLS-RPT is configured"
|
||||||
|
# other valid modes are: "enforce" and "none".
|
||||||
|
PUNY_PRIMARY_HOSTNAME=$(echo "$PRIMARY_HOSTNAME" | idn2)
|
||||||
|
cat conf/mta-sts.txt \
|
||||||
|
| sed "s/MODE/$MTA_STS/" \
|
||||||
|
| sed "s/PRIMARY_HOSTNAME/$PUNY_PRIMARY_HOSTNAME/" \
|
||||||
|
> /var/lib/mailinabox/mta-sts.txt
|
||||||
|
chmod a+r /var/lib/mailinabox/mta-sts.txt
|
||||||
|
|
||||||
# make a default homepage
|
# make a default homepage
|
||||||
if [ -d $STORAGE_ROOT/www/static ]; then mv $STORAGE_ROOT/www/static $STORAGE_ROOT/www/default; fi # migration #NODOC
|
if [ -d $STORAGE_ROOT/www/static ]; then mv $STORAGE_ROOT/www/static $STORAGE_ROOT/www/default; fi # migration #NODOC
|
||||||
mkdir -p $STORAGE_ROOT/www/default
|
mkdir -p $STORAGE_ROOT/www/default
|
||||||
|
@ -137,4 +151,3 @@ restart_service php7.2-fpm
|
||||||
# Open ports.
|
# Open ports.
|
||||||
ufw_allow http
|
ufw_allow http
|
||||||
ufw_allow https
|
ufw_allow https
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue