From 692a677194b00cbdcb11a59b732e2decbe237c8c Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Wed, 30 Aug 2017 00:58:44 +0200 Subject: [PATCH] make configuration a nixos module --- default.nix | 277 +++++++++++++++++++++++++++++++++++++ mail-config.nix | 161 --------------------- mail-server/networking.nix | 2 +- mail-server/postfix.nix | 18 +-- mail-server/services.nix | 4 +- mail-server/users.nix | 10 +- 6 files changed, 292 insertions(+), 180 deletions(-) create mode 100644 default.nix delete mode 100644 mail-config.nix diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..96e6ad6 --- /dev/null +++ b/default.nix @@ -0,0 +1,277 @@ + +# nixos-mailserver: a simple mail server +# Copyright (C) 2016-2017 Robin Raymond +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see + +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.mailserver; +in +{ + options.mailserver = { + enable = mkEnableOption "nixos-mailserver"; + + domain = mkOption { + type = types.str; + example = "example.com"; + description = "The domain that this mail server serves. So far only one domain is supported"; + }; + + host_prefix = mkOption { + type = types.str; + default = "mail"; + description = '' + The prefix of the FQDN of the server. In this example the FQDN of the server + is given by 'mail.example.com' + ''; + }; + + login_accounts = mkOption { + type = types.loaOf (types.submodule ({ name, ... }: { + options = { + name = mkOption { + type = types.str; + example = "user1"; + description = "Username"; + }; + + hashedPassword = mkOption { + type = types.str; + example = "$6$evQJs5CFQyPAW09S$Cn99Y8.QjZ2IBnSu4qf1vBxDRWkaIZWOtmu1Ddsm3.H3CFpeVc0JU4llIq8HQXgeatvYhh5O33eWG3TSpjzu6/"; + description = '' + Hashed password. Use `mkpasswd` as follows + + ``` + mkpasswd -m sha-512 "super secret password" + ``` + ''; + }; + }; + + config.name = mkDefault name; + })); + example = { + user1 = { + hashedPassword = "$6$evQJs5CFQyPAW09S$Cn99Y8.QjZ2IBnSu4qf1vBxDRWkaIZWOtmu1Ddsm3.H3CFpeVc0JU4llIq8HQXgeatvYhh5O33eWG3TSpjzu6/"; + }; + user2 = { + hashedPassword = "$6$oE0ZNv2n7Vk9gOf$9xcZWCCLGdMflIfuA0vR1Q1Xblw6RZqPrP94mEit2/81/7AKj2bqUai5yPyWE.QYPyv6wLMHZvjw3Rlg7yTCD/"; + }; + }; + description = '' + The login account of the domain. Every account is mapped to a unix user, + e.g. `user1@example.com`. To generate the passwords use `mkpasswd` as + follows + + ``` + mkpasswd -m sha-512 "super secret password" + ``` + ''; + }; + + valiases = mkOption { + type = types.attrsOf (types.enum (builtins.attrNames cfg.login_accounts)); + example = { + info = "user1"; + postmaster = "user1"; + abuse = "user1"; + }; + description = '' + Virtual Aliases. A virtual alias `info = "user1"` means that + all mail to `info@example.com` is forwarded to `user1@example.com`. Note + that it is expected that `postmaster@example.com` and `abuse@example.com` is + forwarded to some valid email address. (Alternatively you can create login + accounts for `postmaster` and (or) `abuse`). + ''; + }; + + vmail_id_start = mkOption { + type = types.int; + default = 5000; + description = '' + The unix UID where the login_accounts are created. 5000 means that the first + user will get 5000, the second 5001, ... + ''; + }; + + vmail_user_name = mkOption { + type = types.str; + default = "vmail"; + description = '' + The user name and group name of the user that owns the directory where all + the mail is stored. + ''; + }; + + vmail_group_name = mkOption { + type = types.str; + default = "vmail"; + description = '' + The user name and group name of the user that owns the directory where all + the mail is stored. + ''; + }; + + mail_dir = mkOption { + type = types.string; + default = "/var/vmail"; + description = '' + Where to store the mail. + ''; + }; + + certificate_scheme = mkOption { + type = types.enum [ 1 2 ]; + default = 2; + description = '' + Certificate Files. There are three options for these. + + 1) You specify locations and manually copy certificates there. + 2) You let the server create new (self signed) certificates on the fly. + 3) You let the server create a certificate via `Let's Encrypt`. Note that + this implies that a stripped down webserver has to be started. This also + implies that the FQDN must be set as an `A` record to point to the IP of + the server. TODO: Explain more details + + TODO: Only certificate scheme 1) and 2) work as of yet. + ''; + }; + + cert_file = mkOption { + type = types.path; + example = "/root/mail-server.crt"; + description = '' + Scheme 1) + Location of the certificate + ''; + }; + + key_file = mkOption { + type = types.path; + example = "/root/mail-server.key"; + description = '' + Scheme 1) + Location of the key file + ''; + }; + + cert_dir = mkOption { + type = types.path; + default = "/var/certs"; + description = '' + Sceme 2) + This is the folder where the certificate will be created. The name is + hardcoded to "cert-.pem" and "key-.pem" and the + certificate is valid for 10 years. + ''; + }; + + enable_imap = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable imap / pop3. Both variants are only supported in the + (sane) startTLS configuration. (TODO: Allow SSL ports). The ports are + + 110 - Pop3 + 143 - IMAP + 587 - SMTP with login + ''; + }; + + enable_pop3 = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable POP3. Both variants are only supported in the + (sane) startTLS configuration. (TODO: Allow SSL ports). The ports are + + 110 - Pop3 + 143 - IMAP + 587 - SMTP with login + ''; + }; + + # imapSsl = mkOption {} #< TODO + # pop3Ssl = mkOption {} #< TODO + + virus_scanning = mkOption { + type = types.bool; + default = false; + description = '' + Whether to activate virus scanning. Note that virus scanning is _very_ + expensive memory wise. + ''; + }; + + dkim_signing = mkOption { + type = types.bool; + default = true; + description = '' + Whether to activate dkim signing. + TODO: Explain how to put signature into domain record + ''; + }; + + dkim_selector = mkOption { + type = types.string; + default = "mail"; + description = '' + + ''; + }; + + dkim_dir = mkOption { + type = types.path; + default = "/var/dkim"; + description = '' + + ''; + }; + }; + + config = mkIf cfg.enable { + services = import ./mail-server/services.nix { + inherit lib; + inherit (cfg) mail_dir vmail_user_name vmail_group_name valiases domain + enable_imap enable_pop3 virus_scanning dkim_signing dkim_selector + dkim_dir certificate_scheme cert_file key_file cert_dir; + }; + + environment = import ./mail-server/environment.nix { + inherit pkgs; + inherit (cfg) certificate_scheme; + }; + + networking = import ./mail-server/networking.nix { + inherit (cfg) domain host_prefix enable_imap enable_pop3; + }; + + systemd = import ./mail-server/systemd.nix { + inherit pkgs; + inherit (cfg) mail_dir vmail_group_name certificate_scheme cert_dir host_prefix + domain dkim_selector dkim_dir; + }; + + users = import ./mail-server/users.nix { + inherit lib; + inherit (cfg) vmail_id_start vmail_user_name vmail_group_name domain mail_dir + login_accounts; + }; + }; +} diff --git a/mail-config.nix b/mail-config.nix deleted file mode 100644 index a8e3203..0000000 --- a/mail-config.nix +++ /dev/null @@ -1,161 +0,0 @@ -# nixos-mailserver: a simple mail server -# Copyright (C) 2016-2017 Robin Raymond -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see - -{ config, pkgs, ... }: - -let - - # - # The domain that this mail server serves. So far only one domain is supported - # - domain = "example.com"; - - # - # The prefix of the FQDN of the server. In this example the FQDN of the server - # is given by 'mail.example.com' - # - host_prefix = "mail"; - - # - # The login account of the domain. Every account is mapped to a unix user, - # e.g. `user1@example.com`. To generate the passwords use `mkpasswd` as - # follows - # - # ``` - # mkpasswd -m sha-512 "super secret password" - # ``` - # - login_accounts = [ - { name = "user1"; - password = "$6$evQJs5CFQyPAW09S$Cn99Y8.QjZ2IBnSu4qf1vBxDRWkaIZWOtmu1Ddsm3.H3CFpeVc0JU4llIq8HQXgeatvYhh5O33eWG3TSpjzu6/"; - } - { name = "user2"; - password = "$6$oE0ZNv2n7Vk9gOf$9xcZWCCLGdMflIfuA0vR1Q1Xblw6RZqPrP94mEit2/81/7AKj2bqUai5yPyWE.QYPyv6wLMHZvjw3Rlg7yTCD/"; - } - ]; - - # - # Virtual Aliases. A virtual alias { from = "info"; to = "user1"; } means that - # all mail to `info@example.com` is forwarded to `user1@example.com`. Note - # that it is expected that `postmaster@example.com` and `abuse@example.com` is - # forwarded to some valid email address. (Alternatively you can create login - # accounts for `postmaster` and (or) `abuse`). - # - valiases = [ - { from = "info"; - to = "user1"; - } - { from = "postmaster"; - to = "user1"; - } - { from = "abuse"; - to = "user1"; - } - ]; - - # - # The unix UID where the login_accounts are created. 5000 means that the first - # user will get 5000, the second 5001, ... - # - vmail_id_start = 5000; - - # - # The user name and group name of the user that owns the directory where all - # the mail is stored. - # - vmail_user_name = "vmail"; - vmail_group_name = "vmail"; - - # - # Where to store the mail. - # - mail_dir = "/var/vmail"; - - # - # Certificate Files. There are three options for these. - # - # 1) You specify locations and manually copy certificates there. - # 2) You let the server create new (self signed) certificates on the fly. - # 3) You let the server create a certificate via `Let's Encrypt`. Note that - # this implies that a stripped down webserver has to be started. This also - # implies that the FQDN must be set as an `A` record to point to the IP of - # the server. TODO: Explain more details - # - # TODO: Only certificate scheme 1) and 2) work as of yet. - certificate_scheme = 2; - - # Sceme 1) - cert_file = "/root/mail-server.crt"; - key_file = "/root/mail-server.key"; - - # Sceme 2) - # This is the folder where the certificate will be created. The name is - # hardcoded to "cert-${domain}.pem" and "key-${domain}.pem" and the - # certificate is valid for 10 years. - cert_dir = "/var/certs"; - - # - # Whether to enable imap / pop3. Both variants are only supported in the - # (sane) startTLS configuration. (TODO: Allow SSL ports). The ports are - # - # 110 - Pop3 - # 143 - IMAP - # 587 - SMTP with login - # - enable_imap = true; - enable_pop3 = false; - # imap_ssl = false; #< TODO - # pop3_ssl = false; #< TODO - - # - # Whether to activate virus scanning. Note that virus scanning is _very_ - # expensive memory wise. - # - virus_scanning = false; - - # - # Whether to activate dkim signing. - # TODO: Explain how to put signature into domain record - # - dkim_signing = true; - dkim_selector = "mail"; - dkim_dir = "/var/dkim"; -in -{ - services = import ./mail-server/services.nix { - inherit mail_dir vmail_user_name vmail_group_name valiases domain - enable_imap enable_pop3 virus_scanning dkim_signing dkim_selector - dkim_dir certificate_scheme cert_file key_file cert_dir; - }; - - environment = import ./mail-server/environment.nix { - inherit pkgs certificate_scheme; - }; - - networking = import ./mail-server/networking.nix { - inherit domain host_prefix enable_imap enable_pop3; - }; - - systemd = import ./mail-server/systemd.nix { - inherit mail_dir vmail_group_name certificate_scheme cert_dir host_prefix - domain pkgs dkim_selector dkim_dir; - }; - - users = import ./mail-server/users.nix { - inherit vmail_id_start vmail_user_name vmail_group_name domain mail_dir - login_accounts; - }; -} diff --git a/mail-server/networking.nix b/mail-server/networking.nix index bd8ccd2..990c13a 100644 --- a/mail-server/networking.nix +++ b/mail-server/networking.nix @@ -17,7 +17,7 @@ { domain, host_prefix, enable_imap, enable_pop3 }: { - hostName = "${host_prefix}.${domain}"; + #hostName = "${host_prefix}.${domain}"; firewall = { enable = true; diff --git a/mail-server/postfix.nix b/mail-server/postfix.nix index d9ff629..fb4dbc1 100644 --- a/mail-server/postfix.nix +++ b/mail-server/postfix.nix @@ -14,22 +14,18 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see -{ mail_dir, domain, valiases, cert, key }: +{ lib, mail_dir, domain, valiases, cert, key }: let - # valiasToString :: { from = "..."; to = "..." } -> String - valiasToString = x: "${x.from}@${domain} ${x.to}@${domain}\n"; - # valiases_postfix :: [ String ] - valiases_postfix = map valiasToString valiases; - - # concatString :: [ String ] -> String - concatString = l: if l == [] - then "" - else (builtins.head l) + (concatString (builtins.tail l)); + valiases_postfix = map + (from: + let to = valiases.${from}; + in "${from}@${domain} ${to}@${domain}") + (builtins.attrNames valiases); # valiases_file :: Path - valiases_file = builtins.toFile "valias" (concatString valiases_postfix); + valiases_file = builtins.toFile "valias" (lib.concatStringsSep "\n" valiases_postfix); # vhosts_file :: Path vhosts_file = builtins.toFile "vhosts" domain; diff --git a/mail-server/services.nix b/mail-server/services.nix index ae007fe..259cbfe 100644 --- a/mail-server/services.nix +++ b/mail-server/services.nix @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see -{ mail_dir, vmail_user_name, vmail_group_name, valiases, domain, enable_imap, +{ lib, mail_dir, vmail_user_name, vmail_group_name, valiases, domain, enable_imap, enable_pop3, virus_scanning, dkim_signing, dkim_selector, dkim_dir, certificate_scheme, cert_file, key_file, cert_dir }: @@ -44,7 +44,7 @@ in }; postfix = import ./postfix.nix { - inherit mail_dir domain valiases cert key; + inherit lib mail_dir domain valiases cert key; }; dovecot2 = import ./dovecot.nix { diff --git a/mail-server/users.nix b/mail-server/users.nix index e84de1b..de1c1ab 100644 --- a/mail-server/users.nix +++ b/mail-server/users.nix @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see -{ vmail_id_start, vmail_user_name, vmail_group_name, domain, mail_dir, +{ lib, vmail_id_start, vmail_user_name, vmail_group_name, domain, mail_dir, login_accounts }: let @@ -28,15 +28,15 @@ let }]; # accountsToUser :: String -> UserRecord - accountsToUser = x: { - name = x.name + "@" + domain; + accountsToUser = account: { + name = account.name + "@" + domain; isNormalUser = false; group = vmail_group_name; - hashedPassword = x.password; + inherit (account) hashedPassword; }; # mail_user :: [ UserRecord ] - mail_user = map accountsToUser login_accounts; + mail_user = map accountsToUser (lib.attrValues login_accounts); in {