{"record":{"id":"babbec90d10170f6","repo":"louislam/uptime-kuma","slug":"expected-tls-alert-expectedtlsalert-but-conne","errorCode":null,"errorMessage":"Expected TLS alert '${expectedTlsAlert}' but connection succeeded. The server accepted the connection without requiring a client certificate.","messagePattern":"Expected TLS alert '(.+?)' but connection succeeded\\. The server accepted the connection without requiring a client certificate\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/monitor-types/tcp.js","lineNumber":325,"sourceCode":"        }\n\n        const result = await this.attemptTlsConnection(monitor, options, startTime, timeout);\n\n        heartbeat.ping = result.responseTime;\n\n        // Handle TLS info for certificate expiry monitoring\n        if (result.tlsInfo && monitor.isEnabledExpiryNotification()) {\n            await monitor.handleTlsInfo(result.tlsInfo);\n        }\n\n        // Check if we got the expected alert\n        // Note: Error messages below could be translated, but alert names (e.g., certificate_required)\n        // are from RFC 8446 spec and should remain in English for consistency with the spec.\n        if (result.alertName === expectedTlsAlert) {\n            heartbeat.status = UP;\n            heartbeat.msg = `TLS alert received as expected: ${result.alertName} (${result.alertNumber})`;\n        } else if (result.success) {\n            throw new Error(\n                `Expected TLS alert '${expectedTlsAlert}' but connection succeeded. The server accepted the connection without requiring a client certificate.`\n            );\n        } else if (result.alertNumber !== null) {\n            throw new Error(\n                `Expected TLS alert '${expectedTlsAlert}' but received '${result.alertName}' (${result.alertNumber})`\n            );\n        } else {\n            throw new Error(\n                `Expected TLS alert '${expectedTlsAlert}' but got unexpected error: ${result.errorMessage}`\n            );\n        }\n    }\n\n    /**\n     * Attempt TLS connection and capture result/alert\n     * @param {object} monitor Monitor object\n     * @param {object} options TLS connection options\n     * @param {number} startTime Connection start timestamp","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/louislam/uptime-kuma/blob/6b5ea0155793e666666745fb8d6fef1e829543a2/server/monitor-types/tcp.js#L307-L343","documentation":"Thrown by checkTlsAlert (tcp.js:288-337), a monitor mode that VERIFIES mTLS by asserting a server REJECTS an unauthenticated client with a specific RFC 8446 alert (e.g. 'certificate_required'). This branch fires when attemptTlsConnection resolved with success:true — the server accepted the connection instead of demanding a client certificate — so the security property the user is testing for is absent.","triggerScenarios":"Monitor configured with an expected_tls_alert (e.g. certificate_required) against a server whose TLS endpoint does not require client certs; the mTLS policy was removed or never enabled server-side; testing against the wrong port (a plain-HTTPS listener rather than the mTLS one).","commonSituations":"Security/compliance check expecting a service to enforce client certificates, but a reverse proxy was reconfigured to terminate TLS itself and forward plain HTTP upstream; the expected alert was set against a dev environment that has mTLS disabled; nginx env var SSL_VERIFY_CLIENT reset to 'off' during a deploy.","solutions":["Confirm the target server actually requires client certificates (e.g. nginx `ssl_verify_client on;`, Apache `SSLVerifyClient require`).","Verify the monitor is pointed at the port/listener that enforces mTLS, not a parallel open endpoint.","Re-deploy or roll back the server config that disabled client-cert verification.","If the server intentionally no longer requires client certs, remove or update the monitor's expected_tls_alert setting."],"exampleFix":"// server side (nginx) — restore mTLS enforcement\n// ssl_client_certificate /etc/ssl/ca.pem;\n// ssl_verify_client on;","handlingStrategy":"validation","validationCode":"// Confirm the server actually requires client certs before relying on the alert check\nconst { execSync } = require('child_process');\nfunction serverRequiresClientCert(host, port) {\n    try {\n        // Connect WITHOUT a client cert; openssl returns non-zero with 'alert' if required\n        execSync(`echo | openssl s_client -connect ${host}:${port} -servername ${host} 2>&1`, { stdio: 'pipe', timeout: 8000 });\n        return /alert.*certificate_required|alert.* handshake_failure/i.test(out);\n    } catch (e) {\n        return /alert/i.test(String(e.stdout || '') + e.message);\n    }\n}","typeGuard":"function isExpectedAlertConfigured(v) {\n    return typeof v === 'string' && v.length > 0 && /^[a-z_]+$/.test(v);\n}","tryCatchPattern":"try {\n    await monitor.checkTlsAlert(monitor, heartbeat, expectedTlsAlert);\n} catch (e) {\n    if (/connection succeeded/i.test(e.message)) {\n        // server is NOT enforcing mTLS — escalate as a security finding, not a transient outage\n        heartbeat.status = DOWN;\n        heartbeat.msg = `mTLS NOT enforced: ${e.message}`;\n    } else { throw e; }\n}","preventionTips":["Source expected_tls_alert from an observed run (openssl s_client output) rather than guessing.","Periodically re-run the mTLS posture check after server config deploys."],"tags":["tls","mtls","tcp-monitor","security","certificate"],"backgroundTag":null,"analyzedSha":"6b5ea0155793e666666745fb8d6fef1e829543a2","analyzedAt":"2026-08-12T23:42:12.959Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}