Articles
Examples of practical Exim Filters to Manage Email on CentOS
Exim filter examples
-
Block email from a specific IP address:
deny message = Your message has been rejected because the sender is blacklisted.
senders = 123.123.123.123 -
Block email from a specific sender:
deny message = Your message has been rejected because the sender is blacklisted.
senders = *@example.com -
Block email containing certain keywords:
deny message = Your message has been rejected because it contains spam keywords.
message = This message has been blocked because it contains spam keywords.
condition = ${if match {$h_subject:}{Viagra|Cialis|gambling}{}} -
Block email with a suspicious attachment:
deny message = Your message has been rejected because it contains a suspicious attachment.
condition = ${if match {${lc:$h_content-type:}}{multipart/mixed}|\
match {${lc:$h_content-type:}}{multipart/alternative}|\
match {${lc:$h_content-type:}}{multipart/related}}
condition = ${if ! match {${lc:$h_content-type:}}{boundary=\".*?\"}} -
Block email from a specific country:
deny message = Your message has been rejected because it originates from a blacklisted country.
message = Sorry, we do not accept email from your country.
senders = /etc/exim/blacklisted_countries
Example bash script that creates an Exim filter rule to detect if two or more email addresses receive the same email - assuming the email is spam
When you're setting up a honey-pot or want to detect spam that's sent to multiple addresses, you can set up a bash script and exim rule that detects if two or more email addresses receive the same email and then block it or remove it. In this condition we want to assume the email is spam and we don't want to receive it.
#!/bin/bash
# Set the email addresses to check for duplicates
addresses=("user1@example.com" "user2@example.com" "user3@example.com")
# Set the spam message to be displayed
spam_message="Your message has been rejected because it appears to be spam."
# Set up the Exim filter rule to check for duplicates
cat <<EOF > /etc/exim/duplicate_emails
# Check for duplicate emails and reject them as spam
if first_delivery and ( $h_to:, $h_cc:, $h_bcc: ) matches "(${addresses[@]}){2,}" then
deny message = "${spam_message}"
endif
EOF
# Reload the Exim configuration to apply the new filter rule
exim -q -C /etc/exim/exim.conf
Here's what this script does:
- Set the email addresses to check for duplicates in an array named `addresses`.
- Set the spam message to be displayed if a duplicate email is detected in a variable named `spam_message`.
- Create an Exim filter rule in the file `/etc/exim/duplicate_emails` that checks if two or more of the email addresses in the `addresses` array receive the same email, and rejects it as spam if so.
- Reload the Exim configuration using the command `exim -q -C /etc/exim/exim.conf` to apply the new filter rule.
If I need to get the list of email addresses automatically rather than hard-coding the list
#!/bin/bash
# Set the path to the file containing the email addresses
addresses_file="/path/to/email/addresses.txt"
# Set the spam message to be displayed
spam_message="Your message has been rejected because it appears to be spam."
# Read the email addresses from the file into an array
addresses=($(cat "${addresses_file}"))
# Set up the Exim filter rule to check for duplicates
cat <<EOF > /etc/exim/duplicate_emails
# Check for duplicate emails and reject them as spam
if first_delivery and ( $h_to:, $h_cc:, $h_bcc: ) matches "(${addresses[@]}){2,}" then
deny message = "${spam_message}"
endif
EOF
# Reload the Exim configuration to apply the new filter rule
exim -q -C /etc/exim/exim.conf
Here's how it works:
- Set the path to the file containing the email addresses in a variable named `addresses_file`.
- Set the spam message to be displayed if a duplicate email is detected in a variable named `spam_message`.
- Read the email addresses from the file into an array named `addresses` using the `cat` command.
- Create an Exim filter rule in the file `/etc/exim/duplicate_emails` that checks if two or more of the email addresses in the `addresses` array receive the same email, and rejects it as spam if so.
- Reload the Exim configuration using the command `exim -q -C /etc/exim/exim.conf` to apply the new filter rule.
The email addresses file should contain one email address per line
Write an exim rule that will check if the email came from the same server as the domain name in the sender address so that I can block emails that appear to be from a particular sender but in reality are spoofed
Using the `host` command to lookup the IP address of the sending server and compare it with the IP address of the domain's mail server.
# Check if the email came from the same server as the domain name in the sender address
if $sender_address_domain and $h_received: matches "/from .* \[([^ \]]+)\]/" and \
!verify = host_lookup:${sender_address_domain} by ${1}\
then
# Reject the email as spoofed
deny message = "Email appears to be spoofed"
endif
Here's what this rule does:
- Check if the sender address has a domain name using `$sender_address_domain`.
- Use a regular expression to extract the IP address of the sending server from the `Received` header using `$h_received: matches "/from .* \[([^ \]]+)\]/"`.
- Use the `verify` command to lookup the IP address of the domain's mail server using `!verify = host_lookup:${sender_address_domain} by ${1}`. This command will return true if the sending server's IP address does not match the IP address of the domain's mail server.
- If the sending server's IP address does not match the IP address of the domain's mail server, reject the email as spoofed using `deny message = "Email appears to be spoofed"`.
This rule assumes that the mail server IP address of the domain can be looked up using the `host_lookup` command. If the mail server IP address is not publicly available or is hosted on a private network, this rule may not be effective. Additionally, this rule may generate false positives if the sending server uses a different IP address for sending emails than for other network traffic.
Using a Mattermost to update exim rules to add new spam filters into the exim rule file
If your team's using Mattermost for private, encrypted corporate communications, rather than one of the 3rd party, remotely hosted, unsafe alternatives, then you can create a bot to update Exim rules and add new spam filters from within Mattermost channels. By creating a custom Mattermost slash command that triggers a webhook to send a message to a server-side script, this script can then update the Exim configuration files as needed without having to expose server access to your team members.
Here's an example of how to set this up:
- Create a Mattermost bot account and obtain a webhook URL for it. You can do this by going to your Mattermost server, clicking on the three-dot menu at the top left, selecting "Integrations", and then "Incoming Webhooks". Click "Add Incoming Webhook" and follow the prompts to create a new webhook.
- From within the same menu, select "System Console", and then "Custom Integrations". Click "Add Custom Slash Command" and fill in the required information, including the webhook URL you obtained in step 1.
-
Create a server-side script that receives the webhook message and updates the Exim configuration files. Here's an example script that adds a new spam filter to the `/etc/exim/exim.conf` file:
#!/bin/bash
# Get the filter text from the webhook message
filter_text="$1"
# Add the filter text to the end of the exim configuration file
echo "$filter_text" >> /etc/exim/exim.conf
# Reload the exim configuration
exim -q -C /etc/exim/exim.conf
-
Set up a web server to receive the webhook messages from the Mattermost slash command. The web server should parse the incoming message and pass the filter text to the server-side script.
Here's an example of how the webhook message might appear:
{
"channel_id": "xxxxxxxxxxxx",
"channel_name": "test",
"command": "/addspamfilter",
"response_url": "https://your-server.com",
"team_domain": "your-team",
"team_id": "xxxxxxxxxxxx",
"text": "reject_domains = *@example.com",
"token": "xxxxxxxxxxxx",
"user_id": "xxxxxxxxxxxx",
"user_name": "your-username"
}
In this example, the filter text is contained in the `text` field of the message. You can extract this field using the appropriate programming language and pass it to the server-side script.
You should ensure that the web server and the server-side script are properly secured to prevent unauthorized access and protect the Exim configuration files.
Setting up a sentry fallback to local
I was exploring how to set up sentry on a client's php website but with the ability to capture the data locally to the server and not send the telemetry to the sentry servers where network access is blocked (e.g. UAT servers). You can capture the data locally to a mysql database instead.
Grap a copy of the open source Sentry SDK for PHP along with a local Sentry server such as Sentry On-Premise or Sentry Relay.
-
Install the Sentry SDK for PHP in your application. You can do this using Composer by adding the following line to your `composer.json` file:
"sentry/sentry": "^3.0"
Then run `composer update` to install the package. - Install and configure a local Sentry server such as Sentry On-Premise or Sentry Relay. These servers allow you to capture and store Sentry data locally instead of sending it to the Sentry servers.
-
Configure the Sentry SDK in your PHP application to send data to the local Sentry server. You can do this by adding the following code to your application's initialisation code:
use Sentry\SentrySdk;
SentrySdk::init([
'dsn' => 'http://localhost:8000',
'database' => [
'dsn' => 'mysql:host=localhost;dbname=sentry',
'user' => 'sentry',
'password' => 'sentry',
],
]);
In this example, the `dsn` parameter specifies the URL of the local Sentry server, and the `database` parameter specifies the MySQL database connection details for storing the Sentry data locally.
-
Create the MySQL database schema for storing the Sentry data. You can use the `sentry/sentry` package's built-in schema generation tool to generate the necessary SQL statements:
vendor/bin/sentry-sqlite migrate
This will generate the SQL statements to create the necessary tables and indexes in the MySQL database.
-
Ensure that your application has appropriate error handling and reporting logic in place. The Sentry SDK will automatically capture and report errors and exceptions, but you may need to customise this behavior to suit your needs.
With these steps in place, your application will capture and store Sentry data locally in a MySQL database, allowing you to analyze and debug errors and issues without sending telemetry data to the Sentry servers.
Email us right here
18 Years experience of full-stack development
2,000+ Projects completed
Contact
Drop us an email and tell us how we can help.
UK: +44 07703 577305
US: +1 310 489 5387