Skip to content

Commit 99635ce

Browse files
committed
HA OIDC auth mechanisms, start of white label support, new JSPI Origin Trial token
1 parent 676eb99 commit 99635ce

File tree

6 files changed

+7776
-5280
lines changed

6 files changed

+7776
-5280
lines changed

assets/template-zbr.ejs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<div class="zbr-error">
1010
<h3>
1111
<span>
12-
BrowZer Runtime code:
12+
{{ browzer_name }} Runtime code:
1313
</span>
1414
<span style="color:#d9202e">
1515
{{ code }}

index.js

Lines changed: 84 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -366,16 +366,19 @@ ${thirdPartyHTML}
366366
}
367367
res.setEncoding('utf8');
368368
res.on('data', function (chunk) {
369-
var jsonTargetArray = JSON.parse(chunk);
370-
let controllerVersion = jsonTargetArray.data.version.replace('v','');
371-
logger.info({message: 'attached controller version', controllerVersion: controllerVersion});
372-
let compatibleControllerVersion = `${pjson.compatibleControllerVersion}`;
373-
if (controllerVersion !== '0.0.0') {
374-
if (!satisfies(controllerVersion, compatibleControllerVersion)) {
375-
logger.error({message: 'incompatible controller version', controllerVersion: controllerVersion, compatibleControllerVersion: compatibleControllerVersion});
376-
process.exit(-1);
369+
try {
370+
var jsonTargetArray = JSON.parse(chunk);
371+
let controllerVersion = jsonTargetArray.data.version.replace('v','');
372+
logger.info({message: 'attached controller version', controllerVersion: controllerVersion});
373+
let compatibleControllerVersion = `${pjson.compatibleControllerVersion}`;
374+
if (controllerVersion !== '0.0.0') {
375+
if (!satisfies(controllerVersion, compatibleControllerVersion)) {
376+
logger.error({message: 'incompatible controller version', controllerVersion: controllerVersion, compatibleControllerVersion: compatibleControllerVersion});
377+
process.exit(-1);
378+
}
377379
}
378380
}
381+
catch (e) {}
379382
});
380383
}).end();
381384
request.on('timeout', () => {
@@ -642,6 +645,73 @@ ${thirdPartyHTML}
642645
return;
643646
});
644647

648+
/**
649+
*
650+
*/
651+
app.use(express.json());
652+
app.post('/controller-oidc-proxy', async (req, res) => {
653+
654+
let {
655+
urlWithParams,
656+
method,
657+
headers,
658+
postData
659+
} = req.body; // Extract the proxy variables from POST body
660+
661+
if (!urlWithParams) {
662+
return res.status(400).json({ error: 'Missing urlWithParams parameter' });
663+
}
664+
665+
const url = new URL(urlWithParams);
666+
667+
const options = {
668+
hostname: url.hostname,
669+
port: url.port || 443,
670+
path: url.pathname + url.search,
671+
method: method,
672+
headers: headers,
673+
redirect: 'manual'
674+
};
675+
676+
if (isEqual(method, 'POST')) {
677+
678+
postData = JSON.stringify(postData);
679+
options.headers['Content-Length'] = Buffer.byteLength(postData);
680+
681+
const req = https.request(options, (response) => {
682+
let data = '';
683+
response.on('data', (chunk) => {
684+
data += chunk;
685+
});
686+
687+
response.on('end', () => {
688+
if (response.statusCode >= 300 && response.statusCode < 400) {
689+
res.json({ redirectUrl: response.headers.location });
690+
} else {
691+
res.status(400).json({ error: 'expected redirect did not occur' });
692+
}
693+
res.end();
694+
});
695+
});
696+
697+
req.write(postData);
698+
req.end();
699+
700+
} else {
701+
702+
https.request(options, async function(response) {
703+
704+
if (response.statusCode >= 300 && response.statusCode < 400) {
705+
res.json({ redirectUrl: response.headers.location });
706+
} else {
707+
res.status(400).json({ error: 'expected redirect did not occur' });
708+
}
709+
res.end();
710+
711+
}).end();
712+
}
713+
});
714+
645715
var proxy = httpProxy.createProxyServer(options);
646716

