I have a functional docker-mailserver environment, but I want to know if it's possible (maybe via SpamAssassin?) to apply a daily/hourly/monthly limit to the number of outbound emails from the server. If so, can the limit be applied per user?
N/A
N/A
N/A
N/A
Using PostFWD, it should be achievable.
As a dirty hack, you could overwrite the entrypoint and install PostFWD and add it to supervisorctl services.
I would recommend forking this image and adjust it to your needs.
According to PostFWD documentation, the postfix integration uses check_policy:
127.0.0.1:10040_time_limit = 3600
smtpd_recipient_restrictions = permit_mynetworks
reject_unauth_destination
check_policy_service inet:127.0.0.1:10040
This message from howtoforge forum is a good starting point for your rules. It does apply limitations per mailbox.
@youtous , thank you for the response! I'll see if I can follow the guidance you've given me and make it happen. I appreciate it.
So @youtous I've taken your advice and integrated PostFWD, and it seems to be promising so far. However, I'm having problems configuring Postfix. No matter what steps I try, I can't get Postfix to load my configuration properly.
I have created the file ./config/postfix-main.cf
10.5.0.6::10040_time_limit = 3600
smtpd_recipient_restrictions = permit_mynetworks
reject_unauth_destination
check_policy_service inet:10.5.0.6::10040
smtpd_sender_restrictions = permit_mynetworks
reject_unauth_destination
check_policy_service inet:10.5.0.6::10040
The static IP mapping throughout this comment may be unnecessary, but I've done it to remove DNS issues as a suspect
Here's docker-compose.yml:
version: '2'
services:
mail:
image: tvial/docker-mailserver:latest
hostname: ${HOSTNAME}
domainname: ${DOMAINNAME}
container_name: ${CONTAINER_NAME}
ports:
- "25:25"
- "143:143"
- "587:587"
- "993:993"
- "4190:4190"
volumes:
- /mnt/data/infra/mail/maildata:/var/mail
- /mnt/data/infra/mail/mailstate:/var/mail-state
- /mnt/data/infra/mail/maillogs:/var/log/mail
- ./config/:/tmp/docker-mailserver/
- /mnt/data/infra/letsencrypt/etc:/etc/letsencrypt
env_file:
- .env
- env-mailserver
cap_add:
- NET_ADMIN
- SYS_PTRACE
restart: always
networks:
mailnet:
ipv4_address: 10.5.0.5
postfwd:
image: postfwd/postfwd:stable
environment:
- PROG=postfwd2
- CACHE=0
- EXTRA=-vv --no_parent_dns_cache --summary=600
# - EXTRA=-vv --no_parent_dns_cache --noidlestats --summary=600
restart: always
# port mapping may not be necessary since they're on the same network
# ports:
# - 127.0.0.1:10040:10040
networks:
mailnet:
ipv4_address: 10.5.0.6
volumes:
- ./postfwd.cf:/etc/postfwd/postfwd.cf:ro
networks:
mailnet:
driver: bridge
ipam:
config:
- subnet: 10.5.0.0/16
gateway: 10.5.0.1
The contents of postfwd.cf are valid, here's a snippet:
id=ip_msg_10min
action=rate(client_address/30/600/421 4.7.1: $$client_address: sending too fast.)
id=ip_msg_3hr
client_address!=127.0.0.1
action=rate(client_address/120/10800/421 4.7.1: $$client_address: too many messages, try later.)
id=ip_msg_24hr
client_address!=127.0.0.1
action=rate(client_address/150/86400/421 4.7.1: $$client_address: too many messages, try later.)
id=ip_msg_72hr
client_address!=127.0.0.1
action=rate(client_address/300/259200/421 4.7.1: $$client_address: too many messages, try later.)
The mail container says it can't load the config lines for PostFWD+Postfix when I use the postfix-main.cf posted at the top of this comment:
mail | postconf: fatal: missing '=' after attribute name: "reject_unauth_destination"
mail | postconf: fatal: missing '=' after attribute name: "check_policy_service inet:10.5.0.6::10040"
mail | postconf: fatal: missing '=' after attribute name: "reject_unauth_destination"
mail | postconf: fatal: missing '=' after attribute name: "check_policy_service inet:10.5.0.6::10040"
If I try and condense these into something that Postfix doesn't complain about via postconf, nothing happens at all:
$ docker exec -it mail bash
root@mail:/# postconf smtpd_recipient_restrictions="permit_mynetworks","reject_unauth_destination","check_policy_service inet:10.5.0.6::10040"
root@mail:/# postconf -P | grep -i smtpd_recipient
127.0.0.1:10025/inet/smtpd_recipient_restrictions = permit_mynetworks,reject
root@mail:/# postconf smtpd_recipient_restrictions="reject_unauth_destination"
root@mail:/# postconf -P | grep -i smtpd_recipient
127.0.0.1:10025/inet/smtpd_recipient_restrictions = permit_mynetworks,reject
You can see that it doesn't seem to set the values for the current Postfix config. Am I missing something here? Do you have any thoughts? I appreciate it.
This image uses one line per configuration KV parameter.
https://github.com/tomav/docker-mailserver/blob/master/target/postfix/main.cf#L47
I suspect https://github.com/tomav/docker-mailserver/blob/master/target/start-mailserver.sh#L1213 trying to load each line of ./config/postfix-main.cf as an individual parameter.
Could you try something like:
./config/postfix-main.cf
10.5.0.6::10040_time_limit = 3600
smtpd_recipient_restrictions = permit_mynetworks, reject_unauth_destination, check_policy_service inet:10.5.0.6::10040
smtpd_sender_restrictions = permit_mynetworks, reject_unauth_destination, check_policy_service inet:10.5.0.6::10040
Thanks again @youtous. It seems that your suggestion has helped me get closer to my goal, but PostFWD doesn't seem to be doing anything.
I've got a special test rule to restrict more than 1 email message from coming from the same IP over the last 10 minutes below:
id=ip_msg_10min
action=rate(client_address/1/600/421 4.7.1: $$client_address: sending too fast.)
I send a test email to another account on the server, with a mail client located at an external IP. I don't see any logging in PostFWD at all and I was able to send many mails through.
The good news is that I've verified that the rule has been applied by running docker exec -it mail bash and inspecting /etc/postfix/main.cf:
main.cf:smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, check_policy_service unix:private/policyd-spf, check_policy_service inet:10.5.0.6::10040, reject_un
auth_pipelining, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname, reject_unknown_recipient_domain, reject_rbl_client zen.spamhaus.org, reject_rbl_client bl.spamcop.net
main.cf:smtpd_client_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, reject_unauth_pipelining,check_policy_service inet:10.5.0.6::10040
main.cf:smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unknown_sender_domain, check_policy_service inet:10.5.0.6::10040
For reference, I updated the values in postfix-main.cf to:
10.5.0.6::10040_time_limit=3600
smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, check_policy_service unix:private/policyd-spf, check_policy_service inet:10.5.0.6::10040, reject_unauth_pipelining, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname, reject_unknown_recipient_domain, reject_rbl_client zen.spamhaus.org, reject_rbl_client bl.spamcop.net
smtpd_client_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, reject_unauth_pipelining,check_policy_service inet:10.5.0.6::10040
smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unknown_sender_domain, check_policy_service inet:10.5.0.6::10040
I based these values off of the template values in https://github.com/tomav/docker-mailserver/blob/master/target/postfix/main.cf#L47 and basically added a check_policy_service action in the spots that I thought looked best.
So, it seems the config is definitely working, Do you think it's possible I've misconfigured the order of these check_policy_service configuration parameters maybe? Any suggestions? You've been very helpful, I really can't thank you enough for your help.
I may have gotten it working @youtous !! It turns out that I had two colons in the inet addresses and that was a syntax error. Example:
10.5.0.6::10040_time_limit=3600
should be
10.5.0.6:10040_time_limit=3600
It appears my rule has been processed and run, and it _did_ actually throttle a message successfully. I'll report back if I run into any other problems...
Ok, so this took some tedious work but I finally figured out how to limit outbound emails from my users - example error message from Thunderbird:
An error occurred while sending mail. The mail server responded:
450 4.7.1 <[email protected]>: Sender address rejected: sorry, max 2 requests per hour.
Please check the message recipient "[email protected]" and try again.
In order to ensure that the PostFWD rules _even get run_, you have to check that the order of operations in your postfix-main.cf are valid for what you want. For example, if permit_mynetworks is in front of your check_policy_service action, it might not run since the result of permit_mynetworks returns OK. This is what was happening to me - calls to PostFWD were not running when sending mail with one of my accounts, but it _was_ running when I was receiving mail from an external domain (such as gmail).
I had to shuffle the check_policy_service call high up in the list for each of the options. See my postfix-main.cf below:
10.5.0.6:10040_time_limit=3600
smtpd_recipient_restrictions=reject_unauth_pipelining,check_policy_service inet:10.5.0.6:10040,permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination,check_policy_service unix:private/policyd-spf,reject_invalid_helo_hostname,reject_non_fqdn_helo_hostname,reject_unknown_recipient_domain,reject_rbl_client zen.spamhaus.org,reject_rbl_client bl.spamcop.net
smtpd_client_restrictions=reject_unauth_destination,reject_unauth_pipelining,check_policy_service inet:10.5.0.6:10040,permit_mynetworks,permit_sasl_authenticated,
smtpd_sender_restrictions=reject_unknown_sender_domain,check_policy_service inet:10.5.0.6:10040,permit_sasl_authenticated,permit_mynetworks
And my postfwd.cf now _only has one rule_ to keep things simple:
id=limit_rate ; protocol_state==RCPT
action=rate(client_address/2/3600/450 4.7.1 sorry, max 2 requests per hour)
From the reading I've done and my limited understanding of Postfix/PostFWD, reject_unauth_pipelining has to go before the check_policy_service action. Aside from that, the check_policy_service call to PostFWD is the next immediate action for each of the smtp_*_restrictions config options above.
Some of these options may not be needed, or could potentially be in dangerously wrong order. Please, anyone reading this, correct my rules if you see anything scary. I'll be refining the rules as time goes on, but this is what I've got for now. Thanks again @youtous for all the help.
It's worth noting that this was the thread that helped figure out the postfix rule ordering issue: https://serverfault.com/a/810085
For example, if permit_mynetworks is in front of your check_policy_service action, it might not run since the result of permit_mynetworks returns OK. This is what was happening to me - calls to PostFWD were not running when sending mail with one of my accounts, but it was running when I was receiving mail from an external domain (such as gmail).
Good to know! Thank you.
Interesting use case and your explanations are well detailed!