Skip to content

Modern REST API for managing Dovecot/Postfix mail server with LDA routing and Fetchmail integration.

Notifications You must be signed in to change notification settings

patrikcelko/Mail-Admin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Mail Admin API

Modern REST API for managing Dovecot/Postfix mail server with LDA routing and Fetchmail integration

A minimalistic FastAPI based administration interface for complete mail server management. Provides secure endpoints for user management, routing configuration, and centralized log viewing across Dovecot, Postfix, and Fetchmail services.

System Requirements

Required Services

  • Python 3.10 or higher
  • Dovecot - IMAP/POP3 mail server
  • Postfix - SMTP mail transfer agent
  • Fetchmail - Remote mail retrieval (optional but recommended)
  • systemd - For service management

Operating System

  • Linux (Ubuntu 20.04+, Debian 11+, or similar)
  • systemctl support for service reloading

Installation

1. System Dependencies

Install required mail server components:

# Install Dovecot
sudo apt install dovecot-core dovecot-imapd dovecot-pop3d

# Install Postfix
sudo apt install postfix

# Install Fetchmail (for retrieving mail from remote servers)
sudo apt install fetchmail

# Install Python development tools
sudo apt install python3-pip python3-venv

2. Clone Repository

git clone https://github.com/patrikcelko/Mail-Admin.git
cd Mail-Admin

4. Configuration

Create configuration file:

nano .env

Required environment variables:

# Authentication (REQUIRED)
MAIL_ADMIN_USER=admin
MAIL_ADMIN_PASS=secure_password_here

# Server Settings (OPTIONAL)
MAIL_ADMIN_HOST=0.0.0.0 # Bind address
MAIL_ADMIN_PORT=8000 # Listen port
MAIL_ADMIN_ALLOW_DEBUG=false # Enable debug mode
MAIL_ADMIN_HEALTH_LOGS_LIMIT=50 # Health endpoint log count

Load configuration (if needed):

source .env

5. Fetchmail Integration

Configure Fetchmail to retrieve emails from remote servers:

sudo nano /etc/fetchmailrc

Example configuration:

poll <imap>.net proto imap port 993 envelope "Received":
  user "index@<email_domain>" password "<password>"
  mda "/usr/local/bin/lda-alias %T %F"
  ssl
  sslcertck
  nokeep
  fetchall
  no rewrite
  to *

Set permissions:

sudo chmod 600 /etc/fetchmailrc
sudo chown fetchmail:fetchmail /etc/fetchmailrc

Enable Fetchmail service:

sudo systemctl enable fetchmail
sudo systemctl start fetchmail

6. Setup LDA manager

sudo nano /usr/local/bin/lda-alias

Example lda:

#!/usr/bin/env bash
set -euo pipefail

MAP_FILE="/etc/lda-maps.conf"

TMP="$(mktemp)"
cat > "$TMP"

rcpt_orig="${1:-}"
sender="${2:-}"

normalize_addr() {
  sed -E 's/^[[:space:]]*<?([^>[:space:]]+)>?[[:space:]]*$/\1/i'
}

# --- Determination of rcpt_orig from headers ---
if [[ -z "${rcpt_orig}" || "${rcpt_orig}" == "fetchmail" || "${rcpt_orig}" != *"@"* ]]; then
  if grep -qi '^Delivered-To:' "$TMP"; then
    rcpt_orig="$(grep -i '^Delivered-To:' "$TMP" | head -n1 | cut -d: -f2- | normalize_addr)"
  fi
fi

if [[ -z "${rcpt_orig}" || "${rcpt_orig}" == "fetchmail" || "${rcpt_orig}" != *"@"* ]]; then
  if grep -qi '^X-Original-To:' "$TMP"; then
    rcpt_orig="$(grep -i '^X-Original-To:' "$TMP" | head -n1 | cut -d: -f2- | normalize_addr)"
  fi
fi

if [[ -z "${rcpt_orig}" || "${rcpt_orig}" == "fetchmail" || "${rcpt_orig}" != *"@"* ]]; then
  if grep -qi '^Received:' "$TMP"; then
    cand="$(grep -oi 'for <[^>]\+>' "$TMP" | tail -n1 | sed -E 's/^for <([^>]+)>/\1/i')"
    if [[ -n "${cand}" ]]; then
      rcpt_orig="$(echo "$cand" | normalize_addr)"
    fi
  fi
fi

if [[ -z "${rcpt_orig}" || "${rcpt_orig}" == "fetchmail" || "${rcpt_orig}" != *"@"* ]]; then
  if grep -qi '^To:' "$TMP"; then
    rcpt_orig="$(grep -i '^To:' "$TMP" | head -n1 | sed -E 's/^To:[[:space:]]*//i' | sed 's/,.*$//' | normalize_addr)"
  fi
fi

