Skip to content

Commit

Permalink
Merge pull request #682 from nats-io/tls-first
Browse files Browse the repository at this point in the history
[FEAT] `handshake_first` support
  • Loading branch information
aricart committed Apr 8, 2024
2 parents 1c061a9 + 6171b9f commit 06d16f8
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 7 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ jobs:
TMPDIR: ${{ runner.temp }}
CI: true
NGS_CI_USER: ${{ secrets.NGS_CI_USER }}
run: deno test --allow-all --unstable --parallel --fail-fast --coverage=./cov
run: |
deno test --allow-all --unstable --parallel --fail-fast --coverage=./cov tests/ jetstream/tests
deno test --allow-all --unstable --parallel --fail-fast --unsafely-ignore-certificate-errors --coverage=./cov unsafe_tests/
- name: Build nats.js
run: deno bundle --unstable src/connect.ts nats.js
Expand Down
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ lint:
deno lint --ignore=docs/

test: clean
deno test --allow-all --unstable --parallel --reload --quiet --coverage=coverage tests/ jetstream/tests
deno test --allow-all --parallel --reload --quiet --coverage=coverage tests/ jetstream/tests
deno test --allow-all --parallel --reload --quiet --unsafely-ignore-certificate-errors --coverage=coverage unsafe_tests/


testw: clean
Expand All @@ -26,4 +27,4 @@ bundle:
deno bundle --log-level info --unstable src/mod.ts ./nats.js

fmt:
deno fmt src/ doc/ bin/ nats-base-client/ examples/ tests/ debug/ jetstream/ jetstream.md README.md services.md
deno fmt src/ doc/ bin/ nats-base-client/ examples/ tests/ debug/ unsafe_tests/ jetstream/ jetstream.md README.md services.md
2 changes: 1 addition & 1 deletion jetstream/tests/jetstream_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1213,7 +1213,7 @@ Deno.test("jetstream - detailed errors", async () => {
});

Deno.test("jetstream - repub on 503", async () => {
let servers = await NatsServer.setupDataConnCluster(4);
const servers = await NatsServer.setupDataConnCluster(4);
const nc = await connect({ port: servers[0].port });

const { stream, subj } = await initStream(nc, nuid.next(), {
Expand Down
4 changes: 4 additions & 0 deletions nats-base-client/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1397,6 +1397,10 @@ export interface ConnectionOptions {
* the client is requesting to only use connections that are secured by TLS.
*/
export interface TlsOptions {
/**
* handshakeFirst option requires the server to be configured with `handshakeFirst: true`.
*/
handshakeFirst?: boolean;
certFile?: string;
cert?: string;
caFile?: string;
Expand Down
26 changes: 23 additions & 3 deletions src/deno_transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,29 @@ export class DenoTransport implements Transport {
options: ConnectionOptions,
) {
this.options = options;

const { tls } = this.options;
const { handshakeFirst } = tls || {};

try {
this.conn = await Deno.connect(hp);
if (handshakeFirst === true) {
const opts = await this.loadTlsOptions(hp.hostname);
const ctls = {
caCerts: opts.caCerts,
hostname: hp.hostname,
port: hp.port,
};
this.conn = await Deno.connectTls(ctls);
this.encrypted = true;
// do nothing yet.
} else {
this.conn = await Deno.connect(hp);
}
const info = await this.peekInfo();
checkOptions(info, this.options);
const { tls_required: tlsRequired, tls_available: tlsAvailable } = info;
const desired = tlsAvailable === true && options.tls !== null;
if (tlsRequired || desired) {
if (!handshakeFirst && (tlsRequired || desired)) {
const tlsn = hp.tlsName ? hp.tlsName : hp.hostname;
await this.startTLS(tlsn);
}
Expand Down Expand Up @@ -117,7 +133,7 @@ export class DenoTransport implements Transport {
return JSON.parse(m[1]) as ServerInfo;
}

async startTLS(hostname: string): Promise<void> {
async loadTlsOptions(hostname: string): Promise<Deno.StartTlsOptions> {
const tls = this.options && this.options.tls
? this.options.tls
: {} as TlsOptions;
Expand All @@ -134,7 +150,11 @@ export class DenoTransport implements Transport {
const ca = await Deno.readTextFile(tls.caFile);
sto.caCerts = [ca];
}
return sto;
}

async startTLS(hostname: string): Promise<void> {
const sto = await (this.loadTlsOptions(hostname));
this.conn = await Deno.startTls(
this.conn,
sto,
Expand Down
36 changes: 36 additions & 0 deletions unsafe_tests/tlsunsafe_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { resolve } from "https://deno.land/std@0.221.0/path/resolve.ts";
import { join } from "https://deno.land/std@0.221.0/path/join.ts";
import { NatsServer } from "../tests/helpers/launcher.ts";
import { connect } from "../src/connect.ts";

Deno.test("tls-unsafe - handshake first", async () => {
const cwd = Deno.cwd();
const config = {
host: "localhost",
tls: {
handshake_first: true,
cert_file: resolve(join(cwd, "./tests/certs/localhost.crt")),
key_file: resolve(join(cwd, "./tests/certs/localhost.key")),
ca_file: resolve(join(cwd, "./tests/certs/RootCA.crt")),
},
};

const ns = await NatsServer.start(config);
const nc = await connect({
debug: true,
servers: `localhost:${ns.port}`,
tls: {
handshakeFirst: true,
caFile: config.tls.ca_file,
},
});
nc.subscribe("foo", {
callback(_err, msg) {
msg.respond(msg.data);
},
});

await nc.request("foo", "hello");
await nc.close();
await ns.stop();
});

0 comments on commit 06d16f8

Please sign in to comment.