chore: client / server bifurcation mostly complete

This commit is contained in:
Bill Church 2024-07-11 21:07:29 +00:00
parent b6e5089ee6
commit bf50fca786
No known key found for this signature in database
9 changed files with 4610 additions and 270 deletions

View file

@ -1,88 +1,35 @@
// app/app.js
"use strict";
/* jshint esversion: 6, asi: true, node: true */
'use strict'
const path = require("path");
const express = require("express");
const session = require("express-session");
const logger = require("morgan");
const socketIo = require("socket.io");
const myutil = require("./util");
const config = require("./config");
const socketHandler = require("./socket");
const http = require('http')
const socketIo = require('socket.io')
const config = require('./config')
const socketHandler = require('./socket')
const app = express();
const server = require("http").Server(app);
const server = http.createServer()
// Session middleware
const sessionMiddleware = session({
secret: config.session.secret,
name: config.session.name,
resave: false,
saveUninitialized: true,
cookie: {
secure: process.env.NODE_ENV === "production",
maxAge: 24 * 60 * 60 * 1000,
},
});
// Express middleware
app.use(sessionMiddleware);
if (config.accesslog) app.use(logger("common"));
app.disable("x-powered-by");
// Socket.IO setup
const io = socketIo(server, {
path: "/ssh/socket.io",
path: '/ssh/socket.io',
cors: {
origin: "http://localhost:8080",
methods: ["GET", "POST"],
credentials: true,
},
});
// Socket.io middleware
io.use((socket, next) => {
sessionMiddleware(socket.request, socket.request.res || {}, next);
});
// WebSocket handling
io.on("connection", (socket) => {
console.log(
"New connection:",
socket.id,
"Transport:",
socket.conn.transport.name
);
// Call the imported socket handler function only once per connection
if (!socket.handled) {
socketHandler(io, socket);
socket.handled = true;
origin: 'http://localhost:8080',
methods: ['GET', 'POST'],
credentials: true
}
})
socket.on("disconnect", (reason) => {
console.log("Client disconnected:", socket.id, reason);
});
});
io.on('connection', (socket) => {
console.log(
'New connection:',
socket.id,
'Transport:',
socket.conn.transport.name
)
// Error handling
app.use((req, res, next) => {
res.status(404).send("Sorry can't find that!");
});
socketHandler(io, socket)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send("Something broke!");
});
socket.on('disconnect', (reason) => {
console.log('Client disconnected:', socket.id, reason)
})
})
// Graceful shutdown
process.on("SIGINT", () => {
console.log("SIGINT signal received: closing HTTP server");
server.close(() => {
console.log("HTTP server closed");
process.exit(0);
});
});
module.exports = { server, config, io };
module.exports = { server, config, io }

View file