if [[ -z "${rcpt_orig}" || "${rcpt_orig}" == "fetchmail" || "${rcpt_orig}" != *"@"* ]]; then
  logger -t lda-alias "ERROR: unknown rcpt; sender=$sender"
  rm -f "$TMP"
  exit 67
fi

rcpt="$rcpt_orig"

# --- mapping from /etc/lda-maps.conf ---
if [[ -f "$MAP_FILE" ]]; then
  while read -r pat dest rest; do
    [[ -z "${pat:-}" ]] && continue
    [[ "${pat:0:1}" == "#" ]] && continue

    if [[ "$rcpt" == $pat ]]; then
      rcpt="$dest"
      break
    fi
  done < "$MAP_FILE"
fi

logger -t lda-alias "orig_rcpt=$rcpt_orig mapped_rcpt=$rcpt sender=${sender:-}"

ERR_TMP="$(mktemp)"
if /usr/lib/dovecot/dovecot-lda -f "${sender:-}" -a "${rcpt_orig}" -d "${rcpt}" < "$TMP" 2>"$ERR_TMP"; then
  rm -f "$TMP" "$ERR_TMP"
  exit 0
else
  st=$?
  err_msg="$(cat "$ERR_TMP" 2>/dev/null || echo 'no stderr')"
  logger -t lda-alias "ERROR: dovecot-lda exit=$st for $rcpt (orig=$rcpt_orig) stderr: $err_msg"
  rm -f "$TMP" "$ERR_TMP"
  exit $st
fi

7. Start service

Manual Start

python -m mail_admin

Start as systemd service

For production deployment with automatic startup on boot:

  • I. Create systemd service file:

    sudo nano /etc/systemd/system/mail-admin.service
  • II. Add the following configuration:

    [Unit]
    Description=Mail Admin API
    After=network.target dovecot.service postfix.service
    Wants=dovecot.service postfix.service
    
    [Service]
    Type=simple
    User=root
    Group=root
    WorkingDirectory=<working dir>
    
    # Environment variables
    Environment="MAIL_ADMIN_USER=admin"
    Environment="MAIL_ADMIN_PASS=change_me"
    Environment="MAIL_ADMIN_HOST=0.0.0.0"
    Environment="MAIL_ADMIN_PORT=8000"
    Environment="MAIL_ADMIN_ALLOW_DEBUG=false"
    Environment="MAIL_ADMIN_HEALTH_LOGS_LIMIT=50"
    
    # Virtual environment and Python executable
    ExecStart=python3 -m mail_admin
    
    # Restart policy
    Restart=always
    RestartSec=5
    
    # Security
    NoNewPrivileges=false
    PrivateTmp=true
    
    # Logging
    StandardOutput=journal
    StandardError=journal
    SyslogIdentifier=mail-admin-api
    
    [Install]
    WantedBy=multi-user.target
  • III. Enable and start the service:

    # Enable service to start on boot
    sudo systemctl enable mail-admin.service
    
    # Start service now
    sudo systemctl start mail-admin.service

API Endpoints

Authentication: All endpoints require HTTP Basic Authentication

Health Check

  • GET /health

    Check API health and view recent errors


User Management

  • GET /users

    List all mail users

  • POST /users

    Create a new mail user

    • Creates Dovecot user entry
    • Generates Maildir structure
    • Adds Postfix virtual mapping
    • Auto-generates 20-character password if not provided

    Example (body):

    {
      "email": "[email protected]",
      "password": "optional_custom_password"
    }
  • DELETE /users/{email}

    Delete a mail user

    Example: DELETE /users/[email protected]

  • PUT /users/{email}/password

    Update user password

    Example (body):

    {
      "password": "new_secure_password"
    }

Routing Management

  • GET /routing

    Get all LDA routing rules

  • POST /routing/rules

    Add a new routing rule

    Example (body):

    {
      "pattern": "sales@*",
      "destination": "[email protected]"
    }
  • PUT /routing

    Replace all routing rules

  • DELETE /routing/rules/{index}

    Delete a routing rule by index

    Example: DELETE /routing/rules/0


Log Viewing

  • GET /logs

    View aggregated logs from mail_admin, Dovecot, Postfix, and Fetchmail

    Query Parameters:

    • page - Page number (0-indexed, default: 0)

    • page_size - Items per page (default: 50, max: 500)

    • level - Filter by level: DEBUG, INFO, WARNING, ERROR, CRITICAL

    • sources - Filter by sources (comma-separated): mail_admin, dovecot, postfix, fetchmail

      Examples:

      # Get recent errors
      GET /logs?level=ERROR&page_size=20
      
      # View Postfix logs only
      GET /logs?sources=postfix
      
      # Pagination
      GET /logs?page=2&page_size=100

