Protecting mailcow with Crowdsec

🐮 + 🐋 + 🦙 = 💕
🐮 + 🐋 + 🦙 = 💕

Note: At the moment of publication the monitoring of the dovecot container is not working. A bugfix for that will come in the next release of crowdsec.

I tried to find a nice description of mailcow on their website and this is the best I found :)

The mailserver suite with the 'moo' – 🐮 + 🐋 = 💕

Mailcow is a nicely packaged and containerised mail server. Perfect for self hosters like me or small businesses if you have decent linux skills. Mailcow is using docker compose and consists of a bunch of containers. Some of the containers are:

  • postfix (smtp)
  • dovecot (pop/imap/authentication backend for smtp)
  • nginx (forwarding web proxy)
  • sogo (webmail/calendar)
  • acme (requests Let's encrypt certificates)

I replaced my 13 years old self hosted email system with mailcow 2 months ago and I i like it a lot! I'm also a big fan of Crowdsec and the way I discovered it was by browsing the Mailcow github. There i found a feature request where someone asked for crowdsec support.

Almost a month ago I installed Crowdsec (agent & bouncer) on my mailcow server. I didn't look into how to configure crowdsec to read the logs from the containers. Just adding it gave me a extra layer of protection.

This weekend I decided to investigate how to monitor the logs from postfix, dovecot and nginx containers and to write this guide for other to follow.

Mailcow installation

I installed mailcow on Ubuntu 20.04 by following this guide. If you want to try mailcow I recommend you follow it too.

Crowdsec installation

First of all we need to install the Crowdsec agent. In this case I run a Ubuntu 20.04 installation.

curl -s | sudo bash

sudo apt-get install crowdsec

Then install the iptables bouncer so the machine can block unwanted connections

sudo apt install crowdsec-firewall-bouncer-iptables

For ease of use I recommend installing the bash completions for cscli

cscli completion bash | sudo tee /etc/bash_completion.d/cscli
source ~/.bashrc

I also suggest creating a user at the Crowdsec console. After logging on get a command to enroll your crowdsec instance. Run the command and go back to the crowdsec console to approve it.

Now we need to install the parser for docker logs

sudo cscli parsers install crowdsecurity/docker-logs

We also want parsing support for nginx, postfix and dovecot.

cscli collections install crowdsecurity/nginx
cscli collections install crowdsecurity/postfix
cscli collections install crowdsecurity/dovecot

Then we need to tell the Crowdsec agent what container does what. Do that by adding the following lines to the end of /etc/crowdsec/acquis.yaml

# lines for mailcow
source: docker
  - mailcowdockerized_nginx-mailcow_1
  type: nginx
source: docker
  - mailcowdockerized_dovecot-mailcow_1
  - mailcowdockerized_postfix-mailcow_1
  type: syslog
Add these lines to the end of /etc/crowdsec/acquis.yaml

We set type nginx to the nginx container to direct the log to the nginx log parser. For the dovecot and postfix container we set it to syslog as the dovecot and postfix parsers looks for dovecot and postfix-related lines in that log type.

I also suggest you add your own IPs and the internal docker IPs to a whitelist so you don't lock yourself and to prevent crowdsec from breaking internal communications in between the mailcow containers. Create a whitelist file (/etc/crowdsec/parsers/s02-enrich/whitelists.yaml) and edit it.

name: crowdsecurity/whitelists
description: "Whitelist events from private ipv4 addresses"
  reason: "private ipv4/ipv6 ip/ranges"
    - ""
    - "::1"
    - ""
    - ""
    - ""

As you see I whitelist lots of IPs. My mailcow server is behind a opnsense firewall that also run crowdsec. It also have IPSEC VPN to other networks but access to the servers internal IP is limited to a few trusted machines.

We are now ready to restart crowdsec and look at the crowdsec log. I like to run this chain of commands:

service crowdsec stop && service crowdsec start && tail -n 100 -f /var/log/crowdsec.log

You should see these lines in the log if crowdsec reads the logs from your the containers:

time="03-04-2022 11:30:05" level=info msg="start tail for container /mailcowdockerized_dovecot-mailcow_1" container_name=/mailcowdockerized_dovecot-mailcow_1 type=docker
time="03-04-2022 11:30:05" level=info msg="start tail for container /mailcowdockerized_nginx-mailcow_1" container_name=/mailcowdockerized_nginx-mailcow_1 type=docker
time="03-04-2022 11:30:05" level=info msg="start tail for container /mailcowdockerized_postfix-mailcow_1" container_name=/mailcowdockerized_postfix-mailcow_1 type=docker

Press Ctrl+c to stop stop reading the log.

In theory it should work now. If you run sudo cscli parsers inspect crowdsecurity/nginx-logs you will see something like this:

|                   SOURCE                   | LINES READ | LINES PARSED | LINES UNPARSED | LINES POURED TO BUCKET |
| docker:mailcowdockerized_dovecot-mailcow_1 |          5 | -            |              5 | -                      |
| docker:mailcowdockerized_nginx-mailcow_1   |       6583 |         6390 |            193 |                    928 |
| docker:mailcowdockerized_postfix-mailcow_1 |       4381 |          921 |           3460 |                    920 |

You will not see the docker sources until something is get logged. If the mail server isn't in production yet it can take a while before you see the lines. At the moment of publishing dovecot logs are not working and you will not see that line and crowdsec can not read the logs. That bug will be fixed in the next crowdsec release.


I started writing this a couple of days ago. I got stuck at getting crowdsec read the dovecot logs. The bug is now identified and fix will come at the next release.

Even if crowdsec can't read the logs from the dovecot container it will read logs from postfix and nginx and it will give you a extra layer of security by blocking over 15000 known bad IPs from accessing your server.