denoland / deno

A modern runtime for JavaScript and TypeScript.
https://deno.com
MIT License
96.97k stars 5.35k forks source link

"Error: read UNKNOWN" error when using TLS to connect to database using mysql2 #20293

Open DMeechan opened 1 year ago

DMeechan commented 1 year ago

When using Deno 1.36.3 I get this Uncaught (in promise) Error: read UNKNOWN error when running the script below:

error: Uncaught (in promise) Error: read UNKNOWN
    at createConnection (file:///home/u/node-vs-deno-db-drivers/node_modules/.deno/mysql2@3.6.0/node_modules/mysql2/promise.js:253:31)
    at file:///home/u/node-vs-deno-db-drivers/mysql2.ts:12:28
    at file:///home/u/node-vs-deno-db-drivers/mysql2.ts:21:1

When using Deno 1.36.1 (which precedes https://github.com/denoland/deno/pull/20120) I get an Uncaught Error: tlssock._start is not a function error when running the script:

error: Uncaught Error: tlssock._start is not a function
    at createConnection (file:///home/u/node-vs-deno-db-drivers/node_modules/.deno/mysql2@3.6.0/node_modules/mysql2/promise.js:253:31)
    at file:///home/u/node-vs-deno-db-drivers/mysql2.ts:12:28
    at file:///home/u/node-vs-deno-db-drivers/mysql2.ts:21:1

Given that the stack trace indicates the Error: read UNKNOWN error is being thrown from the same place as the Uncaught Error: tlssock._start is not a function error was, I'm thinking it may be a TLS-related issue in Deno.

I've tested the script below with Node v18.17.1 and it runs fine.

How to reproduce

Create mysql2.ts:

// Swap the commenting on these two lines if you're testing this on Node.js vs Deno
import { createConnection } from "npm:mysql2@^3.6.0/promise"; // For Deno
// import { createConnection } from "mysql2/promise"; // For Node.js

import { readFileSync } from "node:fs";
const cert = readFileSync("/etc/ssl/certs/ca-certificates.crt"); // I'm using Ubuntu - this certificate path may be different for your OS
const planetscaleMysqlUrl = "SOME_MYSQL_URL_HERE";

(async () => {
  const connection = await createConnection({
    uri: planetscaleMysqlUrl,
    ssl: { ca: cert },
  });

  const result = await connection.execute("SELECT 1");
  console.log(result);

  connection.end();
})();

In Deno

Run the script above using Deno 1.36.3: deno run -A mysql2.ts

In Node.js

Using v18.17.1:

  1. npm init -y
  2. Add "type": "module" to package.json
  3. npm install mysql2@3.6.0
  4. Copy or rename the file to .js (it's easier): cp mysql2.ts mysql2.js
  5. Swap the import comments on lines 2 and 3 of mysql2.js
  6. node mysql2.js
brundonsmith commented 1 year ago

I'm getting this too (was getting the tlssock._start error on Deno v1.36.1, and now Error: read UNKNOWN on Deno v1.36.3), but with the pg-pool library instead of mysql

littledivy commented 1 year ago

This may be caused by a race condition in the socket buffer which was fixed in canary (deno upgrade --canary). Can you give it a try maybe it fixes the issue?

DMeechan commented 1 year ago

@littledivy I just updated to deno 1.36.3+64045eb and I'm getting the same error

DMeechan commented 1 year ago

Hello šŸ‘‹ I just tried the same thing with mariadb driver on the latest Deno canary release (deno 1.36.3+c9223bc) and I get the same error but with a different stack trace:

import mariadb from "npm:mariadb@^3.2.0";
const connection = await mariadb.createConnection({
  host: hostname,
  database: db,
  user: username,
  password,
  ssl: true,
});

// Result:
error: Uncaught (in promise) Error: read UNKNOWN
    at __node_internal_captureLargerStackTrace (ext:deno_node/internal/errors.ts:91:9)
    at __node_internal_errnoException (ext:deno_node/internal/errors.ts:139:10)
    at TCP.onStreamRead [as onread] (ext:deno_node/internal/stream_base_commons.ts:209:20)
    at TCP.#read (ext:deno_node/internal_binding/stream_wrap.ts:277:14)
    at eventLoopTick (ext:core/01_core.js:183:11)
littledivy commented 1 year ago

Thanks, will look into it.

littledivy commented 1 year ago

It seems the underlying error was not propogated. I am getting "Unsupported Certificate" error. And the example works if I use deno run with --unsafely-ignore-certificate-errors.

DMeechan commented 1 year ago

Thanks @littledivy! I'll give that a try!

Do you know why we'd be getting an unsupported certificate error in Deno, while it runs ok in Node? Is there a different (type of) certificate we should be using?

chromakode commented 10 months ago

I'm also seeing this Error: read UNKNOWN error using Deno 1.38.4 to connect to a postgres server using TLS with the npm:pg library.

chromakode commented 10 months ago

Tracing through the debugger, the underlying error I'm getting is NotValidForName. It'd be useful to expose this to the user somehow, since currently these errors are difficult to debug. If it'd be difficult to thread the error context up through nread, would a console.error() here be viable for the "UNKNOWN" type errors?

image

chromakode commented 10 months ago

Aha! The underlying issue here is that tlsOptions.hostname is not being set here:

https://github.com/denoland/deno/blob/f6b889b43219e3c9be770c8b2758bff3048ddcbd/ext/node/polyfills/_tls_wrap.ts#L87-L89

It appears the value arrives here in opts.servername. After setting tlsOptions.hostname = opts.servername in the debugger I get a successful connection.

phoenisx commented 9 months ago

Hi

I still get this error in Deno v1.39.4, while using Deno with npm:typeorm with PG driver. The setup that I am using works perfectly fine locally without SSL required, but the connectivity fails when connecting to SSL enabled database server.

error: Uncaught (in promise) Error: read UNKNOWN
    at __node_internal_captureLargerStackTrace (ext:deno_node/internal/errors.ts:91:9)
    at __node_internal_errnoException (ext:deno_node/internal/errors.ts:139:10)
    at TCP.onStreamRead [as onread] (ext:deno_node/internal/stream_base_commons.ts:209:20)
    at TCP.#read (ext:deno_node/internal_binding/stream_wrap.ts:244:12)
    at eventLoopTick (ext:core/01_core.js:182:7)

P.S.: My setup works with NodeJS. I am trying to use Deno on the side for some migration scripts.

Bleeky commented 2 months ago

Hi there šŸ‘‹

Getting a similar error on Deno 1.45.5, using the npm:imapflow package.

error: Uncaught Error: read UNKNOWN
    at __node_internal_captureLargerStackTrace (ext:deno_node/internal/errors.ts:93:9)
    at __node_internal_errnoException (ext:deno_node/internal/errors.ts:141:10)
    at TCP.onStreamRead [as onread] (ext:deno_node/internal/stream_base_commons.ts:209:20)
    at TCP.#read (ext:deno_node/internal_binding/stream_wrap.ts:245:12)
    at eventLoopTick (ext:core/01_core.js:168:7)
kt3k commented 1 month ago

I was able to connect my postgres db (hosted by supabase) with the below snippet with TLS enabled:

import pg from "npm:pg";
import fs from "node:fs";
const client = new pg.Client({
  user: "postgres.<random-id>",
  password: "<password>",
  database: "postgres",
  host: "aws-0-ap-northeast-1.pooler.supabase.com",
  port: "6543",
  ssl: {
    ca: fs.readFileSync("./<path-to-cert>.crt"),
  },
});
await client.connect();
console.log("client connected");
const result = await client.query("select 1;");
console.log(result.rows[0]);

This executed like:

$ deno run -A a.mjs
client connected
{ "?column?": 1 }

and when I commented out 'ssl' option part, I got the error read UNKNOWN

$ deno run -A a.mjs
error: Uncaught (in promise) Error: read UNKNOWN
    at __node_internal_captureLargerStackTrace (ext:deno_node/internal/errors.ts:93:9)
    at __node_internal_errnoException (ext:deno_node/internal/errors.ts:141:10)
    at TCP.onStreamRead (ext:deno_node/internal/stream_base_commons.ts:209:20)
    at TCP.#read (ext:deno_node/internal_binding/stream_wrap.ts:245:12)
    at eventLoopTick (ext:core/01_core.js:175:7)

This unknown error seems like coming from here: https://github.com/denoland/deno/blob/ea8bf0945ac93a16a3d8df2470c70d9cf1c36172/ext/node/polyfills/internal_binding/stream_wrap.ts#L346

, where the internal actual error is InvalidData: invalid peer certificate: UnknownIssuer, but because there's no corresponding system error in node compat layer, it seems mapped to UNKNOWN, and that seems causing the confusion here.

@phoenisx @Bleeky Can you check your database connection settings and see if they work in Node.js? (Or also check the same script with --unsafely-ignore-certificate-errors option enabled?)