Webhooks

  • GET /webhooks

    List all configured webhooks, optionally filtered by group

    Example: GET /webhooks

    Example (group filter): GET /webhooks?group=notifications

  • GET /webhooks/{webhook_id}

    Get details of a specific webhook

    Example: GET /webhooks/550e8400-e29b-41d4-a716-446655440000

  • POST /webhooks

    Create a new webhook that triggers when an email pattern matches in logs

    Example (body):

    {
      "name": "Support Email Alerts",
      "email_pattern": "support@example\\.com",
      "webhook_url": "https://your-service.com/webhook",
      "enabled": true
    }

    Parameters:

    • name - Descriptive name for the webhook
    • email_pattern - Regex pattern to match email addresses in logs
    • webhook_url - URL to POST to when pattern matches
    • enabled - Whether the webhook is active (default: true)
  • PATCH /webhooks/{webhook_id}

    Update an existing webhook (all fields optional)

    Example: PATCH /webhooks/550e8400-e29b-41d4-a716-446655440000

  • DELETE /webhooks/{webhook_id}

    Delete a webhook

    Example: DELETE /webhooks/550e8400-e29b-41d4-a716-446655440000

Webhook Payload Format

When a webhook is triggered, the following JSON is POST-ed to the configured URL:

{
  "webhook_id": "550e8400-e29b-41d4-a716-446655440000",
  "webhook_name": "Support Email Alerts",
  "email": "[email protected]",
  "log_entry": "[postfix] Message received from [email protected]",
  "timestamp": "2025-12-08T10:30:45.123456",
  "pattern": "support@example\\.com"
}

LDA Integration for Immediate Webhook Triggers

For real-time webhook notifications when emails arrive, you can integrate webhooks directly into your LDA (Local Delivery Agent) script. This allows webhooks to fire immediately when an email is delivered.

LDA Trigger Endpoint

  • POST /webhooks/lda-trigger

    Trigger webhooks immediately from the LDA script (localhost only, no authentication required)

    Security: This endpoint only accepts requests from localhost (127.0.0.1, ::1) for security. External requests will be rejected with 403 Forbidden.

    Example:

    curl -X POST http://localhost:8000/webhooks/lda-trigger \
      -H "Content-Type: application/json" \
      -d '{
        "recipient": "[email protected]",
        "sender": "[email protected]",
        "subject": "New message"
      }'

    Parameters:

    • recipient - The email address receiving the message (required)

    • sender - The sender's email address (optional)

    • subject - The email subject line (optional)

      Note: All webhooks matching the recipient's email pattern will be triggered, regardless of their group.

      Response:

      {
        "status": "success",
        "triggered_count": 2,
        "message": "Triggered 2 webhook(s)"
      }

Integrating with LDA-alias script

Add webhook triggering to your lda-alias script (usually in /usr/local/bin/lda-alias):

#!/bin/bash

# Configuration
WEBHOOK_ENABLED=true
WEBHOOK_URL="http://localhost:8000/webhooks/lda-trigger"

# Get email details from environment or arguments
RECIPIENT="${RECIPIENT:-$1}"
SENDER="${SENDER:-unknown}"
SUBJECT="${SUBJECT:-No subject}"

# ... your existing LDA stuff here ...

# Trigger webhooks after successful delivery
if [ "$WEBHOOK_ENABLED" = true ]; then
    curl -s -X POST "$WEBHOOK_URL" \
      -H "Content-Type: application/json" \
      -d "{
        \"recipient\": \"$RECIPIENT\",
        \"sender\": \"$SENDER\",
        \"subject\": \"$SUBJECT\"
        \"group\": \"$WEBHOOK_GROUP\"
      }" > /dev/null 2>&1 &
fi

Example Usage

  • Create user:

    curl -X POST http://localhost:8000/users \
      -u admin:password \
      -H "Content-Type: application/json" \
      -d '{"email": "[email protected]"}'
  • View error logs:

    curl -X GET "http://localhost:8000/logs?level=ERROR&page_size=10" \
      -u admin:password | jq
  • Add routing rule:

    curl -X POST http://localhost:8000/routing/rules \
      -u admin:password \
      -H "Content-Type: application/json" \
      -d '{
        "pattern": "support@*.example.com",
        "destination": "[email protected]"
      }'
  • List all users:

    curl -X GET http://localhost:8000/users \
      -u admin:password | jq
  • Create webhook:

    curl -X POST http://localhost:8000/webhooks \
      -u admin:password \
      -H "Content-Type: application/json" \
      -d '{
        "name": "New User Notifications",
        "email_pattern": ".*@newdomain\\.com",
        "webhook_url": "https://hooks.slack.com/services/YOUR/WEBHOOK/URL",
        "enabled": true
      }'
  • List webhooks:

    curl -X GET http://localhost:8000/webhooks \
      -u admin:password | jq

License

This project is open source. Feel free to use and modify as needed.

About

Modern REST API for managing Dovecot/Postfix mail server with LDA routing and Fetchmail integration.

Topics

Resources

Stars

Watchers

Forks