Skip to content

Commit

Permalink
nixos/maddy: Add module for maddy
Browse files Browse the repository at this point in the history
Co-authored-by: Patrick Hilhorst <[email protected]>
  • Loading branch information
onny and Synthetica9 committed Dec 7, 2021
1 parent 61123a6 commit ecd88f9
Show file tree
Hide file tree
Showing 7 changed files with 319 additions and 1 deletion.
7 changes: 7 additions & 0 deletions nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,13 @@
<link xlink:href="options.html#opt-services.peertube.enable">services.peertube</link>.
</para>
</listitem>
<listitem>
<para>
<link xlink:href="https://maddy.email">maddy</link>, a
composable all-in-one mail server. Available as
<link xlink:href="options.html#opt-services.maddy.enable">services.maddy</link>.
</para>
</listitem>
<listitem>
<para>
<link xlink:href="https://sr.ht">sourcehut</link>, a
Expand Down
2 changes: 2 additions & 0 deletions nixos/doc/manual/release-notes/rl-2111.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ In addition to numerous new and upgraded packages, this release has the followin

- [PeerTube](https://joinpeertube.org/), developed by Framasoft, is the free and decentralized alternative to video platforms. Available at [services.peertube](options.html#opt-services.peertube.enable).

- [maddy](https://maddy.email), a composable all-in-one mail server. Available as [services.maddy](options.html#opt-services.maddy.enable).

- [sourcehut](https://sr.ht), a collection of tools useful for software development. Available as [services.sourcehut](options.html#opt-services.sourcehut.enable).

- [ucarp](https://download.pureftpd.org/pub/ucarp/README), an userspace implementation of the Common Address Redundancy Protocol (CARP). Available as [networking.ucarp](options.html#opt-networking.ucarp.enable).
Expand Down
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@
./services/mail/dovecot.nix
./services/mail/dspam.nix
./services/mail/exim.nix
./services/mail/maddy.nix
./services/mail/mail.nix
./services/mail/mailcatcher.nix
./services/mail/mailhog.nix
Expand Down
247 changes: 247 additions & 0 deletions nixos/modules/services/mail/maddy.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
{ config, lib, pkgs, ... }:

with lib;

let
name = "maddy";
cfg = config.services.maddy;
defaultConfig = ''
tls off
auth.pass_table local_authdb {
table sql_table {
driver sqlite3
dsn credentials.db
table_name passwords
}
}
storage.imapsql local_mailboxes {
driver sqlite3
dsn imapsql.db
}
table.chain local_rewrites {
optional_step regexp "(.+)\+(.+)@(.+)" "$1@$3"
optional_step static {
entry postmaster postmaster@$(primary_domain)
}
optional_step file /etc/maddy/aliases
}
msgpipeline local_routing {
destination postmaster $(local_domains) {
modify {
replace_rcpt &local_rewrites
}
deliver_to &local_mailboxes
}
default_destination {
reject 550 5.1.1 "User doesn't exist"
}
}
smtp tcp://0.0.0.0:25 {
limits {
all rate 20 1s
all concurrency 10
}
dmarc yes
check {
require_mx_record
dkim
spf
}
source $(local_domains) {
reject 501 5.1.8 "Use Submission for outgoing SMTP"
}
default_source {
destination postmaster $(local_domains) {
deliver_to &local_routing
}
default_destination {
reject 550 5.1.1 "User doesn't exist"
}
}
}
submission tcp://0.0.0.0:587 {
limits {
all rate 50 1s
}
auth &local_authdb
source $(local_domains) {
check {
authorize_sender {
prepare_email &local_rewrites
user_to_email identity
}
}
destination postmaster $(local_domains) {
deliver_to &local_routing
}
default_destination {
modify {
dkim $(primary_domain) $(local_domains) default
}
deliver_to &remote_queue
}
}
default_source {
reject 501 5.1.8 "Non-local sender domain"
}
}
target.remote outbound_delivery {
limits {
destination rate 20 1s
destination concurrency 10
}
mx_auth {
dane
mtasts {
cache fs
fs_dir mtasts_cache/
}
local_policy {
min_tls_level encrypted
min_mx_level none
}
}
}
target.queue remote_queue {
target &outbound_delivery
autogenerated_msg_domain $(primary_domain)
bounce {
destination postmaster $(local_domains) {
deliver_to &local_routing
}
default_destination {
reject 550 5.0.0 "Refusing to send DSNs to non-local addresses"
}
}
}
imap tcp://0.0.0.0:143 {
auth &local_authdb
storage &local_mailboxes
}
'';

in {
options = {
services.maddy = {
enable = mkEnableOption "Maddy, a free an open source mail server";

user = mkOption {
default = "maddy";
type = with types; uniq string;
description = ''
Name of the user under which maddy will run. If not specified, a
default user will be created.
'';
};
group = mkOption {
default = "maddy";
type = with types; uniq string;
description = ''
Name of the group under which maddy will run. If not specified, a
default group will be created.
'';
};

hostname = mkOption {
default = "localhost";
type = with types; uniq string;
example = ''example.com'';
description = ''
Hostname to use. It should be FQDN.
'';
};
primaryDomain = mkOption {
default = "localhost";
type = with types; uniq string;
example = ''mail.example.com'';
description = ''
Primary MX domain to use. It should be FQDN.
'';
};
localDomains = mkOption {
type = with types; listOf str;
default = ["$(primary_domain)"];
example = [
"$(primary_domain)"
"example.com"
"other.example.com"
];
description = ''
Define list of allowed domains.
'';
};
config = mkOption {
type = with types; nullOr lines;
default = defaultConfig;
description = ''
Server configuration.
'';
};

openFirewall = mkOption {
type = types.bool;
default = false;
description = ''
Open the configured incoming and outgoing mail server ports.
'';
};

};
};

config = mkIf cfg.enable {

systemd = {
packages = [ pkgs.maddy ];
services.maddy = {
serviceConfig = {
User = "${cfg.user}";
Group = "${cfg.group}";
};
wantedBy = [ "multi-user.target" ];
};
};

environment.etc."maddy/maddy.conf" = {
text = ''
$(hostname) = ${cfg.hostname}
$(primary_domain) = ${cfg.primaryDomain}
$(local_domains) = ${toString cfg.localDomains}
hostname ${cfg.hostname}
${cfg.config}
'';
};

users.users = optionalAttrs (cfg.user == "maddy") {
maddy = {
description = "Maddy service user";
group = cfg.group;
home = "/var/lib/maddy";
createHome = true;
isSystemUser = true;
};
};

users.groups = mkIf (cfg.group == "maddy") {
maddy = pkgs.lib.mkForce {
name = cfg.group;
};
};

networking.firewall = mkIf cfg.openFirewall {
allowedTCPPorts = [ 25 143 587 ];
};

environment.systemPackages = [
pkgs.maddy
];
};
}
1 change: 1 addition & 0 deletions nixos/tests/all-tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ in
lxd-image-server = handleTest ./lxd-image-server.nix {};
#logstash = handleTest ./logstash.nix {};
lorri = handleTest ./lorri/default.nix {};
maddy = handleTest ./maddy.nix {};
magic-wormhole-mailbox-server = handleTest ./magic-wormhole-mailbox-server.nix {};
magnetico = handleTest ./magnetico.nix {};
mailcatcher = handleTest ./mailcatcher.nix {};
Expand Down
58 changes: 58 additions & 0 deletions nixos/tests/maddy.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import ./make-test-python.nix ({ pkgs, ... }: {
name = "maddy";
meta = with pkgs.lib.maintainers; { maintainers = [ onny ]; };

nodes = {
server = { ... }: {
services.maddy = {
enable = true;
hostname = "server";
primaryDomain = "server";
openFirewall = true;
};
};

client = { ... }: {
environment.systemPackages = [
(pkgs.writers.writePython3Bin "send-testmail" { } ''
import smtplib
from email.mime.text import MIMEText
msg = MIMEText("Hello World")
msg['Subject'] = 'Test'
msg['From'] = "postmaster@server"
msg['To'] = "postmaster@server"
with smtplib.SMTP('server', 587) as smtp:
smtp.login('postmaster@server', 'test')
smtp.sendmail('postmaster@server', 'postmaster@server', msg.as_string())
'')
(pkgs.writers.writePython3Bin "test-imap" { } ''
import imaplib
with imaplib.IMAP4('server') as imap:
imap.login('postmaster@server', 'test')
imap.select()
status, refs = imap.search(None, 'ALL')
assert status == 'OK'
assert len(refs) == 1
status, msg = imap.fetch(refs[0], 'BODY[TEXT]')
assert status == 'OK'
assert msg[0][1].strip() == b"Hello World"
'')
];
};
};

testScript = ''
start_all()
server.wait_for_unit("maddy.service")
server.wait_for_open_port(143)
server.wait_for_open_port(587)
server.succeed("echo test | maddyctl creds create postmaster@server")
server.succeed("maddyctl imap-acct create postmaster@server")
client.succeed("send-testmail")
client.succeed("test-imap")
'';
})
4 changes: 3 additions & 1 deletion pkgs/servers/maddy/default.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{ lib, buildGoModule, fetchFromGitHub, coreutils, installShellFiles, scdoc }:
{ lib, buildGoModule, fetchFromGitHub, coreutils, installShellFiles, scdoc, nixosTests }:

buildGoModule rec {
pname = "maddy";
Expand Down Expand Up @@ -37,6 +37,8 @@ buildGoModule rec {
--replace "/bin/kill" "${coreutils}/bin/kill"
'';

passthru.tests.nixos = nixosTests.maddy;

meta = with lib; {
description = "Composable all-in-one mail server";
homepage = "https://maddy.email";
Expand Down

0 comments on commit ecd88f9

Please sign in to comment.