oven-sh / bun

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one
https://bun.sh
Other
74.5k stars 2.79k forks source link

`node:https` module missing client certificate support #6940

Closed davidstevens37 closed 4 months ago

davidstevens37 commented 1 year ago

What version of Bun is running?

1.0.9+98f20170a

What platform is your computer?

Darwin 23.0.0 arm64 arm

What steps can reproduce the bug?

When using the node:https module, the use of requestOptions.cert and requestOptions.key do not present the client certificate to the receiving server.

In the example below, the request is made to https://server.cryptomix.com/sercure/, a public service which echos the presented client certificate.

index.js:

import https from 'node:https';

const PEM_DATA = {
    certificate: '-----BEGIN CERTIFICATE-----\nMIIF7zCCA9egAwIBAgIUctP9s9AtVKL9aMB4qNutVTBV+bcwDQYJKoZIhvcNAQEL\nBQAwgYYxCzAJBgNVBAYTAlhYMRIwEAYDVQQIDAlTdGF0ZU5hbWUxETAPBgNVBAcM\nCENpdHlOYW1lMRQwEgYDVQQKDAtDb21wYW55TmFtZTEbMBkGA1UECwwSQ29tcGFu\neVNlY3Rpb25OYW1lMR0wGwYDVQQDDBRDb21tb25OYW1lT3JIb3N0bmFtZTAeFw0y\nMzExMDIyMTAwMzFaFw0zMzEwMzAyMTAwMzFaMIGGMQswCQYDVQQGEwJYWDESMBAG\nA1UECAwJU3RhdGVOYW1lMREwDwYDVQQHDAhDaXR5TmFtZTEUMBIGA1UECgwLQ29t\ncGFueU5hbWUxGzAZBgNVBAsMEkNvbXBhbnlTZWN0aW9uTmFtZTEdMBsGA1UEAwwU\nQ29tbW9uTmFtZU9ySG9zdG5hbWUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\nAoICAQC7cp8oVsj9BV+a5HeNyZVplXjQeX1B3a7xQtZ2zeg7tCwTSsd2nh4FBuim\nVRorBQc2pmm/ZpyjTp7uKPtzmfuzDBpQ/Q0jvrLEKcYcMYsaYbe6DxC181Wpp3v9\n/9wmsapaN94rN/KU2S1exff9P5Z6zkkv5Y8seir/E1Qc3LmWvT2dhU4NfHUtGsXP\n4kQw9iTgA1BTy1imPDd55himmIBKpHk0xI/gRF2F1GtDPn2VMzTBPYI10+KplzdM\n/d9KAbfdXouLd63HQifDn0g7eSkXZALy6nTnjkQKlIIOg18/YwGSdXdqQVZ6jF5O\nbNRenxCf5cJb+rjU0yPODoIb5jE86HgBd9w2JQkId954I8suN1i/6mVmfEgVno+V\nvj7V9jtkEeT1ILkuYGjILyFdywFMiZPcUqBhJgryh68TsvyNXKUzUWTUu7w9BZ1t\nCysJQHMUyupZBG3nma7DKdsVBlatDGkasudeDoQSEAxy8YeGPLzTAREVo+gIhcc3\nrvHb5zh9JH3uz0N2jXe58vPPQ7mVGBIYv/T9QGMQFfSGdmZWmm+nF06yfqUygA/f\nOt6tWBHJerJasMhOzCYKSlkdN5QUXxn48INAGH/CaONonuFvMiPuOqO+UNo7DXlP\nOd98bJor0mtKeihP8D7RAWmhYXq1CbotpdnumjAePKk2XrzYiwIDAQABo1MwUTAd\nBgNVHQ4EFgQU9As7xzAkMpSmDi+IbnR6McLR6vQwHwYDVR0jBBgwFoAU9As7xzAk\nMpSmDi+IbnR6McLR6vQwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC\nAgEAaSTat7tJlVngcX/kSw0YfIJC76etvA8laYrHy236YaHlZY4VYXaKbv1njr2f\nLaBkv21PZqnqsVFA2Jc1AECB+xiOFBOonCO/kY37DxDlHOW0qgBrkhwMWAI6BylF\nel0dhM/cMPleS8Ico/b5lzMl5N4yhLIfFNv4rM/LGurP+sDJi7nZUaY0oVeRH2Gv\nzdbnZt1T+S0uHSeBKYTY0I/G2QWhkNnUcTO4vMo0bbt00YHtrEdQps0IDpYu7FAa\nmaZlPgrb+QFCQCGQKRxJ8infn+Rd37YaEcHSp+c6QbpeLSVUFdothd4IzTKX90k+\n78lLZOXtYQ7P5+vLgb2wM5LbXUTZ8nBEHMrs35/V5xZLahk9pXb9+FGKjSjNKFo7\nbqtYU/szEd3lK81J8/l/avIREP/gOg38yZ/bfWQ/QmTWmyKszfRI0KTmQZc6cw+a\nvi9Qzl6cgHkX3/uM9zyANZvm0qzoS/kxmkJU49U96jIbxWsFmdnakP4NDukf1cAA\n9zryKvaQwsX3Q7oBi9ZcJmVBABO/AeIcGbWStMiJpAjXlfBdBJTCkxu6sfNKX8J9\nCLah6qpVW7TgtMllCMOKR8IkzRRcX794piaoGP4rg0aykntdl0ViMN6y0uJ075vj\n91nMGr13bNK+BmK+75pN9kSk97p1bTWwop6xGy4/5CRARg0=\n-----END CERTIFICATE-----\n',
    key: '-----BEGIN PRIVATE KEY-----\n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\niP2ZJDb0k7nhwZ+F96HkCSPIl6NoBw==\n-----END PRIVATE KEY-----\n',
};