647717
app.use(require('./lib/inject')(options, [], selects));
@@ -711,7 +781,9 @@ ${thirdPartyHTML}
711781
filter: function(data, req, res) {
712782
if (data.error && data.error.browzer_error_data) {
713783

714-
let footer = `<a href="https://openziti.io/docs/learn/quickstarts/browzer/"><strong>powered by OpenZiti BrowZer v${pjson.version}</strong><img src="https://ziti-logo.s3.amazonaws.com/ziti-browzer-logo.svg" style="width: 2%;position: fixed;bottom: 15px;margin-left: 10px;"></a>`;
784+
let whitelabel = JSON.parse(env('ZITI_BROWZER_WHITELABEL'));
785+
786+
let footer = `<a href="https://openziti.io/docs/learn/quickstarts/browzer/"><strong>powered by ${whitelabel.branding.browZerName} v${pjson.version}</strong><img src="${whitelabel.branding.browZerButtonIconSvgUrl}" style="width: 2%;position: fixed;bottom: 15px;margin-left: 10px;"></a>`;
715787

716788
if (isUndefined(data.error.browzer_error_data.myvar)) {
717789
data.error.browzer_error_data.myvar = {type: 'zbr'}
@@ -723,6 +795,7 @@ ${thirdPartyHTML}
723795
code: data.error.browzer_error_data.code,
724796
title: data.error.browzer_error_data.title,
725797
message: data.error.browzer_error_data.message,
798+
browzer_name: whitelabel.branding.browZerName,
726799
footer: footer,
727800
});
728801
data.body = he.decode(data.body);
@@ -742,6 +815,7 @@ ${thirdPartyHTML}
742815
code: data.error.browzer_error_data.code,
743816
title: data.error.browzer_error_data.title,
744817
message: data.error.browzer_error_data.message,
818+
browzer_name: whitelabel.branding.browZerName,
745819
footer: footer,
746820
});
747821
data.body = he.decode(data.body);
@@ -859,7 +933,7 @@ Hello, from OpenZiti BrowZer v${pjson.version} !
859933
});
860934

861935
server.listen(browzer_bootstrapper_listen_port, () => {
862-
console.log(`Server listening on port ${browzer_bootstrapper_listen_port}`);
936+
logger.info({message: 'listening', port: browzer_bootstrapper_listen_port, scheme: browzer_bootstrapper_scheme});
863937
});
864938

865939
}

lib/env.js

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,36 @@ limitations under the License.
1515
*/
1616

1717
var path = require('path');
18+
var fs = require('fs');
1819
var nconf = require('nconf');
1920
const isEqual = require('lodash.isequal');
2021
var nval = require('nconf-validator')(nconf);
22+
const Ajv = require("ajv");
23+
const ajv = new Ajv({ validateFormats: true });
24+
const addFormats = require("ajv-formats");
25+
addFormats(ajv); // Adds support for formats like "uri", "email", "date", etc.
2126