@ -1,95 +1,95 @@
// config.js
const path = require("path");
const fs = require("fs");
const nodeRoot = path.dirname(require.main.filename);
const configPath = path.join(nodeRoot, "config.json");
const path = require('path')
const fs = require('fs')
const nodeRoot = path.dirname(require.main.filename)
const configPath = path.join(nodeRoot, 'config.json')
// Default configuration
let config = {
listen: {
ip: "0.0.0.0",
port: 2222,
ip: '0.0.0.0',
port: 2222
},
http: {
origins: ["*:*"],
origins: ['*:*']
},
user: {
name: null,
password: null,
password: null
},
ssh: {
host: null,
port: 22,
term: "xterm-color",
term: 'xterm-color',
readyTimeout: 20000,
keepaliveInterval: 120000,
keepaliveCountMax: 10,
keepaliveCountMax: 10
},
terminal: {
cursorBlink: true,
scrollback: 10000,
tabStopWidth: 8,
bellStyle: "sound",
bellStyle: 'sound'
},
header: {
text: null,
background: "green",
background: 'green'
},
session: {
name: "WebSSH2",
secret: "mysecret",
name: 'WebSSH2',
secret: 'mysecret'
},
options: {
challengeButton: true,
allowreauth: true,
allowreauth: true
},
algorithms: {
kex: [
"ecdh-sha2-nistp256",
"ecdh-sha2-nistp384",
"ecdh-sha2-nistp521",
"diffie-hellman-group-exchange-sha256",
"diffie-hellman-group14-sha1",
'ecdh-sha2-nistp256',
'ecdh-sha2-nistp384',
'ecdh-sha2-nistp521',
'diffie-hellman-group-exchange-sha256',
'diffie-hellman-group14-sha1'
],
cipher: [
"aes128-ctr",
"aes192-ctr",
"aes256-ctr",
"aes128-gcm",
"aes128-gcm@openssh.com",
"aes256-gcm",
"aes256-gcm@openssh.com",
"aes256-cbc",
'aes128-ctr',
'aes192-ctr',
'aes256-ctr',
'aes128-gcm',
'aes128-gcm@openssh.com',
'aes256-gcm',
'aes256-gcm@openssh.com',
'aes256-cbc'
],
hmac: ["hmac-sha2-256", "hmac-sha2-512", "hmac-sha1"],
compress: ["none", "zlib@openssh.com", "zlib"],
hmac: ['hmac-sha2-256', 'hmac-sha2-512', 'hmac-sha1'],
compress: ['none', 'zlib@openssh.com', 'zlib']
},
serverlog: {
client: false,
server: false,
server: false
},
accesslog: false,
verify: false,
};
verify: false
}
try {
if (fs.existsSync(configPath)) {
console.log("WebSSH2 service reading config from: " + configPath);
config = require("read-config-ng")(configPath);
console.log('WebSSH2 service reading config from: ' + configPath)
config = require('read-config-ng')(configPath)
} else {
console.error(
"\n\nERROR: Missing config.json for webssh. Current config: " +
'\n\nERROR: Missing config.json for webssh. Current config: ' +
JSON.stringify(config)
);
console.error("\n See config.json.sample for details\n\n");
)
console.error('\n See config.json.sample for details\n\n')
}
} catch (err) {
console.error(
"\n\nERROR: Missing config.json for webssh. Current config: " +
'\n\nERROR: Missing config.json for webssh. Current config: ' +
JSON.stringify(config)
);
console.error("\n See config.json.sample for details\n\n");
console.error("ERROR:\n\n " + err);
)
console.error('\n See config.json.sample for details\n\n')
console.error('ERROR:\n\n ' + err)
}
module.exports = config;
module.exports = config

View file

@ -1,12 +0,0 @@
// app/expressOptions.js
module.exports = {
dotfiles: "ignore",
etag: false,
extensions: ["htm", "html"],
index: false,
maxAge: "1s",
redirect: false,
setHeaders: function (res, path, stat) {
res.set("x-timestamp", Date.now());
},
};

View file

