Skip to content

Commit 0032834

Browse files
authored
Merge pull request #584 from minrk/internal-ssl-5
fix internal ssl, add internal ssl test
2 parents 6514a2b + 1e6516a commit 0032834

File tree

6 files changed

+152
-7
lines changed

6 files changed

+152
-7
lines changed

.github/workflows/test.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ jobs:
7979
restore-keys: |
8080
${{ runner.os }}-npm-
8181
82+
- name: setup internal ssl
83+
run: |
84+
pipx run test/make_internal_ssl.py
85+
8286
- name: "Install dependencies (npm ci)"
8387
run: |
8488
npm ci

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ bench/html
1010
coverage
1111
dist
1212
.nyc_output
13+
test/ssl

lib/configproxy.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -557,11 +557,11 @@ export class ConfigurableProxy extends EventEmitter {
557557
}
558558

559559
target = new URL(target);
560-
var proxyOptions = { target: target };
560+
var proxyOptions = { target };
561561
if (that.options.clientSsl) {
562-
proxyOptions.key = that.options.clientSsl.key;
563-
proxyOptions.cert = that.options.clientSsl.cert;
564-
proxyOptions.ca = that.options.clientSsl.ca;
562+
target.key = that.options.clientSsl.key;
563+
target.cert = that.options.clientSsl.cert;
564+
target.ca = that.options.clientSsl.ca;
565565
}
566566

567567
// add config argument

lib/testutil.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
"use strict";
22

33
import http from "node:http";
4+
import https from "node:https";
45
import { WebSocketServer } from "ws";
56
import { ConfigurableProxy } from "./configproxy.js";
67
import { defaultLogger } from "./log.js";
78

89
var servers = [];
910

