diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix index 8102e0e1f64..758f229d59d 100644 --- a/nixos/modules/rename.nix +++ b/nixos/modules/rename.nix @@ -153,6 +153,17 @@ with lib; # alsa (mkRenamedOptionModule [ "sound" "enableMediaKeys" ] [ "sound" "mediaKeys" "enable" ]) + # postgrey + (mkMergedOptionModule [ [ "services" "postgrey" "inetAddr" ] [ "services" "postgrey" "inetPort" ] ] [ "services" "postgrey" "socket" ] (config: let + value = p: getAttrFromPath p config; + inetAddr = [ "services" "postgrey" "inetAddr" ]; + inetPort = [ "services" "postgrey" "inetPort" ]; + in + if value inetAddr == null + then { path = "/var/run/postgrey.sock"; } + else { addr = value inetAddr; port = value inetPort; } + )) + # Options that are obsolete and have no replacement. (mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ] "") (mkRemovedOptionModule [ "programs" "bash" "enable" ] "") diff --git a/nixos/modules/services/mail/postgrey.nix b/nixos/modules/services/mail/postgrey.nix index 0db631868cc..d4ae25c066a 100644 --- a/nixos/modules/services/mail/postgrey.nix +++ b/nixos/modules/services/mail/postgrey.nix @@ -4,6 +4,43 @@ with lib; let cfg = config.services.postgrey; + natural = with types; addCheck int (x: x >= 0); + natural' = with types; addCheck int (x: x > 0); + + socket = with types; addCheck (either (submodule unixSocket) (submodule inetSocket)) (x: x ? "path" || x ? "port"); + + inetSocket = with types; { + options = { + addr = mkOption { + type = nullOr string; + default = null; + example = "127.0.0.1"; + description = "The address to bind to. Localhost if null"; + }; + port = mkOption { + type = natural'; + default = 10030; + description = "Tcp port to bind to"; + }; + }; + }; + + unixSocket = with types; { + options = { + path = mkOption { + type = path; + default = "/var/run/postgrey.sock"; + description = "Path of the unix socket"; + }; + + mode = mkOption { + type = string; + default = "0777"; + description = "Mode of the unix socket"; + }; + }; + }; + in { options = { @@ -13,21 +50,83 @@ in { default = false; description = "Whether to run the Postgrey daemon"; }; - inetAddr = mkOption { - type = nullOr string; - default = null; - example = "127.0.0.1"; - description = "The inet address to bind to. If none given, bind to /var/run/postgrey.sock"; - }; - inetPort = mkOption { - type = int; - default = 10030; - description = "The tcp port to bind to"; + socket = mkOption { + type = socket; + default = { + path = "/var/run/postgrey.sock"; + mode = "0777"; + }; + example = { + addr = "127.0.0.1"; + port = 10030; + }; + description = "Socket to bind to"; }; greylistText = mkOption { type = string; default = "Greylisted for %%s seconds"; - description = "Response status text for greylisted messages"; + description = "Response status text for greylisted messages; use %%s for seconds left until greylisting is over and %%r for mail domain of recipient"; + }; + greylistAction = mkOption { + type = string; + default = "DEFER_IF_PERMIT"; + description = "Response status for greylisted messages (see access(5))"; + }; + greylistHeader = mkOption { + type = string; + default = "X-Greylist: delayed %%t seconds by postgrey-%%v at %%h; %%d"; + description = "Prepend header to greylisted mails; use %%t for seconds delayed due to greylisting, %%v for the version of postgrey, %%d for the date, and %%h for the host"; + }; + delay = mkOption { + type = natural; + default = 300; + description = "Greylist for N seconds"; + }; + maxAge = mkOption { + type = natural; + default = 35; + description = "Delete entries from whitelist if they haven't been seen for N days"; + }; + retryWindow = mkOption { + type = either string natural; + default = 2; + example = "12h"; + description = "Allow N days for the first retry. Use string with appended 'h' to specify time in hours"; + }; + lookupBySubnet = mkOption { + type = bool; + default = true; + description = "Strip the last N bits from IP addresses, determined by IPv4CIDR and IPv6CIDR"; + }; + IPv4CIDR = mkOption { + type = natural; + default = 24; + description = "Strip N bits from IPv4 addresses if lookupBySubnet is true"; + }; + IPv6CIDR = mkOption { + type = natural; + default = 64; + description = "Strip N bits from IPv6 addresses if lookupBySubnet is true"; + }; + privacy = mkOption { + type = bool; + default = true; + description = "Store data using one-way hash functions (SHA1)"; + }; + autoWhitelist = mkOption { + type = nullOr natural'; + default = 5; + description = "Whitelist clients after successful delivery of N messages"; + }; + whitelistClients = mkOption { + type = listOf path; + default = []; + description = "Client address whitelist files (see postgrey(8))"; + }; + whitelistRecipients = mkOption { + type = listOf path; + default = []; + description = "Recipient address whitelist files (see postgrey(8))"; }; }; }; @@ -52,10 +151,10 @@ in { }; systemd.services.postgrey = let - bind-flag = if isNull cfg.inetAddr then - "--unix=/var/run/postgrey.sock" + bind-flag = if cfg.socket ? "path" then + ''--unix=${cfg.socket.path} --socketmode=${cfg.socket.mode}'' else - "--inet=${cfg.inetAddr}:${cfg.inetPort}"; + ''--inet=${optionalString (cfg.socket.addr != null) (cfg.socket.addr + ":")}${toString cfg.socket.port}''; in { description = "Postfix Greylisting Service"; wantedBy = [ "multi-user.target" ]; @@ -67,7 +166,23 @@ in { ''; serviceConfig = { Type = "simple"; - ExecStart = ''${pkgs.postgrey}/bin/postgrey ${bind-flag} --pidfile=/var/run/postgrey.pid --group=postgrey --user=postgrey --dbdir=/var/postgrey --greylist-text="${cfg.greylistText}"''; + ExecStart = ''${pkgs.postgrey}/bin/postgrey \ + ${bind-flag} \ + --group=postgrey --user=postgrey \ + --dbdir=/var/postgrey \ + --delay=${toString cfg.delay} \ + --max-age=${toString cfg.maxAge} \ + --retry-window=${toString cfg.retryWindow} \ + ${if cfg.lookupBySubnet then "--lookup-by-subnet" else "--lookup-by-host"} \ + --ipv4cidr=${toString cfg.IPv4CIDR} --ipv6cidr=${toString cfg.IPv6CIDR} \ + ${optionalString cfg.privacy "--privacy"} \ + --auto-whitelist-clients=${toString (if cfg.autoWhitelist == null then 0 else cfg.autoWhitelist)} \ + --greylist-action=${cfg.greylistAction} \ + --greylist-text="${cfg.greylistText}" \ + --x-greylist-header="${cfg.greylistHeader}" \ + ${concatMapStringsSep " " (x: "--whitelist-clients=" + x) cfg.whitelistClients} \ + ${concatMapStringsSep " " (x: "--whitelist-recipients=" + x) cfg.whitelistRecipients} + ''; Restart = "always"; RestartSec = 5; TimeoutSec = 10;