Add script for testing smtp
This commit is contained in:
@@ -52,7 +52,15 @@ Set `PUBLIC_URL` and the `MAIL_*` values in `.env` to send recovery emails. The
|
||||
docker compose up --build -d
|
||||
```
|
||||
|
||||
5. Send a delivery test to an inbox you control:
|
||||
5. Confirm the SMTP connection, TLS handshake, and credentials without sending an email:
|
||||
|
||||
```sh
|
||||
python ./scripts/check-smtp.py
|
||||
```
|
||||
|
||||
The command prints `SMTP_CONNECTION_OK` only after the SMTP server accepts the STARTTLS connection and authenticates the configured account. It prints a clear configuration or connection error otherwise and never sends a message.
|
||||
|
||||
6. Send a delivery test to an inbox you control:
|
||||
|
||||
```powershell
|
||||
.\scripts\test-smtp.ps1 -To you@example.com
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
- [x] Local accounts, administrator approval, and TOTP MFA.
|
||||
- [x] QR-based MFA enrollment for iPhone and Android authenticators.
|
||||
- [x] Email addresses, member password changes, and expiring password-reset links.
|
||||
- [ ] Configure production SMTP credentials and verify outgoing email delivery. See the SMTP delivery steps in [README.md](README.md); complete after the test email is received.
|
||||
- [ ] Configure production SMTP credentials and verify outgoing email delivery. Run `python ./scripts/check-smtp.py`, then complete after the delivery test in [README.md](README.md) is received.
|
||||
- [ ] Run `./scripts/rotate-admin.ps1` on Windows or `sh ./scripts/rotate-admin.sh` on Linux/macOS, then sign in as `admin` and scan the new MFA QR code to complete authenticator enrollment.
|
||||
- [x] Add CSRF protection to all state-changing forms.
|
||||
- [ ] Add automated database backups and test restoration.
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verify SMTP connectivity and authentication without sending email."""
|
||||
|
||||
import os
|
||||
import smtplib
|
||||
import ssl
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REQUIRED_SETTINGS = ("MAIL_HOST", "MAIL_PORT", "MAIL_USERNAME", "MAIL_PASSWORD", "MAIL_FROM")
|
||||
|
||||
|
||||
def load_dotenv(path):
|
||||
if not path.is_file():
|
||||
raise RuntimeError("Missing .env. Copy .env.example to .env and enter the SMTP settings.")
|
||||
for line in path.read_text(encoding="utf-8").splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
key, separator, value = line.partition("=")
|
||||
if separator and key.strip():
|
||||
os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'"))
|
||||
|
||||
|
||||
def main():
|
||||
project_root = Path(__file__).resolve().parent.parent
|
||||
load_dotenv(project_root / ".env")
|
||||
missing = [setting for setting in REQUIRED_SETTINGS if not os.environ.get(setting)]
|
||||
if missing:
|
||||
raise RuntimeError(f"Missing required SMTP settings: {', '.join(missing)}")
|
||||
|
||||
host = os.environ["MAIL_HOST"]
|
||||
port = int(os.environ["MAIL_PORT"])
|
||||
username = os.environ["MAIL_USERNAME"]
|
||||
password = os.environ["MAIL_PASSWORD"]
|
||||
context = ssl.create_default_context()
|
||||
|
||||
try:
|
||||
with smtplib.SMTP(host, port, timeout=15) as client:
|
||||
client.ehlo()
|
||||
client.starttls(context=context)
|
||||
client.ehlo()
|
||||
client.login(username, password)
|
||||
except (OSError, smtplib.SMTPException) as error:
|
||||
print(f"SMTP_CONNECTION_FAILED: {error}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
print(f"SMTP_CONNECTION_OK: {host}:{port} authenticated as {username}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except (RuntimeError, ValueError) as error:
|
||||
print(f"SMTP_CONFIGURATION_ERROR: {error}", file=sys.stderr)
|
||||
raise SystemExit(2)
|
||||
Reference in New Issue
Block a user