1
0
mirror of https://github.com/Mailu/Mailu.git synced 2024-12-12 10:45:38 +02:00
3350: Feature: dkim for alternative domains r=mergify[bot] a=Jumper78

## What type of PR?

feature

## What does this PR do?

### General Idea

#### use same DKIM key of main domain for signing

Instead of dealing with key creation for each alternative domain, this implementation of the solution uses one key for all domains, the main domain and all alternative domains. Upon Rspamd requesting the DKIM key of a domain, it is not only checked if the domain is in the list of main domains, it also checked if it part of the alternative domains. If it is in this list, it sends the DKIM key of the connected main domain together with the name of the alternative domain.

#### show needed entries in the domain detailed view of the main domain

To make it easier for the admin to create the DKIM and DMARC entries (and the MX and SPF entries) for the alternative domains, we go through all alternative domains and print the entries.

### missing (and currently not planned to be added)

The zonefile at the top of the detail page will still only cover the main domain.

### Related issue(s)
- DKIM signing of the alternative domains is a requested feature; it closes 
- it keeps the original file based handling of DKIM keys; it does not implement 

## Prerequisites
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.

- [ ] 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: Jumper78 <52802286+Jumper78@users.noreply.github.com>
This commit is contained in:
bors-mailu[bot] 2024-08-11 16:42:45 +00:00 committed by GitHub
commit 2fca41235b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 94 additions and 0 deletions
core/admin/mailu
internal/views
models.py
translations/de/LC_MESSAGES
ui/templates/domain
towncrier/newsfragments

View File

@ -24,6 +24,15 @@ def rspamd_dkim_key(domain_name):
'selector': flask.current_app.config.get('DKIM_SELECTOR', 'dkim'),
}
)
elif domain := models.Alternative.query.get(domain_name):
if key := domain.domain.dkim_key:
selectors.append(
{
'domain' : domain.name,
'key' : key.decode('utf8'),
'selector': flask.current_app.config.get('DKIM_SELECTOR', 'dkim'),
}
)
return flask.jsonify({'data': {'selectors': selectors}})
@internal.route("/rspamd/local_domains", methods=['GET'])

View File

@ -355,6 +355,54 @@ class Alternative(Base):
domain = db.relationship(Domain,
backref=db.backref('alternatives', cascade='all, delete-orphan'))
@property
def dns_dkim(self):
""" return DKIM record for domain """
if self.domain.dkim_key:
selector = app.config['DKIM_SELECTOR']
return f'{selector}._domainkey.{self.name}. 600 IN TXT "v=DKIM1; k=rsa; p={self.domain.dkim_publickey}"'
@cached_property
def dns_dmarc(self):
""" return DMARC record for domain """
if self.domain.dkim_key:
domain = app.config['DOMAIN']
rua = app.config['DMARC_RUA']
rua = f' rua=mailto:{rua}@{domain};' if rua else ''
ruf = app.config['DMARC_RUF']
ruf = f' ruf=mailto:{ruf}@{domain};' if ruf else ''
return f'_dmarc.{self.name}. 600 IN TXT "v=DMARC1; p=reject;{rua}{ruf} adkim=s; aspf=s"'
@cached_property
def dns_dmarc_report(self):
""" return DMARC report record for mailu server """
if self.domain.dkim_key:
domain = app.config['DOMAIN']
return f'{self.name}._report._dmarc.{domain}. 600 IN TXT "v=DMARC1;"'
@cached_property
def dns_mx(self):
""" return MX record for domain """
hostname = app.config['HOSTNAME']
return f'{self.name}. 600 IN MX 10 {hostname}.'
@cached_property
def dns_spf(self):
""" return SPF record for domain """
hostname = app.config['HOSTNAME']
return f'{self.name}. 600 IN TXT "v=spf1 mx a:{hostname} ~all"'
def check_mx(self):
""" checks if MX record for domain points to mailu host """
try:
hostnames = set(app.config['HOSTNAMES'].split(','))
return any(
rset.exchange.to_text().rstrip('.') in hostnames
for rset in dns.resolver.resolve(self.name, 'MX')
)
except dns.exception.DNSException:
return False
class Relay(Base):
""" Relayed mail domain.

View File

@ -545,6 +545,10 @@ msgstr "DNS TLSA Eintrag"
msgid "DNS client auto-configuration entries"
msgstr "DNS Einträge für die automatische Client-Konfiguration"
#: mailu/ui/templates/domain/details.html:71
msgid "Alternative Domain name"
msgstr "Alternativer Domain Name"
#: mailu/ui/templates/domain/edit.html:4
msgid "Edit domain"
msgstr "Domain bearbeiten"

View File

@ -63,4 +63,36 @@
</pre></td>
</tr>
{%- endcall %}
{%- for alternative in domain.alternatives %}
{%- call macros.table(datatable=False) %}
<tr>
<th>{% trans %}Alternative Domain name{% endtrans %}</th>
<td>{{ alternative.name }}</td>
</tr>
<tr>
<th>{% trans %}DNS MX entry{% endtrans %} <i class="fa {{ 'fa-check-circle text-success' if alternative.check_mx() else 'fa-exclamation-circle text-danger' }}"></i></th>
<td>{{ macros.clip("dns_mx") }}<pre id="dns_mx" class="pre-config border bg-light">{{ alternative.dns_mx }}</pre></td>
</tr>
<tr>
<th>{% trans %}DNS SPF entries{% endtrans %}</th>
<td>{{ macros.clip("dns_spf") }}<pre id="dns_spf" class="pre-config border bg-light">{{ alternative.dns_spf }}</pre>
</td>
</tr>
{%- if alternative.domain.dkim_publickey %}
<tr>
<th>{% trans %}DNS DKIM entry{% endtrans %}</th>
<td>{{ macros.clip("dns_dkim") }}<pre id="dns_dkim" class="pre-config border bg-light">{{ alternative.dns_dkim }}</pre></td>
</tr>
<tr>
<th>{% trans %}DNS DMARC entry{% endtrans %}</th>
<td>
{{ macros.clip("dns_dmarc") }}<pre id="dns_dmarc" class="pre-config border bg-light">{{ alternative.dns_dmarc }}</pre>
{{ macros.clip("dns_dmarc_report") }}<pre id="dns_dmarc_report" class="pre-config border bg-light">{{ alternative.dns_dmarc_report }}</pre>
</td>
</tr>
{%- endif %}
{%- endcall %}
{%- endfor %}
{%- endblock %}

View File

@ -0,0 +1 @@
Add DKIM support for alternative domains: alternative domains use the same DKIM key as the main domain.