Skip to content

Commit b18c004

Browse files
committed
Performance optimizations and new sender classification features (see CHANGELOG.md)
1 parent d1c0926 commit b18c004

File tree

6 files changed

+252
-42
lines changed

6 files changed

+252
-42
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# Changelog
22

3+
## [2025-07-18] Improved performance
4+
5+
### Added
6+
- Grouping mode for bounce report: entries can now be grouped by sender address (`mailfrom`) when `GROUP_BY_FROM="true"` is set in config.
7+
- HTML group headers include sender and bounce count, styled with `.grouphead` class.
8+
- Visual improvement: added light gray background (`#e9e9e9`) to `.grouphead` rows for better readability.
9+
- Fully consistent HTML escaping of all dynamic content to avoid rendering issues.
10+
- New sender marking logic to identify bounced emails as **resent** (known senders sending from external domains) or **spoofing** (possible impersonation from internal domains).
11+
- Introduced configuration flags `RECIPIENTS_CHECK` and `SPOOFING_CHECK` to control sender verification and spoofing detection.
12+
- Added support for maintaining a known senders list via the helper script `postfix-build-submission-recipients.sh`.
13+
- Added separate documentation file `SENDER_MARKING.md` explaining the marking logic and setup in detail.
14+
15+
### Changed
16+
- Improved performance of postfix log parsing by replacing external `date` calls with inline `Time::Piece` Perl code.
17+
- Added explicit year handling in log timestamp parsing to ensure correct epoch comparison.
18+
- Optimized `generate_html_report()` function: moved all log parsing and HTML generation to a single `awk` block.
19+
- Report generation time reduced from **several minutes to a few seconds** for large log files.
20+
321
## [2025-07-11] Improved Version
422

523
### Changed

