No Global IP whitelist - fixing failed auth response hang, fixing sender authentication.

This commit is contained in:
nahakubuilde
2025-06-13 09:41:55 +01:00
parent f07b9c2150
commit 38672bea0b
11 changed files with 190 additions and 352 deletions
+69
View File
@@ -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
View File
@@ -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"