Skip to content

Commit

Permalink
Merge pull request #511 from jfly/issue-510-setup-login-accounts
Browse files Browse the repository at this point in the history
Add support for mailing lists with login accounts
  • Loading branch information
Mic92 authored Dec 1, 2024
2 parents d65e24d + f3743a8 commit bc2d239
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 bc2d239

Please sign in to comment.