Skip to content

Instantly share code, notes, and snippets.

@mdpuma
Last active December 12, 2024 13:02
Show Gist options
  • Save mdpuma/f1c347091717240276fcb0bab737f7ec to your computer and use it in GitHub Desktop.
Save mdpuma/f1c347091717240276fcb0bab737f7ec to your computer and use it in GitHub Desktop.
Integrate rspamd with cpanel
service[rspamd]=x,x,x,/etc/init.d/rspamd restart,rspamd,_rspamd
* go to whm -> exim configuration manager
* choose advanced editor
* search spamd & replace with this:
spamd_address = 127.0.0.1 11333 variant=rspamd
set smtputf8_advertise_hosts = *
* find and disable greylisting block
* find and disable acl_smtp_data:default_check_message_pre
* find section acl_smtp_data:custom_begin_spam_scan
* complete block with this:
# Remove spam headers from outside sources
warn remove_header = x-spam-subject : x-spam-status : x-spam-score : x-spam-bar : x-spam-report : x-spam-flag : x-ham-report
# add spam-score and spam-report header when told by rspamd
# also scan outgoing messages
warn spam = nobody:true
log_message = "rspam_score: $spam_score ($spam_bar) rspam_report: $spam_report"
!authenticated = *
add_header = X-Spam-Score: $spam_score ($spam_bar)
add_header = X-Spam-Report: $spam_report
add_header = X-Spam-Action: $spam_action
warn condition = ${if eq{$spam_action}{rewrite subject}}
add_header = X-Spam-Subject: ***SPAM*** $rh_subject
add_header = X-Spam-Status: Yes
defer message = Please try again later
condition = ${if eq{$spam_action}{greylist}}
defer message = Please try again later
condition = ${if eq{$spam_action}{soft reject}}
deny message = Message discarded as high-probability spam
condition = ${if eq{$spam_action}{reject}}
7. disable acl_smtp_data:default_spam_scan, acl_smtp_rcpt:default_spam_scan_check block
8. disable acl_not_smtp:outgoing_spam_scan_over_int, acl_smtp_data:no_forward_outbound_spam_over_int
9. reject mail which get score higher than 6 if they is sent via non_smtp (sendmail, php mail())
Note: this will ignore completely messages which are sent from root user.
** Find block acl_not_smtp:custom_begin_not_smtp and paste this code
warn condition = ${if eq{$sender_address_local_part}{root} {no} {yes}}
spam = nobody:true
log_message = "rspam_score: $spam_score ($spam_bar) rspam_report: $spam_report sender_address_local_part = $sender_address_local_part"
add_header = X-Spam-Score: $spam_score ($spam_bar)
add_header = X-Spam-Report: $spam_report
add_header = X-Spam-Action: $spam_action
discard message = Message discarded as high-probability spam
condition = ${if match {$spam_report} {\NFREEMAIL_ENVFROM\N} {1} {0}}
condition = ${if >= {$spam_score_int}{60}}
deny message = Message rejected as high-probability spam
condition = ${if >= {$spam_score_int}{60}}
deny message = Message rejected as high-probability spam
condition = ${if eq{$spam_action}{reject}}
accept
** disable acl_not_smtp:end_default_outgoing_notsmtp_checkall
10. deliver mail with high score of spam to /dev/null if is try to be forwarded due email forwards
*** Put these block in to begin routers block, before router boxtrapper_autowhitelist or enforce_mail_permissions:
# version with using of delivery as save to /dev/null (logs will show actual delivery to recipient email, which is not true action)
# reject_forwarded_mail_marked_as_spam:
# driver = accept
# ignore_target_hosts = 127.0.0.1
# condition = ${if eq {${lookup {$sender_address_domain} lsearch{/etc/userdomains}{$value}}}{}{true}{false}}
# condition = ${if match{$header_X-Spam-Score:}{\N\+\+\+\+\+\N}{yes}{no}}
# # condition = ${if >= {$spam_score_int}{50}}
# domains = ! +local_domains : !$primary_hostname
# transport = file_to_devnull
silent_drop_forwarded_mail_marked_as_spam:
driver = redirect
ignore_target_hosts = 127.0.0.1
condition = ${if eq {${lookup {$sender_address_domain} lsearch{/etc/userdomains}{$value}}}{}{true}{false}}
# condition = ${if match{$header_X-Spam-Score:}{\N\+\+\+\+\+\N}{yes}{no}}
condition = ${if >= {$spam_score_int}{50}}
domains = ! +local_domains : !$primary_hostname
allow_filter
user = mailnull
file_transport = file_to_devnull
data = #Exim filter\n\
save /dev/null
** Put this transport after begin transports:
file_to_devnull:
driver = appendfile
file = /dev/null
** FIND Section: PREVIRTUALUSER add paste code below
* add router which will store Spam messages in INBOX.Junk directory
* this will cause error when you will try to save settings in to WHM/cpanel exim editor
virtual_user_spam_dir:
driver = redirect
domains = !$primary_hostname
# condition = ${if match{$header_X-Spam-Score:}{\N\+\+\+\+\+\N}{yes}{no}}
condition = ${if >= {$spam_score_int}{60}}
require_files = "+/etc/valiases/$domain:+${extract{5}{::}{${lookup passwd{${lookup{$domain}lsearch{/etc/userdomains}{$value}}}{$value}}}}/mail/$domain/$local_part"
data = "$local_part+Junk@$domain"
redirect_router = virtual_user
# cat options.inc
dns {
nameserver = ["127.0.0.1"];
}
# cat redis.conf
servers = "127.0.0.1"; # Read servers (unless write_servers are unspecified)
write_servers = "127.0.0.1"; # Servers to write data
#disabled_modules = ["ratelimit"]; # List of modules that should not use redis from this section
timeout = 2s;
db = "4";
#password = "some_password";
MM_WHITELISTED_ASN {
type = "asn";
map = "${LOCAL_CONFDIR}/local.d/whitelisted_asn.map";
description = "Messages which are coming from whitelisted ASN";
score = -10;
}
MM_BLACKLISTED_IP {
type = "ip";
map = "${LOCAL_CONFDIR}/local.d/blacklisted_sourceip.map";
description = "Blacklisted ips";
score = 12;
}
MM_WHITELISTED_IP {
type = "ip";
map = "${LOCAL_CONFDIR}/local.d/whitelisted_sourceip.map";
description = "Whitelisted ips";
score = -10;
}
MM_REJECTED_BY_SUBJECT {
type = "header";
header = "Subject";
map = "${LOCAL_CONFDIR}/local.d/rejected_by_subject.map";
regexp = true;
score = 10;
symbols = ["HAS_X_PHP_SCRIPT"];
# action = "reject";
}
MM_BLACKLISTED_BY_FORGED_SENDER {
type = "from";
filter = "email:user";
map = "${LOCAL_CONFDIR}/local.d/blacklisted_by_forged_sender.map";
symbols = ["FORGED_SENDER"];
action = "reject";
}
MM_BLACKLISTED_BY_USER {
type = "user";
map = "${LOCAL_CONFDIR}/local.d/blacklisted_by_user.map";
action = "reject";
}
MM_WHITELISTED_BY_SUBJECT {
type = "header";
header = "Subject";
map = "${LOCAL_CONFDIR}/local.d/whitelisted_by_subject.map";
regexp = true;
score = -5;
# action = "reject";
}
MM_WHITELISTED_BY_USER {
type = "user";
map = "${LOCAL_CONFDIR}/local.d/whitelisted_by_user.map";
score = -5;
# action = "reject";
}
MM_WHITELISTED_BY_ENVELOPE_FROM {
type = "from";
map = "${LOCAL_CONFDIR}/local.d/whitelisted_by_envelope_from.map";
regexp = true;
filter = "email:addr";
score = -7;
}
MM_WHITELISTED_BY_FROM_HEADER {
type = "header";
header = "From";
map = "${LOCAL_CONFDIR}/local.d/whitelisted_by_from_header.map";
regexp = true;
score = -7;
}
#MM_WHITELISTED_RSPAMD_BL {
# type = "header";
# header = "To";
# map = "${LOCAL_CONFDIR}/local.d/whitelisted_by_to_header_rspamd_bl.map";
# symbols = ["RSPAMD_URIBL"];
# action = "accept";
#}
MM_BLACKLISTED_BY_FROM_HEADER {
type = "header";
header = "From";
map = "${LOCAL_CONFDIR}/local.d/blacklisted_by_from_header.map";
regexp = true;
score = 15;
# action = "reject";
}
MM_BLACKLISTED_BY_X_PHP_HEADER {
type = "from";
symbols = ["HAS_X_PHP_SCRIPT"];
action = "reject";
map = "${LOCAL_CONFDIR}/local.d/blacklisted_by_x_php_header.map";
}
MM_MAIL_DELIVERY_FAILED {
type = "header";
header = "Subject";
map = "${LOCAL_CONFDIR}/local.d/mail_delivery_failed_subject.map"
symbols = ["IS_MAIL_DELIVERY_FAILED"];
regexp = true;
}
MM_REJECT_MAIL_DELIVERY_FAILED_BY_TO_HEADER {
type = "header";
header = "To";
map = "${LOCAL_CONFDIR}/local.d/reject_by_to_header.map";
regexp = true;
action = "reject";
#score = 10;
require_symbols = "MM_MAIL_DELIVERY_FAILED";
}
# cat logging.inc
type=file
filename=/var/log/rspamd.log
# cat actions.conf
reject = 15; # Reject when reaching this score
add_header = null; # Add header when reaching this score
greylist = 4; # Apply greylisting when reaching this score (will emit `soft reject action`)
# cat worker-controller.inc
bind_socket = "IP:11334";
password = "PASSWORD HASH";
@robertol
Copy link