README.md

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ This script generates an HTML report based on rejected messages. Another script
1919
- [Clone the Repository](#clone-the-repository)
2020
- [Configuration](#configuration)
2121
- [Configuration via `.conf` File](#configuration-via-conf-file)
22+
- [Output Formatting Options](#output-formatting-options)
2223
- [Scheduled Execution (Cron)](#scheduled-execution-cron)
2324
- [Option A: User Crontab (crontab -e)](#option-a-user-crontab-crontab--e)
2425
- [Option B: System-wide Cron File (/etc/crontab)](#option-b-system-wide-cron-file-etccrontab)
2526
- [Log Rotation Compatibility](#log-rotation-compatibility)
27+
- [Sender Marking (optional)](#sender-marking-optional)
2628
- [Contributions](#contributions)
2729
- [License](#license)
2830

@@ -35,7 +37,7 @@ This script generates an HTML report based on rejected messages. Another script
3537
- The default subject is "[INFO] Postfix Bounce Report". A threshold can be set to change the subject to [WARNING]. If there is a match with the submission recipient list, [CRITICAL] is set.
3638

3739
> ⚠️ **Important Notice**
38-
> This script has undergone **significant changes** in the latest version (2025-07-11).
40+
> This script has undergone **significant changes** in the latest version (2025-07-18).
3941
> If you have used a previous version, please consult the updated [CHANGELOG.md](./CHANGELOG.md) before upgrading.
4042
> Existing setups may require adaptation to the new format and template handling.
4143
@@ -105,25 +107,39 @@ Using a `.conf` file eliminates the need for additional XML parser dependencies.
105107

106108
The script reads its settings from a configuration file. Below is a list of supported options:
107109

108-
| Variable | Description | Example Value(s) |
109-
| -------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------- |
110-
| `LC_ALL` | Locale setting for consistent date/time and string formatting during script execution. | `"de_DE.utf8"` |
111-
| `RED` | ANSI escape code for red terminal text. | `"\033[0;31m"` |
112-
| `GREEN` | ANSI escape code for green terminal text. | `"\033[0;32m"` |
113-
| `YELLOW` | ANSI escape code for yellow terminal text. | `"\033[0;33m"` |
114-
| `NC` | ANSI escape code to reset terminal color. | `"\033[0m"` |
115-
| `TEMPLATE_BASENAME` | Basename (without extension) of the HTML/CSS template files used for the report. | `"template.custom"` |
116-
| `MAILLOG` | Path to the Postfix mail log file to be analyzed. | `"/var/log/maillog"` |
117-
| `MAILLOG_PERIOD` | Time range in hours to scan for relevant log entries. | `"24"` |
118-
| `MAILLOG_PATTERN` | Regular expression to match relevant log lines (e.g. bounces or rejections). | `"blocked "` |
119-
| `LOGMAIL_FROM` | Email address used as sender of the bounce report. | `"do-not-reply@example.com"` |
120-
| `LOGMAIL_TO` | Email address of the recipient who will receive the report. | `"user@example.com"` |
121-
| `LOGMAIL_SUBJECT` | Subject line for the report email. | `"Postfix Bounce Report"` |
122-
| `RECIPIENTS_CHECK` | Enables verification of sender addresses against a known list when set to `true`. | `"true"` or `"false"` |
123-
| `RECIPIENTS_LIST` | Path to file with one valid recipient address per line. | `"/etc/postfix/submission_recipient"` |
124-
| `SEVERETY_THRESHOLD` | Numeric threshold to classify bounce severity (e.g. highlight known spoof attempts). | `"50"` |
125-
| `ONELINE` | Disables line wrapping and enables horizontal scrolling in the HTML report when set to `true`. | `"true"` or `"false"` |
126-
| `DOMAINS` | Pipe-separated list of trusted internal domains for spoof detection. | `"example.com"` |
110+
| Variable | Description | Example Value(s) |
111+
| -------------------- | -------------------------------------------------------------------------------------- | ------------------------------------- |
112+
| `LC_ALL` | Locale setting for consistent date/time and string formatting during script execution. | `"de_DE.utf8"` |
113+
| `RED` | ANSI escape code for red terminal text. | `"\033[0;31m"` |
114+
| `GREEN` | ANSI escape code for green terminal text. | `"\033[0;32m"` |
115+
| `YELLOW` | ANSI escape code for yellow terminal text. | `"\033[0;33m"` |
116+
| `NC` | ANSI escape code to reset terminal color. | `"\033[0m"` |
117+
| `TEMPLATE_BASENAME` | Basename (without extension) of the HTML/CSS template files used for the report. | `"template.custom"` |
118+
| `MAILLOG` | Path to the Postfix mail log file to be analyzed. | `"/var/log/maillog"` |
119+
| `MAILLOG_PERIOD` | Time range in hours to scan for relevant log entries. | `"24"` |
120+
| `MAILLOG_PATTERN` | Regular expression to match relevant log lines (e.g. bounces or rejections). | `"blocked "` |
121+
| `LOGMAIL_FROM` | Email address used as sender of the bounce report. | `"do-not-reply@example.com"` |
122+
| `LOGMAIL_TO` | Email address of the recipient who will receive the report. | `"user@example.com"` |
123+
| `LOGMAIL_SUBJECT` | Subject line for the report email. | `"Postfix Bounce Report"` |
124+
| `RECIPIENTS_CHECK` | Enables verification of sender addresses against a known list when set to `true`. | `"true"` or `"false"` |
125+
| `RECIPIENTS_LIST` | Path to file with one valid recipient address per line. | `"/etc/postfix/submission_recipient"` |
126+
| `SEVERETY_THRESHOLD` | Numeric threshold to classify bounce severity (e.g. highlight known spoof attempts). | `"50"` |
127+
| `DOMAINS` | Pipe-separated list of trusted internal domains for spoof detection. | `"example.com"` |
128+
129+
## Output Formatting Options
130+
131+
The script supports two options to control the formatting of the HTML report:
132+
133+
| Variable | Description |
134+
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
135+
| `ONELINE="true"` | Displays each bounce in a **single unwrapped row**. Long lines are not broken – horizontal scrolling may be required. Useful for **fast scanning** or **compact overviews**. |
136+
| `GROUP_BY_FROM="true"` | Groups all bounces by **sender address** (`from`). Each sender is shown with the **total number of bounces**, followed by a detailed list of individual bounces including **timestamp**, **recipient**, **IP**, and **rejection reason**. Ideal for **clustered analysis** of problematic senders. |
137+
138+
Example Use Cases:
139+
140+
- Set `ONELINE="true"` to review a high number of bounces quickly in a compact layout.
141+
- Set `GROUP_BY_FROM="true"` to identify which senders cause the most issues and inspect grouped bounce details.
142+
- Combine both for a condensed, grouped summary view – or set both to `false` for the full expanded format.
127143

128144
## Scheduled Execution (Cron)
129145

@@ -192,6 +208,10 @@ Or if your system uses systemd timers:
192208
systemctl list-timers --all | grep logrotate
193209
```
194210

211+
## Sender Marking (optional)
212+
213+
For detailed information about how bounced emails are marked as **resent** or **spoofing**, and how the known senders list is generated and used, see the separate documentation file [SENDER_MARKING.md](./SENDER_MARKING.md).
214+
195215
## Contributions
196216

197217
Contributions are welcome! If you would like to improve this project, please feel free to submit a pull request. All contributions, bug reports, and feature suggestions are appreciated.

SENDER_MARKING.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Sender Marking in Postfix Bounce Reports
2+
3+
This document explains the logic behind marking bounced emails as **resent** or **spoofing** in the postfix bounce report script, and how related configuration and helper scripts work together.
4+
5+
## Purpose of Markings
6+
7+
- **Resent**: Marks emails from senders that you have previously sent mail to (i.e., known recipients), but where the sending domain is **outside** your trusted domain list.
8+
These addresses are potentially trustworthy due to prior communication, but replies from external domains can cause false positives in bounce detection.
9+
10+
- **Spoofing**: Marks emails where the sender claims to be from one of your trusted internal domains but is **not recognized** as a known sender.
11+
This signals possible spoofing attempts aiming to impersonate legitimate internal users or domains.
12+
13+
## Configuration Flags
14+
15+
- `RECIPIENTS_CHECK`
16+
Enables checking if the sender is a known recipient based on an authenticated senders list. Necessary for correct **resent** marking.
17+
18+
- `SPOOFING_CHECK`
19+
Enables detection of spoofing attempts by marking emails with sender domains in your trusted list, regardless of sender recognition.
20+
21+
- `DOMAINS`
22+
A pipe-separated list of trusted internal email domains used to differentiate internal vs external domains.
23+
24+
## Known Senders List and Its Generation
25+
26+
The **known senders list** (`RECIPIENTS_LIST`) is a crucial data source used to identify trusted senders for the **resent** marking. This list is **continuously built and updated** by a separate helper script called `postfix-build-submission-recipients.sh`.
27+
28+
### Role of the Helper Script
29+
30+
- It parses the postfix mail logs for authenticated outbound emails (submission via SASL).
31+
- Extracts sender addresses who have legitimately sent mail through your system.
32+
- Normalizes and deduplicates these addresses.
33+
- Updates the `RECIPIENTS_LIST` file.
34+
35+
Maintaining this list is essential because without it, the bounce report script cannot reliably distinguish known senders from unknown ones, reducing the accuracy of the **resent** marking.
36+
37+
## Marking Logic
38+
39+
- If the sender is known (a recipient you have previously sent mail to) and their domain is **not** in the trusted domains list → mark as **resent**.
40+
This indicates mail coming from external domains tied to previously contacted addresses and can sometimes produce false positives.
41+
42+
- If the sender’s domain **is** in the trusted domains list → mark as **spoofing** (regardless of known status).
43+
This helps detect impersonation attempts using your internal domains.
44+
45+
## Summary
46+
47+
To use the marking effectively:
48+
49+
- Enable `RECIPIENTS_CHECK` for known recipient verification.
50+
- Enable `SPOOFING_CHECK` to detect spoofing attempts.
51+
- Keep the known senders list updated regularly with the helper script `postfix-build-submission-recipients.sh`.
52+
- Define your trusted domains clearly in the configuration.
53+
54+
Together, these mechanisms help you monitor and flag suspicious email activity in postfix bounce reports, while being aware that some resent markings may be false positives due to legitimate external replies.

postfix-bounce-report.conf.example

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# Locale (used for date parsing)
22
LC_ALL="de_DE.utf8"
33

4+
# Output formatting
5+
ONELINE="false"
6+
GROUP_BY_FROM="true"
7+
SEVERETY_THRESHOLD="50"
8+
49
# Terminal colors
510
RED="\033[0;31m"
611
GREEN="\033[0;32m"
@@ -24,7 +29,6 @@ LOGMAIL_SUBJECT="Postfix Bounce Report"
2429

2530
# Optional: validate MAILFROM
2631
RECIPIENTS_CHECK="true"
32+
SPOOFING_CHECK="true"
2733
RECIPIENTS_LIST="/etc/postfix/submission_recipient"
28-
SEVERETY_THRESHOLD="50"
29-
ONELINE="false"
3034
DOMAINS="example.com|example.org|example.net"

0 commit comments

Comments
 (0)