@ -1,77 +1,77 @@
// app/socket.js
"use strict";
'use strict'
const debug = require("debug");
const debugWebSSH2 = require("debug")("WebSSH2");
const SSH = require("ssh2").Client;
const debug = require('debug')
const debugWebSSH2 = require('debug')('WebSSH2')
const SSH = require('ssh2').Client
module.exports = function (io) {
io.on("connection", (socket) => {
let conn = null;
let stream = null;
console.log(`SOCKET CONNECT: ${socket.id}`);
io.on('connection', (socket) => {
let conn = null
let stream = null
console.log(`SOCKET CONNECT: ${socket.id}`)
// Remove existing listeners to prevent duplicates
socket.removeAllListeners("authenticate");
socket.removeAllListeners("data");
socket.removeAllListeners("resize");
socket.removeAllListeners("disconnect");
socket.removeAllListeners('authenticate')
socket.removeAllListeners('data')
socket.removeAllListeners('resize')
socket.removeAllListeners('disconnect')
// Authenticate user
socket.on("authenticate", (credentials) => {
console.log(`SOCKET AUTHENTICATE: ${socket.id}`);
socket.on('authenticate', (credentials) => {
console.log(`SOCKET AUTHENTICATE: ${socket.id}`)
if (isValidCredentials(credentials)) {
console.log(`SOCKET AUTHENTICATE SUCCESS: ${socket.id}`);
initializeConnection(socket, credentials);
console.log(`SOCKET AUTHENTICATE SUCCESS: ${socket.id}`)
initializeConnection(socket, credentials)
} else {
console.log(`SOCKET AUTHENTICATE FAILED: ${socket.id}`);
socket.emit("auth_result", {
console.log(`SOCKET AUTHENTICATE FAILED: ${socket.id}`)
socket.emit('auth_result', {
success: false,
message: "Invalid credentials",
});
message: 'Invalid credentials'
})
}
});
})
socket.on("disconnect", (reason) => {
debugWebSSH2(`SOCKET DISCONNECT: ${socket.id}, Reason: ${reason}`);
socket.on('disconnect', (reason) => {
debugWebSSH2(`SOCKET DISCONNECT: ${socket.id}, Reason: ${reason}`)
if (conn) {
conn.end();
conn.end()
}
// Clean up listeners
socket.removeAllListeners();
});
socket.removeAllListeners()
})
socket.on("data", (data) => {
socket.on('data', (data) => {
if (stream) {
stream.write(data);
stream.write(data)
}
});
})
socket.on("resize", (data) => {
socket.on('resize', (data) => {
if (stream) {
stream.setWindow(data.rows, data.cols);
stream.setWindow(data.rows, data.cols)
}
});
})
function initializeConnection (socket, credentials) {
if (conn) {
// If there's an existing connection, end it before creating a new one
conn.end();
conn.end()
}
conn = new SSH();
conn = new SSH()
conn.on("ready", () => {
conn.on('ready', () => {
console.log(
`WebSSH2 Login: user=${credentials.username} from=${socket.handshake.address} host=${credentials.host} port=${credentials.port} sessionID=${socket.id}`
);
)
socket.emit("auth_result", { success: true });
socket.emit("allowreauth", true);
socket.emit("allowreplay", true);
socket.emit("title", `ssh://${credentials.host}`);
socket.emit("status", "SSH CONNECTION ESTABLISHED");
socket.emit("statusBackground", "green");
socket.emit('auth_result', { success: true })
socket.emit('allowreauth', true)
socket.emit('allowreplay', true)
socket.emit('title', `ssh://${credentials.host}`)
socket.emit('status', 'SSH CONNECTION ESTABLISHED')
socket.emit('statusBackground', 'green')
conn.shell(
{
@ -81,37 +81,37 @@ module.exports = function (io) {
},
(err, str) => {
if (err) {
return SSHerror("EXEC ERROR", err);
return SSHerror('EXEC ERROR', err)
}
stream = str;
stream = str
stream.on("data", (data) => {
socket.emit("data", data.toString("utf-8"));
});
stream.on('data', (data) => {
socket.emit('data', data.toString('utf-8'))
})
stream.on("close", (code, signal) => {
SSHerror("STREAM CLOSE", {
stream.on('close', (code, signal) => {
SSHerror('STREAM CLOSE', {
message:
code || signal
? `CODE: ${code} SIGNAL: ${signal}`
: undefined,
});
});
: undefined
})
})
stream.stderr.on("data", (data) => {
console.log("STDERR: " + data);
});
stream.stderr.on('data', (data) => {
console.log('STDERR: ' + data)
})
}
);
});
)
})
conn.on("banner", (data) => {
socket.emit("data", data.replace(/\r?\n/g, "\r\n"));
});
conn.on('banner', (data) => {
socket.emit('data', data.replace(/\r?\n/g, '\r\n'))
})
conn.on("end", () => SSHerror("CONN END BY HOST"));
conn.on("close", () => SSHerror("CONN CLOSE"));
conn.on("error", (err) => SSHerror("CONN ERROR", err));
conn.on('end', () => SSHerror('CONN END BY HOST'))
conn.on('close', () => SSHerror('CONN CLOSE'))
conn.on('error', (err) => SSHerror('CONN ERROR', err))
conn.connect({
host: credentials.host,
@ -123,16 +123,16 @@ module.exports = function (io) {
readyTimeout: credentials.readyTimeout,
keepaliveInterval: credentials.keepaliveInterval,
keepaliveCountMax: credentials.keepaliveCountMax,
debug: debug("ssh2")
});
debug: debug('ssh2')
})
}
function SSHerror (myFunc, err) {
const errorMessage = err ? `: ${err.message}` : "";
console.log(`WebSSH2 error: ${myFunc}${errorMessage}`);
socket.emit("ssherror", `SSH ${myFunc}${errorMessage}`);
const errorMessage = err ? `: ${err.message}` : ''
console.log(`WebSSH2 error: ${myFunc}${errorMessage}`)
socket.emit('ssherror', `SSH ${myFunc}${errorMessage}`)
if (conn) {
conn.end();
conn.end()
}
// Don't disconnect the socket here, let the client handle reconnection if necessary
// socket.disconnect(true);
@ -140,7 +140,7 @@ module.exports = function (io) {
function isValidCredentials (credentials) {
// Implement your credential validation logic here
return credentials && credentials.username && credentials.password;
return credentials && credentials.username && credentials.password
}
})
}
});
};

