mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-09-06 16:07:14 +08:00
feat: add STARTTLS support for SMTP proxy server (#876)
* feat: add STARTTLS support for SMTP proxy server Add smtp_tls_cert and smtp_tls_key environment variables to enable STARTTLS on the SMTP proxy server, matching existing IMAP TLS support. Closes #249 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add E2E tests for SMTP/IMAP STARTTLS - Add smtp-proxy-tls service with self-signed certs in docker-compose - Add smtp-tls.spec.ts: SMTP STARTTLS send plain/HTML/auth tests - Add imap-tls.spec.ts: IMAP STARTTLS login/list/select/fetch tests - Register smtp-proxy project in playwright.config.ts - Wait for TLS proxy readiness in docker-entrypoint.sh Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: enforce auth over TLS when STARTTLS is configured - Set auth_require_tls conditionally based on tls_context presence - Disable insecure SSLv2/SSLv3 protocols in TLS context Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: replace cert-gen service with inline cert generation The cert-gen one-shot container was exiting immediately after generating certificates, triggering --abort-on-container-exit and stopping all services before tests could run. Replace with an entrypoint script in smtp-proxy-tls that generates the self-signed cert before starting the proxy server. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
8341cae28f
commit
8cf1150b15
@@ -0,0 +1,114 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import nodemailer from 'nodemailer';
|
||||
import {
|
||||
createTestAddress,
|
||||
deleteAddress,
|
||||
deleteAllMailpitMessages,
|
||||
requestSendAccess,
|
||||
onMailpitMessage,
|
||||
} from '../../fixtures/test-helpers';
|
||||
|
||||
const TLS_HOST = process.env.SMTP_PROXY_TLS_HOST || 'smtp-proxy-tls';
|
||||
const TLS_SMTP_PORT = parseInt(process.env.SMTP_PROXY_TLS_SMTP_PORT || '8026', 10);
|
||||
|
||||
function createTlsTransport(user: string, pass: string) {
|
||||
return nodemailer.createTransport({
|
||||
host: TLS_HOST,
|
||||
port: TLS_SMTP_PORT,
|
||||
secure: false,
|
||||
auth: { user, pass },
|
||||
tls: { rejectUnauthorized: false },
|
||||
requireTLS: true,
|
||||
});
|
||||
}
|
||||
|
||||
function createNoTlsTransport(user: string, pass: string) {
|
||||
return nodemailer.createTransport({
|
||||
host: TLS_HOST,
|
||||
port: TLS_SMTP_PORT,
|
||||
secure: false,
|
||||
auth: { user, pass },
|
||||
tls: { rejectUnauthorized: false },
|
||||
});
|
||||
}
|
||||
|
||||
test.describe('SMTP Proxy — STARTTLS', () => {
|
||||
let jwt: string;
|
||||
let address: string;
|
||||
|
||||
test.beforeAll(async ({ request }) => {
|
||||
await deleteAllMailpitMessages(request);
|
||||
({ jwt, address } = await createTestAddress(request, 'smtp-tls'));
|
||||
await requestSendAccess(request, jwt);
|
||||
});
|
||||
|
||||
test.afterAll(async ({ request }) => {
|
||||
await deleteAddress(request, jwt);
|
||||
});
|
||||
|
||||
test('send plain text email via STARTTLS', async () => {
|
||||
const subject = `SMTP TLS Plain ${Date.now()}`;
|
||||
const listener = onMailpitMessage((m) => m.Subject === subject);
|
||||
await listener.ready;
|
||||
|
||||
const transport = createTlsTransport(address, jwt);
|
||||
const info = await transport.sendMail({
|
||||
from: address,
|
||||
to: 'recipient@test.example.com',
|
||||
subject,
|
||||
text: 'Hello from SMTP STARTTLS E2E test',
|
||||
});
|
||||
expect(info.accepted).toContain('recipient@test.example.com');
|
||||
|
||||
const delivered = await listener.message;
|
||||
expect(delivered.Subject).toBe(subject);
|
||||
});
|
||||
|
||||
test('send HTML email via STARTTLS', async () => {
|
||||
const subject = `SMTP TLS HTML ${Date.now()}`;
|
||||
const listener = onMailpitMessage((m) => m.Subject === subject);
|
||||
await listener.ready;
|
||||
|
||||
const transport = createTlsTransport(address, jwt);
|
||||
const info = await transport.sendMail({
|
||||
from: address,
|
||||
to: 'recipient@test.example.com',
|
||||
subject,
|
||||
html: '<h1>Hello</h1><p>STARTTLS HTML E2E test</p>',
|
||||
});
|
||||
expect(info.accepted).toContain('recipient@test.example.com');
|
||||
|
||||
const delivered = await listener.message;
|
||||
expect(delivered.Subject).toBe(subject);
|
||||
});
|
||||
|
||||
test('connection without STARTTLS still works', async () => {
|
||||
const subject = `SMTP TLS NoForce ${Date.now()}`;
|
||||
const listener = onMailpitMessage((m) => m.Subject === subject);
|
||||
await listener.ready;
|
||||
|
||||
const transport = createNoTlsTransport(address, jwt);
|
||||
const info = await transport.sendMail({
|
||||
from: address,
|
||||
to: 'recipient@test.example.com',
|
||||
subject,
|
||||
text: 'Hello without forced STARTTLS',
|
||||
});
|
||||
expect(info.accepted).toContain('recipient@test.example.com');
|
||||
|
||||
const delivered = await listener.message;
|
||||
expect(delivered.Subject).toBe(subject);
|
||||
});
|
||||
|
||||
test('auth with wrong password fails over STARTTLS', async () => {
|
||||
const transport = createTlsTransport(address, 'wrong-password');
|
||||
await expect(
|
||||
transport.sendMail({
|
||||
from: address,
|
||||
to: 'recipient@test.example.com',
|
||||
subject: 'Should fail',
|
||||
text: 'This should not be sent',
|
||||
})
|
||||
).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user