1 { config, lib, pkgs, ... }:
5 cfg = config.services.postgrey;
7 natural = with types; addCheck int (x: x >= 0);
8 natural' = with types; addCheck int (x: x > 0);
10 socket = with types; addCheck (either (submodule unixSocket) (submodule inetSocket)) (x: x ? path || x ? port);
12 inetSocket = with types; {
17 example = "127.0.0.1";
18 description = "The address to bind to. Localhost if null";
23 description = "Tcp port to bind to";
28 unixSocket = with types; {
32 default = "/run/postgrey.sock";
33 description = "Path of the unix socket";
39 description = "Mode of the unix socket";
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" ];
51 if value inetAddr == null
52 then { path = "/run/postgrey.sock"; }
53 else { addr = value inetAddr; port = value inetPort; }
58 services.postgrey = with types; {
62 description = "Whether to run the Postgrey daemon";
67 path = "/run/postgrey.sock";
74 description = "Socket to bind to";
76 greylistText = mkOption {
78 default = "Greylisted for %%s seconds";
79 description = "Response status text for greylisted messages; use %%s for seconds left until greylisting is over and %%r for mail domain of recipient";
81 greylistAction = mkOption {
83 default = "DEFER_IF_PERMIT";
84 description = "Response status for greylisted messages (see access(5))";
86 greylistHeader = mkOption {
88 default = "X-Greylist: delayed %%t seconds by postgrey-%%v at %%h; %%d";
89 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";
94 description = "Greylist for N seconds";
99 description = "Delete entries from whitelist if they haven't been seen for N days";
101 retryWindow = mkOption {
102 type = either str natural;
105 description = "Allow N days for the first retry. Use string with appended 'h' to specify time in hours";
107 lookupBySubnet = mkOption {
110 description = "Strip the last N bits from IP addresses, determined by IPv4CIDR and IPv6CIDR";
112 IPv4CIDR = mkOption {
115 description = "Strip N bits from IPv4 addresses if lookupBySubnet is true";
117 IPv6CIDR = mkOption {
120 description = "Strip N bits from IPv6 addresses if lookupBySubnet is true";
125 description = "Store data using one-way hash functions (SHA1)";
127 autoWhitelist = mkOption {
128 type = nullOr natural';
130 description = "Whitelist clients after successful delivery of N messages";
132 whitelistClients = mkOption {
135 description = "Client address whitelist files (see postgrey(8))";
137 whitelistRecipients = mkOption {
140 description = "Recipient address whitelist files (see postgrey(8))";
145 config = mkIf cfg.enable {
147 environment.systemPackages = [ pkgs.postgrey ];
152 description = "Postgrey Daemon";
153 uid = config.ids.uids.postgrey;
159 gid = config.ids.gids.postgrey;
164 systemd.services.postgrey = let
165 bind-flag = if cfg.socket ? path then
166 "--unix=${cfg.socket.path} --socketmode=${cfg.socket.mode}"
168 ''--inet=${optionalString (cfg.socket.addr != null) (cfg.socket.addr + ":")}${toString cfg.socket.port}'';
170 description = "Postfix Greylisting Service";
171 wantedBy = [ "multi-user.target" ];
172 before = [ "postfix.service" ];
174 mkdir -p /var/postgrey
175 chown postgrey:postgrey /var/postgrey
176 chmod 0770 /var/postgrey
180 ExecStart = ''${pkgs.postgrey}/bin/postgrey \
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}