Fix: IMAP/SMTP mit älteren TLS-Versionen zulassen
Der Fehler 'Client network socket disconnected before secure TLS connection was established' tritt auf, wenn der Mailserver nur alte TLS-Versionen (1.0/1.1) oder legacy Cipher-Suites anbietet - Node.js 20+ schließt dann den Socket, noch bevor überhaupt ein Zertifikat gesehen wird. Das Häkchen 'Selbstsignierte Zertifikate erlauben' greift zu spät, weil der Handshake gar nicht startet. Fix: Wenn 'Selbstsignierte Zertifikate erlauben' aktiv ist, setzen wir gleich auch minVersion='TLSv1' und ciphers='DEFAULT:@SECLEVEL=0'. Damit akzeptiert Node.js auch alte Cipher-Suites und TLS-Versionen des Mailservers. Bei aktivem 'allowSelfSignedCerts' heißt das zusammen: - rejectUnauthorized: false (Zertifikate akzeptieren auch wenn selbstsigniert) - minVersion: 'TLSv1' (auch alte TLS-Versionen zulassen) - ciphers: 'DEFAULT:@SECLEVEL=0' (auch schwache Ciphers zulassen) Refactor: - imapService: neuer Helper buildTlsOptions() – ersetzt 8 identische Inline-Setups, damit die Fix-Logik zentral gepflegt wird - smtpService: tls-Type erweitert (minVersion/ciphers), gleiche Logik Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
fd55f3129f
commit
1de8fb9847
|
|
@ -16,6 +16,27 @@ export interface ImapCredentials {
|
|||
allowSelfSignedCerts?: boolean; // Selbstsignierte Zertifikate erlauben
|
||||
}
|
||||
|
||||
/**
|
||||
* TLS-Optionen für IMAP-Verbindungen zusammenbauen.
|
||||
* Wenn `allowSelfSignedCerts` aktiv ist, werden zusätzlich ältere TLS-Versionen
|
||||
* (TLS 1.0+) und legacy Cipher-Suites erlaubt – hilfreich bei älteren Mailservern,
|
||||
* die sonst den Socket sofort nach Connect schließen.
|
||||
*/
|
||||
function buildTlsOptions(credentials: ImapCredentials): Record<string, unknown> | undefined {
|
||||
const encryption = credentials.encryption ?? 'SSL';
|
||||
if (encryption === 'NONE') return undefined;
|
||||
|
||||
const rejectUnauthorized = !credentials.allowSelfSignedCerts;
|
||||
const options: Record<string, unknown> = { rejectUnauthorized };
|
||||
|
||||
if (credentials.allowSelfSignedCerts) {
|
||||
options.minVersion = 'TLSv1';
|
||||
options.ciphers = 'DEFAULT:@SECLEVEL=0';
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
export interface FetchedEmail {
|
||||
uid: number;
|
||||
messageId: string;
|
||||
|
|
@ -106,7 +127,7 @@ export async function fetchEmails(
|
|||
|
||||
// TLS-Optionen nur wenn nicht NONE
|
||||
if (encryption !== 'NONE') {
|
||||
clientOptions.tls = { rejectUnauthorized };
|
||||
const __tls = buildTlsOptions(credentials); if (__tls) clientOptions.tls = __tls as any;
|
||||
}
|
||||
|
||||
// Debug-Logging
|
||||
|
|
@ -260,7 +281,7 @@ export async function testImapConnection(credentials: ImapCredentials): Promise<
|
|||
};
|
||||
|
||||
if (encryption !== 'NONE') {
|
||||
clientOptions.tls = { rejectUnauthorized };
|
||||
const __tls = buildTlsOptions(credentials); if (__tls) clientOptions.tls = __tls as any;
|
||||
}
|
||||
|
||||
const client = new ImapFlow(clientOptions);
|
||||
|
|
@ -325,7 +346,7 @@ export async function getHighestUid(
|
|||
};
|
||||
|
||||
if (encryption !== 'NONE') {
|
||||
clientOptions.tls = { rejectUnauthorized };
|
||||
const __tls = buildTlsOptions(credentials); if (__tls) clientOptions.tls = __tls as any;
|
||||
}
|
||||
|
||||
const client = new ImapFlow(clientOptions);
|
||||
|
|
@ -414,12 +435,12 @@ async function fetchAttachmentInner(
|
|||
};
|
||||
|
||||
if (encryption !== 'NONE') {
|
||||
clientOptions.tls = { rejectUnauthorized };
|
||||
const __tls = buildTlsOptions(credentials); if (__tls) clientOptions.tls = __tls as any;
|
||||
}
|
||||
|
||||
const client = new ImapFlow(clientOptions);
|
||||
|
||||
console.log(`[fetchAttachment] Host: ${credentials.host}:${credentials.port} | User: ${credentials.user} | Folder: ${folder} | UID: ${uid} | File: ${attachmentFilename}`);
|
||||
console.log(`[fetchAttachment] Host: ${credentials.host}:${credentials.port} | User: ${credentials.user} | Folder: ${folder} | UID: ${uid} | File: ${attachmentFilename} | AllowSelfSigned: ${credentials.allowSelfSignedCerts}`);
|
||||
|
||||
try {
|
||||
await client.connect();
|
||||
|
|
@ -527,7 +548,7 @@ export async function appendToSent(
|
|||
};
|
||||
|
||||
if (encryption !== 'NONE') {
|
||||
clientOptions.tls = { rejectUnauthorized };
|
||||
const __tls = buildTlsOptions(credentials); if (__tls) clientOptions.tls = __tls as any;
|
||||
}
|
||||
|
||||
console.log(`[IMAP] Appending email to ${sentFolder} folder...`);
|
||||
|
|
@ -613,7 +634,7 @@ export async function fetchAttachmentList(
|
|||
};
|
||||
|
||||
if (encryption !== 'NONE') {
|
||||
clientOptions.tls = { rejectUnauthorized };
|
||||
const __tls = buildTlsOptions(credentials); if (__tls) clientOptions.tls = __tls as any;
|
||||
}
|
||||
|
||||
const client = new ImapFlow(clientOptions);
|
||||
|
|
@ -705,7 +726,7 @@ export async function moveToTrash(
|
|||
};
|
||||
|
||||
if (encryption !== 'NONE') {
|
||||
clientOptions.tls = { rejectUnauthorized };
|
||||
const __tls = buildTlsOptions(credentials); if (__tls) clientOptions.tls = __tls as any;
|
||||
}
|
||||
|
||||
const client = new ImapFlow(clientOptions);
|
||||
|
|
@ -778,7 +799,7 @@ export async function restoreFromTrash(
|
|||
};
|
||||
|
||||
if (encryption !== 'NONE') {
|
||||
clientOptions.tls = { rejectUnauthorized };
|
||||
const __tls = buildTlsOptions(credentials); if (__tls) clientOptions.tls = __tls as any;
|
||||
}
|
||||
|
||||
const client = new ImapFlow(clientOptions);
|
||||
|
|
@ -849,7 +870,7 @@ export async function permanentDelete(
|
|||
};
|
||||
|
||||
if (encryption !== 'NONE') {
|
||||
clientOptions.tls = { rejectUnauthorized };
|
||||
const __tls = buildTlsOptions(credentials); if (__tls) clientOptions.tls = __tls as any;
|
||||
}
|
||||
|
||||
const client = new ImapFlow(clientOptions);
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ export async function sendEmail(
|
|||
port: number;
|
||||
secure: boolean;
|
||||
auth: { user: string; pass: string };
|
||||
tls?: { rejectUnauthorized: boolean };
|
||||
tls?: { rejectUnauthorized: boolean; minVersion?: string; ciphers?: string };
|
||||
ignoreTLS?: boolean;
|
||||
requireTLS?: boolean;
|
||||
connectionTimeout: number;
|
||||
|
|
@ -91,6 +91,11 @@ export async function sendEmail(
|
|||
// TLS-Optionen nur wenn nicht NONE
|
||||
if (encryption !== 'NONE') {
|
||||
transportOptions.tls = { rejectUnauthorized };
|
||||
if (credentials.allowSelfSignedCerts) {
|
||||
// Auch ältere TLS-Versionen + legacy Cipher-Suites für alte Server zulassen
|
||||
transportOptions.tls.minVersion = 'TLSv1';
|
||||
transportOptions.tls.ciphers = 'DEFAULT:@SECLEVEL=0';
|
||||
}
|
||||
} else {
|
||||
// Keine Verschlüsselung: STARTTLS ignorieren
|
||||
transportOptions.ignoreTLS = true;
|
||||
|
|
@ -273,7 +278,7 @@ export async function testSmtpConnection(credentials: SmtpCredentials): Promise<
|
|||
port: number;
|
||||
secure: boolean;
|
||||
auth: { user: string; pass: string };
|
||||
tls?: { rejectUnauthorized: boolean };
|
||||
tls?: { rejectUnauthorized: boolean; minVersion?: string; ciphers?: string };
|
||||
ignoreTLS?: boolean;
|
||||
connectionTimeout: number;
|
||||
greetingTimeout: number;
|
||||
|
|
|
|||
Loading…
Reference in New Issue