Hi!

I found your gist and I followed your guide but my exim not connect to rspamd. I'm using centos 7 and rspamd in same server with cpanel. Could you help me?
I'm using exim 4.89 (cpanel 11.64).

Thanks!

@vstakhov
Copy link

I'm looking forward towards improvement of the Exim integration document. Would you mind if I include your recipes to the official Rspamd documentation? Or you can add your own pull request against vstakhov/rspamd.com repo

@ScOut3R
Copy link

ScOut3R commented Oct 16, 2018

Thank you @mdupa! This was desperately needed!

I hope you don't mind, I improved upon your solution with the following benefits:

  • outgoing emails are sent through rspamd
  • Replies module will work
  • Suspicious emails can be blocked
  • Greylisting is functional

Additional steps to your instructions:

  1. Disable default_check_message_pre on acl_smtp_date
  2. Use the following code block for custom_begin_spam_scan:
  warn   
        # Remove spam headers from outside sources
        remove_header  = x-spam-subject : x-spam-status : x-spam-score : x-spam-bar : x-spam-report : x-spam-flag : x-ham-report  

  accept condition = ${if match_ip{$sender_host_address}{net-iplsearch;/etc/trustedmailhosts}{1}{0}}
  
  accept hosts = : +loopback

  # add spam-score and spam-report header when told by rspamd
  # also scan outgoing messages
  warn  spam        = nobody:true
        log_message = "rspam_score: $spam_score ($spam_bar) rspam_report: $spam_report"
        !authenticated = *
        add_header = X-Spam-Score: $spam_score ($spam_bar)
        add_header = X-Spam-Report: $spam_report
        add_header = X-Spam-Action: $spam_action

  # allow authenticated users
  accept authenticated = *
        
  warn  condition  = ${if eq{$spam_action}{rewrite subject}}
        remove_header = Subject
        add_header = Subject: ***SPAM*** $h_subject
        add_header = X-Spam-Status: Yes

  defer message    = Please try again later
        condition  = ${if eq{$spam_action}{greylist}}
        
  defer message    = Please try again later
        condition  = ${if eq{$spam_action}{soft reject}}

  deny  message    = Message discarded as high-probability spam
        condition  = ${if eq{$spam_action}{reject}}

