Email Gateway — Postfix Setup
This guide covers how to install and configure Postfix as the MTA for a BrightChain Email Gateway node. It applies to both primary (outbound + inbound) and secondary/relay (inbound-only or relay) node roles.
Prerequisites
- A Linux server (Debian/Ubuntu or RHEL/CentOS) with root access
- A public IP address with a valid reverse DNS (PTR) record matching your mail hostname
- DNS control for your BrightChain domain (e.g.
brightchain.org) - BrightChain node software installed and running
- Node.js 18+ (for the gateway services)
1. Install Postfix
Debian / Ubuntu
sudo apt update
sudo apt install postfix libsasl2-modules
Select Internet Site when prompted. Set the system mail name to your BrightChain domain (e.g. brightchain.org).
RHEL / CentOS
sudo dnf install postfix cyrus-sasl-plain
sudo systemctl enable postfix
2. DNS Records
Before configuring Postfix, set up the required DNS records for your domain.
MX Record
Point your domain’s MX record to the gateway host:
brightchain.org. IN MX 10 mail.brightchain.org.
For secondary nodes, add additional MX records with higher priority values:
brightchain.org. IN MX 10 mail1.brightchain.org.
brightchain.org. IN MX 20 mail2.brightchain.org.
A / AAAA Records
mail.brightchain.org. IN A 203.0.113.10
mail.brightchain.org. IN AAAA 2001:db8::10
PTR Record (Reverse DNS)
Contact your hosting provider to set the PTR record for your IP to match the mail hostname:
10.113.0.203.in-addr.arpa. IN PTR mail.brightchain.org.
SPF Record
Authorize your gateway server(s) to send email for the domain:
brightchain.org. IN TXT "v=spf1 mx a:mail.brightchain.org -all"
For multiple gateway nodes:
brightchain.org. IN TXT "v=spf1 mx a:mail1.brightchain.org a:mail2.brightchain.org -all"
DMARC Record
Publish a DMARC policy. Start with p=none for monitoring, then tighten to p=quarantine or p=reject:
_dmarc.brightchain.org. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@brightchain.org; pct=100"
DKIM DNS Record
After generating your DKIM key (see section 6), publish the public key:
default._domainkey.brightchain.org. IN TXT "v=DKIM1; k=rsa; p=<base64-public-key>"
The selector (default above) must match GATEWAY_DKIM_SELECTOR.
3. Primary Node Configuration
A primary node handles both outbound delivery and inbound reception.
/etc/postfix/main.cf
# Basic identity
myhostname = mail.brightchain.org
mydomain = brightchain.org
myorigin = $mydomain
mydestination =
# Network settings
inet_interfaces = all
inet_protocols = all
# Relay and transport
relayhost =
mynetworks = 127.0.0.0/8 [::1]/128
# Recipient validation via BrightChain Recipient Lookup Service (socketmap)
# The gateway's RecipientLookupService listens on TCP port 2526.
virtual_mailbox_domains = brightchain.org
virtual_mailbox_maps = socketmap:inet:127.0.0.1:2526:virtual
# Inbound mail delivery — deposit into Mail Drop Directory
virtual_transport = brightchain-drop
mailbox_size_limit = 26214400
# TLS — inbound (opportunistic)
smtpd_tls_cert_file = /etc/ssl/certs/mail.brightchain.org.pem
smtpd_tls_key_file = /etc/ssl/private/mail.brightchain.org.key
smtpd_tls_security_level = may
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_ciphers = high
smtpd_tls_mandatory_ciphers = high
# TLS — outbound (opportunistic, prefer encryption)
smtp_tls_security_level = may
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_ciphers = high
smtp_tls_loglevel = 1
# Message size limit (matches GATEWAY_MAX_MESSAGE_SIZE, default 25 MB)
message_size_limit = 26214400
# Milter integration for anti-spam (SpamAssassin or Rspamd)
# Uncomment the appropriate line based on your spam engine.
# SpamAssassin (via spamass-milter):
# smtpd_milters = unix:/run/spamass-milter/spamass-milter.sock
# Rspamd:
# smtpd_milters = inet:127.0.0.1:11332
# non_smtpd_milters = $smtpd_milters
# milter_default_action = accept
# DKIM signing via OpenDKIM milter
# smtpd_milters = inet:127.0.0.1:8891
# non_smtpd_milters = inet:127.0.0.1:8891
# milter_protocol = 6
# milter_default_action = accept
/etc/postfix/master.cf
Add the brightchain-drop transport that deposits mail into the Mail Drop Directory:
# ==========================================================================
# service type private unpriv chroot wakeup maxproc command + args
# ==========================================================================
# Standard SMTP listener
smtp inet n - y - - smtpd
# Submission port (587) for authenticated outbound from BrightChain
submission inet n - y - - 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_recipient_restrictions=permit_sasl_authenticated,reject
# BrightChain Mail Drop transport
# Deposits accepted inbound mail into the Mail Drop Directory as individual files.
brightchain-drop unix - n n - - pipe
flags=DRhu user=brightchain
argv=/usr/local/bin/brightchain-maildrop ${sender} ${recipient}
The brightchain-maildrop script is a simple shell wrapper that writes stdin to a unique file in the Mail Drop Directory:
#!/bin/bash
# /usr/local/bin/brightchain-maildrop
# Deposits a message into the BrightChain Mail Drop Directory.
MAIL_DROP_DIR="/var/spool/brightchain/incoming"
FILENAME="$(date +%s).$(hostname).$$.$RANDOM"
cat > "${MAIL_DROP_DIR}/tmp/${FILENAME}"
mv "${MAIL_DROP_DIR}/tmp/${FILENAME}" "${MAIL_DROP_DIR}/new/${FILENAME}"
exit 0
Make it executable:
sudo chmod +x /usr/local/bin/brightchain-maildrop
4. Secondary / Relay Node Configuration
A secondary node accepts inbound mail and relays it to the primary, or acts as a backup MX. It does not run the full outbound delivery pipeline.
/etc/postfix/main.cf (secondary)
myhostname = mail2.brightchain.org
mydomain = brightchain.org
myorigin = $mydomain
mydestination =
inet_interfaces = all
inet_protocols = all
# Relay to the primary node
relayhost = [mail.brightchain.org]:25
relay_domains = brightchain.org
# Recipient validation — same socketmap lookup against the local
# BrightChain node's RecipientLookupService
virtual_mailbox_domains = brightchain.org
virtual_mailbox_maps = socketmap:inet:127.0.0.1:2526:virtual
# TLS (same settings as primary)
smtpd_tls_cert_file = /etc/ssl/certs/mail2.brightchain.org.pem
smtpd_tls_key_file = /etc/ssl/private/mail2.brightchain.org.key
smtpd_tls_security_level = may
smtp_tls_security_level = may
message_size_limit = 26214400
The secondary node still runs the RecipientLookupService locally so it can reject unknown recipients at SMTP time. The BrightChain user registry is replicated across nodes via the gossip protocol.
5. Mail Drop Directory Setup
Create the Maildir-compatible directory structure and set permissions:
# Create directories
sudo mkdir -p /var/spool/brightchain/incoming/new
sudo mkdir -p /var/spool/brightchain/incoming/cur
sudo mkdir -p /var/spool/brightchain/incoming/tmp
sudo mkdir -p /var/spool/brightchain/errors
# Create the brightchain system user (if not already present)
sudo useradd -r -s /usr/sbin/nologin -d /var/spool/brightchain brightchain
# Set ownership and permissions
sudo chown -R brightchain:brightchain /var/spool/brightchain
sudo chmod 750 /var/spool/brightchain/incoming
sudo chmod 750 /var/spool/brightchain/incoming/new
sudo chmod 750 /var/spool/brightchain/incoming/cur
sudo chmod 750 /var/spool/brightchain/incoming/tmp
sudo chmod 750 /var/spool/brightchain/errors
The InboundProcessor watches /var/spool/brightchain/incoming/ (configurable via GATEWAY_MAIL_DROP_DIR). Failed messages are moved to /var/spool/brightchain/errors/ (configurable via GATEWAY_ERROR_DIR).
6. DKIM Signing Setup (OpenDKIM)
Install OpenDKIM
# Debian/Ubuntu
sudo apt install opendkim opendkim-tools
# RHEL/CentOS
sudo dnf install opendkim opendkim-tools
Generate DKIM Key Pair
sudo mkdir -p /etc/dkim
sudo opendkim-genkey -s default -d brightchain.org -D /etc/dkim
sudo chown opendkim:opendkim /etc/dkim/default.private
sudo chmod 600 /etc/dkim/default.private
This creates:
/etc/dkim/default.private— the private key (setGATEWAY_DKIM_KEY_PATHto this path)/etc/dkim/default.txt— the DNS TXT record to publish
Configure OpenDKIM
/etc/opendkim.conf:
Syslog yes
UMask 007
Socket inet:8891@127.0.0.1
PidFile /run/opendkim/opendkim.pid
OversignHeaders From
TrustAnchorFile /usr/share/dns/root.key
Domain brightchain.org
KeyFile /etc/dkim/default.private
Selector default
Canonicalization relaxed/simple
Mode sv
SubDomains no
Enable and Start
sudo systemctl enable opendkim
sudo systemctl start opendkim
Then uncomment the DKIM milter lines in /etc/postfix/main.cf:
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = inet:127.0.0.1:8891
milter_protocol = 6
milter_default_action = accept
Reload Postfix:
sudo systemctl reload postfix
7. Anti-Spam Integration
BrightChain’s AntiSpamFilter integrates with Postfix via the milter protocol. Choose one engine.
Option A: SpamAssassin
# Install
sudo apt install spamassassin spamass-milter
# Enable and start
# Note: On Ubuntu 24.04+, the service is named "spamd", not "spamassassin".
sudo systemctl enable spamd
sudo systemctl start spamd
sudo systemctl enable spamass-milter
sudo systemctl start spamass-milter
Add to /etc/postfix/main.cf:
smtpd_milters = unix:/run/spamass-milter/spamass-milter.sock, inet:127.0.0.1:8891
milter_default_action = accept
Set the environment variable:
export GATEWAY_SPAM_ENGINE=spamassassin
Option B: Rspamd
# Install (Debian/Ubuntu — add the Rspamd repository first)
sudo apt install rspamd
# Enable and start
sudo systemctl enable rspamd
sudo systemctl start rspamd
Add to /etc/postfix/main.cf:
smtpd_milters = inet:127.0.0.1:11332, inet:127.0.0.1:8891
milter_default_action = accept
Set the environment variable:
export GATEWAY_SPAM_ENGINE=rspamd
Spam Thresholds
Configure the classification thresholds via environment variables:
| Variable | Default | Description |
|---|---|---|
GATEWAY_SPAM_PROBABLE | 5.0 | Score at or above which a message is classified as probable spam (tagged, delivered to spam folder) |
GATEWAY_SPAM_DEFINITE | 10.0 | Score at or above which a message is classified as definite spam (rejected at SMTP time with 550) |
8. TLS Certificate Setup
Use Let’s Encrypt (certbot) for free TLS certificates:
sudo apt install certbot
sudo certbot certonly --standalone -d mail.brightchain.org
Then point Postfix to the certificates in main.cf:
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.brightchain.org/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.brightchain.org/privkey.pem
Set up auto-renewal:
sudo systemctl enable certbot.timer
9. Firewall / Port Requirements
Open the following ports on your gateway node:
| Port | Protocol | Direction | Purpose |
|---|---|---|---|
| 25 | TCP | Inbound | SMTP — receiving mail from external servers |
| 25 | TCP | Outbound | SMTP — delivering mail to external servers |
| 587 | TCP | Inbound | Submission — authenticated outbound from BrightChain nodes |
| 443 | TCP | Outbound | HTTPS — Let’s Encrypt certificate renewal, Rspamd updates |
| 2526 | TCP | Localhost only | Recipient Lookup Service (socketmap) — Postfix ↔ BrightChain |
| 783 | TCP | Localhost only | SpamAssassin spamd (if using SpamAssassin) |
| 11332 | TCP | Localhost only | Rspamd milter proxy (if using Rspamd) |
| 11333 | TCP | Localhost only | Rspamd HTTP API (if using Rspamd) |
| 8891 | TCP | Localhost only | OpenDKIM milter |
Example with ufw:
sudo ufw allow 25/tcp
sudo ufw allow 587/tcp
sudo ufw allow 443/tcp
Ports 2526, 783, 11332, 11333, and 8891 should remain bound to 127.0.0.1 and do not need firewall rules.
10. Applying Configuration
After making changes, validate and reload:
# Check Postfix configuration for errors
sudo postfix check
# Reload Postfix
sudo systemctl reload postfix
# Verify Postfix is listening
sudo ss -tlnp | grep -E ':(25|587)\s'
# Test recipient lookup (from the gateway host)
echo "virtual alice@brightchain.org" | nc 127.0.0.1 2526
# Expected: OK alice@brightchain.org (if user exists)
# Expected: NOTFOUND (if user does not exist)
Related Documentation
- Test Mode Guide — Local development and testing setup
- Email Gateway Configuration Guide — environment variables, enabling the gateway, tuning, and troubleshooting
- Email System Architecture — internal email system overview