Skip to content

Commit 6934237

Browse files
authored
Jsonata: Support creating TLS server (#73)
* Jsonata: Support creating TLS server Signed-off-by: Pierangelo Di Pilato <[email protected]> * Docker exclude dev ssl certs Signed-off-by: Pierangelo Di Pilato <[email protected]> --------- Signed-off-by: Pierangelo Di Pilato <[email protected]>
1 parent 1e6f101 commit 6934237

File tree

7 files changed

+169
-24
lines changed

7 files changed

+169
-24
lines changed

transform-jsonata/.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
node_modules
22
examples
33
README.md
4+
ssl

transform-jsonata/README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ broker <- trigger <- (response) <-
1515
Assuming current working directory is `transform-jsonata`
1616

1717
```shell
18-
npm dev
18+
npm run dev
1919

20-
# or npm dev-kubevirt to inject a different example transformation
20+
npm run dev-kubevirt # to inject a different example transformation
21+
22+
npm run dev-tls # to start the HTTP and HTTPS Servers
23+
npm run dev-tls-only # to start the HTTPS Server only
2124
```
2225

2326
### Tracing

transform-jsonata/jsonata.js

Lines changed: 92 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ const express = require('express');
22
const {HTTP} = require("cloudevents");
33
const jsonata = require('jsonata');
44
const fs = require('node:fs');
5+
const http = require('http');
6+
const https = require('https');
57
const fsPromises = require('node:fs').promises;
68
const {buffer} = require('node:stream/consumers');
79

@@ -31,10 +33,18 @@ const {ZipkinExporter} = require('@opentelemetry/exporter-zipkin');
3133
const {W3CTraceContextPropagator, CompositePropagator} = require("@opentelemetry/core");
3234
const {B3InjectEncoding, B3Propagator} = require("@opentelemetry/propagator-b3");
3335

34-
const port = process.env.PORT = process.env.PORT || 8080;
36+
const httpPort = process.env.HTTP_PORT || 8080;
37+
const httpsPort = process.env.HTTPS_PORT || 8443;
38+
const httpsCertPath = process.env.HTTPS_CERT_PATH;
39+
const httpsKeyPath = process.env.HTTPS_KEY_PATH;
40+
const disableHTTPServer = process.env.DISABLE_HTTP_SERVER === 'true';
3541
const k_sink = process.env.K_SINK || undefined;
3642
const jsonata_transform_file_name = process.env.JSONATA_TRANSFORM_FILE_NAME || undefined;
3743

44+
if (disableHTTPServer && (!httpsKeyPath || !fs.existsSync(httpsKeyPath) || !httpsCertPath || !fs.existsSync(httpsCertPath))) {
45+
throw new Error(`HTTP and HTTPS server are both disabled, disableHTTPServer='${disableHTTPServer}', httpsKeyPath='${httpsKeyPath}', httpsCertPath=${httpsCertPath}`);
46+
}
47+
3848
// Allow transforming the response received by the endpoint defined by K_SINK
3949
const jsonata_response_transform_file_name = process.env.JSONATA_RESPONSE_TRANSFORM_FILE_NAME || undefined;
4050

@@ -250,7 +260,7 @@ app.post("/", async (req, res) => {
250260
const k_sink_url = new URL(k_sink)
251261
const kSinkSendSpan = tracer.startSpan('k_sink_send', {
252262
attributes: {
253-
[ATTR_URL_SCHEME]: k_sink_url.protocol.endsWith(':') ? k_sink_url.protocol.substring(0, k_sink_url.protocol.length-1) : k_sink_url.protocol,
263+
[ATTR_URL_SCHEME]: k_sink_url.protocol.endsWith(':') ? k_sink_url.protocol.substring(0, k_sink_url.protocol.length - 1) : k_sink_url.protocol,
254264
[ATTR_SERVER_ADDRESS]: k_sink_url.hostname,
255265
[ATTR_SERVER_PORT]: k_sink_url.port,
256266
}
@@ -267,6 +277,7 @@ app.post("/", async (req, res) => {
267277
headers: k_sink_request_headers,
268278
body: JSON.stringify(transformed),
269279
redirect: 'error',
280+
signal: req.signal,
270281
})
271282
kSinkSendSpan.setAttributes({
272283
'http.status_code': result.status,
@@ -360,7 +371,7 @@ app.post("/", async (req, res) => {
360371
});
361372

362373
// guessTransformedContentType tries to guess the transformed event content type.
363-
// 1. If the transformed event contains a special "contentype" field, it returns it.
374+
// 1. If the transformed event contains a special "contenttype" field, it returns it.
364375
// 2. Otherwise, it tries to find CloudEvents "specversion" attribute and, if it's present, returns
365376
// the CloudEvent structured content type "application/cloudevents+json".
366377
// 3. Lastly, it falls back to "application/json" if none of the above are specified.
@@ -384,19 +395,71 @@ app.get('/readyz', (req, res) => {
384395

385396
app.disable('x-powered-by');
386397

387-
const server = app.listen(port, () => {
388-
console.log(`Jsonata server listening on port ${port}`)
389-
})
398+
let httpServer = null
399+
let httpsServer = null
400+
401+
if (!disableHTTPServer) {
402+
httpServer = http.createServer(app)
403+
.listen(httpPort, () => {
404+
console.log(`Jsonata HTTP server listening on port ${httpPort}`)
405+
})
406+
}
407+
408+
if (httpsCertPath && httpsKeyPath) {
409+
const httpsServerOptions = {
410+
cert: fs.readFileSync(httpsCertPath),
411+
key: fs.readFileSync(httpsKeyPath),
412+
413+
// TLS Versions
414+
minVersion: 'TLSv1.2', // Minimum TLS version (avoid older, less secure protocols)
415+
maxVersion: 'TLSv1.3', // Maximum TLS version
416+
417+
// Cipher Suites
418+
ciphers: [
419+
'ECDHE-ECDSA-AES128-GCM-SHA256',
420+
'ECDHE-RSA-AES128-GCM-SHA256',
421+
'ECDHE-ECDSA-AES256-GCM-SHA384',
422+
'ECDHE-RSA-AES256-GCM-SHA384',
423+
'ECDHE-ECDSA-CHACHA20-POLY1305',
424+
'ECDHE-RSA-CHACHA20-POLY1305',
425+
'DHE-RSA-AES128-GCM-SHA256',
426+
'DHE-RSA-AES256-GCM-SHA384'
427+
].join(':'),
428+
429+
// Attempt to use server cipher suite preference instead of clients
430+
honorCipherOrder: true,
431+
432+
// Additional security options
433+
secureOptions:
434+
require('constants').SSL_OP_NO_TLSv1 |
435+
require('constants').SSL_OP_NO_TLSv1_1 |
436+
require('constants').SSL_OP_NO_COMPRESSION,
437+
}
438+
439+
httpsServer = https.createServer(httpsServerOptions, app)
440+
.listen(httpsPort, () => {
441+
console.log(`Jsonata HTTPS server listening on port ${httpsPort}`)
442+
})
443+
}
390444

391445
process.on('SIGINT', shutDown);
392446
process.on('SIGTERM', shutDownNow);
393447

394448
let connections = [];
395449

396-
server.on('connection', connection => {
397-
connections.push(connection);
398-
connection.on('close', () => connections = connections.filter(curr => curr !== connection));
399-
});
450+
if (httpServer) {
451+
httpServer.on('connection', connection => {
452+
connections.push(connection);
453+
connection.on('close', () => connections = connections.filter(curr => curr !== connection));
454+
});
455+
}
456+
457+
if (httpsServer) {
458+
httpsServer.on('connection', connection => {
459+
connections.push(connection);
460+
connection.on('close', () => connections = connections.filter(curr => curr !== connection));
461+
});
462+
}
400463

401464
function shutDown() {
402465
console.log('Received interrupt signal, shutting down gracefully');
@@ -413,14 +476,30 @@ function shutDown() {
413476
function shutDownNow() {
414477
console.log('Shutting down gracefully');
415478

416-
server.close(() => {
417-
console.log('Closed out remaining connections');
479+
const closePromises = []
480+
if (httpServer) {
481+
closePromises.push(new Promise((resolve, _) => {
482+
httpServer.close(() => {
483+
console.log('Closed out remaining HTTP connections');
484+
resolve()
485+
});
486+
}))
487+
}
488+
if (httpsServer) {
489+
closePromises.push(new Promise((resolve, _) => {
490+
httpsServer.close(() => {
491+
console.log('Closed out remaining HTTPS connections');
492+
resolve()
493+
});
494+
}))
495+
}
418496

497+
Promise.all(closePromises).then(() => {
419498
console.log('Shutting down tracing...');
420499
batchSpanProcessor.shutdown().then(() => {
421500
process.exit(0);
422501
});
423-
});
502+
})
424503

425504
setTimeout(() => {
426505
console.error('Could not close connections in time, forcefully shutting down');

transform-jsonata/package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

transform-jsonata/package.json

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,28 @@
33
"author": "Knative authors",
44
"scripts": {
55
"dev": "NODE_ENV=development OIDC_TOKEN_FILE=./examples/example-token.txt JSONATA_TRANSFORM_FILE_NAME=./examples/jsonata_transform_identity.jsonata nodemon ./jsonata.js",
6+
"dev-tls": "HTTPS_CERT_PATH=./ssl/server.crt HTTPS_KEY_PATH=./ssl/server.key npm run dev",
7+
"dev-tls-only": "DISABLE_HTTP_SERVER=true HTTPS_CERT_PATH=./ssl/server.crt HTTPS_KEY_PATH=./ssl/server.key npm run dev",
68
"dev-kubevirt": "NODE_ENV=development OIDC_TOKEN_FILE=./examples/example-token.txt JSONATA_TRANSFORM_FILE_NAME=./examples/ce_apiserversource_kubevirt.jsonata nodemon ./jsonata.js",
79
"dev-zipkin": "NODE_ENV=development K_TRACING_CONFIG='{\"backend\":\"zipkin\", \"zipkin-endpoint\": \"http://localhost:9411/api/v2/spans\", \"sample-rate\":\"1\"}' OIDC_TOKEN_FILE=./examples/example-token.txt JSONATA_TRANSFORM_FILE_NAME=./examples/jsonata_transform_identity.jsonata nodemon ./jsonata.js"
810
},
911
"dependencies": {
10-
"jsonata": "^2.0.6",
11-
"cloudevents": "^8.0.2",
12-
"express": "^4.21.2",
1312
"@opentelemetry/api": "1.9.0",
1413
"@opentelemetry/core": "^1.30.1",
15-
"@opentelemetry/sdk-trace-node": "^1.30.1",
14+
"@opentelemetry/exporter-zipkin": "^1.30.1",
15+
"@opentelemetry/propagator-b3": "^1.30.1",
1616
"@opentelemetry/resources": "^1.30.1",
17-
"@opentelemetry/semantic-conventions": "^1.30.0",
1817
"@opentelemetry/sdk-trace-base": "^1.30.1",
19-
"@opentelemetry/exporter-zipkin": "^1.30.1",
20-
"@opentelemetry/propagator-b3": "^1.30.1"
18+
"@opentelemetry/sdk-trace-node": "^1.30.1",
19+
"@opentelemetry/semantic-conventions": "^1.30.0",
20+
"cloudevents": "^8.0.2",
21+
"constants": "^0.0.2",
22+
"express": "^4.21.2",
23+
"jsonata": "^2.0.6"
2124
},
2225
"devDependencies": {
23-
"nodemon": "^3.1.9",
26+
"@types/express": "^4.17.21",
2427
"@types/node": "^22.13.1",
25-
"@types/express": "^4.17.21"
28+
"nodemon": "^3.1.9"
2629
}
2730
}

transform-jsonata/ssl/server.crt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDZTCCAk2gAwIBAgIUYXiPhq/+IDzjVDxnsqaGlLgNxOAwDQYJKoZIhvcNAQEL
3+
BQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE
4+
CgwTRGVmYXVsdCBDb21wYW55IEx0ZDAeFw0yNTAzMDQxNTI1MzhaFw0zNTAzMDIx
5+
NTI1MzhaMEIxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAa
6+
BgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IB
7+
DwAwggEKAoIBAQCj9jIL/fhhn2WKcqgose1uXwSvv8OwdBuX7XtcvFD6TiXgyckR
8+
fnJMHM+UM5oQvr7dDrzksDy62I/xHL6+pKIP6aLpMGgC27qz8EFkK5UcErs6+5xj
9+
wcY3zWEwMWaohcnN/O5uh0Oy0ijk8/sx4GNjhupJ+DMXAupyXE8c3GOoK6Gn2jMa
10+
wyUy8e2Xy5pj4vqIvuv0D0N5xGcMu4JA/7pxmbeP1USVVNf+3hDxO4Vm7jVPIiYW
11+
24gzaY5OghyJFz6ebbj7ngLlcMEHZsCwTHzY+BiCTy2dB9IMfOXmBs6iAZUTceJV
12+
jDrXo4b3W9FL95+nfUDQtmBMmIO5SCvXse1tAgMBAAGjUzBRMB0GA1UdDgQWBBRf
13+
f/9QWyfX2Ch2oZdCW1aNKm9LlDAfBgNVHSMEGDAWgBRff/9QWyfX2Ch2oZdCW1aN
14+
Km9LlDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBJSKdV+ALQ
15+
KBymOHveJJNIqEJBsr5QmBN+JX2zRehE2C+yIX3oDpUqM8Ter3xS3nghmsf3JkFn
16+
HVYo1DqkTtN7QvoCSeZacCul51RYgWIRP4p+CyaizNSbhb79YDtjBgq2kriKccYf
17+
PDYhWV7fSd9UYMfNm9mOOr59I7JhK3+4CBWdctKPQb45Tv9C3k+hr5g4XOn/+f3F
18+
VByoFFrxTWFkXDAgMBeYrp94rkIcOqTdCEjwr1pZghONasl+nuGDFf7CeltYtJ30
19+
vZ9AsO/55lW2XLm14RqeUEm6DSej7JbD6B8ggOie9QVD+Mma29EXcdAeedzjeoBg
20+
+DscIEgUdZW5
21+
-----END CERTIFICATE-----

transform-jsonata/ssl/server.key

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCj9jIL/fhhn2WK
3+
cqgose1uXwSvv8OwdBuX7XtcvFD6TiXgyckRfnJMHM+UM5oQvr7dDrzksDy62I/x
4+
HL6+pKIP6aLpMGgC27qz8EFkK5UcErs6+5xjwcY3zWEwMWaohcnN/O5uh0Oy0ijk
5+
8/sx4GNjhupJ+DMXAupyXE8c3GOoK6Gn2jMawyUy8e2Xy5pj4vqIvuv0D0N5xGcM
6+
u4JA/7pxmbeP1USVVNf+3hDxO4Vm7jVPIiYW24gzaY5OghyJFz6ebbj7ngLlcMEH
7+
ZsCwTHzY+BiCTy2dB9IMfOXmBs6iAZUTceJVjDrXo4b3W9FL95+nfUDQtmBMmIO5
8+
SCvXse1tAgMBAAECggEAD6cSuwtZWXR+nJt6izwFNyqyB1cuxtsmwTfGNaGyt1qb
9+
ihypadag8bw0YukUNbIIBZGBHfHnMk03XKTKXufXot5Ck7Fv1IoGhmQS0g3JE9+D
10+
6UsY8HsQwcYFF7U3oDH5hIU3e+zE3T7r8YOLQQUzZ8568mnT8sfv+s/uK4qspuKw
11+
OS9Dgp8LeNQuxuf2JBsPIw/FJ0/Yy9iRTmsabzs67NCCwPFhCROhP+lCnRhT3lM1
12+
I8nB2B59kzk8V5jqza4jxsA/v4Ru6G/QPZcKl1kkGbnU3pOgTm5bBPQ6tYZuAQZo
13+
Dufx8N5O7g+2ssNBiLdEDNgUGhRyB51U4wFt/zz93wKBgQDcRJk60z8eWy27bYHv
14+
tYNYxIdhqGCkCgYxcZlBcjY/Wivk1KNoSWVch3vsvsn/jV7gGdyCqxJl6mAJy67s
15+
4fjLWFSGHjcL6bwF+87hXg+ylc8QuVSMs0GqbmHx4Q5LWTSDB5slDMmst6QqIODt
16+
0xEMI51oXcwasKAQMSXi3lhcVwKBgQC+j0gPoKG1YaGjCEaOGPnMhS/7bdPk6Au9
17+
4dKIEn+oy2L08Kseyg6GeLE72m6zd2/OsTHZb+eWyfnNeUKWg+lsTUdFkt0YtFt7
18+
gSM1lGl0Hbk9c7pKaU6d0MA7iF8XeADEKuUgzZaY/nB34p0qTTBHMxkclyVFN9Sr
19+
P16onJcp2wKBgQDQfN3MoEcOJJ+U2II0skowq1S5SvauTg6unifBmqleLat+XQaO
20+
n2ohutvBqpToHpe+5ruhsusnLEDbBL/916X2UxUuHUtdK0+dGksnZjDViJcF7WXq
21+
B4IQH4r1t2AgUb8yhvCCkSgTI39voM9GTJHGO6+yKZYXbTcUHHEP3AUm4QKBgFvq
22+
Co0XIsi1Rdy4cie6Hksq9uDksa8YygkVspHVsmO9bobMyw030tfDwWEoU/sWogRl
23+
bbD+jAsscuRMF/U3vVBy2ezSEPkIjZO9zzjZ1B+g8qeUeYfI0ZXHieFtPsi4Jk54
24+
jjpLT9eN6ru4v6wWvTGqkPM8aErByj+rekID/dm/AoGBAKZiUICaXF1wIfvzGx7c
25+
VYc/7vpme//fQ6x+oQnZ4j7KIVc9gzPoe+uj2qIniymUNnGPeV0tUI41clCqHohU
26+
nT0YiDeaZ5QCig+si6ywQXxJ/pmcuzGACKkYHbYGpSPUQftBhczCQTwPz3GCsnQo
27+
2uYRf7jtxuuXy30AzjMjfcmu
28+
-----END PRIVATE KEY-----

0 commit comments

Comments
 (0)