FYI @vstakhov

@mdpuma
Copy link
Author

mdpuma commented Jan 2, 2019

I'm looking forward towards improvement of the Exim integration document. Would you mind if I include your recipes to the official Rspamd documentation? Or you can add your own pull request against vstakhov/rspamd.com repo

Sure, you may include this recipe to official rspamd documentation.

@mdpuma
Copy link
Author

mdpuma commented Jan 2, 2019

I hope you don't mind, I improved upon your solution with the following benefits:

Thank, i have updated gist with some changes

@samuelmf
Copy link

samuelmf commented Oct 9, 2019

There are any video step by step? i'm not so good with english.

@mdpuma
Copy link
Author

mdpuma commented Feb 23, 2023

Hi,

This changes in exim config, apply filtering of emails for whole server, no matter what users have choosed. On rspamd side you may create more precise filters on demand, to filter out someone spam or to increase/decrease score of messages based on various criteria.

@jatin0123
Copy link

I want multiple users can set actions in rspamd configuration

@mdpuma
Copy link
Author

mdpuma commented Feb 23, 2023

Well, if you have time and knowledge, you may fork this gist and make required changes, but i think there need more than several changes in exim config.

@jatin0123
Copy link

jatin0123 commented Mar 10, 2023

I have configured Exim with the help of Rspamd documentation, but now I am facing the issue of not scanning outbound emails I also referred configuration from https://rspamd.com/doc/tutorials/scanning_outbound.html as you can see the configuration which I implemented in Exim.
Exim Configuration:-

 accept hosts = +loopback

 # do not scan messages from submission port (or maybe you want to?)
 accept condition = ${if eq{$interface_port}{587}}
 
   # Set default value for a variable
   warn
           set acl_m_outspam = 0
 
 # scan the message with rspamd
 warn spam = nobody:true
      log_message = "rspam_score: $spam_score ($spam_bar) rspam_report: $spam_report"
      add_header = X-Spam-Score: $spam_score ($spam_bar)
      add_header = X-Spam-Report: $spam_report
      add_header = X-Spam-Action: $spam_action

 deny
     ! authenticated = *
     condition = ${if eq{$spam_action}{reject}}
      message = Discarded high-probability spam

  # If it's our user set acl_m_outspam = 1 instead
 warn
         authenticated = *
         condition = ${if eq{$spam_action}{reject}}
         set acl_m_outspam = 1

 # Remove foreign headers
 warn remove_header = x-spam-bar : x-spam-score : x-spam-report : x-spam-status

 # add spam-score and spam-report header when "add header" action is recommended by rspamd
 warn
     condition = ${if eq{$spam_action}{add header}}
    add_header = X-Spam-Score: $spam_score ($spam_bar)
    add_header = X-Spam-Report: $spam_report

 # add x-spam-status header if message is not ham
 # do not match when $spam_action is empty (e.g. when rspamd is not running)
 warn
    ! condition = ${if match{$spam_action}{^no action$|^greylist$|^$}}
    add_header = X-Spam-Subject: SPAM $rh_subject
    add_header = X-Spam-Flag: Yes
    add_header = X-Spam-Status: Yes

  # add x-spam-bar header if score is positive
  warn
      condition = ${if >{$spam_score_int}{0}}
      add_header = X-Spam-Bar: $spam_bar

 accept

 begin routers

 # Apply special handling to messages with $acl_m_outspam==1
 redirect_outbound_spam:
 driver = redirect
 condition = ${if eq{$acl_m_outspam}{1}}
 data = $local_part@$domain

 # end of exim configuration

