When working with SMTP servers in development environments, we often face port binding limitations. The Node.js smtp-server
module typically needs to run on privileged ports (like 25) which requires root access - a security risk we want to avoid. The solution involves:
- Running SMTP on high-numbered ports (2500 in this case)
- Using NGINX to proxy port 25 traffic
- Maintaining raw TCP passthrough without protocol interpretation
Unlike HTTP proxying, SMTP requires NGINX's stream module for raw TCP forwarding. First ensure your NGINX includes stream support:
nginx -V 2>&1 | grep -o with-stream
If no output, recompile NGINX with --with-stream
or install the stream module package for your distribution.
Create a new configuration file at /etc/nginx/nginx.conf
or include it in your existing setup:
stream {
server {
listen 25;
proxy_pass 127.0.0.1:2500;
proxy_protocol on;
proxy_buffer_size 16k;
}
}
When exposing port 25, consider these hardening measures:
stream {
server {
listen 25;
proxy_pass 127.0.0.1:2500;
proxy_timeout 3m;
proxy_connect_timeout 30s;
allow 192.168.1.0/24;
deny all;
}
}
After reloading NGINX (nginx -s reload
), test with:
telnet your-server-ip 25
EHLO example.com
You should see responses from your Node.js SMTP server. Monitor connections with:
sudo ngrep -d any port 25
If your SMTP server handles TLS directly, NGINX can pass through encrypted traffic:
stream {
server {
listen 25;
listen 587;
proxy_pass 127.0.0.1:2500;
proxy_ssl off; # Crucial for TLS passthrough
}
}
- Connection refused: Verify Node.js server is running on 2500
- Permission denied: Check SELinux/AppArmor for port binding
- No response: Ensure
stream
block exists in main nginx.conf
Remember that some cloud providers block port 25 by default - check your VPS firewall rules.
When developing SMTP server applications (like those using Node.js's smtp-server
module), Linux systems prevent non-root users from binding to privileged ports below 1024. This creates a deployment hurdle for developers wanting to test SMTP services on standard port 25.
While NGINX is primarily an HTTP server, its stream module allows raw TCP proxying - perfect for protocol-agnostic traffic forwarding. This approach offers several advantages:
- No need to run application as root
- Preserves original SMTP protocol semantics
- Leverages existing NGINX infrastructure
- Enables future load balancing if needed
First, ensure your NGINX includes the stream module (standard in most distributions). Then create or modify /etc/nginx/nginx.conf
:
# Main NGINX configuration
user www-data;
worker_processes auto;
# Include stream module configuration
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
}
# HTTP block remains unchanged
http {
...
}
# Critical addition - TCP proxy configuration
stream {
server {
listen 25;
proxy_pass localhost:2500;
proxy_protocol on;
}
}
After reloading NGINX (sudo systemctl reload nginx
), verify the proxy works:
# Test connection
telnet localhost 25
# Should connect to your SMTP server on port 2500
# Verify NGINX is listening
sudo ss -tulnp | grep ':25'
# Should show nginx process
For real-world deployment, consider these enhancements:
stream {
server {
listen 25 so_keepalive=on;
proxy_pass 127.0.0.1:2500;
proxy_timeout 1h;
proxy_connect_timeout 10s;
# Optional TLS passthrough
proxy_ssl on;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
}
}
If connections fail, check:
- Firewall rules (
sudo ufw status
) - SELinux contexts
- Port availability (
netstat -tuln
) - NGINX error logs (
/var/log/nginx/error.log
)