const options = {
    host: `server.cryptomix.com`,
    port: 443,
    path: '/secure/',
    method: 'GET',
    key: PEM_DATA.key,
    cert:  PEM_DATA.certificate
};

const request = https.request(options, (response) => {
    response.on('data', (data) => {
        console.log(data.toString())
    });
});

request.end();

What is the expected behavior?

Node.js v19.8.1 Response: λ node index.js ->

<html>
<head>
<title>TLS Client Authentication Test</title>

<style type="text/css">
  SPAN.sslsuccess {font-weight: bolder; color : green}
  SPAN.sslerror {font-weight: bolder; color : red}
</style>
</head>
<br><span class="sslsuccess">TLSv1.2 Authentication OK!</span><br><br>Technical information follows :<pre>Array
(
    [INVOCATION_ID] => ca9142265a9b4798bee807e7743913ea
    [LANG] => C.UTF-8
    [HTTP_CONNECTION] => keep-alive
    [HTTP_HOST] => server.cryptomix.com
    [SSL_CLIENT_I_DN] => CN=CommonNameOrHostname,OU=CompanySectionName,O=CompanyName,L=CityName,ST=StateName,C=XX
    [SSL_CLIENT_S_DN] => CN=CommonNameOrHostname,OU=CompanySectionName,O=CompanyName,L=CityName,ST=StateName,C=XX
    [SSL_CLIENT_VERIFY] => FAILED:self signed certificate
    [SSL_CLIENT_V_END] => Oct 30 21:00:31 2033 GMT
    [SSL_CLIENT_V_START] => Nov  2 21:00:31 2023 GMT
    [SSL_CLIENT_SERIAL] => 72D3FDB3D02D54A2FD68C078A8DBAD553055F9B7
    [SSL_CLIENT_FINGERPRINT] => 81720b6fa07f1cf45711a2993a1133cce725f7c8
    [SSL_SERVER_NAME] => server.cryptomix.com
    [SSL_CIPHER] => ECDHE-RSA-AES128-SHA256
    [SSL_PROTOCOL] => TLSv1.2
    [HTTPS] => on
    [PATH_INFO] => 
    [SERVER_NAME] => server.cryptomix.com
    [SERVER_PORT] => 443
    [SERVER_ADDR] => 54.36.191.227
    [REMOTE_PORT] => 59416
    [REMOTE_ADDR] => *redacted*
    [SERVER_PROTOCOL] => HTTP/1.1
    [DOCUMENT_URI] => /secure/index.php
    [REQUEST_URI] => /secure/
    [CONTENT_LENGTH] => 
    [CONTENT_TYPE] => 
    [REQUEST_METHOD] => GET
    [QUERY_STRING] => 
    [REQUEST_TIME_FLOAT] => 1699314101.7588
    [REQUEST_TIME] => 1699314101
)
</pre><br><a href="index2.php">Load second page</a>

</body>
</html>

What do you see instead?

Bun Response: λ bun run index.js ->

<html>
<head>
<title>TLS Client Authentication Test</title>

<style type="text/css">
  SPAN.sslsuccess {font-weight: bolder; color : green}
  SPAN.sslerror {font-weight: bolder; color : red}
</style>
</head>
<br><span class="sslerror">Error: No TLS client certificate presented</span>

</body>
</html>

Additional information

No response

david-plugge commented 1 year ago

Same issue for me and it's a blocker.

koooge commented 10 months ago

I got the same issue. My http client with bun can't be authenticated against kube-apiserver's API created by kubeadm due to this. ts-node works.

janhaa commented 9 months ago

Blocker for me aswell, would love to adopt Bun but having client cert auth is a hard requirement.

Does anyone know where in the source code to find the relevant parts to possibly initiate a fork / PR ?

edvald commented 8 months ago

This is a big issue for us unfortunately.

What I've been able to gather so far is that underneath the node:https request() function is a call to fetch(), which appears to be a straight reference to JavaScriptCore's fetch() API. This does not and will not support client-specified certificates for security reasons (as it's not designed for a server-side/local use case like this).

Short of Bun implementing its own version of fetch() (forking it from JavaScriptCore then), we're left to workarounds.

One workaround is to use a separate proxy process, which is likely what we'll go for, but it's not a good long-term solution.

Question to Bun maintainers: Is it possible to somehow supply a certificate (self-signed or otherwise) to the runtime?

davidstevens37 commented 4 months ago

fixed by #11322

asilvas-godaddy commented 2 months ago

Still seeing this issue in the latest 1.1.29. Anyone else?

gabrieljablonski commented 1 month ago

@asilvas-godaddy if you're still struggling with this: https://bun.sh/docs/api/fetch#tls

That's what I get for relying on intellisense as documentation 😅

Bun type definitions are incomplete for this.

Here's a wrapper I wrote so I don't have to worry about the bad internal types.

import type { BunFile } from "bun";

type CertFile = string | Buffer | BunFile;

interface FetchRequestInitWithTls extends RequestInit {
  tls?: {
    rejectUnauthorized?: boolean | undefined;
    // If using a linter, safe to ignore rule for `any` here
    checkServerIdentity?: any;
    ca?: CertFile | CertFile[];
    cert?: CertFile | CertFile[];
    key?: CertFile | CertFile[];
  };
}

/**
 * @description Wrapper for bun's `fetch`, which allows to pass TLS options with correct typings.
 *
 * @param url
 * @param init
 * @returns {Promise<Response>}
 */
export function fetchWithTls(
  url: string | URL | Request,
  init?: FetchRequestInitWithTls,
): Promise<Response> {
  return fetch(url, init);
}
asilvas commented 1 month ago

@gabrieljablonski it isn't a type issue. It's broken for me.