Skip to content

Commit

Permalink
Add support for mailing lists with login accounts
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
jfly committed Nov 10, 2024
1 parent 8edb8de commit f3743a8
Show file tree
Hide file tree
Showing 10 changed files with 360 additions and 176 deletions.
2 changes: 1 addition & 1 deletion non-critical-infra/flake-module.nix
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
pkgs = inputs'.nixpkgs-unstable.legacyPackages;
in
{
packages.encrypt-email-address = pkgs.callPackage ./packages/encrypt-email-address { };
packages.encrypt-email = pkgs.callPackage ./packages/encrypt-email { };

devShells.non-critical-infra = pkgs.mkShellNoCC {
packages = [
Expand Down
9 changes: 3 additions & 6 deletions non-critical-infra/modules/mailserver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ This module will [eventually][issue 485] provide mail services for `nixos.org`.
## Mailing lists

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

## Sending mail

This module does not yet provide SMTP login.

[issue 485]: https://github.com/NixOS/infra/issues/485
Some mailing lists allow login and sending email via `SMTP`. Search for
`loginAccount` to find examples of this.
26 changes: 22 additions & 4 deletions non-critical-infra/modules/mailserver/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,31 @@
enable = true;
certificateScheme = "acme-nginx";

# Until we have login accounts, there's no reason to run either of these.
enablePop3 = false;
enableImap = false;

fqdn = config.networking.fqdn;

# TODO: change to `nixos.org` when ready
domains = [ "mail-test.nixos.org" ];
};

### Mailing lists go here ###
# If you wish to hide your email address, you can encrypt it with SOPS. Just
# run `nix run .#encrypt-email address -- --help` and follow the instructions.
#
# If you wish to set up a login account for sending email, you must generate
# an encrypted password. Run `nix run .#encrypt-email login -- --help` and
# follow the instructions.
mailing-lists = {
# TODO: replace with the real `nixos.org` mailing lists.
"[email protected]" = {
forwardTo = [
"[email protected]"
../../secrets/jfly-email-address.umbriel
"[email protected]"
];
};
"[email protected]" = {
forwardTo = [ "[email protected]" ];
loginAccount.encryptedHashedPassword = ../../secrets/test-sender-email-login.umbriel;
};
};
}
153 changes: 109 additions & 44 deletions non-critical-infra/modules/mailserver/mailing-lists.nix
Original file line number Diff line number Diff line change
@@ -1,69 +1,134 @@
# This module provides the mailing list definitions for `@nixos.org`.
# This module makes it easy to define mailing lists in `simple-nixos-mailserver`
# with a couple of features:
#
# Simply change the `lists` attribute set below to create new mailing lists or
# edit membership of existing lists.
#
# If you wish to hide your email address, you can encrypt it with SOPS. Just
# run `nix run .#encrypt-email-address -- --help` and follow the instructions.
# 1. We can (optionally) encrypt the forward addresses for increase privacy.
# 2. We can set up a login account for mailing addresses to allow sending
# email via `SMTP` from those addresses.

{ config, lib, ... }:

let
# Mailing lists go here.
# TODO: replace with the real `nixos.org` mailing lists.
listsWithSecretFiles = {
"[email protected]" = [
"[email protected]"
../../secrets/jfly-email.umbriel
"[email protected]"
];
};
inherit (lib) types;

fileToSecretId = file: builtins.baseNameOf file;

listsWithSecretPlaceholders = lib.mapAttrs' (name: members: {
listsWithSecretPlaceholders = lib.mapAttrs' (name: mailingList: {
name = name;
value = map (
member:
if builtins.isString member then member else config.sops.placeholder.${fileToSecretId member}
) members;
}) listsWithSecretFiles;
) mailingList.forwardTo;
}) config.mailing-lists;

