cbpolicyd on Carbonio CE Mail

Carbonio is a very fine e-mail server but you must take all measures to avoid the damage an infected account can do to your server reputation. Sending thousands of spams will definitively put your server in blocklists on all major Email providers.

So, this is a tutorial to enable and setup cbpolicy to create sending policies and avoid problems.

How does it work?

cbpolicyd is a smtp filter integrated with Carbonio’s postfix. It’s a very flexible tool and there are many ways to set it up, but the most common one is the sender counting one. So, when a message is sent it register it’s sender and count how many messages this sender sent. Once it reaches a defined policy, a limit, messages will not be allowed to be sent anymore and the user will get an error message.

I like to use 3 policies working together:

1 – limit of 100 messages by sender in the last 5 minutes;
2 – limit of 200 messages by sender in the last hour;
3 – limit of 400 messages by sender in the last 24 hours;

That way we control different types of possible attacks. My experience shows that regular users will never get blocked but once an e-mail is infectes it will only be able to send 100 messages before it gets locked and can’t send messages anymore.

Well, to be totally honest it will. But just one message every 5 minutes, what makes that attack useless anyway.

So, now you know how it works: counting messages by sender and limiting it under some criteria.

Activation

cbpolicyd doesn’t come activated by default on Carbonio. So to do it run the command below:

su - zextras -c "zmprov ms `/opt/zextras/bin/zmhostname` +zimbraServiceEnabled cbpolicyd"

Web Interface setup

Sadly Carbonio CE doesn’t setup it’s webserver to allow you to get into cbpolicyd web panel. So we’ll need to workaround it setting up our own webserver. We’ll use a docker container to do it.

  • Install docker
apt update
apt install docker.io libnet-ssleay-perl
  • Creating config.php

Move old config.php to have a backup

mv /opt/zextras/common/share/webui/includes/config.php /opt/zextras/common/share/webui/includes/config.php.orig

Create a new one with the content below

<?php
$DB_DSN="sqlite:////opt/zextras/data/cbpolicyd/db/cbpolicyd.sqlitedb";
$DB_USER="root";
$DB_TABLE_PREFIX="";
?>
  • Creating cbpolicyd_docker.conf

Let’s create a file to be used by Apache container. Create a new file “/opt/containerd/cbpolicyd_docker.conf” with the content below

<VirtualHost *:80>
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
<Directory /var/www/html>
        Options Indexes Includes FollowSymLinks MultiViews
        AllowOverride All
        Require all granted
</Directory>
  • Creating a .htaccess file

Create a /opt/zextras/common/share/webui/.htaccess file with this content:

AuthType Basic
AuthName "User and Password"
AuthUserFile /var/www/html/.htpasswd
Require valid-user
  • Creating Apache container

Let’s create that Docker Apache container:

docker run -d -p 7780:80 --restart=always -v /opt/zextras/common/share/webui:/var/www/html -v /opt/zextras/data/cbpolicyd/db:/opt/zextras/data/cbpolicyd/db -v /opt/containerd/cbpolicyd_docker.conf:/etc/apache2/sites-available/000-default.conf --name cbpolicyd php:8.1-apache
  • Admin user and password

Once the container is up and running we’ll need to setup an admin account and it’s password. Remember to replace “YOUR_PASSWORD_HERE” for your actually password

docker exec --workdir /var/www/html cbpolicyd htpasswd -b -c .htpasswd admin_policyd YOUR_PASSWORD_HERE
  • Fixing quotas-limits-main.php

There is a line in that file that is not compatible with PHP 8.1. So far that’s the only issue I found. So let’s fix it.

Edit /opt/zextras/common/share/webui/quotas-limits-main.php and comment out line 40 to be like this:

#$stmt = $db->prepare("SELECT Type, CounterLimit, Disabled FROM ${DB_TABLE_PREFIX}quota_limits WHERE QuotaID = ?");
  • Accessing cbpolicyd admin panel

If everything is working as expected you may be able to access the panel using the url below. Remember to replace “YOUR_SERVER_NAME” for your server name or IP address.

http://YOUR_SERVER_NAME:7780/

user: admin_policyd
password: YOUR_PASSWORD

  • Restart Carbonio
su - zextras -c"zmcontrol restart"

Policies

Create policies can be tricky and will not be part of this tutorial at all. Using this interface and all the possible variations and steps it would take forever.

What I’m gonna do is provide you with my preset database so you can start from it and adapt it to your needs.

Download cbpolicy preset db files from here

Let’s decompress and copy it to the right place following the commands below

wget https://www.anahuac.eu/cbpolicyd_db.tgz
tar zxvf cbpolicyd_db.tgz
cp -a /opt/zextras/data/cbpolicyd/db/ /opt/zextras/data/cbpolicyd/db.orig
cp cbpolicyd_db/* /opt/zextras/data/cbpolicyd/db/
chown zextras: /opt/zextras/data/cbpolicyd/db/ -R
chmod 777 /opt/zextras/data/cbpolicyd -R

And if you’re wondering, yes 777 is needed because the container also needs permission to read and write on those files

Reload your browser and take a good look on those menus on cbpolicyd web panel

Policies -> Main

Policies -> Groups
* Add your domain on “internal_domains” group with @ like “@yourdomain”

Quotas -> Configure

http x https

Since ever cbpolicyd web panel has been available only through http but Carbonio brings in an easy way to get it working on HTTPS because of it’s built in Nginx custom templates.

So this is how you get it done.

  • create /opt/zextras/conf/nginx/templates_custom/cbpolicyd.https

Create this file with the content below replacing all entries of YOUR_SERVER_IP, YOUR_SERVER_NAME

server {
    server_name YOUR_SERVER_NAME;
    listen YOUR_SERVER_IP:7781 ssl http2;

    client_max_body_size    0;
    ssl_protocols            TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_session_cache       shared:SSL:10m;
    ssl_session_timeout     600;
    ssl_ciphers             ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:TLS_AES_256_GCM_SHA384:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4;
    ssl_ecdh_curve          prime256v1;
    ssl_verify_client       off;
    proxy_ssl_protocols            TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
    proxy_ssl_ciphers             ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:TLS_AES_256_GCM_SHA384:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4;
    ssl_dhparam             /opt/zextras/conf/dhparam.pem;

    ssl_certificate         /opt/zextras/ssl/carbonio/commercial/commercial.crt;
    ssl_certificate_key     /opt/zextras/ssl/carbonio/commercial/commercial.key;

    location / {
        proxy_pass http://YOUR_SERVER_NAME:7780;
        proxy_set_header Host YOUR_SERVER_NAME;

        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_hide_header 'x-frame-options';
        rewrite ^/(.*) /$1 break;
        port_in_redirect off;
    }
}
  • Creating custom template

To create Nginx custom template do as follows

cp -a /opt/zextras/conf/nginx/templates/nginx.conf.web.https.default.template /opt/zextras/conf/nginx/templates_custom
echo "include /opt/zextras/conf/nginx/templates_custom/cbpolicyd.https;" >> /opt/zextras/conf/nginx/templates_custom/nginx.conf.web.https.default.template
  • Fixing permissions
chown zextras: /opt/zextras/conf/nginx/templates_custom/*
  • Restart Carbonio
su - zextras -c"zmcontrol restart"
  • Access using https

To access it using https use the as it is below:

https://YOUR_SERVER_NAME:7781/index.php

1 comentário em “cbpolicyd on Carbonio CE Mail”

  1. This is great and very useful documentation…. It would be great if the whole cbpolicy module would be part of the Carbonio core.. so that users don’t need to separately install this…..

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *