selfhostblocks/test/services/mailserver.nix
2026-05-24 01:37:52 +02:00

278 lines
8.4 KiB
Nix

{
lib,
pkgs,
shb,
...
}:
let
domain = "example.com";
subdomain = "imap";
fqdn = "${subdomain}.${domain}";
trySendMail = pkgs.writeShellApplication {
name = "trySendMail";
runtimeInputs = [
pkgs.swaks
];
text = ''
USER="''${1:-}"
PASSWORD="''${2:-}"
if [ -z "$USER" ]; then
echo "No user given"
exit 1
fi
if [ -z "$PASSWORD" ]; then
swaks \
--server ${fqdn} \
--port 465 \
--tls-on-connect \
--from "$USER" \
--to "$USER"
else
swaks \
--server ${fqdn} \
--port 465 \
--tls-on-connect \
--auth LOGIN \
--auth-user "$USER" \
--auth-password "$PASSWORD" \
--from "$USER" \
--to "$USER"
fi
'';
};
tryRcvMail = pkgs.writeShellApplication {
name = "tryRcvMail";
runtimeInputs = [
pkgs.curl
];
text = ''
USER="''${1:-}"
PASSWORD="''${2:-}"
if [ -z "$USER" ]; then
echo "No user given"
exit 1
fi
if [ -z "$PASSWORD" ]; then
echo "No password given"
exit 1
fi
curl -s --url "imaps://imap.example.com/INBOX;UID=1" \
--user "$USER:$PASSWORD"
'';
};
in
{
basic = shb.test.runNixOSTest {
name = "mailserver_basic";
# Connect to the running VM started with driverInteractive:
# ssh-keygen -R 'vsock-mux//run/user/1000/tmpcf3hs4yp/server_host.socket'; ssh -o User=root vsock-mux//run/user/1000/tmpcf3hs4yp/server_host.socket
interactive.sshBackdoor.enable = true;
nodes.server =
{ config, ... }:
{
imports = [
../../modules/blocks/ssl.nix
../../modules/services/mailserver.nix
];
networking.hosts = {
"127.0.0.1" = [ fqdn ];
};
shb.certs.cas.selfsigned.myca = {
name = "My CA";
};
shb.certs.certs.selfsigned = {
${domain} = {
ca = config.shb.certs.cas.selfsigned.myca;
domain = "*.${domain}";
group = "nginx";
};
};
shb.mailserver = {
enable = true;
inherit subdomain domain;
stateVersion = 4;
ssl = config.shb.certs.certs.selfsigned.${domain};
# imapSync = {
# # syncTimer = "10s";
# # debug = false;
# # accounts.fastmail = {
# # host = "imap.fastmail.com";
# # port = 993;
# # username = email;
# # password.result = config.shb.sops.secret."mailserver/imap/fastmail/password".result;
# # mapSpecialJunk = "Spam";
# # };
# };
# smtpRelay = {
# host = "smtp.fastmail.com";
# port = 587;
# username = email;
# password.result = config.shb.sops.secret."mailserver/smtp/fastmail/password".result;
# };
};
mailserver.mailboxes = {
Junk = {
auto = "subscribe";
special_use = "\\Junk";
};
};
environment.systemPackages = [
pkgs.openssl
pkgs.swaks
trySendMail
tryRcvMail
];
specialisation = {
ldap.configuration =
{ config, ... }:
{
imports = [
../../modules/blocks/hardcodedsecret.nix
../../modules/blocks/lldap.nix
];
networking.hosts = {
"127.0.0.1" = [ "ldap.${domain}" ];
};
environment.systemPackages = [
pkgs.openldap
];
shb.hardcodedsecret.ldapUserPassword = {
request = config.shb.lldap.ldapUserPassword.request;
settings.content = "ldapUserPassword";
};
shb.hardcodedsecret.jwtSecret = {
request = config.shb.lldap.jwtSecret.request;
settings.content = "jwtSecrets";
};
shb.hardcodedsecret.alice = {
request = config.shb.lldap.ensureUsers.alice.password.request;
settings.content = "alicePassword";
};
shb.hardcodedsecret.charlie = {
request = config.shb.lldap.ensureUsers.charlie.password.request;
settings.content = "charliePassword";
};
shb.lldap = {
enable = true;
inherit domain;
subdomain = "ldap";
ldapPort = 3890;
webUIListenPort = 17170;
dcdomain = "dc=example,dc=com";
ldapUserPassword.result = config.shb.hardcodedsecret.ldapUserPassword.result;
jwtSecret.result = config.shb.hardcodedsecret.jwtSecret.result;
debug = true;
ensureUsers = {
alice = {
email = "alice@example.com";
groups = [ "user_group" ];
password.result = config.shb.hardcodedsecret.alice.result;
};
charlie = {
email = "charlie@example.com";
groups = [ "other_group" ];
password.result = config.shb.hardcodedsecret.charlie.result;
};
};
ensureGroups = {
user_group = { };
admin_group = { };
other_group = { };
};
};
shb.mailserver = {
ldap = {
enable = true;
host = "127.0.0.1";
port = config.shb.lldap.ldapPort;
dcdomain = config.shb.lldap.dcdomain;
adminName = "admin";
adminPassword.result = config.shb.hardcodedsecret.ldapUserPassword.result;
account = "fastmail";
userGroup = "user_group";
};
};
};
};
};
testScript =
{ nodes, ... }:
let
specialisations = "${nodes.server.system.build.toplevel}/specialisation";
switch = name: "${specialisations}/${name}/bin/switch-to-configuration test";
ldapSearch =
let
config = nodes.server.specialisation.ldap.configuration;
in
lib.concatStringsSep " " [
"ldapsearch"
"-H ldap://ldap.example.com:${toString config.shb.lldap.ldapPort}"
];
ldapSearchAdmin =
let
config = nodes.server.specialisation.ldap.configuration;
in
lib.concatStringsSep " " [
"ldapsearch"
"-H ldap://ldap.example.com:${toString config.shb.lldap.ldapPort}"
"-D uid=admin,ou=people,${config.shb.lldap.dcdomain}"
"-w ${config.shb.hardcodedsecret.ldapUserPassword.settings.content}"
];
in
''
server.wait_for_unit("multi-user.target")
server.wait_for_unit("dovecot")
with subtest("ldap"):
server.succeed("${switch "ldap"}")
server.wait_for_unit("lldap")
print(server.succeed("${ldapSearch} -LLL -D uid=alice,ou=people,dc=example,dc=com -w alicePassword -b cn=user_group,ou=groups,dc=example,dc=com | grep uniquemember | grep alice"))
print(server.succeed("${ldapSearchAdmin} -LLL -b cn=user_group,ou=groups,dc=example,dc=com | grep uniquemember | grep alice"))
server.wait_for_unit("dovecot")
print(server.fail("doveadm auth test auser@example.com apassword"))
print(server.fail("doveadm auth test charlie@example.com charliePassword"))
print(server.succeed("doveadm auth test alice@example.com alicePassword"))
server.wait_for_unit("postfix")
print(server.fail("trySendMail alice@example.com"))
print(server.fail("trySendMail auser@example.com apassword"))
print(server.fail("trySendMail charlie@example.com charliePassword"))
print(server.succeed("trySendMail alice@example.com alicePassword"))
print(server.fail("tryRcvMail charlie@example.com charliePassword"))
print(server.succeed("tryRcvMail alice@example.com alicePassword"))
print(server.succeed("find /var/vmail"))
print(server.succeed("find /var/vmail/fastmail/alice@example.com"))
'';
};
}