denoland / deno

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

invalid peer certificate: BadSignature with Cloudflare WARP #21169

Open yacinehmito opened 10 months ago

yacinehmito commented 10 months ago

Disclaimer: It's a bit hard to know whether the root cause is in Deno's scope or Cloudflare's scope, so I am posting an issue both in here and with Cloudflare support.

Description

When using Cloudflare WARP as an HTTPS proxy, deno is unable to fetch any resources as it errors with invalid peer certificate: BadSignature regardless of how Cloudflare's certificate is provided.

Context

Cloudflare WARP is a VPN client that can also act as an HTTPS proxy in an Enterprise setting (this is part of the Cloudflare Zero Trust suite of product). Effectively, Cloudflare acts as a man-in-the-middle for all HTTPS traffic when Cloudflare WARP is activated and configured to run as an HTTPS proxy. It therefore requires the endpoint to trust a certificate from Cloudflare, which can be downloaded here.

When using Deno, this certificate can be provided in one of three ways:

  1. Along the --cert CLI parameter.
  2. By setting the environment variable DENO_CERT to the path of the certificate.
  3. By installing the certificate to the system's trust store and setting the environment variable DENO_TLS_CA_STORE to system.

Regardless of how the certificate is provided, this fails. It may either be because of a defect of Cloudflare's proxy, or a defect in Deno's handling of custom certificates.

Steps to reproduce

  1. Install Cloudflare WARP, login to the appropriate Cloudflare team and configure the WARP client to enable the L7 proxy in the Zero Trust dashboard. Make sure that "Zero Trust" is toggled on the Cloudflare WARP client.
  2. Download the Cloudflare certificate in PEM format. Let's assume that it is stored at ~/cloudflare.pem.
  3. Write a script that depends on a remote module that has not yet been cached (see example script). Let's assume that it is stored at ~/script.ts.
  4. (Optionally: Bust the cache with rm -r $(deno info --json | grep denoDir | cut -d: -f2 | sed 's/[" ,]//g').)
  5. Run deno run --cert ~/cloudflare.pem ~/script.ts.

This will fail with the following output:

error: Import 'https://deno.land/std@0.206.0/uuid/mod.ts' failed:
error sending request for url (https://deno.land/std@0.206.0/uuid/mod.ts):
error trying to connect:
invalid peer certificate: BadSignature

Example script

import { v1 } from "https://deno.land/std@0.206.0/uuid/mod.ts";

console.log(v1.generate());

System settings

Output of deno --version:

deno 1.38.1 (release, aarch64-apple-darwin)
v8 12.0.267.1
typescript 5.2.2

OS: macOS Ventura 13.6.1

Cloudflare WARP version: 2023.9.252.0 (20230927.21)

yacinehmito commented 10 months ago

I managed to reproduce with rustls' example program:

./target/debug/tlsclient-mio --verbose --cafile ~/cloudflare.pem deno.land --http

I'll keep this issue open, unless asked otherwise, and will direct my investigations to rustls.

yacinehmito commented 10 months ago

I think I found the issue: the signature algorithm of the Cloudflare certificate is ECDSA Signature with SHA-512 (1.2.840.10045.4.3.4). It is not supported by rustls.

The issue to track progress in rustls: https://github.com/rustls/rustls/issues/1367 The PR to track progress in ring: https://github.com/briansmith/ring/pull/1631

mmastrac commented 10 months ago

We'll keep an eye on this an update ring/rustls as needed. Thanks for researching it.

ostrolucky commented 1 month ago

FYI rustls supports this since 0.23 (release Feb 29)