Skip to content

Commit f3743a8

Browse files
committed
Add support for mailing lists with login accounts
With this change, mailing lists can now have associated login accounts, which allow folks to send emails via SMTP from a `@nixos.org` email address. This completes NixOS#510 I opted to keep dovecot around. We're not using it for IMAP, just for `SASL` authentication. It would have requires some (brittle) overriding of settings from `nixos-mailserver` to get rid of dovecot. If we really want to get rid of dovecot someday, I believe we should try to add an option upstream in `simple-nixos-mailserver`.
1 parent 8edb8de commit f3743a8

File tree

10 files changed

+360
-176
lines changed

10 files changed

+360
-176
lines changed

non-critical-infra/flake-module.nix

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
pkgs = inputs'.nixpkgs-unstable.legacyPackages;
5757
in
5858
{
59-
packages.encrypt-email-address = pkgs.callPackage ./packages/encrypt-email-address { };
59+
packages.encrypt-email = pkgs.callPackage ./packages/encrypt-email { };
6060

6161
devShells.non-critical-infra = pkgs.mkShellNoCC {
6262
packages = [

non-critical-infra/modules/mailserver/README.md

+3-6
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ This module will [eventually][issue 485] provide mail services for `nixos.org`.
55
## Mailing lists
66

77
To create a new mailing list, or change membership of a mailing list, see the
8-
instructions at the top of [`mailing-lists.nix`](./mailing-lists.nix).
8+
instructions under `### Mailing lists go here ###` in [`default.nix`](./default.nix).
99

10-
## Sending mail
11-
12-
This module does not yet provide SMTP login.
13-
14-
[issue 485]: https://github.com/NixOS/infra/issues/485
10+
Some mailing lists allow login and sending email via `SMTP`. Search for
11+
`loginAccount` to find examples of this.

non-critical-infra/modules/mailserver/default.nix

+22-4
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,31 @@
77
enable = true;
88
certificateScheme = "acme-nginx";
99

10-
# Until we have login accounts, there's no reason to run either of these.
11-
enablePop3 = false;
12-
enableImap = false;
13-
1410
fqdn = config.networking.fqdn;
1511

1612
# TODO: change to `nixos.org` when ready
1713
domains = [ "mail-test.nixos.org" ];
1814
};
15+
16+
### Mailing lists go here ###
17+
# If you wish to hide your email address, you can encrypt it with SOPS. Just
18+
# run `nix run .#encrypt-email address -- --help` and follow the instructions.
19+
#
20+
# If you wish to set up a login account for sending email, you must generate
21+
# an encrypted password. Run `nix run .#encrypt-email login -- --help` and
22+
# follow the instructions.
23+
mailing-lists = {
24+
# TODO: replace with the real `nixos.org` mailing lists.
25+
26+
forwardTo = [
27+
28+
../../secrets/jfly-email-address.umbriel
29+
30+
];
31+
};
32+
33+
forwardTo = [ "[email protected]" ];
34+
loginAccount.encryptedHashedPassword = ../../secrets/test-sender-email-login.umbriel;
35+
};
36+
};
1937
}
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,134 @@
1-
# This module provides the mailing list definitions for `@nixos.org`.
1+
# This module makes it easy to define mailing lists in `simple-nixos-mailserver`
2+
# with a couple of features:
23
#
3-
# Simply change the `lists` attribute set below to create new mailing lists or
4-
# edit membership of existing lists.
5-
#
6-
# If you wish to hide your email address, you can encrypt it with SOPS. Just
7-
# run `nix run .#encrypt-email-address -- --help` and follow the instructions.
4+
# 1. We can (optionally) encrypt the forward addresses for increase privacy.
5+
# 2. We can set up a login account for mailing addresses to allow sending
6+
# email via `SMTP` from those addresses.
87

98
{ config, lib, ... }:
109

1110
let
12-
# Mailing lists go here.
13-
# TODO: replace with the real `nixos.org` mailing lists.
14-
listsWithSecretFiles = {
15-
16-
17-
../../secrets/jfly-email.umbriel
18-
19-
];
20-
};
11+
inherit (lib) types;
2112

2213
fileToSecretId = file: builtins.baseNameOf file;
2314

24-
listsWithSecretPlaceholders = lib.mapAttrs' (name: members: {
15+
listsWithSecretPlaceholders = lib.mapAttrs' (name: mailingList: {
2516
name = name;
2617
value = map (
2718
member:
2819
if builtins.isString member then member else config.sops.placeholder.${fileToSecretId member}
29-
) members;
30-
}) listsWithSecretFiles;
20+
) mailingList.forwardTo;
21+
}) config.mailing-lists;
3122