22-
27+
/**
28+
* Reach into teh ZBR dependency, and obtain the "hashed" name of the ZBR CSS file.
29+
* This will be the default used
30+
*/
31+
function getZBRCSSname() {
32+
try {
33+
let pathToZitiBrowzerRuntimeModule = require.resolve('@openziti/ziti-browzer-runtime');
34+
pathToZitiBrowzerRuntimeModule = pathToZitiBrowzerRuntimeModule.substring(0, pathToZitiBrowzerRuntimeModule.lastIndexOf('/'));
35+
let zbrCSSName;
36+
fs.readdirSync(pathToZitiBrowzerRuntimeModule).forEach(file => {
37+
if (file.startsWith('ziti-browzer-css-')) {
38+
zbrCSSName = file;
39+
}
40+
});
41+
return zbrCSSName;
42+
}
43+
catch (e) {
44+
console.error(e);
45+
}
46+
}
47+
2348
/** -----------------------------------------------------------------------------------------------
2449
* Config value Order-of-precedence is:
2550
* 1) cmd line args
@@ -50,9 +75,24 @@ nconf
5075
ZITI_BROWZER_RUNTIME_HOTKEY: 'alt+f12',
5176

5277
// This token is for *.browzer.cloudziti.io
53-
ZITI_BROWZER_RUNTIME_ORIGIN_TRIAL_TOKEN: `AqxdaPfKzgJ7G5+oEtS/w/eesei0cf9vBuiHZ8Tq+5mRKOArzxpKS3CarVqc31oAWMjUoOihM8UjpTJl8rxaxAQAAACAeyJvcmlnaW4iOiJodHRwczovL2Jyb3d6ZXIuY2xvdWR6aXRpLmlvOjQ0MyIsImZlYXR1cmUiOiJXZWJBc3NlbWJseUpTUHJvbWlzZUludGVncmF0aW9uIiwiZXhwaXJ5IjoxNzM5OTIzMTk5LCJpc1N1YmRvbWFpbiI6dHJ1ZX0=`,
78+
ZITI_BROWZER_RUNTIME_ORIGIN_TRIAL_TOKEN: `Al07oTIxNYQaS0fbv5GmElf9VsYoCQe0DU2tF4ogqmQDEXIA48+0ulbKxrVIRqUZCY0xC9BKLHTvthU5wcMQJQUAAACAeyJvcmlnaW4iOiJodHRwczovL2Jyb3d6ZXIuY2xvdWR6aXRpLmlvOjQ0MyIsImZlYXR1cmUiOiJXZWJBc3NlbWJseUpTUHJvbWlzZUludGVncmF0aW9uIiwiZXhwaXJ5IjoxNzUzMTQyNDAwLCJpc1N1YmRvbWFpbiI6dHJ1ZX0=`,
5479

5580
NODE_EXTRA_CA_CERTS: 'node_modules/node_extra_ca_certs_mozilla_bundle/ca_bundle/ca_intermediate_root_bundle.pem',
81+
82+
ZITI_BROWZER_WHITELABEL: JSON.stringify({
83+
"enable": {
84+
"browZerButton": true,
85+
"browZerToastMessages": true,
86+
"browZerSplashScreen": true,
87+
"browZerThroughputChart": true
88+
},
89+
"branding": {
90+
"browZerName": "OpenZiti BrowZer",
91+
"browZerSplashMessage": "Stand by while OpenZiti BrowZer Bootstraps your private web app",
92+
"browZerButtonIconSvgUrl": `https://${nconf.get('ZITI_BROWZER_BOOTSTRAPPER_HOST')}/ziti-browzer-logo.svg`,
93+
"browZerCSS": `https://${nconf.get('ZITI_BROWZER_BOOTSTRAPPER_HOST')}/${getZBRCSSname()}`
94+
}
95+
}),
5696
}
5797
)
5898
.required(
@@ -63,6 +103,45 @@ nconf
63103
]
64104
);
65105

106+
const whitelabelSchema = {
107+
type: "object",
108+
properties: {
109+
enable: {
110+
type: "object",
111+
properties: {
112+
browZerButton: { type: "boolean" },
113+
browZerToastMessages: { type: "boolean" },
114+
browZerSplashScreen: { type: "boolean" },
115+
browZerThroughputChart: { type: "boolean" }
116+
},
117+
required: [
118+
"browZerButton",
119+
"browZerToastMessages",
120+
"browZerSplashScreen",
121+
"browZerThroughputChart"
122+
],
123+
additionalProperties: false // Prevent misspelled keys
124+
},
125+
branding: {
126+
type: "object",
127+
properties: {
128+
browZerName: { type: "string" },
129+
browZerSplashMessage: { type: "string" },
130+
browZerButtonIconSvgUrl: { type: "string", format: "uri"},
131+
browZerCSS: { type: "string", format: "uri"},
132+
},
133+
required: [
134+
"browZerName",
135+
"browZerSplashMessage",
136+
"browZerButtonIconSvgUrl",
137+
"browZerCSS"
138+
],
139+
additionalProperties: false // Prevent misspelled keys
140+
}
141+
},
142+
additionalProperties: false // Prevent misspelled keys
143+
};
144+
66145
/** -----------------------------------------------------------------------------------------------
67146
* config validation rules
68147
* -----------------------------------------------------------------------------------------------*/
@@ -71,6 +150,8 @@ if (nconf.get('ZITI_BROWZER_BOOTSTRAPPER_LOG_TAGS')) {
71150
nval.addRule('ZITI_BROWZER_BOOTSTRAPPER_LOG_TAGS', 'json');
72151
}
73152

153+
nval.addRule('ZITI_BROWZER_WHITELABEL', 'json');
154+
74155
nval.addRule('ZITI_BROWZER_BOOTSTRAPPER_WILDCARD_VHOSTS', Boolean);
75156

76157
nval.addRule('ZITI_BROWZER_BOOTSTRAPPER_SCHEME', ['http', 'https']);
@@ -145,8 +226,16 @@ if (nconf.get('ZITI_BROWZER_BOOTSTRAPPER_GITHUB_API_TOKEN')) {
145226
*/
146227
nval.validate();
147228

229+
const validateWhitelabel = ajv.compile(whitelabelSchema);
230+
const whitelabelConfigData = JSON.parse(nconf.get('ZITI_BROWZER_WHITELABEL'));
231+
const valid = validateWhitelabel(whitelabelConfigData);
232+
233+
if (!valid) {
234+
console.error("Whitelabel Configuration validation failed:", validateWhitelabel.errors);
235+
throw new Error("Whitelabel Configuration validation failed")
236+
}
237+
148238
function asBool (value) {
149-
console.log('asBool: value: ', value);
150239
const val = value.toLowerCase()
151240

152241
const allowedValues = [

lib/http-proxy/common.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ common.generateZitiConfigObject = function(url, req, options) {
358358
host: browzer_load_balancer ? `${browzer_load_balancer}` : undefined,
359359
port: browzer_load_balancer ? `${browzer_load_balancer_port}` : undefined
360360
},
361+
whitelabel: JSON.parse( env('ZITI_BROWZER_WHITELABEL') ),
361362
},
362363
idp: {
363364
host: `${idp_issuer_url}`,

package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ziti-browzer-bootstrapper",
3-
"version": "0.81.2",
3+
"version": "0.82.0",
44
"compatibleControllerVersion": ">=0.27.9",
55
"description": "Ziti BrowZer Bootstrapper -- providing Ziti network access into Dark web server",
66
"main": "index.js",
@@ -20,9 +20,11 @@
2020
"license": "Apache-2.0",
2121
"dependencies": {
2222
"@openziti/ziti-browzer-edge-client": "^0.6.2",
23-
"@openziti/ziti-browzer-runtime": "0.97.6",
24-
"@openziti/ziti-browzer-sw": "^0.73.1",
23+
"@openziti/ziti-browzer-runtime": "^0.98.0",
24+
"@openziti/ziti-browzer-sw": "^0.74.0",
2525
"acme-http-01-standalone": "^3.0.5",
26+
"ajv": "^8.17.1",
27+
"ajv-formats": "^3.0.1",
2628
"compare-versions": "^6.0.0-rc.1",
2729
"connect": "^3.7.0",
2830
"cookie": "^0.5.0",

0 commit comments

Comments
 (0)