1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-23 15:49:44 -05:00

Revert "refactor(ext/net): extract TLS key and certificate from inter… (#23325)

…faces (#23296)"

This reverts commit e190acbfa8.

Reverting because it broke stable API type declarations. We will reland
it for v1.43 with updated interfaces
This commit is contained in:
Bartek Iwańczuk 2024-04-11 21:31:11 +01:00 committed by GitHub
parent 4ab0215727
commit 5758470ee4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 116 additions and 241 deletions

View file

@ -6276,7 +6276,13 @@ declare namespace Deno {
*
* @category HTTP Server
*/
export type ServeTlsOptions = ServeOptions & TlsCertifiedKeyOptions;
export interface ServeTlsOptions extends ServeOptions {
/** Server private key in PEM format */
cert: string;
/** Cert chain in PEM format */
key: string;
}
/**
* @category HTTP Server

View file

@ -882,6 +882,10 @@ declare namespace Deno {
caCerts?: string[];
/** A HTTP proxy to use for new connections. */
proxy?: Proxy;
/** Cert chain in PEM format. */
cert?: string;
/** Server private key in PEM format. */
key?: string;
/** Sets the maximum numer of idle connections per host allowed in the pool. */
poolMaxIdlePerHost?: number;
/** Set an optional timeout for idle sockets being kept-alive.
@ -958,27 +962,6 @@ declare namespace Deno {
options: CreateHttpClientOptions,
): HttpClient;
/** **UNSTABLE**: New API, yet to be vetted.
*
* Create a custom HttpClient to use with {@linkcode fetch}. This is an
* extension of the web platform Fetch API which allows Deno to use custom
* TLS certificates and connect via a proxy while using `fetch()`.
*
* @example ```ts
* const caCert = await Deno.readTextFile("./ca.pem");
* // Load a client key and certificate that we'll use to connect
* const key = await Deno.readTextFile("./key.key");
* const cert = await Deno.readTextFile("./cert.crt");
* const client = Deno.createHttpClient({ caCerts: [ caCert ], key, cert });
* const response = await fetch("https://myserver.com", { client });
* ```
*
* @category Fetch API
*/
export function createHttpClient(
options: CreateHttpClientOptions & TlsCertifiedKeyOptions,
): HttpClient;
/** **UNSTABLE**: New API, yet to be vetted.
*
* Represents membership of a IPv4 multicast group.

View file

@ -25,7 +25,12 @@ const { ObjectDefineProperty } = primordials;
*/
function createHttpClient(options) {
options.caCerts ??= [];
const keyPair = loadTlsKeyPair("Deno.createHttpClient", options);
const keyPair = loadTlsKeyPair(
options.cert,
undefined,
options.key,
undefined,
);
return new HttpClient(
op_fetch_custom_client(
options,

View file

@ -51,46 +51,54 @@ async function connectTls({
port,
hostname = "127.0.0.1",
transport = "tcp",
caCerts = [],
alpnProtocols = undefined,
keyFormat = undefined,
cert = undefined,
certFile = undefined,
caCerts = [],
certChain = undefined,
key = undefined,
keyFile = undefined,
privateKey = undefined,
cert = undefined,
key = undefined,
alpnProtocols = undefined,
}) {
if (transport !== "tcp") {
throw new TypeError(`Unsupported transport: '${transport}'`);
}
let deprecatedCertFile = undefined;
// Deno.connectTls has an irregular option where you can just pass `certFile` and
// not `keyFile`. In this case it's used for `caCerts` rather than the client key.
if (certFile !== undefined && keyFile === undefined) {
if (certFile !== undefined) {
internals.warnOnDeprecatedApi(
"Deno.ConnectTlsOptions.certFile",
new Error().stack,
"Pass the cert file's contents to the `Deno.ConnectTlsOptions.caCerts` option instead.",
"Pass the cert file contents to the `Deno.ConnectTlsOptions.cert` option instead.",
);
deprecatedCertFile = certFile;
certFile = undefined;
}
const keyPair = loadTlsKeyPair("Deno.connectTls", {
keyFormat,
cert,
certFile,
certChain,
key,
keyFile,
privateKey,
});
if (certChain !== undefined) {
internals.warnOnDeprecatedApi(
"Deno.ConnectTlsOptions.certChain",
new Error().stack,
"Use the `Deno.ConnectTlsOptions.cert` option instead.",
);
}
if (privateKey !== undefined) {
internals.warnOnDeprecatedApi(
"Deno.ConnectTlsOptions.privateKey",
new Error().stack,
"Use the `Deno.ConnectTlsOptions.key` option instead.",
);
}
if (transport !== "tcp") {
throw new TypeError(`Unsupported transport: '${transport}'`);
}
if (certChain !== undefined && cert !== undefined) {
throw new TypeError(
"Cannot specify both `certChain` and `cert`",
);
}
if (privateKey !== undefined && key !== undefined) {
throw new TypeError(
"Cannot specify both `privateKey` and `key`",
);
}
cert ??= certChain;
key ??= privateKey;
const keyPair = loadTlsKeyPair(cert, undefined, key, undefined);
const { 0: rid, 1: localAddr, 2: remoteAddr } = await op_net_connect_tls(
{ hostname, port },
{ certFile: deprecatedCertFile, caCerts, alpnProtocols },
{ certFile, caCerts, cert, key, alpnProtocols },
keyPair,
);
localAddr.transport = "tcp";
@ -129,96 +137,29 @@ class TlsListener extends Listener {
}
}
/**
* Returns true if this object has the shape of one of the certified key material
* interfaces.
*/
function hasTlsKeyPairOptions(options) {
return (ReflectHas(options, "cert") || ReflectHas(options, "key") ||
ReflectHas(options, "certFile") ||
ReflectHas(options, "keyFile") || ReflectHas(options, "privateKey") ||
ReflectHas(options, "certChain"));
ReflectHas(options, "keyFile"));
}
/**
* Loads a TLS keypair from one of the various options. If no key material is provided,
* returns a special Null keypair.
*/
function loadTlsKeyPair(api, {
keyFormat,
function loadTlsKeyPair(
cert,
certFile,
certChain,
key,
keyFile,
privateKey,
}) {
// Check for "pem" format
if (keyFormat !== undefined && keyFormat !== "pem") {
throw new TypeError('If `keyFormat` is specified, it must be "pem"');
) {
if ((certFile !== undefined) ^ (keyFile !== undefined)) {
throw new TypeError(
"If certFile is specified, keyFile must also be specified",
);
}
function exclusive(a1, a1v, a2, a2v) {
if (a1v !== undefined && a2v !== undefined) {
throw new TypeError(
`Cannot specify both \`${a1}\` and \`${a2}\` for \`${api}\`.`,
);
}
if ((cert !== undefined) ^ (key !== undefined)) {
throw new TypeError("If cert is specified, key must also be specified");
}
// Ensure that only one pair is valid
exclusive("certChain", certChain, "cert", cert);
exclusive("certChain", certChain, "certFile", certFile);
exclusive("key", key, "keyFile", keyFile);
exclusive("key", key, "privateKey", privateKey);
function both(a1, a1v, a2, a2v) {
if (a1v !== undefined && a2v === undefined) {
throw new TypeError(
`If \`${a1}\` is specified, \`${a2}\` must be specified as well for \`${api}\`.`,
);
}
if (a1v === undefined && a2v !== undefined) {
throw new TypeError(
`If \`${a2}\` is specified, \`${a1}\` must be specified as well for \`${api}\`.`,
);
}
}
// Pick one pair of cert/key, certFile/keyFile or certChain/privateKey
both("cert", cert, "key", key);
both("certFile", certFile, "keyFile", keyFile);
both("certChain", certChain, "privateKey", privateKey);
if (certFile !== undefined) {
internals.warnOnDeprecatedApi(
"Deno.TlsCertifiedKeyOptions.keyFile",
new Error().stack,
"Pass the key file's contents to the `Deno.TlsCertifiedKeyPem.key` option instead.",
);
internals.warnOnDeprecatedApi(
"Deno.TlsCertifiedKeyOptions.certFile",
new Error().stack,
"Pass the cert file's contents to the `Deno.TlsCertifiedKeyPem.cert` option instead.",
);
return op_tls_key_static_from_file(api, certFile, keyFile);
} else if (certChain !== undefined) {
if (api !== "Deno.connectTls") {
throw new TypeError(
`Invalid options 'certChain' and 'privateKey' for ${api}`,
);
}
internals.warnOnDeprecatedApi(
"Deno.TlsCertifiedKeyOptions.privateKey",
new Error().stack,
"Use the `Deno.TlsCertifiedKeyPem.key` option instead.",
);
internals.warnOnDeprecatedApi(
"Deno.TlsCertifiedKeyOptions.certChain",
new Error().stack,
"Use the `Deno.TlsCertifiedKeyPem.cert` option instead.",
);
return op_tls_key_static(certChain, privateKey);
return op_tls_key_static_from_file("Deno.listenTls", certFile, keyFile);
} else if (cert !== undefined) {
return op_tls_key_static(cert, key);
} else {
@ -228,6 +169,10 @@ function loadTlsKeyPair(api, {
function listenTls({
port,
cert,
certFile,
key,
keyFile,
hostname = "0.0.0.0",
transport = "tcp",
alpnProtocols = undefined,
@ -236,11 +181,22 @@ function listenTls({
if (transport !== "tcp") {
throw new TypeError(`Unsupported transport: '${transport}'`);
}
if (!hasTlsKeyPairOptions(arguments[0])) {
throw new TypeError("A key and certificate are required for `listenTls`");
if (keyFile !== undefined) {
internals.warnOnDeprecatedApi(
"Deno.ListenTlsOptions.keyFile",
new Error().stack,
"Pass the key file contents to the `Deno.ListenTlsOptions.key` option instead.",
);
}
const keyPair = loadTlsKeyPair("Deno.listenTls", arguments[0]);
if (certFile !== undefined) {
internals.warnOnDeprecatedApi(
"Deno.ListenTlsOptions.certFile",
new Error().stack,
"Pass the cert file contents to the `Deno.ListenTlsOptions.cert` option instead.",
);
}
const keyPair = loadTlsKeyPair(cert, certFile, key, keyFile);
const { 0: rid, 1: localAddr } = op_net_listen_tls(
{ hostname, port: Number(port) },
{ alpnProtocols, reusePort },

View file

@ -197,50 +197,12 @@ declare namespace Deno {
options: UnixListenOptions & { transport: "unix" },
): UnixListener;
/** Provides TLS certified keys, ie: a key that has been certified by a trusted certificate authority.
* A certified key generally consists of a private key and certificate part.
*
* @category Network
*/
export type TlsCertifiedKeyOptions =
| TlsCertifiedKeyPem
| TlsCertifiedKeyFromFile
| TlsCertifiedKeyConnectTls;
/**
* Provides certified key material from strings. The key material is provided in
* `PEM`-format (Privacy Enhanced Mail, https://www.rfc-editor.org/rfc/rfc1422) which can be identified by having
* `-----BEGIN-----` and `-----END-----` markers at the beginning and end of the strings. This type of key is not compatible
* with `DER`-format keys which are binary.
*
* Deno supports RSA, EC, and PKCS8-format keys.
*
* ```ts
* const key = {
* key: "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
* cert: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n" }
* };
* ```
*
* @category Network
*/
export interface TlsCertifiedKeyPem {
/** The format of this key material, which must be PEM. */
keyFormat?: "pem";
/** Private key in `PEM` format. RSA, EC, and PKCS8-format keys are supported. */
key: string;
/** Certificate chain in `PEM` format. */
cert: string;
}
/**
* @deprecated This will be removed in Deno 2.0. See the
* {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide}
* for migration instructions.
*
* @category Network
*/
export interface TlsCertifiedKeyFromFile {
/** @category Network */
export interface ListenTlsOptions extends TcpListenOptions {
/** Server private key in PEM format */
key?: string;
/** Cert chain in PEM format */
cert?: string;
/** Path to a file containing a PEM formatted CA certificate. Requires
* `--allow-read`.
*
@ -249,45 +211,16 @@ declare namespace Deno {
* {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide}
* for migration instructions.
*/
certFile: string;
/** Path to a file containing a private key file. Requires `--allow-read`.
certFile?: string;
/** Server private key file. Requires `--allow-read`.
*
* @tags allow-read
* @deprecated This will be removed in Deno 2.0. See the
* {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide}
* for migration instructions.
*/
keyFile: string;
}
keyFile?: string;
/**
* @deprecated This will be removed in Deno 2.0. See the
* {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide}
* for migration instructions.
*
* @category Network
*/
export interface TlsCertifiedKeyConnectTls {
/**
* Certificate chain in `PEM` format.
*
* @deprecated This will be removed in Deno 2.0. See the
* {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide}
* for migration instructions.
*/
certChain: string;
/**
* Private key in `PEM` format. RSA, EC, and PKCS8-format keys are supported.
*
* @deprecated This will be removed in Deno 2.0. See the
* {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide}
* for migration instructions.
*/
privateKey: string;
}
/** @category Network */
export interface ListenTlsOptions extends TcpListenOptions {
transport?: "tcp";
/** Application-Layer Protocol Negotiation (ALPN) protocols to announce to
@ -313,9 +246,7 @@ declare namespace Deno {
* @tags allow-net
* @category Network
*/
export function listenTls(
options: ListenTlsOptions & TlsCertifiedKeyOptions,
): TlsListener;
export function listenTls(options: ListenTlsOptions): TlsListener;
/** @category Network */
export interface ConnectOptions {
@ -412,11 +343,9 @@ declare namespace Deno {
*
* @default {"127.0.0.1"} */
hostname?: string;
/** Path to a file containing a PEM formatted list of root certificates that will
* be used in addition to the default root certificates to verify the peer's certificate. Requires
* `--allow-read`.
/**
* Server certificate file.
*
* @tags allow-read
* @deprecated This will be removed in Deno 2.0. See the
* {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide}
* for migration instructions.
@ -432,6 +361,26 @@ declare namespace Deno {
* TLS handshake.
*/
alpnProtocols?: string[];
/**
* PEM formatted client certificate chain.
*
* @deprecated This will be removed in Deno 2.0. See the
* {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide}
* for migration instructions.
*/
certChain?: string;
/**
* PEM formatted (RSA or PKCS8) private key of client certificate.
*
* @deprecated This will be removed in Deno 2.0. See the
* {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide}
* for migration instructions.
*/
privateKey?: string;
/** Server private key in PEM format. */
key?: string;
/** Cert chain in PEM format. */
cert?: string;
}
/** Establishes a secure connection over TLS (transport layer security) using
@ -454,30 +403,6 @@ declare namespace Deno {
*/
export function connectTls(options: ConnectTlsOptions): Promise<TlsConn>;
/** Establishes a secure connection over TLS (transport layer security) using
* an optional cert file, client certificate, hostname (default is "127.0.0.1") and
* port. The cert file is optional and if not included Mozilla's root certificates will
* be used (see also https://github.com/ctz/webpki-roots for specifics)
*
* ```ts
* const caCert = await Deno.readTextFile("./certs/my_custom_root_CA.pem");
* const key = "----BEGIN PRIVATE KEY----...";
* const cert = "----BEGIN CERTIFICATE----...";
* const conn1 = await Deno.connectTls({ port: 80, key, cert });
* const conn2 = await Deno.connectTls({ caCerts: [caCert], hostname: "192.0.2.1", port: 80, key, cert });
* const conn3 = await Deno.connectTls({ hostname: "[2001:db8::1]", port: 80, key, cert });
* const conn4 = await Deno.connectTls({ caCerts: [caCert], hostname: "golang.org", port: 80, key, cert });
* ```
*
* Requires `allow-net` permission.
*
* @tags allow-net
* @category Network
*/
export function connectTls(
options: ConnectTlsOptions & TlsCertifiedKeyOptions,
): Promise<TlsConn>;
/** @category Network */
export interface StartTlsOptions {
/** A literal IP address or host name that can be resolved to an IP address.

View file

@ -1336,7 +1336,7 @@ Deno.test(
});
},
TypeError,
"Cannot specify both `key` and `privateKey` for `Deno.connectTls`.",
"Cannot specify both `privateKey` and `key`",
);
},
);