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