Postfix SMTP Server (F4.1)
Feature: F4.1 — Postfix SMTP Server
Status: 🟢 COMPLETED
Package:@vexlyx/api,@vexlyx/dashboard,@vexlyx/shared
Prisma Models:Domain,DnsRecord,Mailbox
Infrastructure: Postfix 3.x MTA, OpenDKIM Milter, RFC 6409 Submission
1. Overview
Postfix SMTP Server provides enterprise-grade outgoing email transport (MTA) and mail submission for all domains managed in Vexlyx.
Drawing from modern cloud-native standards (Mailcow, Plesk, CloudPanel, Resend), Vexlyx decouples outgoing mail into:
- RFC 6409 Port Separation: Clear segregation between port 25 (server-to-server relay/delivery with strict open relay denial) and port 587 (authenticated client submission with mandatory TLS encryption).
- Cryptographic DKIM Signing via OpenDKIM: Automated 2048-bit RSA key pair generation per domain, auto-configuring
KeyTable,SigningTable, andTrustedHosts, with direct integration into Vexlyx CoreDNS (F3.3) for instant one-click DNS TXT record deployment. - Decoupled Virtual Domain Sync: Virtual domain tables (
/etc/postfix/virtual_domains,virtual_mailbox_maps) synchronized as flat hash maps viapostfix_manager.py, isolating mail delivery from runtime database schema changes. - Hybrid Dev & Production Parity: Full containerized development environment in
docker-compose.yml(vexlyx-postfix) exposing ports 25 and 587, alongside a dedicated bare-metal production installer (system/scripts/setup-postfix.sh). - Interactive Diagnostic Sandbox: Real-time SMTP handshake testing and open relay verification directly within the Vexlyx dashboard.
2. Architecture & Data Flow
┌─────────────────────────────────────────────────────────────┐
│ Next.js Dashboard UI │
│ - /mail (Global Mail & Outgoing SMTP hub) │
│ - Daemon status & Port 25 / 587 health badges │
│ - Virtual domains accordion with DKIM DNS TXT copy helpers │
│ - Interactive SMTP Test Email & Anti-Relay Probe modals │
└──────────────────────────────┬──────────────────────────────┘
│ HTTP / JSON
▼
┌─────────────────────────────────────────────────────────────┐
│ Fastify API Server │
│ - GET /api/mail/status (Daemon & port status) │
│ - GET /api/mail/domains (List domains & DKIM state) │
│ - POST /api/mail/sync (Sync virtual domains) │
│ - POST /api/mail/dkim/:id (Generate DKIM / add to DNS) │
│ - POST /api/mail/test-send (Live SMTP test delivery) │
│ - GET /api/mail/test-relay (Probe open relay defense) │
└───────────────┬──────────────────────────────┬──────────────┘
│ │
▼ ▼
┌───────────────────────────────┐ ┌───────────────────────────┐
│ PostgreSQL (Prisma) │ │ system/python/ │
│ - model Domain │ │ postfix_manager.py │
│ - model DnsRecord │ │ - 2048-bit RSA DKIM keys │
│ - model Mailbox │ │ - sync virtual maps │
└───────────────────────────────┘ │ - handshake probing │
└─────────────┬─────────────┘
│ Configures
▼
┌───────────────────────────┐
│ Postfix & OpenDKIM Engine │
│ - Port 25 (SMTP MTA) │
│ - Port 587 (Submission) │
│ - Port 8891 (Milter) │
│ - Strict Anti-Open-Relay │
└───────────────────────────┘3. Configuration Specifications
Postfix main.cf (docker/postfix/main.cf)
text
# Identity
myhostname = mail.vexlyx.local
mydomain = vexlyx.local
myorigin = $mydomain
mydestination = localhost.$mydomain, localhost
inet_interfaces = all
inet_protocols = ipv4
# Virtual Domains & Mailboxes
virtual_mailbox_domains = /etc/postfix/virtual_domains
virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox_maps
virtual_alias_maps = hash:/etc/postfix/virtual_alias_maps
# Relay Security (No Open Relay)
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
# TLS Encryption
smtpd_tls_security_level = may
smtpd_tls_cert_file = /etc/postfix/certs/cert.pem
smtpd_tls_key_file = /etc/postfix/certs/key.pem
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
# OpenDKIM Milter
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = $smtpd_milters
milter_default_action = accept
milter_protocol = 6Postfix master.cf (docker/postfix/master.cf)
text
# Standard SMTP MTA
smtp inet n - n - - smtpd
# Submission Port 587 (RFC 6409)
submission inet n - n - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_reject_unlisted_recipient=no
-o smtpd_client_restrictions=permit_sasl_authenticated,permit_mynetworks,reject
-o smtpd_relay_restrictions=permit_sasl_authenticated,permit_mynetworks,reject
-o milter_macro_daemon_name=ORIGINATING4. OpenDKIM & DNS TXT Auto-Configuration
- Key Generation: 2048-bit RSA private keys are stored in
/etc/opendkim/keys/{domain}/{selector}.private. - OpenDKIM Configuration:
KeyTable:default._domainkey.domain.com domain.com:default:/etc/opendkim/keys/domain.com/default.privateSigningTable:*@domain.com default._domainkey.domain.comTrustedHosts: Local subnets and domain names.
- RFC 6376 DNS TXT Record:
- Record Name:
default._domainkey.domain.com - Record Value:
v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCA... - Auto-inserted into
model DnsRecordzone when CoreDNS (F3.3) is active.
- Record Name:
5. Testing & Verification
Run the dedicated test suite:
bash
python tests/test_postfix_smtp.pyCovered tests:
test_01_generate_dkim_keys_and_dns_record: Verifies 2048-bit key generation andv=DKIM1; k=rsa; p=...format.test_02_opendkim_tables_updated: VerifiesKeyTable,SigningTable, andTrustedHosts.test_03_get_dkim_existing: Checks retrieving existing DKIM keys from disk.test_04_sync_virtual_domains: Verifies flat map creation for virtual domains and mailboxes.test_05_status_inspection: Verifies service status structure.test_01_main_cf_security_parameters: Checksmain.cfparameters.test_02_master_cf_submission_service: Checks port 587 submission parameters.test_03_opendkim_conf_parameters: Checksopendkim.conf.test_04_host_setup_script_exists: Validates VPS installation script.test_01_unauthenticated_requests_return_401: Enforces security on all mail endpoints.
6. How to Extend
- F4.2 Dovecot IMAP Server (🟢 COMPLETED): Postfix is configured with
smtpd_sasl_type = dovecotandsmtpd_sasl_path = inet:dovecot:12345(a TCP auth listener, not a shared unix socket — Postfix and Dovecot run in separate containers). Seedocs/dev/email/dovecot.md. - F4.3 Mailbox Management UI: The virtual mailbox map structure (
/etc/postfix/virtual_mailbox_maps) is ready for per-user quota and address provisioning. - F4.5 SPF & DMARC Auto-Configuration: The DKIM public keys generated in F4.1 directly populate the DMARC and SPF alignment policies.