denoland / deno

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

RFC: `Deno.upgradeHttpConnect` API #21870

Open mmastrac opened 6 months ago

mmastrac commented 6 months ago

RFC: Deno.upgradeHttpConnect API for HTTP CONNECT Upgrade

Overview

This introduces a Deno.upgradeHttpConnect function for the Deno serve API, enabling the upgrade of incoming HTTP requests with a specific CONNECT method. This upgrade mechanism extends the capabilities of the serve API, allowing transitions to a tunnelled connection (see HTTP/1.1 CONNECT Method and HTTP/2 CONNECT Method).

To ensure strict adherence to Deno's security model and sandboxing principles, the Deno.upgradeHttpConnect function requires users to explicitly provide a Conn object. This deliberate requirement serves multiple purposes:

This API enables developers to handle advanced connection upgrades securely within the Deno runtime environment, while still allowing them to maintain control over network-related permissions.

Motivation

HTTP proxies play a pivotal role in modern networking by acting as intermediaries between clients and servers. They facilitate various functionalities, including caching, content filtering, access control, and network optimization.

The implementation of HTTP's CONNECT method is fundamental for building HTTP proxies that perform transparent SSL proxing. The CONNECT HTTP method establishes a tunnel through an HTTP server, allowing clients to directly communicate with external servers, enabling advanced use cases such as low-overhead proxying of WebSocket connections, secure tunneling for SSL/TLS, and accessing resources beyond typical HTTP traffic.

Proposal

Deno.upgradeHttpConnect API

The Deno.upgradeHttpConnect function facilitates the upgrade of an incoming HTTP request with the CONNECT method to a raw connection.

Syntax:

function upgradeHttpConnect(
  request: ServerRequest,
  options?: UpgradeHttpOptions
): HttpConnectUpgradeResult;

Parameters:

Return value:

An HttpConnectUpgradeResult object representing the upgraded connection and response to be used for the upgrade.

UpgradeHttpOptions

The UpgradeHttpOptions object allows for configuring the upgrade process and includes:

Properties:

This updated structure enables explicit control and specification of the target connection to be used during the upgrade process.

interface UpgradeHttpOptions {
  conn: Deno.Conn;
  // Other optional properties as needed
}

HttpConnectUpgradeResult

The HttpConnectUpgradeResult object represents the upgraded connection and includes:

Properties:

Example Usage

if (req.method === "CONNECT") {
  if (req.uri !== "internal.server:1234") {
    throw new Error("Invalid server");
  }
  const { response } = Deno.upgradeHttpConnect(req, { conn: await Deno.connect({ host: "internal.server", port: 1234 }));

  // Return the sentinel response
  return response;
}

Protocol Notes

HTTP/1 CONNECT

The Deno.upgradeHttpConnect method for HTTP/1 effectively "hijacks" the underlying HTTP/1 connection when transitioning to an upgraded connection. The connection is no longer usable by HTTP transactions after this operation.

HTTP/2 CONNECT

In contrast to HTTP/1, in HTTP/2, the Deno.upgradeHttpConnect method leverages the re-use of a single HTTP/2 stream to transition to an upgraded connection. This maintains the existing HTTP/2 connection while employing a dedicated stream for the upgraded communication protocol.

Security Considerations

The Deno.upgradeHttpConnect API introduces powerful capabilities for upgrading HTTP connections, which inherently raises security considerations that developers must address to ensure robust and safe application behavior.

Network Permissions and Sandbox Compliance

The requirement for users to provide a Conn object within the Deno.upgradeHttpConnect function serves as a deliberate security measure. This necessity ensures adherence to Deno's permission system, particularly the --allow-net flag, which governs network-related activities. By requiring the explicit provision of a connection object rather than automatically making the underlying connection, it forces the user to make the underlying network connection operation explicit and visible.

Potential Risks of Arbitrary Connections

Allowing arbitrary connections through the Deno.upgradeHttpConnect API poses inherent risks, particularly when handling external or untrusted sources. Attackers could potentially exploit this capability to intrude into private networks or initiate malicious activities. Developers must meticulously validate and authenticate connections before upgrading them, implementing robust authorization checks and validating the target server's legitimacy to mitigate potential security threats.

Generally, and as with all CONNECT implementations, this API should only be available within the security boundaries of an organization, and should not be available for outside use.

Protection Against Denial-of-Service (DoS) Attacks

The ability to upgrade connections can potentially expose the application to Denial-of-Service (DoS) attacks. Malicious actors might attempt to overwhelm the application by initiating numerous connection upgrade requests. To mitigate such risks, developers should implement rate limiting, request throttling, and robust request validation mechanisms to safeguard the application and other reachable servers against DoS attacks.

References

imcotton commented 6 months ago

I would like to share my initial feedback on the API surface:

  1. Having signal: AbortSignal in UpgradeHttpOptions for users to abort requests at will.

  2. Provide a way for users to handle errors during the individual proxy request lifecycle, such as logging or catch-ignoring errors raised from internal IO, e.g.:

    • Deno.errors.BadResource
    • Deno.errors.BrokenPipe
    • Deno.errors.ConnectionReset
    • Deno.errors.Interrupted