View file

@ -1,13 +0,0 @@
"use strict";
// app/util.js
/* jshint esversion: 6, asi: true, node: true */
// private
require("colors"); // allow for color property extensions in log messages
var debug = require("debug")("WebSSH2");
var Auth = require("basic-auth");
// takes a string, makes it boolean (true if the string is true, false otherwise)
exports.parseBool = function parseBool(str) {
return str.toLowerCase() === "true";
};

View file

@ -1,4 +1,4 @@
"use strict";
'use strict'
/* jshint esversion: 6, asi: true, node: true */
/*
* index.js
@ -7,24 +7,14 @@
* Bill Church - https://github.com/billchurch/WebSSH2 - May 2017
*
*/
const { server, config } = require("./app/app");
const { server, config } = require('./app/app')
server.listen(config.listen.port, config.listen.ip, () => {
console.log(
`WebSSH2 service listening on ${config.listen.ip}:${config.listen.port}`
);
});
)
})
server.on("error", function (err) {
if (err.code === "EADDRINUSE") {
config.listen.port++;
console.warn(
"WebSSH2 Address in use, retrying on port " + config.listen.port
);
setTimeout(function () {
server.listen(config.listen.port);
}, 250);
} else {
console.log("WebSSH2 server.listen ERROR: " + err.code);
}
});
server.on('error', function (err) {
console.log('WebSSH2 server.listen ERROR: ' + err.code)
})

4434
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "webssh2-server",
"version": "0.2.13",
"version": "0.2.14",
"ignore": [
".gitignore"
],
@ -32,16 +32,10 @@
"url": "https://github.com/billchurch/WebSSH2/issues"
},
"dependencies": {
"basic-auth": "~2.0.1",
"debug": "~4.1.0",
"express": "~4.16.4",
"express-session": "~1.15.6",
"morgan": "~1.9.1",
"read-config-ng": "~3.0.7",
"serve-favicon": "~2.5.0",
"socket.io": "~2.2.0",
"ssh2": "~0.8.9",
"validator": "~10.9.0"
"ssh2": "~0.8.9"
},
"scripts": {
"start": "node index.js",

View file

@ -1,38 +1,38 @@
var express = require("express");
var app = express();
var server = require("http").createServer(app);
var io = require("socket.io")(server, {
path: "/ssh/socket.io",
var express = require('express')
var app = express()
var server = require('http').createServer(app)
var io = require('socket.io')(server, {
path: '/ssh/socket.io',
cors: {
origin: "http://localhost:8080",
methods: ["GET", "POST"],
credentials: true,
},
});
origin: 'http://localhost:8080',
methods: ['GET', 'POST'],
credentials: true
}
})
var PORT = 3000;
var PORT = 3000
io.on("connection", function (socket) {
console.log("A client connected");
io.on('connection', function (socket) {
console.log('A client connected')
socket.on("authenticate", function (credentials) {
console.log("Received credentials:", credentials);
socket.on('authenticate', function (credentials) {
console.log('Received credentials:', credentials)
// Here you would typically validate the credentials
// For this example, we'll just echo back a success message
var authResult = {
success: true,
message: "Authentication successful",
};
message: 'Authentication successful'
}
socket.emit("auth_result", authResult);
});
socket.emit('auth_result', authResult)
})
socket.on("disconnect", function () {
console.log("A client disconnected");
});
});
socket.on('disconnect', function () {
console.log('A client disconnected')
})
})
server.listen(PORT, function () {
console.log("Server running on port " + PORT);
});
console.log('Server running on port ' + PORT)
})