mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-09-05 23:47:50 +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
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
- feat: |用户注册| 新增用户注册邮箱正则校验功能,管理员可配置邮箱格式验证规则
|
- feat: |用户注册| 新增用户注册邮箱正则校验功能,管理员可配置邮箱格式验证规则
|
||||||
- feat: |前端| 新增可配置的 Status 菜单按钮,通过 `STATUS_URL` 环境变量配置状态监控页面链接
|
- feat: |前端| 新增可配置的 Status 菜单按钮,通过 `STATUS_URL` 环境变量配置状态监控页面链接
|
||||||
|
- feat: |SMTP| SMTP 代理服务支持 STARTTLS,通过 `smtp_tls_cert` 和 `smtp_tls_key` 环境变量配置
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
- feat: |User Registration| Add email regex validation for user registration, admins can configure email format validation rules
|
- feat: |User Registration| Add email regex validation for user registration, admins can configure email format validation rules
|
||||||
- feat: |Frontend| Add configurable Status menu button via `STATUS_URL` environment variable for status monitoring page link
|
- feat: |Frontend| Add configurable Status menu button via `STATUS_URL` environment variable for status monitoring page link
|
||||||
|
- feat: |SMTP| Add STARTTLS support for SMTP proxy server via `smtp_tls_cert` and `smtp_tls_key` environment variables
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
# Keep this version in sync with @playwright/test in package.json
|
# Keep this version in sync with @playwright/test in package.json
|
||||||
FROM mcr.microsoft.com/playwright:v1.58.2-noble
|
FROM mcr.microsoft.com/playwright:v1.58.2-noble
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update && apt-get install -y curl netcat-openbsd && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
WORKDIR /app/e2e
|
WORKDIR /app/e2e
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,28 @@ services:
|
|||||||
worker:
|
worker:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
|
smtp-proxy-tls:
|
||||||
|
build:
|
||||||
|
context: ../smtp_proxy_server
|
||||||
|
dockerfile: dockerfile
|
||||||
|
ports:
|
||||||
|
- "11026:8026"
|
||||||
|
- "11144:11144"
|
||||||
|
environment:
|
||||||
|
PROXY_URL: http://worker:8787
|
||||||
|
PORT: "8026"
|
||||||
|
IMAP_PORT: "11144"
|
||||||
|
smtp_tls_cert: /certs/cert.pem
|
||||||
|
smtp_tls_key: /certs/key.pem
|
||||||
|
imap_tls_cert: /certs/cert.pem
|
||||||
|
imap_tls_key: /certs/key.pem
|
||||||
|
entrypoint: ["/bin/bash", "/e2e-scripts/smtp-tls-entrypoint.sh"]
|
||||||
|
volumes:
|
||||||
|
- ./scripts:/e2e-scripts:ro
|
||||||
|
depends_on:
|
||||||
|
worker:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
e2e-runner:
|
e2e-runner:
|
||||||
build:
|
build:
|
||||||
context: ..
|
context: ..
|
||||||
@@ -54,7 +76,11 @@ services:
|
|||||||
FRONTEND_URL: http://frontend:5173
|
FRONTEND_URL: http://frontend:5173
|
||||||
MAILPIT_API: http://mailpit:8025/api
|
MAILPIT_API: http://mailpit:8025/api
|
||||||
SMTP_PROXY_HOST: smtp-proxy
|
SMTP_PROXY_HOST: smtp-proxy
|
||||||
|
SMTP_PROXY_SMTP_PORT: "8025"
|
||||||
SMTP_PROXY_IMAP_PORT: "11143"
|
SMTP_PROXY_IMAP_PORT: "11143"
|
||||||
|
SMTP_PROXY_TLS_HOST: smtp-proxy-tls
|
||||||
|
SMTP_PROXY_TLS_SMTP_PORT: "8026"
|
||||||
|
SMTP_PROXY_TLS_IMAP_PORT: "11144"
|
||||||
CI: "true"
|
CI: "true"
|
||||||
depends_on:
|
depends_on:
|
||||||
worker:
|
worker:
|
||||||
@@ -63,6 +89,8 @@ services:
|
|||||||
condition: service_started
|
condition: service_started
|
||||||
smtp-proxy:
|
smtp-proxy:
|
||||||
condition: service_started
|
condition: service_started
|
||||||
|
smtp-proxy-tls:
|
||||||
|
condition: service_started
|
||||||
volumes:
|
volumes:
|
||||||
- ./test-results:/app/e2e/test-results
|
- ./test-results:/app/e2e/test-results
|
||||||
- ./playwright-report:/app/e2e/playwright-report
|
- ./playwright-report:/app/e2e/playwright-report
|
||||||
|
|||||||
@@ -16,6 +16,13 @@ export default defineConfig({
|
|||||||
baseURL: WORKER_BASE,
|
baseURL: WORKER_BASE,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'smtp-proxy',
|
||||||
|
testDir: './tests/smtp-proxy',
|
||||||
|
use: {
|
||||||
|
baseURL: WORKER_BASE,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'browser',
|
name: 'browser',
|
||||||
testDir: './tests/browser',
|
testDir: './tests/browser',
|
||||||
|
|||||||
@@ -27,6 +27,18 @@ for i in $(seq 1 60); do
|
|||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
|
||||||
|
echo "==> Waiting for smtp-proxy-tls SMTP on $SMTP_PROXY_TLS_HOST:$SMTP_PROXY_TLS_SMTP_PORT ..."
|
||||||
|
for i in $(seq 1 30); do
|
||||||
|
if nc -z "$SMTP_PROXY_TLS_HOST" "$SMTP_PROXY_TLS_SMTP_PORT" 2>/dev/null; then
|
||||||
|
echo " smtp-proxy-tls SMTP ready after ${i}s"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if [ "$i" -eq 30 ]; then
|
||||||
|
echo "WARNING: smtp-proxy-tls SMTP not ready after 30s, continuing anyway"
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
echo "==> Initializing database"
|
echo "==> Initializing database"
|
||||||
curl -sf -X POST "$WORKER_URL/admin/db_initialize" > /dev/null
|
curl -sf -X POST "$WORKER_URL/admin/db_initialize" > /dev/null
|
||||||
curl -sf -X POST "$WORKER_URL/admin/db_migration" > /dev/null
|
curl -sf -X POST "$WORKER_URL/admin/db_migration" > /dev/null
|
||||||
|
|||||||
Executable
+15
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CERT_DIR="/certs"
|
||||||
|
mkdir -p "$CERT_DIR"
|
||||||
|
|
||||||
|
if [ ! -f "$CERT_DIR/cert.pem" ] || [ ! -f "$CERT_DIR/key.pem" ]; then
|
||||||
|
echo "==> Generating self-signed TLS certificate"
|
||||||
|
openssl req -x509 -newkey rsa:2048 -nodes \
|
||||||
|
-keyout "$CERT_DIR/key.pem" -out "$CERT_DIR/cert.pem" \
|
||||||
|
-days 1 -subj "/CN=smtp-proxy-tls"
|
||||||
|
echo " Certificate generated"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec python3 main.py
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { ImapFlow } from 'imapflow';
|
||||||
|
import { createTestAddress, seedTestMail, deleteAddress } from '../../fixtures/test-helpers';
|
||||||
|
|
||||||
|
const IMAP_TLS_HOST = process.env.SMTP_PROXY_TLS_HOST || 'smtp-proxy-tls';
|
||||||
|
const IMAP_TLS_PORT = parseInt(process.env.SMTP_PROXY_TLS_IMAP_PORT || '11144', 10);
|
||||||
|
|
||||||
|
function createClient(user: string, pass: string) {
|
||||||
|
return new ImapFlow({
|
||||||
|
host: IMAP_TLS_HOST,
|
||||||
|
port: IMAP_TLS_PORT,
|
||||||
|
secure: false,
|
||||||
|
auth: { user, pass },
|
||||||
|
logger: false,
|
||||||
|
tls: { rejectUnauthorized: false },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
test.describe('IMAP Proxy — STARTTLS', () => {
|
||||||
|
let jwt: string;
|
||||||
|
let address: string;
|
||||||
|
|
||||||
|
test.beforeAll(async ({ request }) => {
|
||||||
|
({ jwt, address } = await createTestAddress(request, 'imap-tls'));
|
||||||
|
await seedTestMail(request, address, { subject: 'IMAP TLS Test 1', text: 'First TLS test email' });
|
||||||
|
await seedTestMail(request, address, { subject: 'IMAP TLS Test 2', text: 'Second TLS test email' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test.afterAll(async ({ request }) => {
|
||||||
|
await deleteAddress(request, jwt);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('login with JWT over STARTTLS', async () => {
|
||||||
|
const client = createClient(address, jwt);
|
||||||
|
await client.connect();
|
||||||
|
expect(client.usable).toBe(true);
|
||||||
|
await client.logout();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('login with wrong password fails over STARTTLS', async () => {
|
||||||
|
const client = createClient(address, 'wrong-password');
|
||||||
|
await expect(client.connect()).rejects.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('LIST returns INBOX over STARTTLS', async () => {
|
||||||
|
const client = createClient(address, jwt);
|
||||||
|
await client.connect();
|
||||||
|
const mailboxes = await client.list();
|
||||||
|
const names = mailboxes.map(m => m.path);
|
||||||
|
expect(names).toContain('INBOX');
|
||||||
|
await client.logout();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('SELECT INBOX returns messages over STARTTLS', async () => {
|
||||||
|
const client = createClient(address, jwt);
|
||||||
|
await client.connect();
|
||||||
|
const lock = await client.getMailboxLock('INBOX');
|
||||||
|
try {
|
||||||
|
expect(client.mailbox).toBeTruthy();
|
||||||
|
expect(client.mailbox!.exists).toBeGreaterThanOrEqual(2);
|
||||||
|
} finally {
|
||||||
|
lock.release();
|
||||||
|
}
|
||||||
|
await client.logout();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('FETCH source over STARTTLS contains valid MIME', async () => {
|
||||||
|
const client = createClient(address, jwt);
|
||||||
|
await client.connect();
|
||||||
|
const lock = await client.getMailboxLock('INBOX');
|
||||||
|
try {
|
||||||
|
const msg = await client.fetchOne('1', { source: true });
|
||||||
|
const source = msg.source.toString('utf-8');
|
||||||
|
expect(source).toContain('Content-Type:');
|
||||||
|
expect(source).toContain('Subject:');
|
||||||
|
} finally {
|
||||||
|
lock.release();
|
||||||
|
}
|
||||||
|
await client.logout();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
proxy_url=https://temp-email-api.xxx.xxx
|
proxy_url=https://temp-email-api.xxx.xxx
|
||||||
port=8025
|
port=8025
|
||||||
imap_port=11143
|
imap_port=11143
|
||||||
|
# smtp_tls_cert=/path/to/cert.pem
|
||||||
|
# smtp_tls_key=/path/to/key.pem
|
||||||
# imap_tls_cert=/path/to/cert.pem
|
# imap_tls_cert=/path/to/cert.pem
|
||||||
# imap_tls_key=/path/to/key.pem
|
# imap_tls_key=/path/to/key.pem
|
||||||
# imap_cache_size=500
|
# imap_cache_size=500
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ class Settings(BaseSettings):
|
|||||||
port: int = 8025
|
port: int = 8025
|
||||||
imap_port: int = 11143
|
imap_port: int = 11143
|
||||||
basic_password: str = ""
|
basic_password: str = ""
|
||||||
|
smtp_tls_cert: str = ""
|
||||||
|
smtp_tls_key: str = ""
|
||||||
imap_tls_cert: str = ""
|
imap_tls_cert: str = ""
|
||||||
imap_tls_key: str = ""
|
imap_tls_key: str = ""
|
||||||
imap_cache_size: int = 500
|
imap_cache_size: int = 500
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import email
|
import email
|
||||||
|
import ssl
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
from aiosmtpd.controller import Controller
|
from aiosmtpd.controller import Controller
|
||||||
@@ -132,17 +134,35 @@ class CustomSMTPHandler:
|
|||||||
|
|
||||||
def start_smtp_server():
|
def start_smtp_server():
|
||||||
handler = CustomSMTPHandler()
|
handler = CustomSMTPHandler()
|
||||||
|
|
||||||
|
tls_context = None
|
||||||
|
has_cert = bool(settings.smtp_tls_cert)
|
||||||
|
has_key = bool(settings.smtp_tls_key)
|
||||||
|
if has_cert != has_key:
|
||||||
|
raise ValueError(
|
||||||
|
"Both smtp_tls_cert and smtp_tls_key must be set together"
|
||||||
|
)
|
||||||
|
if has_cert and has_key:
|
||||||
|
_logger.info("TLS enabled for SMTP (STARTTLS)")
|
||||||
|
tls_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||||
|
tls_context.options |= ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
|
||||||
|
tls_context.load_cert_chain(settings.smtp_tls_cert, settings.smtp_tls_key)
|
||||||
|
|
||||||
server = Controller(
|
server = Controller(
|
||||||
handler,
|
handler,
|
||||||
hostname="",
|
hostname="",
|
||||||
port=settings.port,
|
port=settings.port,
|
||||||
auth_require_tls=False,
|
auth_require_tls=bool(tls_context),
|
||||||
decode_data=True,
|
decode_data=True,
|
||||||
authenticator=handler.authenticator,
|
authenticator=handler.authenticator,
|
||||||
auth_exclude_mechanism=["DONT"]
|
auth_exclude_mechanism=["DONT"],
|
||||||
|
tls_context=tls_context,
|
||||||
)
|
)
|
||||||
|
|
||||||
_logger.info("Starting SMTP server on port %s", settings.port)
|
_logger.info(
|
||||||
|
"Starting SMTP server on port %s tls=%s",
|
||||||
|
settings.port, bool(tls_context),
|
||||||
|
)
|
||||||
server.start()
|
server.start()
|
||||||
|
|
||||||
loop = asyncio.new_event_loop()
|
loop = asyncio.new_event_loop()
|
||||||
@@ -156,7 +176,8 @@ def start_smtp_server():
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
_logger.info(
|
_logger.info(
|
||||||
"Starting SMTP server proxy_url=%s port=%s",
|
"Starting SMTP server proxy_url=%s port=%s tls=%s",
|
||||||
settings.proxy_url, settings.port,
|
settings.proxy_url, settings.port,
|
||||||
|
bool(settings.smtp_tls_cert and settings.smtp_tls_key),
|
||||||
)
|
)
|
||||||
start_smtp_server()
|
start_smtp_server()
|
||||||
|
|||||||
@@ -58,17 +58,21 @@ services:
|
|||||||
| `proxy_url` | `http://localhost:8787` | Worker backend URL |
|
| `proxy_url` | `http://localhost:8787` | Worker backend URL |
|
||||||
| `port` | `8025` | SMTP port |
|
| `port` | `8025` | SMTP port |
|
||||||
| `imap_port` | `11143` | IMAP port |
|
| `imap_port` | `11143` | IMAP port |
|
||||||
| `imap_tls_cert` | empty | TLS certificate file path (PEM), enables STARTTLS when configured |
|
| `smtp_tls_cert` | empty | SMTP TLS certificate file path (PEM), enables STARTTLS when configured |
|
||||||
| `imap_tls_key` | empty | TLS private key file path (PEM) |
|
| `smtp_tls_key` | empty | SMTP TLS private key file path (PEM) |
|
||||||
|
| `imap_tls_cert` | empty | IMAP TLS certificate file path (PEM), enables STARTTLS when configured |
|
||||||
|
| `imap_tls_key` | empty | IMAP TLS private key file path (PEM) |
|
||||||
| `imap_cache_size` | `500` | Max cached messages per mailbox |
|
| `imap_cache_size` | `500` | Max cached messages per mailbox |
|
||||||
| `imap_http_timeout` | `30.0` | Backend HTTP request timeout (seconds) |
|
| `imap_http_timeout` | `30.0` | Backend HTTP request timeout (seconds) |
|
||||||
|
|
||||||
## Enabling STARTTLS
|
## Enabling STARTTLS
|
||||||
|
|
||||||
Configure `imap_tls_cert` and `imap_tls_key` environment variables to enable STARTTLS support for the IMAP server.
|
Configure the TLS certificate environment variables for SMTP and/or IMAP to enable STARTTLS support. SMTP and IMAP can share the same certificate.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# .env example
|
# .env example
|
||||||
|
smtp_tls_cert=/path/to/cert.pem
|
||||||
|
smtp_tls_key=/path/to/key.pem
|
||||||
imap_tls_cert=/path/to/cert.pem
|
imap_tls_cert=/path/to/cert.pem
|
||||||
imap_tls_key=/path/to/key.pem
|
imap_tls_key=/path/to/key.pem
|
||||||
```
|
```
|
||||||
@@ -77,6 +81,8 @@ In Docker Compose:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
environment:
|
environment:
|
||||||
|
- smtp_tls_cert=/certs/cert.pem
|
||||||
|
- smtp_tls_key=/certs/key.pem
|
||||||
- imap_tls_cert=/certs/cert.pem
|
- imap_tls_cert=/certs/cert.pem
|
||||||
- imap_tls_key=/certs/key.pem
|
- imap_tls_key=/certs/key.pem
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -58,17 +58,21 @@ services:
|
|||||||
| `proxy_url` | `http://localhost:8787` | Worker 后端 URL |
|
| `proxy_url` | `http://localhost:8787` | Worker 后端 URL |
|
||||||
| `port` | `8025` | SMTP 端口 |
|
| `port` | `8025` | SMTP 端口 |
|
||||||
| `imap_port` | `11143` | IMAP 端口 |
|
| `imap_port` | `11143` | IMAP 端口 |
|
||||||
| `imap_tls_cert` | 空 | TLS 证书文件路径(PEM),配置后启用 STARTTLS |
|
| `smtp_tls_cert` | 空 | SMTP TLS 证书文件路径(PEM),配置后启用 STARTTLS |
|
||||||
| `imap_tls_key` | 空 | TLS 私钥文件路径(PEM) |
|
| `smtp_tls_key` | 空 | SMTP TLS 私钥文件路径(PEM) |
|
||||||
|
| `imap_tls_cert` | 空 | IMAP TLS 证书文件路径(PEM),配置后启用 STARTTLS |
|
||||||
|
| `imap_tls_key` | 空 | IMAP TLS 私钥文件路径(PEM) |
|
||||||
| `imap_cache_size` | `500` | 每个邮箱的消息缓存上限 |
|
| `imap_cache_size` | `500` | 每个邮箱的消息缓存上限 |
|
||||||
| `imap_http_timeout` | `30.0` | 后端 HTTP 请求超时时间(秒) |
|
| `imap_http_timeout` | `30.0` | 后端 HTTP 请求超时时间(秒) |
|
||||||
|
|
||||||
## 启用 STARTTLS
|
## 启用 STARTTLS
|
||||||
|
|
||||||
配置 `imap_tls_cert` 和 `imap_tls_key` 环境变量后,IMAP 服务会自动支持 STARTTLS。
|
分别配置 SMTP 和 IMAP 的 TLS 证书环境变量后,对应服务会自动支持 STARTTLS。SMTP 和 IMAP 可以使用同一套证书。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# .env 示例
|
# .env 示例
|
||||||
|
smtp_tls_cert=/path/to/cert.pem
|
||||||
|
smtp_tls_key=/path/to/key.pem
|
||||||
imap_tls_cert=/path/to/cert.pem
|
imap_tls_cert=/path/to/cert.pem
|
||||||
imap_tls_key=/path/to/key.pem
|
imap_tls_key=/path/to/key.pem
|
||||||
```
|
```
|
||||||
@@ -77,6 +81,8 @@ Docker Compose 中配置:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
environment:
|
environment:
|
||||||
|
- smtp_tls_cert=/certs/cert.pem
|
||||||
|
- smtp_tls_key=/certs/key.pem
|
||||||
- imap_tls_cert=/certs/cert.pem
|
- imap_tls_cert=/certs/cert.pem
|
||||||
- imap_tls_key=/certs/key.pem
|
- imap_tls_key=/certs/key.pem
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
Reference in New Issue
Block a user