If I am not doing it correctly, please help me with the configuration to scan outbound and inbound mail.

@mdpuma
Copy link
Author

mdpuma commented Mar 10, 2023

In your case, first lines will accept email with authentication on port 587, without spam checking

# do not scan messages from submission port (or maybe you want to?)
accept condition = ${if eq{$interface_port}{587}}

@thomasstr
Copy link

Hi,

Is this setup for local rspamd?
What if you want to run it as a remote service? Just change the local IP to a remote?

@mdpuma
Copy link
Author

mdpuma commented Jun 4, 2023

Hello,

rspamd may be on same server or on remote, there is just one parameter change.

spamd_address = 127.0.0.1 11333 variant=rspamd

@jatin0123
Copy link

jatin0123 commented Jun 4, 2023 via email

@mdpuma
Copy link
Author

mdpuma commented Jun 4, 2023

Did you propose to create separate gist for remote rspamd setup, where just SINGLE line of configuration is different?

spamd_address = 127.0.0.1 11333 variant=rspamd

@thomasstr
Copy link

Hello,

rspamd may be on same server or on remote, there is just one parameter change.

spamd_address = 127.0.0.1 11333 variant=rspamd

Thanks. This setup is for incoming email right?
So then you would have to create global MX-pointers and DKIM, if you want a remote setup?

@mdpuma
Copy link
Author

mdpuma commented Jun 7, 2023

This config is useful for both, incoming and outgoing, this is defined by exim acl which every incoming message has to proceed (not matter incoming or outgoing).

MX pointers and DKIM didn't have anything in common with rspamd as software for antispam.

MX pointers anyway you have to configure to point to your mailserver, but rspamd, may reside on same server (localhost) or on remote server, but it doesn't matter for MX pointer.

@jatin0123
Copy link

jatin0123 commented Jun 7, 2023 via email

@AndersonOuverney
Copy link

Hello everyone!
We want to use rspamd as a centralized environment for spam filtering across our entire infrastructure.
We have some cpanels and directadmin servers.
We want to perform spam filtering remotely, to centralize a strong and robust database.
This topic gave me hope that this is really possible.

Is the script above working?
I saw that colleague @jatin0123 commented on checking outgoing emails as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment