at 23.11-pre 6.7 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; let 4 5 cfg = config.services.postgrey; 6 7 natural = with types; addCheck int (x: x >= 0); 8 natural' = with types; addCheck int (x: x > 0); 9 10 socket = with types; addCheck (either (submodule unixSocket) (submodule inetSocket)) (x: x ? path || x ? port); 11 12 inetSocket = with types; { 13 options = { 14 addr = mkOption { 15 type = nullOr str; 16 default = null; 17 example = "127.0.0.1"; 18 description = lib.mdDoc "The address to bind to. Localhost if null"; 19 }; 20 port = mkOption { 21 type = natural'; 22 default = 10030; 23 description = lib.mdDoc "Tcp port to bind to"; 24 }; 25 }; 26 }; 27 28 unixSocket = with types; { 29 options = { 30 path = mkOption { 31 type = path; 32 default = "/run/postgrey.sock"; 33 description = lib.mdDoc "Path of the unix socket"; 34 }; 35 36 mode = mkOption { 37 type = str; 38 default = "0777"; 39 description = lib.mdDoc "Mode of the unix socket"; 40 }; 41 }; 42 }; 43 44in { 45 imports = [ 46 (mkMergedOptionModule [ [ "services" "postgrey" "inetAddr" ] [ "services" "postgrey" "inetPort" ] ] [ "services" "postgrey" "socket" ] (config: let 47 value = p: getAttrFromPath p config; 48 inetAddr = [ "services" "postgrey" "inetAddr" ]; 49 inetPort = [ "services" "postgrey" "inetPort" ]; 50 in 51 if value inetAddr == null 52 then { path = "/run/postgrey.sock"; } 53 else { addr = value inetAddr; port = value inetPort; } 54 )) 55 ]; 56 57 options = { 58 services.postgrey = with types; { 59 enable = mkOption { 60 type = bool; 61 default = false; 62 description = lib.mdDoc "Whether to run the Postgrey daemon"; 63 }; 64 socket = mkOption { 65 type = socket; 66 default = { 67 path = "/run/postgrey.sock"; 68 mode = "0777"; 69 }; 70 example = { 71 addr = "127.0.0.1"; 72 port = 10030; 73 }; 74 description = lib.mdDoc "Socket to bind to"; 75 }; 76 greylistText = mkOption { 77 type = str; 78 default = "Greylisted for %%s seconds"; 79 description = lib.mdDoc "Response status text for greylisted messages; use %%s for seconds left until greylisting is over and %%r for mail domain of recipient"; 80 }; 81 greylistAction = mkOption { 82 type = str; 83 default = "DEFER_IF_PERMIT"; 84 description = lib.mdDoc "Response status for greylisted messages (see access(5))"; 85 }; 86 greylistHeader = mkOption { 87 type = str; 88 default = "X-Greylist: delayed %%t seconds by postgrey-%%v at %%h; %%d"; 89 description = lib.mdDoc "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"; 90 }; 91 delay = mkOption { 92 type = natural; 93 default = 300; 94 description = lib.mdDoc "Greylist for N seconds"; 95 }; 96 maxAge = mkOption { 97 type = natural; 98 default = 35; 99 description = lib.mdDoc "Delete entries from whitelist if they haven't been seen for N days"; 100 }; 101 retryWindow = mkOption { 102 type = either str natural; 103 default = 2; 104 example = "12h"; 105 description = lib.mdDoc "Allow N days for the first retry. Use string with appended 'h' to specify time in hours"; 106 }; 107 lookupBySubnet = mkOption { 108 type = bool; 109 default = true; 110 description = lib.mdDoc "Strip the last N bits from IP addresses, determined by IPv4CIDR and IPv6CIDR"; 111 }; 112 IPv4CIDR = mkOption { 113 type = natural; 114 default = 24; 115 description = lib.mdDoc "Strip N bits from IPv4 addresses if lookupBySubnet is true"; 116 }; 117 IPv6CIDR = mkOption { 118 type = natural; 119 default = 64; 120 description = lib.mdDoc "Strip N bits from IPv6 addresses if lookupBySubnet is true"; 121 }; 122 privacy = mkOption { 123 type = bool; 124 default = true; 125 description = lib.mdDoc "Store data using one-way hash functions (SHA1)"; 126 }; 127 autoWhitelist = mkOption { 128 type = nullOr natural'; 129 default = 5; 130 description = lib.mdDoc "Whitelist clients after successful delivery of N messages"; 131 }; 132 whitelistClients = mkOption { 133 type = listOf path; 134 default = []; 135 description = lib.mdDoc "Client address whitelist files (see postgrey(8))"; 136 }; 137 whitelistRecipients = mkOption { 138 type = listOf path; 139 default = []; 140 description = lib.mdDoc "Recipient address whitelist files (see postgrey(8))"; 141 }; 142 }; 143 }; 144 145 config = mkIf cfg.enable { 146 147 environment.systemPackages = [ pkgs.postgrey ]; 148 149 users = { 150 users = { 151 postgrey = { 152 description = "Postgrey Daemon"; 153 uid = config.ids.uids.postgrey; 154 group = "postgrey"; 155 }; 156 }; 157 groups = { 158 postgrey = { 159 gid = config.ids.gids.postgrey; 160 }; 161 }; 162 }; 163 164 systemd.services.postgrey = let 165 bind-flag = if cfg.socket ? path then 166 "--unix=${cfg.socket.path} --socketmode=${cfg.socket.mode}" 167 else 168 ''--inet=${optionalString (cfg.socket.addr != null) (cfg.socket.addr + ":")}${toString cfg.socket.port}''; 169 in { 170 description = "Postfix Greylisting Service"; 171 wantedBy = [ "multi-user.target" ]; 172 before = [ "postfix.service" ]; 173 preStart = '' 174 mkdir -p /var/postgrey 175 chown postgrey:postgrey /var/postgrey 176 chmod 0770 /var/postgrey 177 ''; 178 serviceConfig = { 179 Type = "simple"; 180 ExecStart = ''${pkgs.postgrey}/bin/postgrey \ 181 ${bind-flag} \ 182 --group=postgrey --user=postgrey \ 183 --dbdir=/var/postgrey \ 184 --delay=${toString cfg.delay} \ 185 --max-age=${toString cfg.maxAge} \ 186 --retry-window=${toString cfg.retryWindow} \ 187 ${if cfg.lookupBySubnet then "--lookup-by-subnet" else "--lookup-by-host"} \ 188 --ipv4cidr=${toString cfg.IPv4CIDR} --ipv6cidr=${toString cfg.IPv6CIDR} \ 189 ${optionalString cfg.privacy "--privacy"} \ 190 --auto-whitelist-clients=${toString (if cfg.autoWhitelist == null then 0 else cfg.autoWhitelist)} \ 191 --greylist-action=${cfg.greylistAction} \ 192 --greylist-text="${cfg.greylistText}" \ 193 --x-greylist-header="${cfg.greylistHeader}" \ 194 ${concatMapStringsSep " " (x: "--whitelist-clients=" + x) cfg.whitelistClients} \ 195 ${concatMapStringsSep " " (x: "--whitelist-recipients=" + x) cfg.whitelistRecipients} 196 ''; 197 Restart = "always"; 198 RestartSec = 5; 199 TimeoutSec = 10; 200 }; 201 }; 202 203 }; 204 205}