diff options
| author | Alejandro Soto <alejandro@34project.org> | 2023-02-11 19:14:07 -0600 |
|---|---|---|
| committer | Alejandro Soto <alejandro@34project.org> | 2023-02-11 19:14:07 -0600 |
| commit | 9596e0e75c6a49083167ef6e2edca9cc448695e1 (patch) | |
| tree | 9262c44043d3a250e32a21d23fb3b37eb6efcccd | |
| parent | b8a62165e4f3dd6a35ebad0dcaea0971d4323735 (diff) | |
env/users: implement mail client certificates
| -rw-r--r-- | env/users/default.nix | 21 | ||||
| -rw-r--r-- | env/users/mailbox.nix | 96 |
2 files changed, 105 insertions, 12 deletions
diff --git a/env/users/default.nix b/env/users/default.nix index 3602630..0e77e0d 100644 --- a/env/users/default.nix +++ b/env/users/default.nix @@ -8,7 +8,19 @@ in ./mailbox.nix ]; - options.local = with types; { + options.local = with types; let + mailOption = mkOption { + default = { }; + + type = submodule { + options.certs = mkOption { + type = listOf str; + default = [ ]; + }; + }; + }; + in + { sysadmin = mkOption { type = str; }; @@ -50,6 +62,8 @@ in type = listOf str; default = [ ]; }; + + mail = mailOption; }; config.groups = mkBefore (optional config.sysadmin "wheel"); @@ -83,8 +97,11 @@ in }; users = mkOption { - type = attrsOf (submodule { }); default = { }; + + type = attrsOf (submodule { + options.mail = mailOption; + }); }; }; diff --git a/env/users/mailbox.nix b/env/users/mailbox.nix index 9fe460e..8b2c4b2 100644 --- a/env/users/mailbox.nix +++ b/env/users/mailbox.nix @@ -56,19 +56,75 @@ in ${username}:::::::user=${canonical} nopassword userdb_user=${canonical} ''; - localEntries = concatStrings - (flatten (mapAttrsToList + localMailboxes = + pkgs.writeText "local-mailboxes" + (concatStrings + (flatten (mapAttrsToList + (canonical: user: + map (localEntry canonical) ([ canonical ] ++ user.hardAliases)) + cfg.users))); + + localCerts = + flatten (mapAttrsToList (canonical: user: - map (localEntry canonical) ([ canonical ] ++ user.hardAliases)) - cfg.users)); - - localMailboxes = pkgs.writeText "local-mailboxes" localEntries; + let + certNames = { + inherit canonical; + logins = [ canonical ] ++ user.hardAliases; + }; + in + map (flip nameValuePair certNames) user.mail.certs) + cfg.users); + + vmailCerts = + flatten (flatten (mapAttrsToList + (domain: virtual: mapAttrsToList + (username: user: + let + address = "${username}@${domain}"; + + certNames = { + canonical = address; + logins = [ address ]; + }; + in + map (flip nameValuePair certNames) user.mail.certs) + virtual.users) + cfg.virtual)); + + certLogins = + pkgs.writeText "cert-logins" + (concatStrings (flatten (mapAttrsToList + (uuid: names: map + (addr: '' + ${uuid}.mail-client@nodomain,${addr}:::::::user=${names.canonical} + '') + names.logins) + (listToAttrs (localCerts ++ vmailCerts))))); vmailPath = "/var/lib/vmail/%{if;%d;ne;;%Ld;${domain}}"; in '' auth_mechanisms = plain login external + #TODO: automatizar implantación de archivo de CA + + # Orden de concatenación de mail-fullchain-crl.crt: + # - Issuing CA cert + # - Issuing CA CRL + # - Intermediate CA cert + # - Intermediate CA CRL + # - Root CA cert + # - Root CA CRL + ssl_ca = </var/trust/ca/mail-fullchain-crl.crt + ssl_require_crl = yes + ssl_verify_client_cert = yes + + # Esto descarta @domain.tld de locales explícitos, pero lo exige para los demás. + # Implicación: locales implícitos sin dominio fallan en autenticar + auth_username_format = %{if;%Ld;eq;${domain};%Ln;%{if;%d;ne;;%Lu;%Ln@nodomain}} + auth_ssl_username_from_cert = yes + # TODO: los defaults de nixpkgs dejan los sockets bajo # /run/dovecot2 con demasiados permisos rwx, arreglar @@ -86,21 +142,41 @@ in } } - # 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 = static + args = nopassword + + master = yes + mechanisms = external + + result_success = continue-fail + result_failure = return-fail + result_internalfail = return-fail + } + + passdb { + driver = passwd-file + args = scheme=PLAIN username_format=%{master_user},%Lu ${certLogins} + + mechanisms = external + override_fields = nopassword + + result_failure = return-fail + result_internalfail = return-fail + } + + passdb { driver = passwd-file args = username_format=%Ln ${vmailPath}/passwd } passdb { driver = passwd-file - args = ${localMailboxes} + args = scheme=PLAIN ${localMailboxes} # Esta es una forma de determinar si se encontró el usuario en # el passwd-file por medio de nopassword sin realmente |
