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