Merge pull request #137260 from onny/maddy
This commit is contained in:
commit
29671bc365
@ -273,6 +273,13 @@
|
|||||||
<link xlink:href="options.html#opt-services.peertube.enable">services.peertube</link>.
|
<link xlink:href="options.html#opt-services.peertube.enable">services.peertube</link>.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</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>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
<link xlink:href="https://sr.ht">sourcehut</link>, a
|
<link xlink:href="https://sr.ht">sourcehut</link>, a
|
||||||
|
@ -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).
|
- [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).
|
- [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).
|
- [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).
|
||||||
|
@ -467,6 +467,7 @@
|
|||||||
./services/mail/dovecot.nix
|
./services/mail/dovecot.nix
|
||||||
./services/mail/dspam.nix
|
./services/mail/dspam.nix
|
||||||
./services/mail/exim.nix
|
./services/mail/exim.nix
|
||||||
|
./services/mail/maddy.nix
|
||||||
./services/mail/mail.nix
|
./services/mail/mail.nix
|
||||||
./services/mail/mailcatcher.nix
|
./services/mail/mailcatcher.nix
|
||||||
./services/mail/mailhog.nix
|
./services/mail/mailhog.nix
|
||||||
|
247
nixos/modules/services/mail/maddy.nix
Normal file
247
nixos/modules/services/mail/maddy.nix
Normal file
@ -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
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
@ -247,6 +247,7 @@ in
|
|||||||
lxd-image-server = handleTest ./lxd-image-server.nix {};
|
lxd-image-server = handleTest ./lxd-image-server.nix {};
|
||||||
#logstash = handleTest ./logstash.nix {};
|
#logstash = handleTest ./logstash.nix {};
|
||||||
lorri = handleTest ./lorri/default.nix {};
|
lorri = handleTest ./lorri/default.nix {};
|
||||||
|
maddy = handleTest ./maddy.nix {};
|
||||||
magic-wormhole-mailbox-server = handleTest ./magic-wormhole-mailbox-server.nix {};
|
magic-wormhole-mailbox-server = handleTest ./magic-wormhole-mailbox-server.nix {};
|
||||||
magnetico = handleTest ./magnetico.nix {};
|
magnetico = handleTest ./magnetico.nix {};
|
||||||
mailcatcher = handleTest ./mailcatcher.nix {};
|
mailcatcher = handleTest ./mailcatcher.nix {};
|
||||||
|
58
nixos/tests/maddy.nix
Normal file
58
nixos/tests/maddy.nix
Normal file
@ -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")
|
||||||
|
'';
|
||||||
|
})
|
@ -1,4 +1,4 @@
|
|||||||
{ lib, buildGoModule, fetchFromGitHub, coreutils, installShellFiles, scdoc }:
|
{ lib, buildGoModule, fetchFromGitHub, coreutils, installShellFiles, scdoc, nixosTests }:
|
||||||
|
|
||||||
buildGoModule rec {
|
buildGoModule rec {
|
||||||
pname = "maddy";
|
pname = "maddy";
|
||||||
@ -37,6 +37,8 @@ buildGoModule rec {
|
|||||||
--replace "/bin/kill" "${coreutils}/bin/kill"
|
--replace "/bin/kill" "${coreutils}/bin/kill"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
passthru.tests.nixos = nixosTests.maddy;
|
||||||
|
|
||||||
meta = with lib; {
|
meta = with lib; {
|
||||||
description = "Composable all-in-one mail server";
|
description = "Composable all-in-one mail server";
|
||||||
homepage = "https://maddy.email";
|
homepage = "https://maddy.email";
|
||||||
|
Loading…
Reference in New Issue
Block a user