feature: Implement first backend.
This commit is contained in:
parent
69498c2a6e
commit
c8f317b0e5
7 changed files with 465 additions and 29 deletions
145
package-lock.json
generated
145
package-lock.json
generated
|
@ -11,11 +11,13 @@
|
|||
"dependencies": {
|
||||
"@prisma/client": "^5.13.0",
|
||||
"bcrypt": "^5.1.1",
|
||||
"fastify": "^4.26.2"
|
||||
"fastify": "^4.26.2",
|
||||
"node-ssh": "^13.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
"@types/node": "^20.12.7",
|
||||
"@types/ssh2": "^1.15.0",
|
||||
"nodemon": "^3.0.3",
|
||||
"prisma": "^5.13.0",
|
||||
"typescript": "^5.3.3"
|
||||
|
@ -165,6 +167,24 @@
|
|||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ssh2": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.0.tgz",
|
||||
"integrity": "sha512-YcT8jP5F8NzWeevWvcyrrLB3zcneVjzYY9ZDSMAMboI+2zR1qYWFhwsyOFVzT7Jorn67vqxC0FRiw8YyG9P1ww==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "^18.11.18"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ssh2/node_modules/@types/node": {
|
||||
"version": "18.19.31",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.31.tgz",
|
||||
"integrity": "sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
|
@ -284,6 +304,14 @@
|
|||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/asn1": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||
"dependencies": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/atomic-sleep": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
|
||||
|
@ -340,6 +368,14 @@
|
|||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
|
||||
"dependencies": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
|
@ -393,6 +429,15 @@
|
|||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/buildcheck": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz",
|
||||
"integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||
|
@ -454,6 +499,20 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cpu-features": {
|
||||
"version": "0.0.9",
|
||||
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.9.tgz",
|
||||
"integrity": "sha512-AKjgn2rP2yJyfbepsmLfiYcmtNn/2eUvocUyM/09yB0YDiz39HteK/5/T4Onf0pmdYDMgkBoGvRLvEguzyL7wQ==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"buildcheck": "~0.0.6",
|
||||
"nan": "^2.17.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
|
@ -858,6 +917,17 @@
|
|||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-stream": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/json-schema-ref-resolver": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz",
|
||||
|
@ -972,6 +1042,12 @@
|
|||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/nan": {
|
||||
"version": "2.19.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz",
|
||||
"integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||
|
@ -996,6 +1072,22 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-ssh": {
|
||||
"version": "13.2.0",
|
||||
"resolved": "https://registry.npmjs.org/node-ssh/-/node-ssh-13.2.0.tgz",
|
||||
"integrity": "sha512-7vsKR2Bbs66th6IWCy/7SN4MSwlVt+G6QrHB631BjRUM8/LmvDugtYhi0uAmgvHS/+PVurfNBOmELf30rm0MZg==",
|
||||
"dependencies": {
|
||||
"is-stream": "^2.0.0",
|
||||
"make-dir": "^3.1.0",
|
||||
"sb-promise-queue": "^2.1.0",
|
||||
"sb-scandir": "^3.1.0",
|
||||
"shell-escape": "^0.2.0",
|
||||
"ssh2": "^1.14.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz",
|
||||
|
@ -1312,6 +1404,30 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/sb-promise-queue": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sb-promise-queue/-/sb-promise-queue-2.1.0.tgz",
|
||||
"integrity": "sha512-zwq4YuP1FQFkGx2Q7GIkZYZ6PqWpV+bg0nIO1sJhWOyGyhqbj0MsTvK6lCFo5TQwX5pZr6SCQ75e8PCDCuNvkg==",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/sb-scandir": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sb-scandir/-/sb-scandir-3.1.0.tgz",
|
||||
"integrity": "sha512-70BVm2xz9jn94zSQdpvYrEG101/UV9TVGcfWr9T5iob3QhCK4lYXeculfBqPGFv3XTeKgx4dpWyYIDeZUqo4kg==",
|
||||
"dependencies": {
|
||||
"sb-promise-queue": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/secure-json-parse": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
|
||||
|
@ -1341,6 +1457,11 @@
|
|||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz",
|
||||
"integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ=="
|
||||
},
|
||||
"node_modules/shell-escape": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/shell-escape/-/shell-escape-0.2.0.tgz",
|
||||
"integrity": "sha512-uRRBT2MfEOyxuECseCZd28jC1AJ8hmqqneWQ4VWUTgCAFvb3wKU1jLqj6egC4Exrr88ogg3dp+zroH4wJuaXzw=="
|
||||
},
|
||||
"node_modules/signal-exit": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||
|
@ -1374,6 +1495,23 @@
|
|||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/ssh2": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.15.0.tgz",
|
||||
"integrity": "sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"asn1": "^0.2.6",
|
||||
"bcrypt-pbkdf": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.16.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"cpu-features": "~0.0.9",
|
||||
"nan": "^2.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
|
@ -1479,6 +1617,11 @@
|
|||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"node_modules/tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
"devDependencies": {
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
"@types/node": "^20.12.7",
|
||||
"@types/ssh2": "^1.15.0",
|
||||
"nodemon": "^3.0.3",
|
||||
"prisma": "^5.13.0",
|
||||
"typescript": "^5.3.3"
|
||||
|
@ -23,6 +24,7 @@
|
|||
"dependencies": {
|
||||
"@prisma/client": "^5.13.0",
|
||||
"bcrypt": "^5.1.1",
|
||||
"fastify": "^4.26.2"
|
||||
"fastify": "^4.26.2",
|
||||
"node-ssh": "^13.2.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,35 +3,57 @@ export type ParameterReturnedValue = {
|
|||
message?: string
|
||||
}
|
||||
|
||||
export type ConnectedDevice = {
|
||||
export type ForwardRule = {
|
||||
sourceIP: string,
|
||||
sourcePort: number,
|
||||
destPort: number,
|
||||
|
||||
protocol: "tcp" | "udp"
|
||||
destPort: number
|
||||
};
|
||||
|
||||
export interface BackendInterface {
|
||||
new(): {
|
||||
addConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void;
|
||||
removeConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void;
|
||||
export type ConnectedClient = {
|
||||
ip: string,
|
||||
port: number,
|
||||
|
||||
start(): Promise<boolean>,
|
||||
stop(): Promise<boolean>,
|
||||
connectionDetails: ForwardRule
|
||||
};
|
||||
|
||||
getAllConnections(): {
|
||||
sourceIP: string,
|
||||
sourcePort: number,
|
||||
destPort: number,
|
||||
protocol: "tcp" | "udp"
|
||||
}[];
|
||||
export class BackendBaseClass {
|
||||
state: "stopped" | "stopping" | "started" | "starting";
|
||||
|
||||
state: "stopped" | "running" | "starting";
|
||||
clients?: ConnectedClient[]; // Not required to be implemented, but more consistency
|
||||
logs: string[];
|
||||
|
||||
probeConnectedClients: ConnectedDevice[],
|
||||
logs: string[]
|
||||
},
|
||||
constructor(parameters: string) {
|
||||
this.logs = [];
|
||||
this.clients = [];
|
||||
|
||||
checkParametersConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): ParameterReturnedValue;
|
||||
checkParametersBackendInstance(data: string): ParameterReturnedValue;
|
||||
this.state = "stopped";
|
||||
}
|
||||
|
||||
addConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void {};
|
||||
removeConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void {};
|
||||
|
||||
async start(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
async stop(): Promise<boolean> {
|
||||
return true;
|
||||
};
|
||||
|
||||
getAllConnections(): ConnectedClient[] {
|
||||
if (this.clients == null) return [];
|
||||
return this.clients;
|
||||
};
|
||||
|
||||
static checkParametersConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): ParameterReturnedValue {
|
||||
return {
|
||||
success: true
|
||||
}
|
||||
};
|
||||
|
||||
static checkParametersBackendInstance(data: string): ParameterReturnedValue {
|
||||
return {
|
||||
success: true
|
||||
}
|
||||
};
|
||||
}
|
6
src/backendimpl/index.ts
Normal file
6
src/backendimpl/index.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import type { BackendBaseClass } from "./base.js";
|
||||
import { SSHBackendProvider } from "./ssh.js";
|
||||
|
||||
export const backendProviders: Record<string, typeof BackendBaseClass> = {
|
||||
"ssh": SSHBackendProvider
|
||||
};
|
211
src/backendimpl/ssh.ts
Normal file
211
src/backendimpl/ssh.ts
Normal file
|
@ -0,0 +1,211 @@
|
|||
import { NodeSSH } from "node-ssh";
|
||||
import { Socket } from "node:net";
|
||||
|
||||
import type { BackendBaseClass, ForwardRule, ConnectedClient, ParameterReturnedValue } from "./base.js";
|
||||
|
||||
// Fight me (for better naming)
|
||||
type BackendParsedProviderString = {
|
||||
ip: string,
|
||||
port: number,
|
||||
|
||||
username: string,
|
||||
privateKey: string
|
||||
}
|
||||
|
||||
function parseBackendProviderString(data: string): BackendParsedProviderString {
|
||||
try {
|
||||
JSON.parse(data);
|
||||
} catch (e) {
|
||||
throw new Error("Payload body is not JSON")
|
||||
}
|
||||
|
||||
const jsonData = JSON.parse(data);
|
||||
|
||||
if (typeof jsonData.ip != "string") throw new Error("IP field is not a string");
|
||||
if (typeof jsonData.port != "number") throw new Error("Port is not a number");
|
||||
|
||||
if (typeof jsonData.username != "string") throw new Error("Username is not a string");
|
||||
if (typeof jsonData.privateKey != "string") throw new Error("Private key is not a string");
|
||||
|
||||
return {
|
||||
ip: jsonData.ip,
|
||||
port: jsonData.port,
|
||||
|
||||
username: jsonData.username,
|
||||
privateKey: jsonData.privateKey
|
||||
}
|
||||
}
|
||||
|
||||
export class SSHBackendProvider implements BackendBaseClass {
|
||||
state: "stopped" | "stopping" | "started" | "starting";
|
||||
|
||||
clients: ConnectedClient[];
|
||||
proxies: ForwardRule[];
|
||||
logs: string[];
|
||||
|
||||
sshInstance: NodeSSH;
|
||||
options: BackendParsedProviderString;
|
||||
|
||||
constructor(parameters: string) {
|
||||
this.logs = [];
|
||||
this.proxies = [];
|
||||
this.clients = [];
|
||||
|
||||
this.options = parseBackendProviderString(parameters);
|
||||
|
||||
this.state = "stopped";
|
||||
}
|
||||
|
||||
async start(): Promise<boolean> {
|
||||
this.state = "starting";
|
||||
this.logs.push("Starting SSHBackendProvider...");
|
||||
|
||||
if (this.sshInstance) {
|
||||
this.sshInstance.dispose();
|
||||
}
|
||||
|
||||
this.sshInstance = new NodeSSH();
|
||||
|
||||
try {
|
||||
await this.sshInstance.connect({
|
||||
host: this.options.ip,
|
||||
port: this.options.port,
|
||||
|
||||
username: this.options.username,
|
||||
privateKey: this.options.privateKey
|
||||
});
|
||||
} catch (e) {
|
||||
this.logs.push(`Failed to start SSHBackendProvider! Error: '${e}'`);
|
||||
this.state = "stopped";
|
||||
|
||||
// @ts-ignore
|
||||
this.sshInstance = null;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
this.state = "started";
|
||||
this.logs.push("Successfully started SSHBackendProvider.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async stop(): Promise<boolean> {
|
||||
this.state = "stopping";
|
||||
this.logs.push("Stopping SSHBackendProvider...");
|
||||
|
||||
this.proxies.splice(0, this.proxies.length);
|
||||
|
||||
this.sshInstance.dispose();
|
||||
|
||||
// @ts-ignore
|
||||
this.sshInstance = null;
|
||||
|
||||
this.logs.push("Successfully stopped SSHBackendProvider.");
|
||||
this.state = "stopped";
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
addConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void {
|
||||
const connectionCheck = SSHBackendProvider.checkParametersConnection(sourceIP, sourcePort, destPort, protocol);
|
||||
if (!connectionCheck.success) throw new Error(connectionCheck.message);
|
||||
|
||||
const foundProxyEntry = this.proxies.find((i) => i.sourceIP == sourceIP && i.sourcePort == sourcePort && i.destPort == destPort);
|
||||
if (foundProxyEntry) return;
|
||||
|
||||
console.log("connection added");
|
||||
|
||||
(async() => {
|
||||
await this.sshInstance.forwardIn("0.0.0.0", destPort, (info, accept, reject) => {
|
||||
const foundProxyEntry = this.proxies.find((i) => i.sourceIP == sourceIP && i.sourcePort == sourcePort && i.destPort == destPort);
|
||||
if (!foundProxyEntry) return reject();
|
||||
|
||||
const client: ConnectedClient = {
|
||||
ip: info.srcIP,
|
||||
port: info.srcPort,
|
||||
|
||||
connectionDetails: foundProxyEntry
|
||||
};
|
||||
|
||||
this.clients.push(client);
|
||||
|
||||
const srcConn = new Socket();
|
||||
|
||||
srcConn.connect({
|
||||
host: sourceIP,
|
||||
port: sourcePort
|
||||
});
|
||||
|
||||
// Why is this so confusing
|
||||
const destConn = accept();
|
||||
|
||||
destConn.on("data", (chunk: Uint8Array) => {
|
||||
srcConn.write(chunk);
|
||||
});
|
||||
|
||||
destConn.on("exit", () => {
|
||||
this.clients.splice(this.clients.indexOf(client), 1);
|
||||
srcConn.end();
|
||||
});
|
||||
|
||||
srcConn.on("data", (data) => {
|
||||
destConn.write(data);
|
||||
});
|
||||
|
||||
srcConn.on("end", () => {
|
||||
this.clients.splice(this.clients.indexOf(client), 1);
|
||||
destConn.close();
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
this.proxies.push({
|
||||
sourceIP,
|
||||
sourcePort,
|
||||
destPort
|
||||
});
|
||||
};
|
||||
|
||||
removeConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void {
|
||||
const connectionCheck = SSHBackendProvider.checkParametersConnection(sourceIP, sourcePort, destPort, protocol);
|
||||
if (!connectionCheck.success) throw new Error(connectionCheck.message);
|
||||
|
||||
const foundProxyEntry = this.proxies.find((i) => i.sourceIP == sourceIP && i.sourcePort == sourcePort && i.destPort == destPort);
|
||||
if (!foundProxyEntry) return;
|
||||
|
||||
const proxyIndex = this.proxies.indexOf(foundProxyEntry);
|
||||
this.proxies.splice(proxyIndex, 1);
|
||||
};
|
||||
|
||||
getAllConnections(): ConnectedClient[] {
|
||||
return this.clients;
|
||||
};
|
||||
|
||||
static checkParametersConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): ParameterReturnedValue {
|
||||
if (protocol == "udp") return {
|
||||
success: false,
|
||||
message: "SSH does not support UDP tunneling! Please use something like PortCopier instead (if it gets done)"
|
||||
};
|
||||
|
||||
return {
|
||||
success: true
|
||||
}
|
||||
};
|
||||
|
||||
static checkParametersBackendInstance(data: string): ParameterReturnedValue {
|
||||
try {
|
||||
parseBackendProviderString(data);
|
||||
// @ts-ignore
|
||||
} catch (e: Error) {
|
||||
return {
|
||||
success: false,
|
||||
message: e.toString()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true
|
||||
}
|
||||
};
|
||||
}
|
56
src/index.ts
56
src/index.ts
|
@ -4,7 +4,9 @@ import { PrismaClient } from '@prisma/client';
|
|||
import Fastify from "fastify";
|
||||
|
||||
import type { ServerOptions, SessionToken, RouteOptions } from "./libs/types.js";
|
||||
import type { BackendInterface } from "./backendimpl/base.js";
|
||||
import type { BackendBaseClass } from "./backendimpl/base.js";
|
||||
|
||||
import { backendProviders } from "./backendimpl/index.js";
|
||||
|
||||
import { route as getPermissions } from "./routes/getPermissions.js";
|
||||
|
||||
|
@ -20,6 +22,7 @@ import { route as userCreate } from "./routes/user/create.js";
|
|||
import { route as userRemove } from "./routes/user/remove.js";
|
||||
import { route as userLookup } from "./routes/user/lookup.js";
|
||||
import { route as userLogin } from "./routes/user/login.js";
|
||||
import { connect } from "node:http2";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
|
@ -40,7 +43,7 @@ const serverOptions: ServerOptions = {
|
|||
};
|
||||
|
||||
const sessionTokens: Record<number, SessionToken[]> = {};
|
||||
const backends: Record<number, BackendInterface> = {};
|
||||
const backends: Record<number, BackendBaseClass> = {};
|
||||
|
||||
const fastify = Fastify({
|
||||
logger: true
|
||||
|
@ -55,6 +58,55 @@ const routeOptions: RouteOptions = {
|
|||
backends: backends
|
||||
};
|
||||
|
||||
console.log("Initializing forwarding rules...");
|
||||
|
||||
const createdBackends = await prisma.desinationProvider.findMany();
|
||||
|
||||
for (const backend of createdBackends) {
|
||||
console.log(`Running init steps for ID '${backend.id}' (${backend.name})`);
|
||||
|
||||
const ourProvider = backendProviders[backend.backend];
|
||||
|
||||
if (!ourProvider) {
|
||||
console.log(" - Error: Invalid backend recieved!");
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(" - Initializing backend...");
|
||||
|
||||
backends[backend.id] = new ourProvider(backend.connectionDetails);
|
||||
const ourBackend = backends[backend.id];
|
||||
|
||||
if (!await ourBackend.start()) {
|
||||
console.log(" - Error initializing backend!");
|
||||
console.log(" - " + ourBackend.logs.join("\n - "));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(" - Initializing clients...");
|
||||
|
||||
const clients = await prisma.forwardRule.findMany({
|
||||
where: {
|
||||
destProviderID: backend.id,
|
||||
enabled: true
|
||||
}
|
||||
});
|
||||
|
||||
for (const client of clients) {
|
||||
if (client.protocol != "tcp" && client.protocol != "udp") {
|
||||
console.error(` - Error: Client with ID of '${client.id}' has an invalid protocol! (must be either TCP or UDP)`);
|
||||
continue;
|
||||
}
|
||||
|
||||
ourBackend.addConnection(client.sourceIP, client.sourcePort, client.destPort, client.protocol);
|
||||
}
|
||||
|
||||
console.log("Init successful.");
|
||||
}
|
||||
|
||||
console.log("Done.");
|
||||
|
||||
getPermissions(routeOptions);
|
||||
|
||||
backendCreate(routeOptions);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { PrismaClient } from "@prisma/client";
|
||||
import type { FastifyInstance } from "fastify";
|
||||
|
||||
import type { BackendInterface } from "../backendimpl/base.js";
|
||||
import type { BackendBaseClass } from "../backendimpl/base.js";
|
||||
|
||||
export type ServerOptions = {
|
||||
isSignupEnabled: boolean;
|
||||
|
@ -24,5 +24,5 @@ export type RouteOptions = {
|
|||
tokens: Record<number, SessionToken[]>,
|
||||
|
||||
options: ServerOptions,
|
||||
backends: Record<number, BackendInterface>
|
||||
backends: Record<number, BackendBaseClass>
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue