No description
| email_tunnel.py | ||
| email_tunnel.service | ||
| install_email_tunnel.sh | ||
| logs_email_tunnel.sh | ||
| README.md | ||
| uninstall_email_tunnel.sh | ||
email_tunnel
Poll Proton Bridge via IMAP and inject messages into a local SMTP server (e.g., Haraka). The script is designed for headless, continuous operation with basic safety/observability baked in.
What it does
- Connects to Proton Bridge IMAP via STARTTLS and selects a required folder.
- Polls for new messages by UID, fetches each, and relays it verbatim to a local SMTP server using the original headers.
- Strips
Bcc, deduplicates recipients, and skips malformed addresses. - Tracks progress in a JSON state file so messages are processed once, even across restarts.
- Retries IMAP fetch/search and SMTP delivery with bounded attempts/backoff; advances state past permanent delivery failures to avoid loops.
- Enforces single-instance execution via a pidfile lock.
- Emits scrape-friendly INFO logs each cycle with counts, last UID, and cycle duration.
Requirements
- Python 3.x
- Proton Bridge IMAP reachable at the configured host/port
- Local SMTP server reachable (e.g., Haraka bound to a non-25 port)
Configuration (environment variables)
IMAP_IP(required): Proton Bridge IMAP host.IMAP_PORT(required): Proton Bridge IMAP port (typically 1143).IMAP_USER(required): IMAP username.IMAP_PASSWORD(required): IMAP password.IMAP_FOLDER(required): IMAP folder to poll; must exist (no fallback).IMAP_TLS_VERIFY(default1): Set to0/falseto disable cert/hostname checks (dev only).SMTP_IP(default127.0.0.1): SMTP host to deliver to.SMTP_PORT(default2525): SMTP port to deliver to.POLL_INTERVAL(default15): Seconds between mailbox polls.STATE_FILE(default./state.json): Path to persist last processed UID.PIDFILE(default/tmp/email_tunnel.pid): Pidfile for single-instance lock.LOG_LEVEL(defaultINFO): Standard logging levels (DEBUG,INFO, etc.).
Usage
export IMAP_IP=127.0.0.1
export IMAP_PORT=1143
export IMAP_USER=...
export IMAP_PASSWORD=...
export IMAP_FOLDER=loomio
export SMTP_IP=127.0.0.1
export SMTP_PORT=2525
python3 email_tunnel.py
Run under a process supervisor (systemd, supervisord, etc.) so restarts and log rotation are handled.
Operational behavior
- State tracking:
STATE_FILErecords the last successfully handled UID. It is only advanced after successful delivery, or after a loggedDeliveryErrorto avoid reprocessing a permanently failed message. - Retries: IMAP SEARCH/FETCH and SMTP SEND are retried up to 3 times with small backoff. Persistent failures log and move on.
- Single instance: pidfile lock prevents multiple concurrent runs; stale pidfiles are replaced if the recorded PID is not running.
- Metrics logging: every poll cycle emits a single INFO line with
metricsprefix and key/value pairs (cycle_processed,cycle_delivered,cycle_failed,session_*,last_uid,cycle_duration_ms). - Missing state: if
STATE_FILEis absent on startup, the script initializes it to the current max UID (or 0 for an empty mailbox) to avoid reprocessing the entire mailbox.
Notes and safety
- Leave
IMAP_TLS_VERIFYenabled in production; disable only for trusted, local endpoints. - Ensure the
STATE_FILEandPIDFILEpaths are writable by the service user. - If delivering to a server that rejects certain recipients, the script will log the error, advance the UID, and continue.
Troubleshooting
- Increase verbosity with
LOG_LEVEL=DEBUGto see raw message dumps and IMAP folder listings. - If the script exits immediately, verify required env vars and that
IMAP_FOLDERexists on the server. - For pidfile conflicts, check the process holding the PID or remove a stale pidfile if no process is running.***