secretFiles = lib.pipe listsWithSecretFiles [
(lib.mapAttrsToList (_name: members: members))
secretAddressFiles = lib.pipe config.mailing-lists [
(lib.mapAttrsToList (_name: mailingList: mailingList.forwardTo))
lib.flatten
(builtins.filter (member: !builtins.isString member))
];

secretPasswordFiles = lib.pipe config.mailing-lists [
(lib.filterAttrs (_name: mailingList: mailingList.loginAccount != null))
(lib.mapAttrsToList (_name: mailingList: mailingList.loginAccount.encryptedHashedPassword))
];
in

{
# Declare secrets for every secret email in the lists above.
sops.secrets = builtins.listToAttrs (
map (file: {
name = fileToSecretId file;
value = {
format = "binary";
sopsFile = file;
};
}) secretFiles
);
options = {
mailing-lists = lib.mkOption {
type = types.attrsOf (
types.submodule {
options = {
forwardTo = lib.mkOption {
type = types.listOf (types.either types.str types.path);
description = ''
Either a plaintext email address, or a path to an email address
encrypted with `nix run .#encrypt-email address`
'';
};
loginAccount = lib.mkOption {
type = types.nullOr (
types.submodule {
options = {
encryptedHashedPassword = lib.mkOption {
type = types.path;
description = ''
If specified, this enables sending emails from this address via SMTP.
Must be a path to encrypted file generated with `nix run .#encrypt-email login`
'';
};
};
}
);
default = null;
};
};
}
);
description = ''
Mailing lists. Supports both forward-only mailing lists, as well as mailing
lists that allow sending via SMTP.
'';
};
};

sops.templates."postfix-virtual-mailing-lists" = {
content = lib.concatStringsSep "\n" (
lib.mapAttrsToList (
name: members: "${name} ${lib.concatStringsSep ", " members}"
) listsWithSecretPlaceholders
config = {
# Disable IMAP. We don't need it, as we don't store email on this server, we
# only forward emails.
mailserver.enableImap = false;
mailserver.enableImapSsl = false;
services.dovecot2.enableImap = false;

mailserver.loginAccounts = lib.pipe config.mailing-lists [
(lib.filterAttrs (_name: mailingList: mailingList.loginAccount != null))
(lib.mapAttrs (
_name: mailingList: {
hashedPasswordFile =
config.sops.secrets.${fileToSecretId mailingList.loginAccount.encryptedHashedPassword}.path;
}
))
];

# Declare secrets for every secret file.
sops.secrets = builtins.listToAttrs (
(map (file: {
name = fileToSecretId file;
value = {
format = "binary";
sopsFile = file;
};
}) secretAddressFiles)
++ (map (file: {
name = fileToSecretId file;
value = {
format = "binary";
sopsFile = file;
# Need to restart `dovecot2.service` to trigger `genPasswdScript` in
# `nixos-mailserver`:
# https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/blob/af7d3bf5daeba3fc28089b015c0dd43f06b176f2/mail-server/dovecot.nix#L369
# This could go away if sops-nix gets support for "input addressed secret
# paths": https://github.com/Mic92/sops-nix/issues/648
restartUnits = [ "dovecot2.service" ];
};
}) secretPasswordFiles)
);

# Need to restart postfix-setup to rerun `postmap` and generate updated `.db`
# files whenever mailing list membership changes.
# This could go away if sops-nix gets support for "input addressed secret
# paths": https://github.com/Mic92/sops-nix/issues/648
restartUnits = [ "postfix-setup.service" ];
};
sops.templates."postfix-virtual-mailing-lists" = {
content = lib.concatStringsSep "\n" (
lib.mapAttrsToList (
name: members: "${name} ${lib.concatStringsSep ", " members}"
) listsWithSecretPlaceholders
);

# Need to restart postfix-setup to rerun `postmap` and generate updated `.db`
# files whenever mailing list membership changes.
# This could go away if sops-nix gets support for "input addressed secret
# paths": https://github.com/Mic92/sops-nix/issues/648
restartUnits = [ "postfix-setup.service" ];
};

services.postfix.mapFiles.virtual-mailing-lists =
config.sops.templates."postfix-virtual-mailing-lists".path;
services.postfix.mapFiles.virtual-mailing-lists =
config.sops.templates."postfix-virtual-mailing-lists".path;

services.postfix.config.virtual_alias_maps = [ "hash:/etc/postfix/virtual-mailing-lists" ];
services.postfix.config.virtual_alias_maps = [ "hash:/etc/postfix/virtual-mailing-lists" ];
};
}
20 changes: 0 additions & 20 deletions non-critical-infra/packages/encrypt-email-address/default.nix

This file was deleted.

This file was deleted.

26 changes: 26 additions & 0 deletions non-critical-infra/packages/encrypt-email/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
lib,
mkpasswd,
python3,
sops,
}:

python3.pkgs.buildPythonApplication {
name = "encrypt-email";
src = ./.;

format = "other";

propagatedBuildInputs = [ python3.pkgs.click ];

installPhase = ''
mkdir -p $out/bin
mv ./encrypt-email.py $out/bin/encrypt-email
wrapProgram $out/bin/encrypt-email --prefix PATH : ${
lib.makeBinPath [
sops
mkpasswd
]
}
'';
}
Loading

0 comments on commit f3743a8

Please sign in to comment.