Nginx Mail Auth HTTP Server provides an auth service for Nginx Mail module.
Benifits of using nginx as a mail proxy:
- Nginx is fast and thin
- You can do load balancing
- You can use multiple upstream servers
- Configuration is dynamic
work in progress
+-------------+ +---------------+ +--------------+
| | | | | |
| MTA <----7------+ Nginx <----2-----+ Gmail |
| | SMTP | | SMTP | |
+------+------+ +-----^---+-----+ +------^-------+
| | | |
| | | |
8 6 3 HTTP(S) 1 SMTP
| | | |
| | | |
+------v------+ +-----+---v-----+ +------+-------+
| +-----5-----> | | |
| MySQL | | Auth Server | | Client |
| <-----4-----+ | | |
+-------------+ MySQL +---------------+ +--------------+
./nginx-mail-auth-http-server -h
Usage of ./nginx-mail-auth-http-server:
-config string
Path to configuration file (default "nginx-mail-auth-http-server.conf")
-log-secrets
Show plaintext passwords in logs
-verbose
Verbose output
-version
Show version
We currently publish docker images on github and quay.io.
In order to pull any images from there you need to have a personal github token. Please, refer to the official documantation: https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token#creating-a-token
docker run \
--log-driver=journald \
--log-opt=tag="nginx-auth" \
--network host \
--interactive \
--tty \
--name nginx-mail-auth-http-server \
-v /opt/nginx-mail-auth-http-server.conf:/nginx-mail-auth-http-server.conf:ro \
"docker.pkg.github.com/reinvented-stuff/nginx-mail-auth-http-server/nginx-mail-auth-http-server:1.4.2"
podman run \
--log-driver=journald \
--log-opt=tag="nginx-auth" \
--network host \
--interactive \
--tty \
--name nginx-mail-auth-http-server \
-v /opt/nginx-mail-auth-http-server.conf:/nginx-mail-auth-http-server.conf:ro \
"quay.io/reinventedstuff/nginx-mail-auth-http-server:1.4.2"
nginx should be listening on 25/tcp port of your mail server.
user nginx;
worker_processes auto;
...
http {
...
}
mail {
server_name mx.example.com;
auth_http http://localhost:8080/auth;
auth_http_header X-Origin-Mail-Key 9TlBLGKoOa;
starttls on;
ssl_certificate /etc/pki/tls/certs/mx.example.com.crt;
ssl_certificate_key /etc/pki/tls/private/mx.example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
server {
listen 25;
protocol smtp;
smtp_auth login plain none;
auth_http_header X-Origin-Server-Key zb4xKm9XmD;
error_log /var/log/nginx/mx.example.com-mail-error.log;
proxy_pass_error_message on;
}
}
postfix is supposed to be listening a different port from the one nginx does listen.
mynetworks
should contain your nginx host. This will let postfix accept all mail from nginx.
smtpd_authorized_xclient_hosts
should contain your nginx host. This allows Nginx to pass XCLIENT command.
inet_interfaces = localhost
mynetworks = 127.0.0.0/8
smtpd_authorized_xclient_hosts = 127.0.0.0/8
smtpd_recipient_restrictions =
permit_mynetworks,
...
To make postfix listen on a custom port you can comment out the default smtp ...
line and add a new one as proposed below.
# ==========================================================================
# service type private unpriv chroot wakeup maxproc command + args
# (yes) (yes) (no) (never) (100)
# ==========================================================================
# smtp inet n - n - - smtpd
31025 inet n - n - - smtpd -o smtpd_tls_auth_only=no
...
The Auth Server shold be reachable by nginx.
{
"listen": "127.0.0.1:8080",
"database": {
"uri": "mysqluser:mysqlpass@tcp(127.0.0.1:3306)/postfix",
"auth_lookup_query": "SELECT '127.0.0.1' as address, 25 as port;",
"relay_lookup_query": "SELECT '127.0.0.1' as address, 25 as port;"
}
}
It is required for queries to return two named values: address
and port
(of the upstream mail server).
You can use the following named parameters in your lookup queries:
:User
– Username part of the authentication request (only on AUTH command):Pass
– Password part of the authentication request (only on AUTH command):RcptTo
– RCPT TO command content (if no AUTH command passed):MailFrom
– MAIL FROM command content (if no AUTH command passed)
Example:
SELECT address, port
FROM transport
JOIN account ON account.transport_id = transport.id
WHERE account.username = :User AND account.password = MD5(:Pass);
Currently the server strips everything from the first found "+" symbol until the first "@" symbol.
Grafana dashboard: https://grafana.com/grafana/dashboards/16427
There is a /metrics
endpoint with a few things:
# TYPE AuthRequests counter
AuthRequests{result="started"} 59203
AuthRequests{result="fail"} 15483
AuthRequests{result="fail_relay"} 282
AuthRequests{result="fail_login"} 8411
AuthRequests{result="success"} 2694
AuthRequests{result="success_relay"} 494
AuthRequests{result="success_login"} 2200
AuthRequests{kind="relay"} 776
AuthRequests{kind="login"} 10611
# TYPE InternalErrors counter
InternalErrors 52
Generate self-signed certificate:
openssl req -x509 \
-newkey rsa:4096 \
-subj '/CN=localhost' \
-nodes \
-keyout localhost.key \
-out localhost.crt \
-sha256 \
-days 9365
Add to the configuration file the following section:
{
...
"ssl": {
"certificate": "localhost.crt",
"private_key": "localhost.key"
}
}
To be done.
Request authentication with login and password:
curl -v -k \
-H "Auth-Method: none" \
-H "Auth-User: pepe_likes" \
-H "Auth-Pass: koalas" \
-H "Auth-Protocol: smtp" \
-H "Auth-Login-Attempt: 1" \
-H "Client-IP: 10.13.199.8" \
-H "Client-Host: [UNAVAILABLE]" \
-H "Auth-SMTP-Helo: pepes_workstation" \
-H "Auth-SMTP-From: MAIL FROM:<pepe@example.com>" \
-H "Auth-SMTP-To: RCPT TO:<mario@example.com>" \
https://127.0.0.1:8443/auth
Request authentication via relay:
curl -v -k \
-H "Auth-Method: none" \
-H "Auth-Protocol: smtp" \
-H "Auth-Login-Attempt: 1" \
-H "Client-IP: 10.13.199.8" \
-H "Client-Host: [UNAVAILABLE]" \
-H "Auth-SMTP-Helo: pepes_workstation" \
-H "Auth-SMTP-From: MAIL FROM:<pepe@example.com>" \
-H "Auth-SMTP-To: RCPT TO:<mario@example.com>" \
https://127.0.0.1:8443/auth