10-
export function addTarget(proxy, path, port, websocket, targetPath) {
11-
var target = "http://127.0.0.1:" + port;
11+
// TODO: make this an options dict
12+
export function addTarget(proxy, path, port, websocket, targetPath, sslOptions) {
13+
var proto = sslOptions ? "https" : "http";
14+
var target = proto + "://127.0.0.1:" + port;
1215
if (targetPath) {
1316
target = target + targetPath;
1417
}
@@ -17,8 +20,12 @@ export function addTarget(proxy, path, port, websocket, targetPath) {
1720
target: target,
1821
path: path,
1922
};
23+
var createServer = http.createServer;
24+
if (sslOptions) {
25+
createServer = (cb) => https.createServer(sslOptions, cb);
26+
}
2027

21-
server = http.createServer(function (req, res) {
28+
server = createServer(function (req, res) {
2229
var reply = {};
2330
Object.assign(reply, data);
2431
reply.url = req.url;

test/make_internal_ssl.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
"""
2+
Regenerate internal ssl certificates for tests
3+
"""
4+
5+
# PEP 723 dependencies
6+
# /// script
7+
# dependencies = [
8+
# "certipy",
9+
# ]
10+
# ///
11+
12+
import asyncio
13+
import shutil
14+
import ssl
15+
from pathlib import Path
16+
17+
from certipy import Certipy
18+
19+
ssl_dir = Path(__file__).parent.resolve() / "ssl"
20+
port = 12345
21+
22+
23+
def make_certs():
24+
"""Create certificates for proxy client and ssl backend"""
25+
# start fresh
26+
if ssl_dir.exists():
27+
shutil.rmtree(ssl_dir)
28+
alt_names = [
29+
"IP:127.0.0.1",
30+
"IP:0:0:0:0:0:0:0:1",
31+
"DNS:localhost",
32+
]
33+
certipy = Certipy(store_dir=ssl_dir)
34+
_trust_bundles = certipy.trust_from_graph(
35+
{
36+
"backend-ca": ["proxy-client-ca"],
37+
"proxy-client-ca": ["backend-ca"],
38+
}
39+
)
40+
for name in ("backend", "proxy-client"):
41+
certipy.create_signed_pair(name, f"{name}-ca", alt_names=alt_names)
42+
43+
44+
async def client_connected(reader, writer):
45+
"""Callback for ssl server"""
46+
print("client connected")
47+
msg = await reader.read(5)
48+
print("server received", msg.decode())
49+
writer.write(b"pong")
50+
51+
52+
async def ssl_backend():
53+
"""Run a test ssl server"""
54+
ssl_context = ssl.create_default_context(
55+
ssl.Purpose.CLIENT_AUTH, cafile=ssl_dir / "backend-ca_trust.crt"
56+
)
57+
ssl_context.verify_mode = ssl.CERT_REQUIRED
58+
ssl_context.load_default_certs()
59+
ssl_context.load_cert_chain(
60+
ssl_dir / "backend/backend.crt", ssl_dir / "backend/backend.key"
61+
)
62+
await asyncio.start_server(
63+
client_connected, host="localhost", port=port, ssl=ssl_context
64+
)
65+
66+
67+
async def ssl_client():
68+
"""Run a test ssl client"""
69+
ssl_context = ssl.create_default_context(
70+
ssl.Purpose.SERVER_AUTH, cafile=ssl_dir / "proxy-client-ca_trust.crt"
71+
)
72+
ssl_context.verify_mode = ssl.CERT_REQUIRED
73+
ssl_context.load_default_certs()
74+
ssl_context.check_hostname = True
75+
ssl_context.load_cert_chain(
76+
ssl_dir / "proxy-client/proxy-client.crt",
77+
ssl_dir / "proxy-client/proxy-client.key",
78+
)
79+
reader, writer = await asyncio.open_connection("localhost", port, ssl=ssl_context)
80+
writer.write(b"ping")
81+
msg = await reader.read(5)
82+
print("client received", msg.decode())
83+
84+
85+
async def main():
86+
# make the certs
87+
print(f"Making internal ssl certificates in {ssl_dir}")
88+
make_certs()
89+
print("Testing internal ssl setup")
90+
await ssl_backend()
91+
await ssl_client()
92+
print("OK")
93+
94+
95+
if __name__ == "__main__":
96+
asyncio.run(main())

test/proxy_spec.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import fs from "node:fs";
12
import path from "node:path";
23
import { fileURLToPath } from "node:url";
34
import fetch from "node-fetch";
@@ -475,4 +476,40 @@ describe("Proxy Tests", function () {
475476
done();
476477
});
477478
});
479+
480+
it("internal ssl test", function (done) {
481+
if (!fs.existsSync(path.resolve(__dirname, "ssl"))) {
482+
console.log("skipping ssl test without ssl certs. Run make_internal_ssl.py first.");
483+
done();
484+
return;
485+
}
486+
var proxyPort = 55556;
487+
var testPort = proxyPort + 20;
488+
var options = {
489+
clientSsl: {
490+
key: fs.readFileSync(path.resolve(__dirname, "ssl/proxy-client/proxy-client.key")),
491+
cert: fs.readFileSync(path.resolve(__dirname, "ssl/proxy-client/proxy-client.crt")),
492+
ca: fs.readFileSync(path.resolve(__dirname, "ssl/proxy-client-ca_trust.crt")),
493+
},
494+
};
495+
496+
util
497+
.setupProxy(proxyPort, options, [])
498+
.then((proxy) =>
499+
util.addTarget(proxy, "/backend/", testPort, false, null, {
500+
key: fs.readFileSync(path.resolve(__dirname, "ssl/backend/backend.key")),
501+
cert: fs.readFileSync(path.resolve(__dirname, "ssl/backend/backend.crt")),
502+
ca: fs.readFileSync(path.resolve(__dirname, "ssl/backend-ca_trust.crt")),
503+
requestCert: true,
504+
})
505+
)
506+
.then(() => fetch("http://127.0.0.1:" + proxyPort + "/backend/urlpath/"))
507+
.then((res) => {
508+
expect(res.status).toEqual(200);
509+
})
510+
.catch((err) => {
511+
done.fail(err);
512+
})
513+
.then(done);
514+
});
478515
});

0 commit comments

Comments
 (0)