Merge branch 'release-1.1'

master v1.1.0
Ruakij 3 years ago
commit a610f209d5

@ -1,4 +1,4 @@
FROM node:16 FROM node:16-alpine
# Create app directory # Create app directory
WORKDIR /usr/src/app WORKDIR /usr/src/app
@ -7,9 +7,8 @@ WORKDIR /usr/src/app
COPY package*.json ./ COPY package*.json ./
RUN npm install RUN npm install
RUN apt-get update RUN apk update
RUN apt-get -y install \ RUN apk add tcpdump
tcpdump
# Bundle app source # Bundle app source
COPY ./src/ . COPY ./src/ .

@ -1,9 +1,9 @@
function requireEnvVars(requiredEnv){ function requireEnvVars(requiredEnv){
// Ensure required ENV vars are set // Ensure required ENV vars are set
let unsetEnv = requiredEnv.filter((env) => !(typeof process.env[env] !== 'undefined')); let unsetEnv = requiredEnv.filter((env) => (typeof process.env[env] === "undefined"));
if (unsetEnv.length > 0) { if (unsetEnv.length > 0) {
return "Required ENV variables are not set: [" + unsetEnv.join(', ') + "]"; return "Required ENV variables are not set: [" + unsetEnv.join(", ") + "]";
} }
} }

