This commit is contained in:
@@ -1,147 +1,239 @@
|
|||||||
# Inbound Email (SMTP) to Webhook
|
# Inbound Email (SMTP) to Webhook
|
||||||
|
|
||||||
Author: Martin Krivosija - [LinkedIn](https://linkedin.com/in/martin-alexander-k)
|
Diese Version ist eine gehärtete Weiterentwicklung des ursprünglichen
|
||||||
|
Projekts. Sie nimmt E-Mails über SMTP an, parst Inhalt und Header, lädt Anhänge
|
||||||
|
optional nach Amazon S3 und sendet die Nachricht an einen HTTP(S)-Webhook.
|
||||||
|
|
||||||
A simple, efficient script that provides an SMTP server to receive emails, parse content (including headers), store attachments in Amazon S3, and forward email content to a webhook. Graceful handling of multiple concurrent SMTP sessions and webhook requests.
|
Originalentwickler: Martin Krivosija – [LinkedIn](https://linkedin.com/in/martin-alexander-k)
|
||||||
|
|
||||||
## Features
|
Dieses Repository ist von [`kriiv/inbound-email`](https://github.com/kriiv/inbound-email)
|
||||||
|
geforkt und wird hier als eigenständige, gehärtete Weiterentwicklung gepflegt.
|
||||||
|
|
||||||
- SMTP server to receive emails concurrently
|
Das Repository liegt unter:
|
||||||
- Parses incoming emails using `mailparser`
|
|
||||||
- Uploads attachments to Amazon S3
|
|
||||||
- Forwards parsed email content to a specified webhook
|
|
||||||
- Configurable via environment variables
|
|
||||||
- Handles large attachments gracefully
|
|
||||||
- Robust queue system for processing multiple emails and webhook requests simultaneously
|
|
||||||
|
|
||||||
## Prerequisites
|
```text
|
||||||
|
https://git.hacker.schule/mherrlein/inbound-email
|
||||||
|
```
|
||||||
|
|
||||||
- Node.js (v18 or later recommended)
|
## Funktionsweise
|
||||||
- If saving attachments, an Amazon Web Services (AWS) account with S3 access or a compatible system
|
|
||||||
- A HTTP(s) webhook endpoint to receive the processed emails
|
Der Zustellweg lautet:
|
||||||
|
|
||||||
|
```text
|
||||||
|
SMTP -> mailparser -> optional S3 -> persistenter Spool -> Webhook
|
||||||
|
```
|
||||||
|
|
||||||
|
Eine Nachricht wird vor der SMTP-Bestätigung als JSON unter `SPOOL_DIR/pending`
|
||||||
|
gespeichert. Erst nach erfolgreicher Webhook-Antwort wird sie gelöscht. Dadurch
|
||||||
|
überlebt die Zustellung Neustarts und vorübergehende Webhook-Fehler.
|
||||||
|
|
||||||
|
Die Zustellung ist „at least once“. Wenn der Prozess nach erfolgreicher
|
||||||
|
Webhook-Annahme, aber vor dem Löschen der Spool-Datei abstürzt, kann die
|
||||||
|
Nachricht doppelt zugestellt werden. Der Webhook sollte deshalb idempotent sein.
|
||||||
|
|
||||||
|
## Voraussetzungen
|
||||||
|
|
||||||
|
- Node.js 18 oder neuer und npm
|
||||||
|
- ein erreichbarer HTTP(S)-Webhook
|
||||||
|
- AWS-S3-Zugangsdaten und ein Bucket, wenn Anhänge gespeichert werden sollen
|
||||||
|
- bei Produktion ein eigener Dienstbenutzer, z. B. `smtpwebhook`
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
1. Clone this repository:
|
```bash
|
||||||
```
|
git clone https://git.hacker.schule/mherrlein/inbound-email.git
|
||||||
git clone https://github.com/kriiv/inbound-email.git
|
cd inbound-email
|
||||||
cd inbound-email
|
npm ci
|
||||||
```
|
cp .env.example .env
|
||||||
|
${EDITOR:-vi} .env
|
||||||
2. Install dependencies:
|
chmod 640 .env
|
||||||
```
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Copy the `.env.example` file to `.env` and set the required configuration: (eg. `mv .env.example .env`)
|
|
||||||
|
|
||||||
| Variable | Description | Required | Default |
|
|
||||||
| --------------------- | --------------------------------------------------------------- | -------- | ----------- |
|
|
||||||
| `WEBHOOK_URL` | The URL where parsed emails will be sent | Yes | `null` |
|
|
||||||
| `PORT` | The port for the SMTP server to listen on | No | `25` |
|
|
||||||
| `SMTP_SECURE` | Set to 'true' for TLS support (requires key/cert setup) | No | `false` |
|
|
||||||
| `WEBHOOK_CONCURRENCY` | Number of concurrent webhook requests | No | `5` |
|
|
||||||
| `MAX_FILE_SIZE` | Maximum attachment size in bytes (0 to disable S3 uploads) | No | `5242880` (5MB) |
|
|
||||||
| `AWS_REGION` | Your AWS region | If saving| `null` |
|
|
||||||
| `AWS_ACCESS_KEY_ID` | Your AWS access key ID | If saving| `null` |
|
|
||||||
| `AWS_SECRET_ACCESS_KEY`| Your AWS secret access key | If saving| `null` |
|
|
||||||
| `S3_BUCKET_NAME` | The name of your S3 bucket for storing attachments | If saving| `null` |
|
|
||||||
| `TLS_KEY_PATH` | Path to the TLS private key file (if `SMTP_SECURE=true`) | If secure| `null` |
|
|
||||||
| `TLS_CERT_PATH` | Path to the TLS certificate file (if `SMTP_SECURE=true`) | If secure| `null` |
|
|
||||||
|
|
||||||
*Note: S3 credentials (`AWS_*`, `S3_BUCKET_NAME`) are required if `MAX_FILE_SIZE` > 0.*
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Start the server:
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`WEBHOOK_URL` muss gesetzt werden. Für einen lokalen Test kann ein nicht
|
||||||
|
privilegierter Port verwendet werden:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PORT=25252 \
|
||||||
|
SPOOL_DIR=/tmp/smtpwebhook-spool \
|
||||||
|
LOG_DIR=/tmp/smtpwebhook-logs \
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
The SMTP server will start and listen on the specified port (default: 25) on all network interfaces.
|
Produktive Spool- und Log-Verzeichnisse müssen dem Dienstbenutzer gehören und
|
||||||
|
restriktiv geschützt sein:
|
||||||
|
|
||||||
You can use pm2 or supervisor to keep the server running after restart. Example: `pm2 start server.js`
|
```bash
|
||||||
|
install -d -o smtpwebhook -g smtpwebhook -m 700 \
|
||||||
|
/var/lib/smtpwebhook \
|
||||||
|
/var/lib/smtpwebhook/spool/pending \
|
||||||
|
/var/lib/smtpwebhook/spool/failed \
|
||||||
|
/var/lib/smtpwebhook/logs
|
||||||
|
```
|
||||||
|
|
||||||
## Sample Use Cases
|
## Konfiguration
|
||||||
|
|
||||||
1. **Email to Ticket System**: Use this bridge to receive support emails and automatically create tickets in your helpdesk system via the webhook.
|
Die Anwendung liest `.env` aus dem Arbeitsverzeichnis. `WEBHOOK_URL` ist
|
||||||
|
zwingend erforderlich und hat keinen öffentlichen Defaultwert.
|
||||||
|
|
||||||
2. **Document Processing**: Receive emails with document attachments, store them in S3, and trigger a document processing pipeline through the webhook.
|
| Variable | Bedeutung | Standardwert |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `WEBHOOK_URL` | Ziel-URL für die geparste Nachricht | erforderlich |
|
||||||
|
| `PORT` | SMTP-Port | `25` (Produktion: `2525`) |
|
||||||
|
| `SMTP_SECURE` | TLS/SMTPS beim Verbindungsaufbau | `false` |
|
||||||
|
| `TLS_KEY_PATH` | TLS-Privatschlüssel bei aktiviertem TLS | keiner |
|
||||||
|
| `TLS_CERT_PATH` | TLS-Zertifikat bei aktiviertem TLS | keines |
|
||||||
|
| `SMTP_ALLOWED_IPS` | Komma-separierte Quell-IP-Allowlist | leer = alle |
|
||||||
|
| `MAX_MESSAGE_SIZE` | Maximale Nachrichtengröße in Bytes; `0` deaktiviert das Limit | `26214400` (25 MiB) |
|
||||||
|
| `MAX_FILE_SIZE` | Maximale Größe eines S3-Anhangs in Bytes | `5242880` (5 MiB) |
|
||||||
|
| `MAX_SMTP_CLIENTS` | Maximale parallele SMTP-Verbindungen | `50` |
|
||||||
|
| `SMTP_SOCKET_TIMEOUT` | SMTP-Socket-Timeout in Millisekunden | `300000` |
|
||||||
|
| `SMTP_CLOSE_TIMEOUT` | Timeout für den Verbindungsabbau in Millisekunden | `10000` |
|
||||||
|
| `WEBHOOK_CONCURRENCY` | Parallele Webhook-Jobs | `5` |
|
||||||
|
| `WEBHOOK_QUEUE_RETRIES` | Queue-Wiederholungen bei Fehlern | `10` |
|
||||||
|
| `WEBHOOK_QUEUE_RETRY_DELAY` | Wartezeit zwischen Queue-Wiederholungen in Millisekunden | `60000` |
|
||||||
|
| `WEBHOOK_QUEUE_TIMEOUT` | Maximale Dauer eines Queue-Jobs in Millisekunden | `35000` |
|
||||||
|
| `SPOOL_DIR` | Basisverzeichnis des persistenten Spools | `/var/lib/smtpwebhook/spool` |
|
||||||
|
| `LOG_DIR` | Verzeichnis der aktuellen Anwendungslogs | `/var/lib/smtpwebhook/logs` |
|
||||||
|
| `AWS_REGION` | AWS-Region | keiner |
|
||||||
|
| `AWS_ACCESS_KEY_ID` | AWS-Zugangsschlüssel | keiner |
|
||||||
|
| `AWS_SECRET_ACCESS_KEY` | AWS-Geheimschlüssel | keiner |
|
||||||
|
| `S3_BUCKET_NAME` | S3-Bucket für Anhänge | keiner |
|
||||||
|
|
||||||
3. **Email Marketing Analysis**: Collect incoming emails from a campaign, store any images or attachments, and send the content to an analytics system for processing.
|
Wenn `MAX_FILE_SIZE=0` gesetzt ist, werden normale Anhänge nicht nach S3
|
||||||
|
hochgeladen. Für S3 müssen Bucket, Region und Zugangsdaten korrekt gesetzt sein.
|
||||||
|
Bei `SMTP_SECURE=true` müssen die TLS-Dateien für den Dienstbenutzer lesbar sein.
|
||||||
|
|
||||||
4. **Automated Reporting**: Set up an email address that receives automated reports, stores them in S3, and notifies your team via the webhook.
|
## Start und Produktion
|
||||||
|
|
||||||
5. **DMARC Reporting**: Receive DMARC reports via email and store them in S3.
|
Für einen Vordergrundstart:
|
||||||
|
|
||||||
Using inbound parse for something interesting? Please let me know, I'd love to hear about it.
|
```bash
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
## Todo
|
Die produktive Installation verwendet Supervisor. Der Prozess wird mit
|
||||||
|
`/usr/bin/node /opt/inbound-email/server.js` unter dem Benutzer `smtpwebhook`
|
||||||
|
gestartet. Die Konfiguration liegt außerhalb des Repositories unter
|
||||||
|
`/etc/supervisor/conf.d/inbound-email.conf`.
|
||||||
|
|
||||||
- Rate limiting
|
Nach Änderungen an der Supervisor-Konfiguration:
|
||||||
- ~~Log Storage~~ (completed)
|
|
||||||
|
|
||||||
## Contributing
|
```bash
|
||||||
|
supervisorctl -c /etc/supervisor/supervisord.conf reread
|
||||||
|
supervisorctl -c /etc/supervisor/supervisord.conf update inbound-email
|
||||||
|
supervisorctl -c /etc/supervisor/supervisord.conf status inbound-email
|
||||||
|
```
|
||||||
|
|
||||||
Contributions are welcome! Please feel free to submit a Pull Request or get in touch.
|
Bei Änderungen an Code oder `.env` genügt normalerweise:
|
||||||
|
|
||||||
## License
|
```bash
|
||||||
|
supervisorctl -c /etc/supervisor/supervisord.conf restart inbound-email
|
||||||
|
```
|
||||||
|
|
||||||
This project is licensed under the MIT License. This means you are free to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the software, including for commercial purposes.
|
Falls Supervisor nicht verfügbar ist, kann die Anwendung alternativ direkt als
|
||||||
|
systemd-Unit `smtpwebhook.service` betrieben werden. Eine gehärtete Beispiel-
|
||||||
|
Unit mit `EnvironmentFile`, `User=smtpwebhook`, `Restart=on-failure`,
|
||||||
|
`ProtectSystem=strict` und persistentem Spool ist in [AGENTS.md](AGENTS.md)
|
||||||
|
dokumentiert. Supervisor und die systemd-App-Unit dürfen nicht gleichzeitig
|
||||||
|
dieselbe Instanz starten.
|
||||||
|
|
||||||
## Disclaimer
|
## Spool und Zustellung
|
||||||
|
|
||||||
Please ensure you have the necessary permissions and security measures in place when deploying an SMTP server. Depending on your firewall configuration, you may be exposing this service to the internet.
|
Die Verzeichnisse haben folgende Bedeutung:
|
||||||
|
|
||||||
## Security Considerations
|
| Pfad | Zweck |
|
||||||
|
| --- | --- |
|
||||||
|
| `SPOOL_DIR/pending` | Noch nicht erfolgreich zugestellte Nachrichten |
|
||||||
|
| `SPOOL_DIR/failed` | Ungültige Spool-Dateien zur manuellen Prüfung |
|
||||||
|
| `LOG_DIR` | Aktuelle JSON-Anwendungslogs |
|
||||||
|
|
||||||
When deploying this SMTP server, please keep the following security considerations in mind:
|
Der Spool wird beim Start und etwa jede Minute erneut eingelesen. Bei einem
|
||||||
|
Webhook-Fehler bleibt die Nachricht in `pending`; sie wird nicht verworfen.
|
||||||
|
Unlesbare Spool-Dateien werden nach `failed` verschoben. Die JSON-Datei wird
|
||||||
|
erst nach erfolgreicher Webhook-Antwort gelöscht.
|
||||||
|
|
||||||
- Ensure that your server is properly secured and that only authorized IPs can access the SMTP port.
|
Die Zustellung ist „at least once“. Der Webhook muss doppelte Zustellungen
|
||||||
- Use strong, unique passwords for your AWS credentials and keep them secure.
|
vertragen können, insbesondere bei einem Absturz zwischen erfolgreicher
|
||||||
- Regularly update the Node.js runtime and all dependencies to their latest versions.
|
Webhook-Annahme und dem Löschen der Spool-Datei.
|
||||||
- Consider implementing additional authentication mechanisms for the SMTP server if needed.
|
|
||||||
|
|
||||||
## Logging and Monitoring
|
## Einsatzbeispiele
|
||||||
|
|
||||||
The server logs information about received emails, webhook responses, and any errors that occur. The current logging setup includes:
|
- E-Mail-to-Ticket-Systeme
|
||||||
|
- Dokumentenverarbeitung mit optionaler S3-Ablage
|
||||||
|
- Automatisierte Reports und Benachrichtigungen
|
||||||
|
- DMARC- oder andere maschinell erzeugte Reports
|
||||||
|
|
||||||
- Console output for immediate visibility
|
## Sicherheit und bekannte Grenzen
|
||||||
- Daily rotating log files for persistent storage
|
|
||||||
- JSON formatting of log entries for easy parsing
|
|
||||||
- Timestamp inclusion for each log entry
|
|
||||||
|
|
||||||
Logging settings:
|
- `.env`, Zugangsdaten, private TLS-Schlüssel, Logs und Spool-Dateien niemals
|
||||||
|
committen.
|
||||||
|
- `.env` produktiv restriktiv schützen, z. B. Besitzer `root`, Dienstgruppe als
|
||||||
|
Gruppe und Modus `640`.
|
||||||
|
- TLS-Schlüssel außerhalb des Repositories aufbewahren.
|
||||||
|
- SMTP AUTH ist deaktiviert. Bei leerer `SMTP_ALLOWED_IPS` ist der Listener für
|
||||||
|
alle erreichbaren IPs offen; Firewall oder Allowlist bewusst konfigurieren.
|
||||||
|
- Keine zweite Prozessverwaltung wie PM2 parallel zu Supervisor/systemd
|
||||||
|
einsetzen.
|
||||||
|
- Rate-Limiting ist derzeit nicht implementiert.
|
||||||
|
|
||||||
- Log files are stored in the `logs/` directory
|
## Logging und Fehleranalyse
|
||||||
- Files are named `application-YYYY-MM-DD.log`
|
|
||||||
- Log files are rotated daily and compressed
|
|
||||||
- Maximum log file size is set to 20MB
|
|
||||||
- Log files are kept for 90 days
|
|
||||||
|
|
||||||
I recommend:
|
`server.js` schreibt strukturierte JSON-Logs nach
|
||||||
|
`/var/lib/smtpwebhook/logs/application-YYYY-MM-DD.log`. Die Logs rotieren täglich
|
||||||
|
und ab 20 MiB; die Aufbewahrung beträgt ungefähr 90 Tage. Supervisor führt
|
||||||
|
zusätzlich `/var/log/inbound-email.out.log` und
|
||||||
|
`/var/log/inbound-email.err.log`.
|
||||||
|
|
||||||
- Review log files regularly for errors or unusual patterns.
|
Die wichtigsten Checks:
|
||||||
- Consider setting up log aggregation and analysis tools (e.g., ELK stack, Splunk).
|
|
||||||
- Implement alerts for critical errors or unusual activity patterns.
|
|
||||||
- Monitor system resources (CPU, memory, disk space) to ensure smooth operation.
|
|
||||||
- Set up uptime monitoring for the SMTP server and webhook endpoint.
|
|
||||||
|
|
||||||
## System Requirements
|
```bash
|
||||||
|
supervisorctl -c /etc/supervisor/supervisord.conf status inbound-email
|
||||||
|
ss -ltnp | grep ':2525'
|
||||||
|
ps -C node -o user,group,pid,ppid,stat,etime,args
|
||||||
|
find /var/lib/smtpwebhook/spool -maxdepth 2 -type f -ls
|
||||||
|
```
|
||||||
|
|
||||||
- Node.js v18 or later
|
`EADDRINUSE` bedeutet normalerweise, dass eine zweite oder verwaiste Instanz
|
||||||
- Sufficient disk space for temporary storage of attachments before S3 upload and 90 days of logging.
|
den Port belegt. Nicht einfach eine weitere Instanz starten, sondern zuerst den
|
||||||
- Outbound internet access for S3 uploads and webhook calls
|
Portbesitzer identifizieren. Die alten Logs unter `/opt/inbound-email/logs`
|
||||||
- Inbound access on the configured SMTP port. (Default: 25, or 587 if `SMTP_SECURE` is set to 'true')
|
sind historische Daten und nicht der aktuelle Spool.
|
||||||
|
|
||||||
## Troubleshooting
|
## Entwicklung und Tests
|
||||||
|
|
||||||
If you encounter issues:
|
Abhängigkeiten reproduzierbar installieren:
|
||||||
|
|
||||||
1. Check the server logs for any error messages.
|
```bash
|
||||||
2. Ensure all environment variables are correctly set.
|
npm ci
|
||||||
3. Verify that your AWS credentials have the necessary permissions for S3 operations.
|
```
|
||||||
4. Check that the webhook endpoint is accessible and responding correctly.
|
|
||||||
5. For attachment issues, verify that the `MAX_FILE_SIZE` setting is appropriate for your use case.
|
|
||||||
|
|
||||||
If problems persist, please open an issue on the GitHub repository with detailed information about the error and your setup.
|
Syntaxchecks:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node --check server.js
|
||||||
|
node --check config.js
|
||||||
|
git diff --check
|
||||||
|
```
|
||||||
|
|
||||||
|
Es gibt derzeit keine automatisierten Testfälle. Ein SMTP-EHLO/QUIT-Handshake
|
||||||
|
gegen einen lokalen Testport prüft nur Listener und Protokollannahme; S3 und
|
||||||
|
Webhook werden dadurch nicht getestet. Keine Testmail an ein produktives
|
||||||
|
Webhook-Ziel senden.
|
||||||
|
|
||||||
|
Das vorhandene `DockerFile` klont noch das öffentliche GitHub-Original und
|
||||||
|
bildet die lokale gehärtete Installation nicht automatisch ab. Vor einem
|
||||||
|
produktiven Docker-Einsatz Clone-URL, Branch, Spool-Persistenz und Logging
|
||||||
|
anpassen bzw. prüfen.
|
||||||
|
|
||||||
|
## Git und weitere Dokumentation
|
||||||
|
|
||||||
|
`origin` zeigt auf das private Repository
|
||||||
|
`https://git.hacker.schule/mherrlein/inbound-email`; `upstream` zeigt auf das
|
||||||
|
ursprüngliche GitHub-Projekt [`kriiv/inbound-email`](https://github.com/kriiv/inbound-email).
|
||||||
|
Keine History per Force-Push überschreiben.
|
||||||
|
|
||||||
|
Die detaillierte Referenz für Codex-Instanzen und die vollständige systemd-
|
||||||
|
Beispiel-Unit steht in [AGENTS.md](AGENTS.md).
|
||||||
|
|
||||||
|
## Lizenz
|
||||||
|
|
||||||
|
Dieses Projekt steht unter der MIT-Lizenz. Siehe [LICENSE](LICENSE).
|
||||||
|
|||||||
Reference in New Issue
Block a user