mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-05-20 10:34:30 +00:00
email: postfix: dovecot: Set strong security parameters
Other changes: - Fix linter error - Postfix port 25: do not trust localhost IP addresses
This commit is contained in:
parent
b172b0b1cd
commit
9a98ddadd4
@ -1,6 +1,8 @@
|
|||||||
"""FreedomBox email server app"""
|
"""FreedomBox email server app"""
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
@ -10,7 +12,9 @@ import plinth.frontpage
|
|||||||
import plinth.menu
|
import plinth.menu
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth.modules.apache.components import Webserver
|
from plinth.modules.apache.components import Webserver
|
||||||
|
from plinth.modules.config import get_domainname
|
||||||
from plinth.modules.firewall.components import Firewall
|
from plinth.modules.firewall.components import Firewall
|
||||||
|
from plinth.modules.letsencrypt.components import LetsEncrypt
|
||||||
|
|
||||||
from . import audit
|
from . import audit
|
||||||
from . import manifest
|
from . import manifest
|
||||||
@ -36,6 +40,7 @@ managed_services = ['postfix', 'dovecot', 'rspamd']
|
|||||||
|
|
||||||
managed_packages = packages + packages_bloat
|
managed_packages = packages + packages_bloat
|
||||||
app = None
|
app = None
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class EmailServerApp(plinth.app.App):
|
class EmailServerApp(plinth.app.App):
|
||||||
@ -56,6 +61,18 @@ class EmailServerApp(plinth.app.App):
|
|||||||
urls=['https://{host}/rspamd'])
|
urls=['https://{host}/rspamd'])
|
||||||
self.add(webserver)
|
self.add(webserver)
|
||||||
|
|
||||||
|
# Let's Encrypt event hook
|
||||||
|
default_domain = get_domainname()
|
||||||
|
domains = [default_domain] if default_domain else []
|
||||||
|
letsencrypt = LetsEncrypt(
|
||||||
|
'letsencrypt-email-server', domains=domains,
|
||||||
|
daemons=['postfix', 'dovecot'], should_copy_certificates=False,
|
||||||
|
managing_app='email_server')
|
||||||
|
self.add(letsencrypt)
|
||||||
|
|
||||||
|
if not domains:
|
||||||
|
logger.warning('Could not fetch the FreedomBox domain name!')
|
||||||
|
|
||||||
def _add_ui_components(self):
|
def _add_ui_components(self):
|
||||||
info = plinth.app.Info(
|
info = plinth.app.Info(
|
||||||
app_id=self.app_id,
|
app_id=self.app_id,
|
||||||
@ -119,6 +136,7 @@ class EmailServerApp(plinth.app.App):
|
|||||||
results.extend([r.summarize() for r in audit.domain.get()])
|
results.extend([r.summarize() for r in audit.domain.get()])
|
||||||
results.extend([r.summarize() for r in audit.ldap.get()])
|
results.extend([r.summarize() for r in audit.ldap.get()])
|
||||||
results.extend([r.summarize() for r in audit.spam.get()])
|
results.extend([r.summarize() for r in audit.spam.get()])
|
||||||
|
results.extend([r.summarize() for r in audit.tls.get()])
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
@ -128,6 +146,7 @@ def setup(helper, old_version=None):
|
|||||||
helper.install(packages_bloat, skip_recommends=True)
|
helper.install(packages_bloat, skip_recommends=True)
|
||||||
helper.call('post', audit.ldap.repair)
|
helper.call('post', audit.ldap.repair)
|
||||||
helper.call('post', audit.spam.repair)
|
helper.call('post', audit.spam.repair)
|
||||||
|
helper.call('post', audit.tls.repair)
|
||||||
for srvname in managed_services:
|
for srvname in managed_services:
|
||||||
actions.superuser_run('service', ['reload', srvname])
|
actions.superuser_run('service', ['reload', srvname])
|
||||||
# Final step: expose service daemons to public internet
|
# Final step: expose service daemons to public internet
|
||||||
|
|||||||
@ -126,7 +126,7 @@ def schedule_hash_update():
|
|||||||
key = alias.email_name.encode('ascii') + b'\0'
|
key = alias.email_name.encode('ascii') + b'\0'
|
||||||
if alias.enabled:
|
if alias.enabled:
|
||||||
value = str(alias.uid_number).encode('ascii')
|
value = str(alias.uid_number).encode('ascii')
|
||||||
value += b'@localhost\0'
|
value += b'@localhost\0'
|
||||||
else:
|
else:
|
||||||
value = b'/dev/null\0'
|
value = b'/dev/null\0'
|
||||||
db[key] = value
|
db[key] = value
|
||||||
|
|||||||
@ -7,5 +7,6 @@ from . import domain
|
|||||||
from . import home
|
from . import home
|
||||||
from . import ldap
|
from . import ldap
|
||||||
from . import spam
|
from . import spam
|
||||||
|
from . import tls
|
||||||
|
|
||||||
__all__ = ['domain', 'home', 'ldap', 'spam']
|
__all__ = ['domain', 'home', 'ldap', 'spam', 'tls']
|
||||||
|
|||||||
@ -15,7 +15,11 @@ default_config = {
|
|||||||
'smtpd_sasl_type': 'dovecot',
|
'smtpd_sasl_type': 'dovecot',
|
||||||
'smtpd_sasl_path': 'private/auth',
|
'smtpd_sasl_path': 'private/auth',
|
||||||
'mailbox_transport': 'lmtp:unix:private/dovecot-lmtp',
|
'mailbox_transport': 'lmtp:unix:private/dovecot-lmtp',
|
||||||
'virtual_transport': 'lmtp:unix:private/dovecot-lmtp'
|
'virtual_transport': 'lmtp:unix:private/dovecot-lmtp',
|
||||||
|
|
||||||
|
'smtpd_relay_restrictions': ','.join([
|
||||||
|
'permit_sasl_authenticated', 'defer_unauth_destination',
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
submission_flags = postconf.ServiceFlags(
|
submission_flags = postconf.ServiceFlags(
|
||||||
|
|||||||
84
plinth/modules/email_server/audit/tls.py
Normal file
84
plinth/modules/email_server/audit/tls.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
"""TLS configuration"""
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from plinth import actions
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
from plinth.modules.email_server import postconf
|
||||||
|
|
||||||
|
# Mozilla Guideline v5.6, Postfix 1.17.7, OpenSSL 1.1.1d, intermediate
|
||||||
|
# Generated 2021-08
|
||||||
|
# https://ssl-config.mozilla.org/
|
||||||
|
tls_medium_cipherlist = [
|
||||||
|
'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'
|
||||||
|
]
|
||||||
|
|
||||||
|
postfix_config = {
|
||||||
|
# Enable TLS
|
||||||
|
'smtpd_tls_security_level': 'may',
|
||||||
|
'smtpd_tls_auth_only': 'yes',
|
||||||
|
|
||||||
|
# Debugging information
|
||||||
|
'smtpd_tls_received_header': 'yes',
|
||||||
|
|
||||||
|
# Use a strong hashing algorithm
|
||||||
|
'smtp_tls_fingerprint_digest': 'sha256',
|
||||||
|
'smtpd_tls_fingerprint_digest': 'sha256',
|
||||||
|
|
||||||
|
# Mozilla Intermediate Configuration
|
||||||
|
'smtpd_tls_mandatory_protocols': '!SSLv2, !SSLv3, !TLSv1, !TLSv1.1',
|
||||||
|
'smtpd_tls_protocols': '!SSLv2, !SSLv3, !TLSv1, !TLSv1.1',
|
||||||
|
'smtpd_tls_mandatory_ciphers': 'medium',
|
||||||
|
'tls_medium_cipherlist': ':'.join(tls_medium_cipherlist),
|
||||||
|
'tls_preempt_cipherlist': 'no',
|
||||||
|
|
||||||
|
# Postfix SMTP client
|
||||||
|
'smtp_tls_mandatory_protocols': '!SSLv2, !SSLv3, !TLSv1, !TLSv1.1',
|
||||||
|
'smtp_tls_protocols': '!SSLv2, !SSLv3, !TLSv1, !TLSv1.1',
|
||||||
|
'smtp_tls_mandatory_ciphers': 'medium',
|
||||||
|
|
||||||
|
# Use DNSSEC to validate TLS certificates
|
||||||
|
'smtp_host_lookup': 'dns',
|
||||||
|
'smtp_dns_support_level': 'dnssec',
|
||||||
|
'smtp_tls_security_level': 'dane', # Opportunistic DANE TLS
|
||||||
|
|
||||||
|
# Maintain 1 cipherlist and keep it the most secure
|
||||||
|
'tls_low_cipherlist': '$tls_medium_cipherlist',
|
||||||
|
'tls_high_cipherlist': '$tls_medium_cipherlist',
|
||||||
|
}
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def get():
|
||||||
|
results = []
|
||||||
|
with postconf.mutex.lock_all():
|
||||||
|
results.append(check_tls())
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def repair():
|
||||||
|
actions.superuser_run('email_server', ['-i', 'tls', 'set_up'])
|
||||||
|
|
||||||
|
|
||||||
|
def check_tls():
|
||||||
|
diagnosis = models.MainCfDiagnosis('Postfix TLS')
|
||||||
|
current = postconf.get_many_unsafe(list(postfix_config.keys()))
|
||||||
|
diagnosis.compare_and_advise(current=current, default=postfix_config)
|
||||||
|
return diagnosis
|
||||||
|
|
||||||
|
|
||||||
|
def repair_tls(diagnosis):
|
||||||
|
diagnosis.assert_resolved()
|
||||||
|
logger.info('Setting postconf: %r', diagnosis.advice)
|
||||||
|
postconf.set_many_unsafe(diagnosis.advice)
|
||||||
|
|
||||||
|
|
||||||
|
def action_set_up():
|
||||||
|
with postconf.mutex.lock_all():
|
||||||
|
repair_tls(check_tls())
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
# Direct edits to this file will be lost!
|
||||||
|
# Manage your settings on Plinth <https://localhost/plinth/apps/email_server>
|
||||||
|
|
||||||
|
# Mozilla Guideline v5.6, Dovecot 2.3.9, OpenSSL 1.1.1d, intermediate
|
||||||
|
# Generated 2021-08
|
||||||
|
# https://ssl-config.mozilla.org/
|
||||||
|
ssl = required
|
||||||
|
|
||||||
|
ssl_min_protocol = TLSv1.2
|
||||||
|
ssl_prefer_server_ciphers = no
|
||||||
|
|
||||||
|
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
|
||||||
Loading…
x
Reference in New Issue
Block a user