32-
secretFiles = lib.pipe listsWithSecretFiles [
33-
(lib.mapAttrsToList (_name: members: members))
23+
secretAddressFiles = lib.pipe config.mailing-lists [
24+
(lib.mapAttrsToList (_name: mailingList: mailingList.forwardTo))
3425
lib.flatten
3526
(builtins.filter (member: !builtins.isString member))
3627
];
28+
29+
secretPasswordFiles = lib.pipe config.mailing-lists [
30+
(lib.filterAttrs (_name: mailingList: mailingList.loginAccount != null))
31+
(lib.mapAttrsToList (_name: mailingList: mailingList.loginAccount.encryptedHashedPassword))
32+
];
3733
in
3834

3935
{
40-
# Declare secrets for every secret email in the lists above.
41-
sops.secrets = builtins.listToAttrs (
42-
map (file: {
43-
name = fileToSecretId file;
44-
value = {
45-
format = "binary";
46-
sopsFile = file;
47-
};
48-
}) secretFiles
49-
);
36+
options = {
37+
mailing-lists = lib.mkOption {
38+
type = types.attrsOf (
39+
types.submodule {
40+
options = {
41+
forwardTo = lib.mkOption {
42+
type = types.listOf (types.either types.str types.path);
43+
description = ''
44+
Either a plaintext email address, or a path to an email address
45+
encrypted with `nix run .#encrypt-email address`
46+
'';
47+
};
48+
loginAccount = lib.mkOption {
49+
type = types.nullOr (
50+
types.submodule {
51+
options = {
52+
encryptedHashedPassword = lib.mkOption {
53+
type = types.path;
54+
description = ''
55+
If specified, this enables sending emails from this address via SMTP.
56+
Must be a path to encrypted file generated with `nix run .#encrypt-email login`
57+
'';
58+
};
59+
};
60+
}
61+
);
62+
default = null;
63+
};
64+
};
65+
}
66+
);
67+
description = ''
68+
Mailing lists. Supports both forward-only mailing lists, as well as mailing
69+
lists that allow sending via SMTP.
70+
'';
71+
};
72+
};
5073

51-
sops.templates."postfix-virtual-mailing-lists" = {
52-
content = lib.concatStringsSep "\n" (
53-
lib.mapAttrsToList (
54-
name: members: "${name} ${lib.concatStringsSep ", " members}"
55-
) listsWithSecretPlaceholders
74+
config = {
75+
# Disable IMAP. We don't need it, as we don't store email on this server, we
76+
# only forward emails.
77+
mailserver.enableImap = false;
78+
mailserver.enableImapSsl = false;
79+
services.dovecot2.enableImap = false;
80+
81+
mailserver.loginAccounts = lib.pipe config.mailing-lists [
82+
(lib.filterAttrs (_name: mailingList: mailingList.loginAccount != null))
83+
(lib.mapAttrs (
84+
_name: mailingList: {
85+
hashedPasswordFile =
86+
config.sops.secrets.${fileToSecretId mailingList.loginAccount.encryptedHashedPassword}.path;
87+
}
88+
))
89+
];
90+
91+
# Declare secrets for every secret file.
92+
sops.secrets = builtins.listToAttrs (
93+
(map (file: {
94+
name = fileToSecretId file;
95+
value = {
96+
format = "binary";
97+
sopsFile = file;
98+
};
99+
}) secretAddressFiles)
100+
++ (map (file: {
101+
name = fileToSecretId file;
102+
value = {
103+
format = "binary";
104+
sopsFile = file;
105+
# Need to restart `dovecot2.service` to trigger `genPasswdScript` in
106+
# `nixos-mailserver`:
107+
# https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/blob/af7d3bf5daeba3fc28089b015c0dd43f06b176f2/mail-server/dovecot.nix#L369
108+
# This could go away if sops-nix gets support for "input addressed secret
109+
# paths": https://github.com/Mic92/sops-nix/issues/648
110+
restartUnits = [ "dovecot2.service" ];
111+
};
112+
}) secretPasswordFiles)
56113
);
57114

58-
# Need to restart postfix-setup to rerun `postmap` and generate updated `.db`
59-
# files whenever mailing list membership changes.
60-
# This could go away if sops-nix gets support for "input addressed secret
61-
# paths": https://github.com/Mic92/sops-nix/issues/648
62-
restartUnits = [ "postfix-setup.service" ];
63-
};
115+
sops.templates."postfix-virtual-mailing-lists" = {
116+
content = lib.concatStringsSep "\n" (
117+
lib.mapAttrsToList (
118+
name: members: "${name} ${lib.concatStringsSep ", " members}"
119+
) listsWithSecretPlaceholders
120+
);
121+
122+
# Need to restart postfix-setup to rerun `postmap` and generate updated `.db`
123+
# files whenever mailing list membership changes.
124+
# This could go away if sops-nix gets support for "input addressed secret
125+
# paths": https://github.com/Mic92/sops-nix/issues/648
126+
restartUnits = [ "postfix-setup.service" ];
127+
};
64128

65-
services.postfix.mapFiles.virtual-mailing-lists =
66-
config.sops.templates."postfix-virtual-mailing-lists".path;
129+
services.postfix.mapFiles.virtual-mailing-lists =
130+
config.sops.templates."postfix-virtual-mailing-lists".path;
67131

68-
services.postfix.config.virtual_alias_maps = [ "hash:/etc/postfix/virtual-mailing-lists" ];
132+
services.postfix.config.virtual_alias_maps = [ "hash:/etc/postfix/virtual-mailing-lists" ];
133+
};
69134
}

non-critical-infra/packages/encrypt-email-address/default.nix

-20
This file was deleted.

non-critical-infra/packages/encrypt-email-address/encrypt-email-address.py

-101
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
lib,
3+
mkpasswd,
4+
python3,
5+
sops,
6+
}:
7+
8+
python3.pkgs.buildPythonApplication {
9+
name = "encrypt-email";
10+
src = ./.;
11+
12+
format = "other";
13+
14+
propagatedBuildInputs = [ python3.pkgs.click ];
15+
16+
installPhase = ''
17+
mkdir -p $out/bin
18+
mv ./encrypt-email.py $out/bin/encrypt-email
19+
wrapProgram $out/bin/encrypt-email --prefix PATH : ${
20+
lib.makeBinPath [
21+
sops
22+
mkpasswd
23+
]
24+
}
25+
'';
26+
}

0 commit comments

Comments
 (0)