Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test-and-deploy-ipv4only.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ jobs:

- name: set DNS entries
run: |
ssh -o StrictHostKeyChecking=accept-new -v root@staging-ipv4.testrun.org chown opendkim:opendkim -R /etc/dkimkeys
ssh -o StrictHostKeyChecking=accept-new -v root@staging-ipv4.testrun.org chown dkim-milter:dkim-milter -R /etc/dkimkeys
cmdeploy dns --zonefile staging-generated.zone
cat staging-generated.zone >> .github/workflows/staging-ipv4.testrun.org-default.zone
cat .github/workflows/staging-ipv4.testrun.org-default.zone
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-and-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ jobs:

- name: set DNS entries
run: |
ssh -o StrictHostKeyChecking=accept-new root@staging2.testrun.org chown opendkim:opendkim -R /etc/dkimkeys
ssh -o StrictHostKeyChecking=accept-new root@staging2.testrun.org chown dkim-milter:dkim-milter -R /etc/dkimkeys
cmdeploy dns --zonefile staging-generated.zone --verbose
cat staging-generated.zone >> .github/workflows/staging.testrun.org-default.zone
cat .github/workflows/staging.testrun.org-default.zone
Expand Down
4 changes: 2 additions & 2 deletions cmdeploy/src/cmdeploy/deployers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@
configure_remote_units,
get_resource,
)
from .dkim_milter.deployer import DkimMilterDeployer
from .dovecot.deployer import DovecotDeployer
from .filtermail.deployer import FiltermailDeployer
from .mtail.deployer import MtailDeployer
from .nginx.deployer import NginxDeployer
from .opendkim.deployer import OpendkimDeployer
from .postfix.deployer import PostfixDeployer
from .www import build_webpages, find_merge_conflict, get_paths

Expand Down Expand Up @@ -572,7 +572,7 @@ def deploy_chatmail(config_path: Path, disable_mail: bool, website_only: bool) -
WebsiteDeployer(config),
ChatmailVenvDeployer(config),
MtastsDeployer(),
OpendkimDeployer(mail_domain),
DkimMilterDeployer(mail_domain),
# Dovecot should be started before Postfix
# because it creates authentication socket
# required by Postfix.
Expand Down
169 changes: 169 additions & 0 deletions cmdeploy/src/cmdeploy/dkim_milter/deployer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
"""
Installs DKIM Milter.
"""

from pyinfra import facts, host
from pyinfra.facts.files import File, Sha256File
from pyinfra.operations import apt, files, server, systemd

from cmdeploy.basedeploy import Deployer, get_resource


class DkimMilterDeployer(Deployer):
required_users = [("dkim-milter", None, ["dkim-milter"])]

def __init__(self, mail_domain):
self.mail_domain = mail_domain
self.need_restart = False

def install(self):
"""Builds and installs dkim-milter"""

# openssl is required to generate the signing key
apt.packages(
name="Install openssl required by DKIM Milter",
packages=["openssl"],
)

(url, sha256sum) = {
"x86_64": (
"https://github.com/chatmail/dkim-milter/releases/download/0.1.0/dkim-milter-x86_64",
"e676837b362ebef461881079e3e1151ed2db2d942d98b7103974921ac69ce5de",
),
"aarch64": (
"https://github.com/chatmail/dkim-milter/releases/download/0.1.0/dkim-milter-aarch64",
"b853ab85a535b7e7e548ae0e4d85a61d4c0fd44f2912c3439662c56ca8a369e6",
),
}[host.get_fact(facts.server.Arch)]

existing_sha256sum = host.get_fact(Sha256File, "/usr/local/sbin/dkim-milter")
if existing_sha256sum != sha256sum:
server.shell(
name="Download DKIM Milter",
commands=[
f"(curl -L {url} >/usr/local/sbin/dkim-milter.new && (echo '{sha256sum} /usr/local/sbin/dkim-milter.new' | sha256sum -c) && mv /usr/local/sbin/dkim-milter.new /usr/local/sbin/dkim-milter)",
"chmod 755 /usr/local/sbin/dkim-milter",
],
)
self.need_restart = True

def configure(self):
"""Configures dkim-milter"""

domain = self.mail_domain
# note - we are using "opendkim" for backward compatibility
# for relays that were set up before we migrated from OpenDKIM
# to DKIM Milter.
selector = "opendkim"
signing_key_name = selector
# for backward compatibility with opendkim-genkey
signing_key_filename = f"{signing_key_name}.private"
config_common = {
"domain": domain,
"selector": selector,
"signing_key_name": signing_key_name,
"signing_key_filename": signing_key_filename,
}
config_verify = {
**config_common,
"mode": "verify",
"config_file": "/etc/dkim-milter/dkim-milter-verify.conf",
"socket_name": "dkim-milter-verify.sock",
}
config_sign = {
**config_common,
"mode": "sign",
"config_file": "/etc/dkim-milter/dkim-milter-sign.conf",
"socket_name": "dkim-milter-sign.sock",
}

self.need_restart |= files.directory(
name="Create a directory for DKIM Milter configs",
path="/etc/dkim-milter",
user="dkim-milter",
group="dkim-milter",
mode="750",
present=True,
).changed

for config in [config_verify, config_sign]:
self.need_restart |= files.template(
src=get_resource("dkim_milter/dkim-milter.conf.j2"),
dest=config["config_file"],
user="dkim-milter",
group="dkim-milter",
mode="644",
config=config,
).changed

self.need_restart |= files.directory(
name="Create dkimkeys directory",
path="/etc/dkimkeys",
user="dkim-milter",
group="dkim-milter",
mode="750",
present=True,
).changed

self.need_restart |= files.template(
src=get_resource("dkim_milter/signing-keys"),
dest="/etc/dkim-milter/signing-keys",
user="dkim-milter",
group="dkim-milter",
mode="644",
config=config_common,
).changed

self.need_restart |= files.template(
src=get_resource("dkim_milter/signing-senders"),
dest="/etc/dkim-milter/signing-senders",
user="dkim-milter",
group="dkim-milter",
mode="644",
config=config_common,
).changed

self.need_restart |= files.directory(
name="Create DKIM Milter unix sockets directory",
path="/var/spool/postfix/dkim-milter",
user="dkim-milter",
group="dkim-milter",
mode="770",
).changed

if not host.get_fact(File, f"/etc/dkimkeys/{signing_key_filename}"):
server.shell(
name=f"Generate DKIM Milter signing key '{signing_key_name}'",
commands=[
f"openssl genpkey -algorithm RSA -out /etc/dkimkeys/{signing_key_filename}"
],
)
self.need_restart = True

# enforce restrictive permissions for the signing key
self.need_restart |= files.file(
path=f"/etc/dkimkeys/{signing_key_filename}",
present=True,
user="dkim-milter",
group="dkim-milter",
mode="0400",
).changed

self.need_restart |= files.put(
name="Create dkim-milter service",
src=get_resource("dkim_milter/dkim-milter@.service"),
dest=f"/etc/systemd/system/dkim-milter@.service",
).changed

def activate(self):
"""Start and enable DKIM Milter"""
for mode in ["sign", "verify"]:
systemd.service(
name=f"Start and enable DKIM Milter in {mode} mode",
service=f"dkim-milter@{mode}",
running=True,
enabled=True,
daemon_reload=self.need_restart,
restarted=self.need_restart,
)
self.need_restart = False
30 changes: 30 additions & 0 deletions cmdeploy/src/cmdeploy/dkim_milter/dkim-milter.conf.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
mode = {{ config.mode }}

{% if config.mode == "verify" %}
# DKIM milter will skip verification for trusted sources,
# which in our case is everything, since we run DKIM milter on a reinjection port,
# and all connections are local.
# We force verification for local connections by not trusting anyone.
trusted_networks =
{% endif %}

log_destination = syslog
log_level = info

canonicalization = relaxed/simple

lookup_timeout = 60s

signing_keys = /etc/dkim-milter/signing-keys
signing_senders = /etc/dkim-milter/signing-senders

# Signing
sign_headers = default; autocrypt:content-type
oversign_headers = signed-extended

# Verification
required_signed_headers = From*
forbid_unsigned_content = yes
reject_failures = missing, no-pass, author-mismatch

socket = unix:/var/spool/postfix/dkim-milter/{{ config.socket_name }}
15 changes: 15 additions & 0 deletions cmdeploy/src/cmdeploy/dkim_milter/dkim-milter@.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[Unit]
Description=DKIM Milter %i
Documentation=man:dkim-milter(8) man:dkim-milter.conf(5)
After=network-online.target nss-lookup.target
Wants=network-online.target

[Service]
User=dkim-milter
UMask=007
ExecStart=/usr/local/sbin/dkim-milter -c /etc/dkim-milter/dkim-milter-%i.conf
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure

[Install]
WantedBy=multi-user.target
2 changes: 2 additions & 0 deletions cmdeploy/src/cmdeploy/dkim_milter/signing-keys
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Key name Signing key
{{ config.signing_key_name }} </etc/dkimkeys/{{ config.signing_key_filename }}
2 changes: 2 additions & 0 deletions cmdeploy/src/cmdeploy/dkim_milter/signing-senders
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Sender expression Domain Selector Key name
.{{ config.domain }} {{ config.domain }} {{ config.selector }} {{ config.signing_key_name }}
1 change: 0 additions & 1 deletion cmdeploy/src/cmdeploy/opendkim/KeyTable

This file was deleted.

1 change: 0 additions & 1 deletion cmdeploy/src/cmdeploy/opendkim/SigningTable

This file was deleted.

123 changes: 0 additions & 123 deletions cmdeploy/src/cmdeploy/opendkim/deployer.py

This file was deleted.

Loading
Loading