diff options
Diffstat (limited to 'env/users')
| -rw-r--r-- | env/users/default.nix | 126 | ||||
| -rw-r--r-- | env/users/mailbox.nix | 163 | ||||
| -rw-r--r-- | env/users/users.nix | 1 | ||||
| -rw-r--r-- | env/users/virtual.nix | 1 |
4 files changed, 291 insertions, 0 deletions
diff --git a/env/users/default.nix b/env/users/default.nix new file mode 100644 index 0000000..3602630 --- /dev/null +++ b/env/users/default.nix @@ -0,0 +1,126 @@ +{ config, lib, ... }: +with lib; let + cfg = config.local; + inherit (config.networking) domain; +in +{ + imports = [ + ./mailbox.nix + ]; + + options.local = with types; { + sysadmin = mkOption { + type = str; + }; + + users = mkOption { + default = { }; + + type = attrsOf (submodule ({ config, ... }: { + options = { + uid = mkOption { + type = int; + }; + + gid = mkOption { + type = int; + }; + + gecos = mkOption { + type = str; + default = ""; + }; + + sysadmin = mkOption { + type = bool; + default = false; + }; + + groups = mkOption { + type = listOf str; + default = [ ]; + }; + + allowLogin = mkOption { + type = bool; + default = true; + }; + + hardAliases = mkOption { + type = listOf str; + default = [ ]; + }; + }; + + config.groups = mkBefore (optional config.sysadmin "wheel"); + })); + }; + + virtual = mkOption { + default = { }; + + type = attrsOf (submodule ({ name, ... }: { + options = { + aliases = mkOption { + type = attrsOf (listOf str); + default = { }; + }; + + rules = mkOption { + default = [ ]; + + type = listOf (submodule { + options = { + pattern = mkOption { + type = str; + }; + + targets = mkOption { + type = listOf str; + }; + }; + }); + }; + + users = mkOption { + type = attrsOf (submodule { }); + default = { }; + }; + }; + + config.aliases = + let + sysadmin = mkDefault [ "sysadmin@${name}" ]; + in + { + abuse = sysadmin; + security = sysadmin; + webmaster = sysadmin; + hostmaster = sysadmin; + postmaster = sysadmin; + + sysadmin = mkDefault [ "sysadmin@${domain}" ]; + }; + })); + }; + }; + + config.local = mkMerge [ + { + users = import ./users.nix; + virtual = import ./virtual.nix; + + sysadmin = + (findSingle + (user: user.value.sysadmin) + (throw "no user is declared as sysadmin") + (throw "more than one user is declared as sysadmin") + (mapAttrsToList nameValuePair cfg.users) + ).name; + } + + { + virtual.${domain}.aliases.sysadmin = [ cfg.sysadmin ]; + } + ]; +} diff --git a/env/users/mailbox.nix b/env/users/mailbox.nix new file mode 100644 index 0000000..e603214 --- /dev/null +++ b/env/users/mailbox.nix @@ -0,0 +1,163 @@ +{ config, lib, pkgs, ... }: +with lib; let + cfg = config.local; +in +{ + options.local.mailHost = with types; { + enable = mkEnableOption "mailbox host service"; + + mdaListen = mkOption { + type = str; + }; + + saslPort = mkOption { + type = port; + }; + + lmtpPort = mkOption { + type = port; + }; + }; + + config = + let + imapHostname = cfg.domains.imap.main; + in + mkIf cfg.mailHost.enable { + services.dovecot2 = + let + cert = config.security.acme.certs.${imapHostname}.directory; + in + { + enable = true; + enablePAM = false; + enableLmtp = true; + + #sslServerKey = "${cert}/key.pem"; + #sslServerCert = "${cert}/fullchain.pem"; + + modules = [ pkgs.dovecot_pigeonhole ]; + + mailUser = "vmail"; + mailGroup = "vmail"; + mailLocation = "maildir:~/mail"; + mailPlugins.perProtocol.lmtp.enable = [ "sieve" ]; + + extraConfig = + let + inherit (config.networking) domain; + + # https://dovecot.org/list/dovecot/2019-March/115250.html + # Otra solución posible (https://serverfault.com/a/1062274/980378): + # auth_username_format = %{if;%d;eq;${domain};%Ln;%Lu} + localEntry = canonical: username: '' + ${username}:::::::user=${canonical} nopassword userdb_user=${canonical} + ''; + + localEntries = concatStrings + (flatten (mapAttrsToList + (canonical: user: + map (localEntry canonical) ([ canonical ] ++ user.hardAliases)) + cfg.users)); + + localMailboxes = pkgs.writeText "local-mailboxes" localEntries; + + vmailPath = "/var/lib/vmail/%{if;%d;ne;;%Ld;${domain}}"; + in + '' + auth_mechanisms = plain login external + + # TODO: los defaults de nixpkgs dejan los sockets bajo + # /run/dovecot2 con demasiados permisos rwx, arreglar + + service auth { + inet_listener mta-sasl { + port = ${toString cfg.mailHost.saslPort} + address = ${cfg.mailHost.mdaListen} + } + } + + service lmtp { + inet_listener mta-lmtp { + port = ${toString cfg.mailHost.lmtpPort} + address = ${cfg.mailHost.mdaListen} + } + } + + # Esto enfuerza user@domain.tld + auth_username_format = %{if;%Ld;eq;${domain};%Ln;%{if;%d;ne;;%Lu;%Ln@invalid}} + + # FIXME: Esta cadena de passdbs hace que 'doveadm user lookup' + # falle para usuarios locales, pero todo lo demás sirve. Parece + # ser debido a que pam no puede enumerar. + + passdb { + driver = passwd-file + args = username_format=%Ln ${vmailPath}/passwd + } + + passdb { + driver = passwd-file + args = ${localMailboxes} + + # Esta es una forma de determinar si se encontró el usuario en + # el passwd-file por medio de nopassword sin realmente + # autenticarlo. Cuidado con result_success, porque si eso se + # configura mal se permite inicio de sesión con cualquier + # contraseña (!!!). + result_success = continue + result_failure = return-fail + result_internalfail = return-fail + + username_filter = !*@* + } + + passdb { + driver = pam + args = dovecot2 + username_filter = !*@* + #TODO: algo como 'override_fields = allow_nets=...' + } + + userdb { + driver = passwd-file + args = username_format=%Ln ${vmailPath}/passwd + override_fields = uid=vmail gid=vmail home=${vmailPath}/home/%Ln + } + + userdb { + driver = passwd-file + args = ${localMailboxes} + + result_success = continue-ok + result_internalfail = return-fail + skip = found + } + + userdb { + driver = passwd + args = blocking=no + skip = notfound + } + ''; + }; + + security = { + # Necesario debido a 'enablePAM = false' + pam.services.dovecot2 = { }; + + #acme.certs.${imapHostname} = { + # inherit (config.services.dovecot2) group; + #}; + }; + + users = { + users.${config.services.dovecot2.mailUser}.uid = 995; + groups.${config.services.dovecot2.mailGroup}.gid = 993; + }; + + #networking.firewall.allowedTCPPorts = [ 143 993 ]; + + #local.certs.imap.enable = true; + }; +} diff --git a/env/users/users.nix b/env/users/users.nix new file mode 100644 index 0000000..1bb3788 --- /dev/null +++ b/env/users/users.nix @@ -0,0 +1 @@ +# This file has been lustrated. diff --git a/env/users/virtual.nix b/env/users/virtual.nix new file mode 100644 index 0000000..1bb3788 --- /dev/null +++ b/env/users/virtual.nix @@ -0,0 +1 @@ +# This file has been lustrated. |
