From e74af077be8c2d7d2f8caa03fc62b7f734a917b4 Mon Sep 17 00:00:00 2001 From: Curt Tudor Date: Thu, 19 Sep 2024 12:18:53 -0600 Subject: [PATCH] Analytics prototype --- index.js | 67 ++++++++++++++++++++++++++++++++++++++++ lib/env.js | 10 +++++- lib/http-proxy/common.js | 8 +++++ package.json | 3 +- yarn.lock | 22 +++++++++++++ 5 files changed, 108 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 7f0fb60..40366f8 100644 --- a/index.js +++ b/index.js @@ -37,6 +37,7 @@ const _httpErrorPages = require('http-error-pages'); const URLON = require('urlon'); const favicon = require('serve-favicon'); const { X509Certificate } = require('crypto'); +const requestIp = require('request-ip'); const cron = require('node-cron'); const { isEqual, isUndefined } = require('lodash'); const NodeCache = require("node-cache"); @@ -45,6 +46,7 @@ var ZITI_CONSTANTS = require('./lib/edge/constants'); const Mustache = require('mustache'); const he = require('he'); +let uniqueIPs = new Set(); var latestBrowZerReleaseVersion; @@ -215,6 +217,15 @@ var selects = []; */ const startBootstrapper = async ( logger ) => { + common.posthog_capture_event({ + distinctId: browzer_bootstrapper_host, + event: "Bootstrapper Initialization", + properties: { + browZerHost: browzer_bootstrapper_host, + browZerVersion: pjson.version, + } + }); + /** -------------------------------------------------------------------------------------------------- * Dynamically modify the proxied site's element as we stream it back to the browser. We will: * 1) inject the zitiConfig needed by the SDK @@ -690,6 +701,14 @@ ${thirdPartyHTML} next() }) + app.use((req, res, next) => { + const clientIP = requestIp.getClientIp(req) + if (clientIP) { + uniqueIPs.add(clientIP); + } + next(); + }); + app.use(verifyCache, function (req, res) { proxy.web(req, res); }); @@ -746,6 +765,54 @@ ${thirdPartyHTML} } }); + /** + * + */ + cron.schedule('*/15 * * * *', () => { // ... run every 15 minutes + try { + + common.posthog_capture_event({ + distinctId: browzer_bootstrapper_host, + event: "Bootstrapper Heartbeat", + properties: { + browZerHost: browzer_bootstrapper_host, + browZerVersion: pjson.version, + } + }); + + } catch (e) { + console.log(`error: `, e); + } + + }); + + /** + * + */ + cron.schedule('*/15 * * * *', () => { // ... run every 15 minutes + + uniqueIPs.forEach(async function(ip) { + try { + + common.posthog_capture_event({ + distinctId: browzer_bootstrapper_host, + event: "BrowZer Client Unique IP", + properties: { + browZerHost: browzer_bootstrapper_host, + browZerVersion: pjson.version, + browZerClientIP: ip, + } + }); + + + } catch (e) { + console.log(`error: `, e); + } + }); + + }); + + /** * When listening on HTTPS, then detect certificate refreshes, and reload TLS context accordingly */ diff --git a/lib/env.js b/lib/env.js index aad0f39..a030ba2 100644 --- a/lib/env.js +++ b/lib/env.js @@ -41,6 +41,9 @@ nconf ZITI_BROWZER_BOOTSTRAPPER_SCHEME: 'http', ZITI_BROWZER_BOOTSTRAPPER_WILDCARD_VHOSTS: false, ZITI_BROWZER_BOOTSTRAPPER_LISTEN_PORT: 80, + ZITI_BROWZER_BOOTSTRAPPER_ANALYTICS: true, + ZITI_BROWZER_BOOTSTRAPPER_ANALYTICS_TOKEN: 'phc_qNO1itBGPxgdxGLj58LYYGe7oyNvbG2zoJYvsodr8Hs', + ZITI_BROWZER_BOOTSTRAPPER_ANALYTICS_HOST: 'https://us.i.posthog.com', ZITI_BROWZER_LOAD_BALANCER_PORT: 443, @@ -72,7 +75,12 @@ if (nconf.get('ZITI_BROWZER_BOOTSTRAPPER_LOG_TAGS')) { } nval.addRule('ZITI_BROWZER_BOOTSTRAPPER_WILDCARD_VHOSTS', Boolean); - +nval.addRule('ZITI_BROWZER_BOOTSTRAPPER_ANALYTICS', Boolean); +if (nconf.get('ZITI_BROWZER_BOOTSTRAPPER_ANALYTICS')) { + nval.addRule('ZITI_BROWZER_BOOTSTRAPPER_ANALYTICS_TOKEN', String) + nval.addRule('ZITI_BROWZER_BOOTSTRAPPER_ANALYTICS_HOST', String) +} + nval.addRule('ZITI_BROWZER_BOOTSTRAPPER_SCHEME', ['http', 'https']); if (nconf.get('ZITI_BROWZER_LOAD_BALANCER_HOST')) { diff --git a/lib/http-proxy/common.js b/lib/http-proxy/common.js index 67f083e..b5d8154 100644 --- a/lib/http-proxy/common.js +++ b/lib/http-proxy/common.js @@ -30,10 +30,12 @@ var common = exports, ZitiContext = require('../../lib/edge/context'), Mutex = require('async-mutex').Mutex, withTimeout = require('async-mutex').withTimeout, + PostHog = require('posthog-node').PostHog; pjson = require('../../package.json'); var zitiContextMutex = withTimeout(new Mutex(), 3 * 1000, new Error('timeout on zitiContextMutex')); +const postHogClient = new PostHog(env('ZITI_BROWZER_BOOTSTRAPPER_ANALYTICS_TOKEN'), { host: env('ZITI_BROWZER_BOOTSTRAPPER_ANALYTICS_HOST'), disableGeoip: false }) var upgradeHeader = /(^|,)\s*upgrade\s*($|,)/i, isSSL = /^https/; @@ -651,3 +653,9 @@ common.trimFirstSection = function(hostname) { common.delay = function(time) { return new Promise(resolve => setTimeout(resolve, time)); } + +common.posthog_capture_event = function(event) { + if (env('ZITI_BROWZER_BOOTSTRAPPER_ANALYTICS') ) { + postHogClient.capture(event); + } +} diff --git a/package.json b/package.json index 5f7e44f..daad57d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ziti-browzer-bootstrapper", - "version": "0.72.0", + "version": "0.73.0", "compatibleControllerVersion": ">=0.27.9", "description": "Ziti BrowZer Bootstrapper -- providing Ziti network access into Dark web server", "main": "index.js", @@ -57,6 +57,7 @@ "node-fetch": "^3.3.2", "node_extra_ca_certs_mozilla_bundle": "^1.0.6", "openid-client": "^5.6.5", + "posthog-node": "^4.2.0", "pump": "^3.0.0", "request-ip": "^3.3.0", "requires-port": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index 483a12f..9d651fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -836,6 +836,15 @@ axios@^1.6.5: form-data "^4.0.0" proxy-from-env "^1.1.0" +axios@^1.7.4: + version "1.7.7" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" + integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + babel-runtime@^5.8.20: version "5.8.38" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-5.8.38.tgz#1c0b02eb63312f5f087ff20450827b425c9d4c19" @@ -3965,6 +3974,14 @@ possible-typed-array-names@^1.0.0: resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== +posthog-node@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-4.2.0.tgz#b7213200e12535ce60c9aaa26e8bd0470852ba19" + integrity sha512-hgyCYMyzMvuF3qWMw6JvS8gT55v7Mtp5wKWcnDrw+nu39D0Tk9BXD7I0LOBp0lGlHEPaXCEVYUtviNKrhMALGA== + dependencies: + axios "^1.7.4" + rusha "^0.8.14" + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -4233,6 +4250,11 @@ rollup-plugin-css-only@^4.5.2: dependencies: "@rollup/pluginutils" "5" +rusha@^0.8.14: + version "0.8.14" + resolved "https://registry.yarnpkg.com/rusha/-/rusha-0.8.14.tgz#a977d0de9428406138b7bb90d3de5dcd024e2f68" + integrity sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA== + sade@^1.7.3: version "1.8.1" resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701"