@ -1,7 +1,7 @@
const logger = require("./logger.js")("exec"); const logger = require("./logger.js")("exec");
const { spawn } = require("child_process"); const { spawn } = require("child_process");
const { parseArgsStringToArgv } = require('string-argv'); const { parseArgsStringToArgv } = require("string-argv");
function exec(cmd, options){ function exec(cmd, options){

@ -21,4 +21,4 @@ function bytesToHex(bytes) {
module.exports = { module.exports = {
hexToBytes, hexToBytes,
bytesToHex bytesToHex
} };

@ -1,8 +1,8 @@
const logger = require.main.require("./helper/logger.js")("influx-checks"); const logger = require.main.require("./helper/logger.js")("influx-checks");
const Os = require("os"); const Os = require("os");
const { InfluxDB, Point } = require('@influxdata/influxdb-client') const { InfluxDB, Point } = require("@influxdata/influxdb-client")
const Influx = require('@influxdata/influxdb-client-apis'); const Influx = require("@influxdata/influxdb-client-apis");
function checkHealth(influxDb){ function checkHealth(influxDb){
@ -39,7 +39,7 @@ function checkBucket(influxDb, options){
function checkWriteApi(influxDb, options){ function checkWriteApi(influxDb, options){
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const writeApi = influxDb.getWriteApi(options.org, options.bucket); // Get WriteAPI const writeApi = influxDb.getWriteApi(options.org, options.bucket); // Get WriteAPI
writeApi.writePoint(new Point("worker_connectionTest").tag("hostname", Os.hostname())) // Write point writeApi.writePoint(new Point("worker_connectionTest").tag("hostname", Os.hostname())); // Write point
writeApi.close() writeApi.close()
.catch((err) => { .catch((err) => {
logger.error("Could not get writeApi:"); logger.error("Could not get writeApi:");

@ -5,15 +5,15 @@ function detectStreamData(stream, timeout = 5000){
let timeoutHandler; let timeoutHandler;
if(timeout){ if(timeout){
timeoutHandler = setTimeout(() => { timeoutHandler = setTimeout(() => {
reject('timeout'); reject("timeout");
remListeners(); remListeners();
}, },
timeout); timeout);
} }
function remListeners(){ function remListeners(){
stream.removeListener('error', errorHandler); stream.removeListener("error", errorHandler);
stream.removeListener('data', dataHandler); stream.removeListener("data", dataHandler);
if(timeoutHandler) clearTimeout(timeoutHandler); if(timeoutHandler) clearTimeout(timeoutHandler);
} }
@ -25,8 +25,8 @@ function detectStreamData(stream, timeout = 5000){
remListeners(); remListeners();
} }
stream.on('error', errorHandler); stream.on("error", errorHandler);
stream.on('data', dataHandler); stream.on("data", dataHandler);
}); });
} }
@ -34,7 +34,7 @@ function detectStreamsData(streams, timeout = 5000){
let promises = []; let promises = [];
streams.forEach((stream) => { streams.forEach((stream) => {
promises.push(detectStreamData(stream, timeout)); promises.push(detectStreamData(stream, timeout));
}) });
return promises; return promises;
} }

@ -1,4 +1,4 @@
const { HandshakeStage } = require.main.require('./dto/Packet.js'); const { HandshakeStage } = require.main.require("./dto/Packet.js");
function keyInfoFromRaw(keyInfoRaw) { function keyInfoFromRaw(keyInfoRaw) {
return { return {
@ -27,7 +27,7 @@ function handshakeStageFromKeyInfo(keyInfo){
// Extract compare-keys // Extract compare-keys
let keyData = ""; let keyData = "";
for (const key of HANDSHAKE_STAGE_KEYINFO['keys']) { for (const key of HANDSHAKE_STAGE_KEYINFO["keys"]) {
keyData += keyInfo[key].toString(); keyData += keyInfo[key].toString();
} }

@ -6,8 +6,8 @@ const { requireEnvVars } = require("./helper/env.js");
const { exit } = require("process"); const { exit } = require("process");
const { exec } = require("./helper/exec.js"); const { exec } = require("./helper/exec.js");
const { InfluxDB } = require('@influxdata/influxdb-client'); const { InfluxDB } = require("@influxdata/influxdb-client");
const InfluxChecks = require('./helper/influx-checks.js'); const InfluxChecks = require("./helper/influx-checks.js");
const { RegexBlockStream } = require("./streamHandler/RegexBlockStream.js"); const { RegexBlockStream } = require("./streamHandler/RegexBlockStream.js");
const { PacketStreamFactory } = require("./streamHandler/PacketStreamFactory.js"); const { PacketStreamFactory } = require("./streamHandler/PacketStreamFactory.js");
@ -42,11 +42,11 @@ if(errorMsg){
.then((res) => {return InfluxChecks.checkBucket(influxDb, { .then((res) => {return InfluxChecks.checkBucket(influxDb, {
org: env.INFLUX_ORG, org: env.INFLUX_ORG,
name: env.INFLUX_BUCKET name: env.INFLUX_BUCKET
})}) });})
.then((res) => {return InfluxChecks.checkWriteApi(influxDb, { .then((res) => {return InfluxChecks.checkWriteApi(influxDb, {
org: env.INFLUX_ORG, org: env.INFLUX_ORG,
bucket: env.INFLUX_BUCKET bucket: env.INFLUX_BUCKET
})}) });})
.catch((err) => { .catch((err) => {
if(err) { if(err) {
logger.error("Error whilst checking influx:"); logger.error("Error whilst checking influx:");
@ -59,7 +59,7 @@ if(errorMsg){
logger.info("Influx ok"); logger.info("Influx ok");
logger.info("Starting tcpdump.."); logger.info("Starting tcpdump..");
const TCPDUMP_BASECMD = "tcpdump -vvv -e -n -X -s0 -i" const TCPDUMP_BASECMD = "tcpdump -vvv -e -n -X -s0 -i";
let cmd = `${TCPDUMP_BASECMD} ${env.WIFI_INTERFACE}`; let cmd = `${TCPDUMP_BASECMD} ${env.WIFI_INTERFACE}`;
let proc = exec(cmd); let proc = exec(cmd);
@ -85,7 +85,7 @@ if(errorMsg){
}); });
// FIXME: This is a hacky workaround to not let errors from subprocess bubble up and terminate our process // FIXME: This is a hacky workaround to not let errors from subprocess bubble up and terminate our process
regexBlockStream.on('error', (err) => {}); regexBlockStream.on("error", (err) => {});
proc.on("error", (err) => { proc.on("error", (err) => {
loggerTcpdump.error(err); loggerTcpdump.error(err);
@ -100,11 +100,11 @@ if(errorMsg){
loggerPacketStream.debug("Got first packet"); loggerPacketStream.debug("Got first packet");
}) })
.catch((err) => { .catch((err) => {
if(err == 'timeout') loggerPacketStream.warn("No packets"); if(err == "timeout") loggerPacketStream.warn("No packets");
}); });
}) })
.catch((err) => { .catch((err) => {
if(err == 'timeout') loggerTcpdump.warn("No data after 10s! Wrong configuration?"); if(err == "timeout") loggerTcpdump.warn("No data after 10s! Wrong configuration?");
}); });
logger.debug("Attaching exit-handler.."); logger.debug("Attaching exit-handler..");
@ -124,8 +124,8 @@ if(errorMsg){
logger.debug("Stopping subprocess tcpdump, then exiting myself.."); logger.debug("Stopping subprocess tcpdump, then exiting myself..");
proc.kill(); // Kill process (send SIGTERM), then upper event-handler will stop self proc.kill(); // Kill process (send SIGTERM), then upper event-handler will stop self
} }
process.on('SIGTERM', shutdownReq); process.on("SIGTERM", shutdownReq);
process.on('SIGINT', shutdownReq); process.on("SIGINT", shutdownReq);
logger.info("Startup complete"); logger.info("Startup complete");
})(); })();

@ -1,6 +1,6 @@
const logger = require.main.require("./helper/logger.js")("InfluxPointWriter"); const logger = require.main.require("./helper/logger.js")("InfluxPointWriter");
const { Writable } = require('stream'); const { Writable } = require("stream");
const {InfluxDB, Point, HttpError} = require('@influxdata/influxdb-client') const {InfluxDB, Point, HttpError} = require("@influxdata/influxdb-client");
/** /**
* Get points and write them into influx * Get points and write them into influx
@ -17,7 +17,7 @@ class InfluxPointWriter extends Writable{
super({ super({
objectMode: true objectMode: true
}); });
this._api = influxDb.getWriteApi(org, bucket, 'us', options); this._api = influxDb.getWriteApi(org, bucket, "us", options);
} }
_write(point, encoding, next){ _write(point, encoding, next){

@ -1,6 +1,6 @@
const logger = require.main.require("./helper/logger.js")("PacketStreamFactory"); const logger = require.main.require("./helper/logger.js")("PacketStreamFactory");
const { Transform } = require('stream'); const { Transform } = require("stream");
const {Point} = require('@influxdata/influxdb-client') const {Point} = require("@influxdata/influxdb-client");
/** Keys to always use as tags */ /** Keys to always use as tags */
const TAG_LIST = [ const TAG_LIST = [
@ -21,6 +21,7 @@ const MEASUREMENT_MAP = new Map([
["AuthenticationType", "authenticationType"], ["AuthenticationType", "authenticationType"],
["AssociationSuccess", "associationIsSuccessful"], ["AssociationSuccess", "associationIsSuccessful"],
["DisassociationReason", "disassociationReason"], ["DisassociationReason", "disassociationReason"],
["HandshakeStage", "handshakeStage"],
]); ]);
@ -49,7 +50,7 @@ class PacketInfluxPointFactory extends Transform{
tagObjectRecursively(point, tag, packet[tag]); tagObjectRecursively(point, tag, packet[tag]);
}); });
point.setField('value', packet[objKey]); // Set field point.setField("value", packet[objKey]); // Set field
this.push(point); // Push point into stream this.push(point); // Push point into stream
}); });
@ -70,14 +71,14 @@ function tagObjectRecursively(point, tag, field, suffix = ""){
/** Mapping for type -> field-method */ /** Mapping for type -> field-method */
const POINT_FIELD_TYPE = new Map([ const POINT_FIELD_TYPE = new Map([
['boolean', function(key, value){ return this.booleanField(key, value); }], ["boolean", function(key, value){ return this.booleanField(key, value); }],
['number', function(key, value){ return this.intField(key, value); }], ["number", function(key, value){ return this.intField(key, value); }],
['string', function(key, value){ return this.stringField(key, value); }], ["string", function(key, value){ return this.stringField(key, value); }],
]); ]);
Point.prototype.setField = function(key, value){ Point.prototype.setField = function(key, value){
let setField = POINT_FIELD_TYPE.get(typeof value); let setField = POINT_FIELD_TYPE.get(typeof value);
return setField.apply(this, [key, value]); return setField.apply(this, [key, value]);
} };
// Specify exports // Specify exports
module.exports = { module.exports = {

@ -1,7 +1,7 @@
const logger = require.main.require("./helper/logger.js")("PacketStreamFactory"); const logger = require.main.require("./helper/logger.js")("PacketStreamFactory");
const { Transform } = require('stream'); const { Transform } = require("stream");
const { DateTime } = require("luxon"); const { DateTime } = require("luxon");
const { PacketType, FlagType, Packet, PacketWithSSID, BeaconPacket, ProbeRequestPacket, ProbeResponsePacket, AuthenticationPacket, AuthenticationType, AssociationResponsePacket, DisassociationPacket, HandshakePacket, HandshakeStage } = require.main.require('./dto/Packet.js'); const { PacketType, FlagType, Packet, PacketWithSSID, BeaconPacket, ProbeRequestPacket, ProbeResponsePacket, AuthenticationPacket, AuthenticationType, AssociationResponsePacket, DisassociationPacket, HandshakePacket, HandshakeStage } = require.main.require("./dto/Packet.js");
const hexConv = require.main.require("./helper/hexConverter.js"); const hexConv = require.main.require("./helper/hexConverter.js");
const wifiStateAnalyser = require.main.require("./helper/wifiStateAnalyzer.js"); const wifiStateAnalyser = require.main.require("./helper/wifiStateAnalyzer.js");
@ -20,20 +20,20 @@ const PACKET_TYPE_MAP = {
"Disassociation:": PacketType.Disassociation, "Disassociation:": PacketType.Disassociation,
"EAPOL": PacketType.Handshake, "EAPOL": PacketType.Handshake,
}; };
const PACKET_TYPES_REGEX = Object.keys(PACKET_TYPE_MAP).join('|'); const PACKET_TYPES_REGEX = Object.keys(PACKET_TYPE_MAP).join("|");
const AUTHENTICATION_TYPE_MAP = { const AUTHENTICATION_TYPE_MAP = {
"(Open System)-1": AuthenticationType.OpenSystem_1, "(Open System)-1": AuthenticationType.OpenSystem_1,
"(Open System)-2": AuthenticationType.OpenSystem_2, "(Open System)-2": AuthenticationType.OpenSystem_2,
} };
const FLAG_TYPE_MAP = { const FLAG_TYPE_MAP = {
"Retry": FlagType.Retry, "Retry": FlagType.Retry,
"Pwr Mgmt": FlagType.PwrMgt, "Pwr Mgmt": FlagType.PwrMgt,
"More Data": FlagType.MoreData, "More Data": FlagType.MoreData,
"Protected": FlagType.Protected, "Protected": FlagType.Protected,
} };
const FLAG_TYPE_MAPS_REGEX = Object.keys(FLAG_TYPE_MAP).join('|'); const FLAG_TYPE_MAPS_REGEX = Object.keys(FLAG_TYPE_MAP).join("|");
/** /**
* Read data from text-blocks and convert them to Packet * Read data from text-blocks and convert them to Packet
@ -49,8 +49,8 @@ class PacketStreamFactory extends Transform{
_transform(chunk, encoding, next){ _transform(chunk, encoding, next){
let packet = new Packet(); let packet = new Packet();
const lines = chunk.split('\n'); const lines = chunk.split("\n");
const header = lines.splice(0, 1)[0]; // Grab first line, 'lines' is now the payload const header = lines.splice(0, 1)[0]; // Grab first line, "lines" is now the payload
packet = this._handleHeader(packet, header); packet = this._handleHeader(packet, header);
packet = this._handlePayload(packet, lines); packet = this._handlePayload(packet, lines);
@ -62,7 +62,7 @@ class PacketStreamFactory extends Transform{
packet.timestampMicros = DateTime.fromISO(data.slice(0, 12)).toSeconds() + data.slice(12, 15)/1000000; packet.timestampMicros = DateTime.fromISO(data.slice(0, 12)).toSeconds() + data.slice(12, 15)/1000000;
// Find flags // Find flags
data.match(data.match(new RegExp('(?<=^|\\s)('+ FLAG_TYPE_MAPS_REGEX +')(?=$|\\s)', 'ig')) data.match(data.match(new RegExp("(?<=^|\\s)("+ FLAG_TYPE_MAPS_REGEX +")(?=$|\\s)", "ig"))
?.forEach(match => packet.flags[FLAG_TYPE_MAP[match]] = true) // Set them to true in flags ?.forEach(match => packet.flags[FLAG_TYPE_MAP[match]] = true) // Set them to true in flags
); );
@ -73,11 +73,11 @@ class PacketStreamFactory extends Transform{
packet.signal = Number(data.match(/(?<=^|\s)-\d{2,3}(?=dBm\sSignal($|\s))/i)?.[0]) || null; packet.signal = Number(data.match(/(?<=^|\s)-\d{2,3}(?=dBm\sSignal($|\s))/i)?.[0]) || null;
let packetTypeStr = data.match(new RegExp('(?<=^|\\s)('+ PACKET_TYPES_REGEX +')(?=$|\\s)', 'i'))?.[0]; let packetTypeStr = data.match(new RegExp("(?<=^|\\s)("+ PACKET_TYPES_REGEX +")(?=$|\\s)", "i"))?.[0];
if(packetTypeStr) if(packetTypeStr)
packet.packetType = PACKET_TYPE_MAP[packetTypeStr]; packet.packetType = PACKET_TYPE_MAP[packetTypeStr];
else if(data.match(/(SA|TA|DA|RA|BSSID):.{17}\s*$/i)){ else if(data.match(/(SA|TA|DA|RA|BSSID):.{17}\s*$/i)){
packet.packetType = PacketType.NoData packet.packetType = PacketType.NoData;
} }
else { else {
packet.packetType = PacketType.Unknown; packet.packetType = PacketType.Unknown;
@ -97,12 +97,12 @@ class PacketStreamFactory extends Transform{
case PacketType.ProbeResponse: case PacketType.ProbeResponse:
case PacketType.AssociationRequest: case PacketType.AssociationRequest:
newPacket = new PacketWithSSID(); newPacket = new PacketWithSSID();
newPacket.ssid = data.match(new RegExp('(?<=(^|\\s)'+ packetTypeStr +'\\s\\().{0,32}(?=\\)($|\\s))', 'i'))?.[0] ?? null; newPacket.ssid = data.match(new RegExp("(?<=(^|\\s)"+ packetTypeStr +"\\s\\().{0,32}(?=\\)($|\\s))", "i"))?.[0] ?? null;
break; break;
case PacketType.Authentication: case PacketType.Authentication:
newPacket = new AuthenticationPacket(); newPacket = new AuthenticationPacket();
newPacket.authenticationType = AUTHENTICATION_TYPE_MAP[data.match(/(?<=(^|\s)Authentication\s).{3,}(?=\:(\s|$))/i)[0]] ?? AuthenticationType.Unknown; newPacket.authenticationType = AUTHENTICATION_TYPE_MAP[data.match(/(?<=(^|\s)Authentication\s).{3,}(?=:(\s|$))/i)[0]] ?? AuthenticationType.Unknown;
break; break;
case PacketType.AssociationResponse: case PacketType.AssociationResponse:
@ -121,16 +121,16 @@ class PacketStreamFactory extends Transform{
} }
_handlePayload(packet, data){ _handlePayload(packet, data){
data = data.join(''); data = data.join("");
// Get payload-Hex-Data. If there is no data: empty // Get payload-Hex-Data. If there is no data: empty
packet.payloadData = hexConv.hexToBytes(data.match(/(?<=\s)([A-F0-9]{1,4}(?=\s))/igm)?.join('') ?? ''); packet.payloadData = hexConv.hexToBytes(data.match(/(?<=\s)([A-F0-9]{1,4}(?=\s))/igm)?.join("") ?? "");
packet.payloadData.splice(packet.payloadData.length-4, 4); // Remove FrameCheck sequence packet.payloadData.splice(packet.payloadData.length-4, 4); // Remove FrameCheck sequence
// Cover special cases with more data // Cover special cases with more data
let newPacket; let newPacket;
switch(packet.packetType){ switch(packet.packetType){
case PacketType.Handshake: case PacketType.Handshake: {
newPacket = new HandshakePacket(); newPacket = new HandshakePacket();
// Read key-information // Read key-information
@ -140,6 +140,7 @@ class PacketStreamFactory extends Transform{
newPacket.handshakeStage = wifiStateAnalyser.handshakeStageFromKeyInfo(keyInfo); // Get stage newPacket.handshakeStage = wifiStateAnalyser.handshakeStageFromKeyInfo(keyInfo); // Get stage
break; break;
} }
}
if(newPacket) packet = Object.assign(newPacket, packet); if(newPacket) packet = Object.assign(newPacket, packet);
return packet; return packet;

@ -1,5 +1,5 @@
const logger = require.main.require("./helper/logger.js")("RegexBlockStream"); const logger = require.main.require("./helper/logger.js")("RegexBlockStream");
const { Transform } = require('stream') const { Transform } = require("stream");
/** /**
* Matches whole blocks as regex and passes them on * Matches whole blocks as regex and passes them on
@ -27,7 +27,7 @@ class RegexBlockStream extends Transform{
} }
_transform(chunk, encoding, next){ _transform(chunk, encoding, next){
chunk = this.readableBuffer.length? this.readableBuffer.join('') + chunk: chunk; // Add previous buffer to current chunk chunk = this.readableBuffer.length? this.readableBuffer.join("") + chunk: chunk; // Add previous buffer to current chunk
this.readableBuffer.length && this.readableBuffer.clear(); // Clear buffer once we read it this.readableBuffer.length && this.readableBuffer.clear(); // Clear buffer once we read it
let matches = chunk.match(this.matcher); // Match let matches = chunk.match(this.matcher); // Match
@ -44,17 +44,17 @@ class RegexBlockStream extends Transform{
if(matches){ if(matches){
matches.forEach((match) => { matches.forEach((match) => {
this.push(match); // Write match to stream this.push(match); // Write match to stream
if(chunk) chunk = chunk.replace(match, ''); // Remove match from chunks if(chunk) chunk = chunk.replace(match, ""); // Remove match from chunks
}); });
} }
if(chunk) return chunk; if(chunk) return chunk;
} }
_flush(next){ _flush(next){
if(matchAllOnFlush){ // When requested, we'll match one last time over the remaining buffer if(this.matchAllOnFlush){ // When requested, we'll match one last time over the remaining buffer
let chunk = this.readableBuffer.join(''); let chunk = this.readableBuffer.join("");
let matches = chunk.match(this.matcher); // Match remaining buffer let matches = chunk.match(this.matcher); // Match remaining buffer
_writeMatches(matches); // Write matches including last element this._writeMatches(matches); // Write matches including last element
} }
next(); // Tell system we are done next(); // Tell system we are done

Loading…
Cancel
Save