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
|
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 {
|
export interface FetchedEmail {
|
||||||
uid: number;
|
uid: number;
|
||||||
messageId: string;
|
messageId: string;
|
||||||
|
|
@ -106,7 +127,7 @@ export async function fetchEmails(
|
||||||
|
|
||||||
// TLS-Optionen nur wenn nicht NONE
|
// TLS-Optionen nur wenn nicht NONE
|
||||||
if (encryption !== 'NONE') {
|
if (encryption !== 'NONE') {
|
||||||
clientOptions.tls = { rejectUnauthorized };
|
const __tls = buildTlsOptions(credentials); if (__tls) clientOptions.tls = __tls as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug-Logging
|
// Debug-Logging
|
||||||
|
|
@ -260,7 +281,7 @@ export async function testImapConnection(credentials: ImapCredentials): Promise<
|
||||||
};
|
};
|
||||||
|
|
||||||
if (encryption !== 'NONE') {
|
if (encryption !== 'NONE') {
|
||||||
clientOptions.tls = { rejectUnauthorized };
|
const __tls = buildTlsOptions(credentials); if (__tls) clientOptions.tls = __tls as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new ImapFlow(clientOptions);
|
const client = new ImapFlow(clientOptions);
|
||||||
|
|
@ -325,7 +346,7 @@ export async function getHighestUid(
|
||||||
};
|
};
|
||||||
|
|
||||||
if (encryption !== 'NONE') {
|
if (encryption !== 'NONE') {
|
||||||
clientOptions.tls = { rejectUnauthorized };
|
const __tls = buildTlsOptions(credentials); if (__tls) clientOptions.tls = __tls as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new ImapFlow(clientOptions);
|
const client = new ImapFlow(clientOptions);
|
||||||
|
|
@ -414,12 +435,12 @@ async function fetchAttachmentInner(
|
||||||
};
|
};
|
||||||
|
|
||||||
if (encryption !== 'NONE') {
|
if (encryption !== 'NONE') {
|
||||||
clientOptions.tls = { rejectUnauthorized };
|
const __tls = buildTlsOptions(credentials); if (__tls) clientOptions.tls = __tls as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new ImapFlow(clientOptions);
|
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 {
|
try {
|
||||||
await client.connect();
|
await client.connect();
|
||||||
|
|
@ -527,7 +548,7 @@ export async function appendToSent(
|
||||||
};
|
};
|
||||||
|
|
||||||
if (encryption !== 'NONE') {
|
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...`);
|
console.log(`[IMAP] Appending email to ${sentFolder} folder...`);
|
||||||
|
|
@ -613,7 +634,7 @@ export async function fetchAttachmentList(
|
||||||
};
|
};
|
||||||
|
|
||||||
if (encryption !== 'NONE') {
|
if (encryption !== 'NONE') {
|
||||||
clientOptions.tls = { rejectUnauthorized };
|
const __tls = buildTlsOptions(credentials); if (__tls) clientOptions.tls = __tls as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new ImapFlow(clientOptions);
|
const client = new ImapFlow(clientOptions);
|
||||||
|
|
@ -705,7 +726,7 @@ export async function moveToTrash(
|
||||||
};
|
};
|
||||||
|
|
||||||
if (encryption !== 'NONE') {
|
if (encryption !== 'NONE') {
|
||||||
clientOptions.tls = { rejectUnauthorized };
|
const __tls = buildTlsOptions(credentials); if (__tls) clientOptions.tls = __tls as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new ImapFlow(clientOptions);
|
const client = new ImapFlow(clientOptions);
|
||||||
|
|
@ -778,7 +799,7 @@ export async function restoreFromTrash(
|
||||||
};
|
};
|
||||||
|
|
||||||
if (encryption !== 'NONE') {
|
if (encryption !== 'NONE') {
|
||||||
clientOptions.tls = { rejectUnauthorized };
|
const __tls = buildTlsOptions(credentials); if (__tls) clientOptions.tls = __tls as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new ImapFlow(clientOptions);
|
const client = new ImapFlow(clientOptions);
|
||||||
|
|
@ -849,7 +870,7 @@ export async function permanentDelete(
|
||||||
};
|
};
|
||||||
|
|
||||||
if (encryption !== 'NONE') {
|
if (encryption !== 'NONE') {
|
||||||
clientOptions.tls = { rejectUnauthorized };
|
const __tls = buildTlsOptions(credentials); if (__tls) clientOptions.tls = __tls as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = new ImapFlow(clientOptions);
|
const client = new ImapFlow(clientOptions);
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ export async function sendEmail(
|
||||||
port: number;
|
port: number;
|
||||||
secure: boolean;
|
secure: boolean;
|
||||||
auth: { user: string; pass: string };
|
auth: { user: string; pass: string };
|
||||||
tls?: { rejectUnauthorized: boolean };
|
tls?: { rejectUnauthorized: boolean; minVersion?: string; ciphers?: string };
|
||||||
ignoreTLS?: boolean;
|
ignoreTLS?: boolean;
|
||||||
requireTLS?: boolean;
|
requireTLS?: boolean;
|
||||||
connectionTimeout: number;
|
connectionTimeout: number;
|
||||||
|
|
@ -91,6 +91,11 @@ export async function sendEmail(
|
||||||
// TLS-Optionen nur wenn nicht NONE
|
// TLS-Optionen nur wenn nicht NONE
|
||||||
if (encryption !== 'NONE') {
|
if (encryption !== 'NONE') {
|
||||||
transportOptions.tls = { rejectUnauthorized };
|
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 {
|
} else {
|
||||||
// Keine Verschlüsselung: STARTTLS ignorieren
|
// Keine Verschlüsselung: STARTTLS ignorieren
|
||||||
transportOptions.ignoreTLS = true;
|
transportOptions.ignoreTLS = true;
|
||||||
|
|
@ -273,7 +278,7 @@ export async function testSmtpConnection(credentials: SmtpCredentials): Promise<
|
||||||
port: number;
|
port: number;
|
||||||
secure: boolean;
|
secure: boolean;
|
||||||
auth: { user: string; pass: string };
|
auth: { user: string; pass: string };
|
||||||
tls?: { rejectUnauthorized: boolean };
|
tls?: { rejectUnauthorized: boolean; minVersion?: string; ciphers?: string };
|
||||||
ignoreTLS?: boolean;
|
ignoreTLS?: boolean;
|
||||||
connectionTimeout: number;
|
connectionTimeout: number;
|
||||||
greetingTimeout: number;
|
greetingTimeout: number;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue