A simple policy daemon which rate-limits sending mails based on sasl_username. Recipient count is rate-limited.
Check out examples.
A sample postfix configuration might look like:
smtpd_data_restrictions = ...
check_policy_service { inet:postfix-ratelimiter:10028, { default_action=dunno } },
permit
$ ./postfix-ratelimiter -h
Usage of ./postfix-ratelimiter:
-dbdriver="": Database type for dynamic rate/burst lookups (mysql or postgresql)
-dbdsn="": Database DSN for dynamic rate/burst lookup
-default-burst=60: Default burst for policing
-default-rate=1: Default rate for policing (recipient/seconds)
-policy-listen-address=":10028": Postfix Policy listen address
-querystring="": SQL Query returning dynamic (rate, burst) settings for a (local_part, domain) lookup
-web-listen-address=":9026": Exporter WEB listen address
The policy daemon will create a token-bucket rate-limiter for each sasl authenticated user. Rate-limits against unauthenticated mails are not enforced. The token-buckets will have default-rate
rate and default-burst
burst settings.
By default the daemon listens on :10028
for policy requests.
Rejected recipient count is export in Prometheus format:
$ curl -s http://127.0.0.1:9026/metrics | grep postfix_ratelimiter_rejects
# HELP postfix_ratelimiter_rejects Rejected recipient count
# TYPE postfix_ratelimiter_rejects counter
postfix_ratelimiter_rejects{sasl_username="[email protected]"} 8
You can specify a database to look up rate/burst settings dynamically.
For this, you'll have to specify -dbdriver
(mysql or postgres), the DSN the driver uses (mysql or postgres), and the -querystring
which must return one row with two columns: (rate, burst)
. The querystring is prepared, and during lookup, (local_part, domain)
is passed as an argument.
Null
returned for any of the columns is treated as Infinity
.
Example with postgresql, minimal schema:
create table rate_limits (
local_part varchar(128),
domain varchar(128),
rate float,
burst float,
primary key (local_part, domain)
);
$ ./postfix-ratelimiter -dbdriver=postgres -dbdsn "postgres://localhost?sslmode=disable" -querystring='select rate, burst from rate_limits where local_part = $1 and domain = $2'
Configuration arguments are parsed using flag, so they can be specified using capitalized environment variables too. For example, you can start the app as:
$ docker run -d --restart=always -p 10028:10028 -e DEFAULT_RATE=2 -e DEFAULT_BURST=100 ghcr.io/rkojedzinszky/postfix-ratelimiter