No Global IP whitelist - fixing failed auth response hang, fixing sender authentication.
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
# SMTP Server Authentication Order and Best Practices
|
||||
|
||||
## Summary of Fixes (June 2025)
|
||||
|
||||
This document describes the authentication logic and order for the SMTP server, as well as the recent fixes applied to ensure correct sender authentication and IP whitelisting behavior.
|
||||
|
||||
### What Was Fixed
|
||||
- **Authentication Response:**
|
||||
- The server now immediately responds with an SMTP error (e.g., `535 Authentication failed`) if the username or password is incorrect, instead of hanging the session. This is achieved by returning `AuthResult(success=False, handled=False, message='535 Authentication failed')` from the authenticator, allowing the aiosmtpd framework to send the error to the client.
|
||||
- **No Forced Connection Close:**
|
||||
- The server does not forcibly close the connection after failed authentication, but lets the SMTP client decide whether to retry or quit, as per SMTP protocol best practices.
|
||||
- **AUTH on Both Ports:**
|
||||
- Both the plain SMTP port (`smtp_port`) and the secure TLS port (`smtp_tls_port`) now advertise and allow authentication (AUTH LOGIN/PLAIN). IP whitelist fallback is also available on both ports.
|
||||
|
||||
## Authentication Order and Logic
|
||||
|
||||
1. **Connection Handling**
|
||||
- If a client connects to the plain SMTP port, both AUTH and IP whitelisting are available.
|
||||
- If a client connects to the TLS SMTP port, the connection is immediately secured with TLS. Both AUTH and IP whitelisting are available.
|
||||
|
||||
2. **Sender Authentication (Username/Password)**
|
||||
- When a client issues the AUTH command (LOGIN or PLAIN) on either port:
|
||||
- The server checks the username and password against the database.
|
||||
- If valid, the session is marked as authenticated and the sender can send as their own address or, if permitted, as any address in their domain.
|
||||
- If invalid, the server responds with `535 Authentication failed` and does not hang the session.
|
||||
|
||||
**Code Snippet for Immediate Authentication Failure Response:**
|
||||
```python
|
||||
# In email_server/auth.py
|
||||
def __call__(self, server, session, envelope, mechanism, auth_data):
|
||||
# ...existing code...
|
||||
if not isinstance(auth_data, LoginPassword):
|
||||
logger.warning(f'Invalid auth data format: {type(auth_data)}')
|
||||
return AuthResult(success=False, handled=False, message='535 Authentication failed')
|
||||
# ...existing code...
|
||||
try:
|
||||
sender = get_sender_by_email(username)
|
||||
if sender and check_password(password, sender.password_hash):
|
||||
# ...success logic...
|
||||
return AuthResult(success=True, handled=True)
|
||||
else:
|
||||
# ...failure logging...
|
||||
return AuthResult(success=False, handled=False, message='535 Authentication failed')
|
||||
except Exception as e:
|
||||
# ...error logging...
|
||||
return AuthResult(success=False, handled=False, message='451 Internal server error')
|
||||
```
|
||||
- Returning `handled=False` ensures the SMTP client is immediately informed of the failure and does not hang.
|
||||
|
||||
3. **IP Whitelisting (Secondary/Fallback)**
|
||||
- If no AUTH is provided, the server checks if the client's IP is whitelisted for the target domain.
|
||||
- If the IP is whitelisted, the session is authorized to send for that domain.
|
||||
- If not, the server rejects the mail transaction.
|
||||
|
||||
## Best Practices for Future Development
|
||||
|
||||
- **Always return `handled=False` in `AuthResult` for failed authentication** to ensure the SMTP client receives an error and the session does not hang.
|
||||
- **Advertise AUTH on both the plain SMTP and TLS ports**; allow both user authentication and IP whitelist fallback.
|
||||
- **Do not use or advertise STARTTLS** on any port if only direct TLS is desired.
|
||||
- **Log all authentication attempts** (success and failure) for auditing and troubleshooting.
|
||||
- **Keep authentication and IP whitelisting logic modular** for easy updates and security reviews.
|
||||
|
||||
## Example Client Setup
|
||||
- For user authentication, connect to either the plain SMTP port (e.g., 25 or 4025) or the TLS port (e.g., 40587) and use the correct username and password.
|
||||
- For IP whitelisting, connect from an authorized IP to either port; no authentication is required, but the sender must be allowed for the domain.
|
||||
|
||||
---
|
||||
|
||||
**This document should be updated if the authentication logic or port usage changes in the future.**
|
||||
+85
-44
@@ -1,69 +1,110 @@
|
||||
#!/bin/bash
|
||||
sender="test@example.com"
|
||||
receiver="info@example.com"
|
||||
password="testpass123"
|
||||
EMAIL_SERVER="localhost" #"pymta.example.com" "localhost"
|
||||
EMAIL_SERVER_auth="10.100.111.1" # IP for authenticated server ( not localhost), use your main interface ip
|
||||
|
||||
sender="test@example.com"
|
||||
username="test@example.com"
|
||||
password="ZjDvcjPSs-nwK2Ghj5vQY7L4LdmTpmn_AEZMokJTFS" # password you setup for the user!
|
||||
domain="example.com"
|
||||
body_content_file="@tests/email_body.txt"
|
||||
SMTP_PORT=4025
|
||||
SMTP_TLS_PORT=40587
|
||||
cc_recipient="targetcc@example.com"
|
||||
bcc_recipient="targetbcc@example.com"
|
||||
SMTP_TLS_PORT=40465
|
||||
cc_recipient="ccrecipient@example.com"
|
||||
bcc_recipient="bccrecipient@example.com"
|
||||
|
||||
<<com
|
||||
# Setup domain and user via web interface first
|
||||
# Visit http://localhost:5000/email to configure:
|
||||
# - Add domain: $domain
|
||||
# - Add user: $sender with password $password
|
||||
# - Add IP whitelist: 127.0.0.1 and 10.100.111.1
|
||||
# - Generate DKIM key for domain
|
||||
python -m email_server.cli_tools add-domain $domain
|
||||
python -m email_server.cli_tools add-user $username $password $domain
|
||||
python -m email_server.cli_tools add-ip 127.0.0.1 $domain
|
||||
python -m email_server.cli_tools add-ip 213.249.224.235 $domain
|
||||
python -m email_server.cli_tools generate-dkim $domain
|
||||
python -m email_server.cli_tools add-custom-header $domain X-Auth-Token "abc123-example-auth"
|
||||
python -m email_server.cli_tools add-custom-header $domain X-Server-ID "mail01.example.com"
|
||||
|
||||
# options to add CC and BCC recipients for swaks
|
||||
--cc $cc_recipient
|
||||
--bcc $bcc_recipient
|
||||
com
|
||||
|
||||
swaks --to $receiver \
|
||||
--from $sender \
|
||||
--server localhost \
|
||||
--server $EMAIL_SERVER \
|
||||
--port $SMTP_TLS_PORT \
|
||||
--auth LOGIN \
|
||||
--auth-user $sender \
|
||||
--auth-user $username \
|
||||
--auth-password $password \
|
||||
--tls \
|
||||
--header "Subject: TLS - Large body email" \
|
||||
--body $body_content_file \
|
||||
--attach tests/email_body.txt \
|
||||
--attach tests/Hello.jpg
|
||||
--body $body_content_file
|
||||
# --attach @/home/nahaku/Documents/Projects/SMTP_Server/tests/email_body.txt
|
||||
# --attach @/home/nahaku/Documents/Projects/SMTP_Server/tests/Hello.jpg
|
||||
|
||||
swaks --to $receiver \
|
||||
--from $sender \
|
||||
--server localhost \
|
||||
--port $SMTP_PORT \
|
||||
--auth LOGIN \
|
||||
--auth-user $sender \
|
||||
--auth-password $password \
|
||||
--data "Subject: Test Email - authenticated\n\nThis is the message body."
|
||||
|
||||
swaks --to $receiver \
|
||||
--from $sender \
|
||||
--server localhost \
|
||||
--port $SMTP_TLS_PORT \
|
||||
--auth LOGIN \
|
||||
--auth-user $sender \
|
||||
--auth-password $password \
|
||||
--tls \
|
||||
--data "Subject: Test via STARTTLS - authenticated\n\nThis is the body."
|
||||
|
||||
swaks --to $receiver \
|
||||
--from $sender \
|
||||
--server localhost \
|
||||
--port $SMTP_TLS_PORT \
|
||||
--tls \
|
||||
--data "Subject: Test via STARTTLS - no auth\n\nThis is the body."
|
||||
com
|
||||
<<com
|
||||
|
||||
com
|
||||
swaks --to $receiver \
|
||||
--from $sender \
|
||||
--server localhost \
|
||||
--server $EMAIL_SERVER \
|
||||
--port $SMTP_PORT \
|
||||
--data "Subject: Test Email - no auth\n\nThis is the message body."
|
||||
--auth LOGIN \
|
||||
--auth-user $username \
|
||||
--auth-password $password \
|
||||
--data "Subject: SMTP - authenticated success\n\nThis is the message body."
|
||||
|
||||
# Test with Authentication TLS
|
||||
swaks --to $receiver \
|
||||
--from $sender \
|
||||
--server $EMAIL_SERVER \
|
||||
--port $SMTP_TLS_PORT \
|
||||
--auth LOGIN \
|
||||
--auth-user $username \
|
||||
--auth-password $password \
|
||||
--tls \
|
||||
--header "Subject: TLS - authenticated success" \
|
||||
--body "This is the message body with proper headers."
|
||||
|
||||
# Test TLS + authentication and IP whitelist
|
||||
swaks --to $receiver \
|
||||
--from $sender \
|
||||
--server $EMAIL_SERVER_auth \
|
||||
--port $SMTP_TLS_PORT \
|
||||
--auth LOGIN \
|
||||
--auth-user $username \
|
||||
--auth-password $password \
|
||||
--tls \
|
||||
--data "Subject: TLS - auth + IP Whitelist \n\nTest TLS + authentication and IP whitelist"
|
||||
|
||||
|
||||
# Test with IP authentication TLS
|
||||
swaks --to $receiver \
|
||||
--from $sender \
|
||||
--server $EMAIL_SERVER_auth \
|
||||
--port $SMTP_TLS_PORT \
|
||||
--tls \
|
||||
--data "Subject: TLS - IP Whitelist - no auth\n\nTest with IP authentication TLS"
|
||||
|
||||
# Test with IP authentication SMTP
|
||||
swaks --to $receiver \
|
||||
--from $sender \
|
||||
--server $EMAIL_SERVER_auth \
|
||||
--port $SMTP_PORT \
|
||||
--data "Subject: SMTP - IP Whitelist - no auth\n\nTest with IP authentication SMTP"
|
||||
|
||||
|
||||
<<com
|
||||
com
|
||||
# SMTP un-auth test "Email_server - no Whitelist - no auth"
|
||||
swaks --to $receiver \
|
||||
--from $sender \
|
||||
--server $EMAIL_SERVER \
|
||||
--port $SMTP_PORT \
|
||||
--data "Subject: SMTP - no Whitelist - no auth\n\nSMTP un-auth test Email_server - no Whitelist - no auth."
|
||||
|
||||
# Test TLS un-auth test "Email_server - no Whitelist - no auth"
|
||||
swaks --to $receiver \
|
||||
--from $sender \
|
||||
--server $EMAIL_SERVER \
|
||||
--port $SMTP_TLS_PORT \
|
||||
--tls \
|
||||
--data "Subject: TLS - no Whitelist - no auth\n\nTest TLS un-auth test Email_server - no Whitelist - no auth"
|
||||
Reference in New Issue
Block a user