diff --git a/flake.nix b/flake.nix index 1b6b89f..a83ceaf 100644 --- a/flake.nix +++ b/flake.nix @@ -39,6 +39,10 @@ url = "github:nix-community/home-manager/master"; inputs.nixpkgs.follows = "nixpkgs"; # use the same nixpkgs as the system }; + simple-nixos-mailserver = { + url = "gitlab:simple-nixos-mailserver/nixos-mailserver/nixos-23.11"; + inputs.nixpkgs.follows = "nixpkgs"; # use the same nixpkgs as the system + }; }; outputs = { diff --git a/hosts/sigma/acme.nix b/hosts/sigma/acme.nix index a7a3f6f..e56ef5f 100644 --- a/hosts/sigma/acme.nix +++ b/hosts/sigma/acme.nix @@ -4,6 +4,8 @@ domain = "*.caspervk.net"; reloadServices = [ "caddy.service" + "dovecot2.service" + "postfix.service" ]; # The NixOS Caddy module is a little too clever and sets the cert's group # to 'caddy', which means other services can't load it. This is not needed @@ -19,5 +21,7 @@ }; users.groups.acme.members = [ "caddy" + "dovecot2" + "postfix" ]; } diff --git a/hosts/sigma/default.nix b/hosts/sigma/default.nix index 923c54a..d60d066 100644 --- a/hosts/sigma/default.nix +++ b/hosts/sigma/default.nix @@ -10,6 +10,7 @@ ./forgejo.nix ./hardware.nix ./jellyfin.nix + ./mail.nix ./network.nix ./sonarr.nix ]; diff --git a/hosts/sigma/deluge.nix b/hosts/sigma/deluge.nix index 38c6ec6..aa65be1 100644 --- a/hosts/sigma/deluge.nix +++ b/hosts/sigma/deluge.nix @@ -44,7 +44,10 @@ }; }; - # Only allow deluged internet access through wg-sigma-p2p + # Only allow deluged internet access through wg-sigma-p2p. Note that this + # does not tell it to use the correct routing table. For proper internet + # access, the correct routing table is also configured by + # routingPolicyRuleConfig in networking.nix. systemd.services.deluged = { serviceConfig = { RestrictNetworkInterfaces = "lo wg-sigma-p2p"; diff --git a/hosts/sigma/mail.nix b/hosts/sigma/mail.nix new file mode 100644 index 0000000..8193387 --- /dev/null +++ b/hosts/sigma/mail.nix @@ -0,0 +1,130 @@ +{ + config, + secrets, + simple-nixos-mailserver, + ... +}: { + imports = [ + simple-nixos-mailserver.nixosModule + ]; + + # Simple NixOS Mailserver. + # https://nixos-mailserver.readthedocs.io + # https://nixos.wiki/wiki/Imapsync + # + # DNS + # Each domain delegates mail-handling to mail.caspervk.net using an MX + # record. mail.caspervk.net MUST be an A/AAAA record *NOT* CNAME. For spam + # purposes, the IP-addresses pointed to by mail.caspervk.net MUST point back + # to mail.caspervk.net using reverse-DNS. + # > dig mail.caspervk.net + # > dig -x 1.2.3.4 + # Mail to e.g. vkristensen.dk should be delegated to mail.caspervk.net. Each + # domain's DKIM key in /var/dkim/ MUST be added to its DNS zone. + # > dig MX vkristensen.dk + # > dig TXT vkristensen.dk + # > dig TXT mail._domainkey.vkristensen.dk + # > dig TXT _dmarc.vkristensen.dk + # + # Online verification tools: + # https://www.mail-tester.com/ + # https://mxtoolbox.com/deliverability + # + # Client Setup + # Account: casper@vkristensen.dk + # IMAP: mail.caspervk.net:993 (SSL/TLS) + # SMTP: mail.caspervk.net:465 (SSL/TLS) + mailserver = { + enable = true; + # Firewall is handled manually in networking.nix + openFirewall = false; + # Don't run a local DNS resolver + localDnsResolver = false; + # Disable opportunistic TLS encryption and force instead. This only applies + # to client connections from e.g. Thunderbird or K9. Submission from other + # mailservers is always opportunistic TLS as per RFC. + # https://docker-mailserver.github.io/docker-mailserver/latest/config/security/understanding-the-ports/ + enableImap = false; + enableSubmission = false; + # The fully qualified domain name of the mail server. Used for TLS and must + # have a matching reverse-DNS record. + fqdn = "mail.caspervk.net"; + # TLS Certificate + # https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/merge_requests/303 + certificateScheme = "manual"; + certificateFile = "${config.security.acme.certs."caspervk.net".directory}/fullchain.pem"; + keyFile = "${config.security.acme.certs."caspervk.net".directory}/key.pem"; + # Use more than 1024-bit DKIM keys + dkimKeyBits = 4096; + # Rewrite the MessageID's hostname-part of outgoing emails to the + # mailserver's FQDN. Avoids leaking local hostnames. + rewriteMessageId = true; + # The hierarchy separator for mailboxes used by dovecot for the namespace + # 'inbox'. Dovecot defaults to "." but recommends "/". + hierarchySeparator = "/"; + # The domains that this mail server serves + domains = [ + "caspervk.net" + "spervk.com" + "sudomail.org" + "vkristensen.dk" + ]; + # The login account. All mail is delivered to the same account to ease + # client configuration, but it is allowed to send mail as any of the + # configured aliases. To generate a password use 'mkpasswd -sm bcrypt'. + loginAccounts = { + "casper@vkristensen.dk" = { + hashedPasswordFile = config.age.secrets.mail-hashed-password-file.path; + aliases = secrets.sigma.mail.aliases; + }; + }; + }; + + # Only allow mail delivery through through wg-sigma-public. Note that this + # does not tell it to use the correct routing table. For proper internet + # access, the correct routing table is also configured by + # routingPolicyRuleConfig in networking.nix. + systemd.services.postfix = { + serviceConfig = { + RestrictNetworkInterfaces = "lo wg-sigma-public"; + }; + }; + + # Disable rspamd filtering[1]. The rspamd service cannot be disabled + # completely due to [2]. + # [1]: https://nixos-mailserver.readthedocs.io/en/latest/rspamd-tuning.html + # [2]: https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/merge_requests/249 + services.rspamd.extraConfig = '' + actions { + reject = null; + add_header = null; + greylist = null; + } + ''; + + environment.persistence."/nix/persist" = { + directories = [ + # The generated DKIM keys are manually added to each domain's DNS zone + # and therefore need to be persisted. + { + directory = "/var/dkim"; + user = "opendkim"; + group = "opendkim"; + mode = "0755"; + } + { + directory = "/var/vmail"; + user = "virtualMail"; + group = "virtualMail"; + mode = "2770"; + } + ]; + }; + + age.secrets.mail-hashed-password-file = { + file = "${secrets}/secrets/mail-hashed-password-file.age"; + mode = "600"; + owner = "root"; + group = "root"; + }; +} diff --git a/hosts/sigma/network.nix b/hosts/sigma/network.nix index fb38f62..4f0a187 100644 --- a/hosts/sigma/network.nix +++ b/hosts/sigma/network.nix @@ -57,6 +57,15 @@ Table = "wg-sigma-public"; }; } + { + # The postfix systemd service has + # RestrictNetworkInterfaces=wg-sigma-public, but that does not tell + # it to use the correct routing table. + routingPolicyRuleConfig = { + User = config.services.postfix.user; + Table = "wg-sigma-public"; + }; + } ]; }; @@ -119,15 +128,21 @@ "enp5s0" = { allowedTCPPorts = [ 22 # SSH - 80 # Caddy + 25 # Mail SMTP 443 # Caddy + 465 # Mail ESMTP + 80 # Caddy + 993 # Mail IMAPS ]; }; "wg-sigma-public" = { allowedTCPPorts = [ 22 # SSH - 80 # Caddy + 25 # Mail SMTP 443 # Caddy + 465 # Mail ESMTP + 80 # Caddy + 993 # Mail IMAPS ]; }; "wg-sigma-p2p" = {