chore: formatting
This commit is contained in:
parent
40715023b2
commit
aa633aef0b
6 changed files with 374 additions and 298 deletions
83
app/app.js
83
app/app.js
|
@ -1,50 +1,57 @@
|
||||||
// server
|
// server
|
||||||
// app/app.js
|
// app/app.js
|
||||||
'use strict'
|
"use strict"
|
||||||
|
|
||||||
const createDebug = require('debug')
|
const createDebug = require("debug")
|
||||||
const debug = createDebug('webssh2')
|
const debug = createDebug("webssh2")
|
||||||
const http = require('http')
|
const http = require("http")
|
||||||
const express = require('express')
|
const express = require("express")
|
||||||
const socketIo = require('socket.io')
|
const socketIo = require("socket.io")
|
||||||
const path = require('path')
|
const path = require("path")
|
||||||
const bodyParser = require('body-parser')
|
const bodyParser = require("body-parser")
|
||||||
const session = require('express-session')
|
const session = require("express-session")
|
||||||
const sharedsession = require("express-socket.io-session")
|
const sharedsession = require("express-socket.io-session")
|
||||||
const config = require('./config')
|
const config = require("./config")
|
||||||
const socketHandler = require('./socket')
|
const socketHandler = require("./socket")
|
||||||
const sshRoutes = require('./routes')
|
const sshRoutes = require("./routes")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and configures the Express application
|
* Creates and configures the Express application
|
||||||
* @returns {express.Application} The Express application instance
|
* @returns {express.Application} The Express application instance
|
||||||
*/
|
*/
|
||||||
function createApp() {
|
function createApp() {
|
||||||
const app = express();
|
const app = express()
|
||||||
|
|
||||||
// Resolve the correct path to the webssh2_client module
|
// Resolve the correct path to the webssh2_client module
|
||||||
const clientPath = path.resolve(__dirname, '..', 'node_modules', 'webssh2_client', 'client', 'public');
|
const clientPath = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
"..",
|
||||||
|
"node_modules",
|
||||||
|
"webssh2_client",
|
||||||
|
"client",
|
||||||
|
"public"
|
||||||
|
)
|
||||||
|
|
||||||
// Set up session middleware
|
// Set up session middleware
|
||||||
const sessionMiddleware = session({
|
const sessionMiddleware = session({
|
||||||
secret: config.session.secret || 'webssh2_secret',
|
secret: config.session.secret || "webssh2_secret",
|
||||||
resave: false,
|
resave: false,
|
||||||
saveUninitialized: true,
|
saveUninitialized: true,
|
||||||
name: config.session.name || 'webssh2.sid'
|
name: config.session.name || "webssh2.sid"
|
||||||
});
|
})
|
||||||
app.use(sessionMiddleware);
|
app.use(sessionMiddleware)
|
||||||
|
|
||||||
// Handle POST and GET parameters
|
// Handle POST and GET parameters
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
app.use(bodyParser.urlencoded({ extended: true }))
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json())
|
||||||
|
|
||||||
// Serve static files from the webssh2_client module with a custom prefix
|
// Serve static files from the webssh2_client module with a custom prefix
|
||||||
app.use('/ssh/assets', express.static(clientPath));
|
app.use("/ssh/assets", express.static(clientPath))
|
||||||
|
|
||||||
// Use the SSH routes
|
// Use the SSH routes
|
||||||
app.use('/ssh', sshRoutes);
|
app.use("/ssh", sshRoutes)
|
||||||
|
|
||||||
return { app, sessionMiddleware };
|
return { app, sessionMiddleware }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,18 +74,20 @@ function createServer(app) {
|
||||||
function configureSocketIO(server, sessionMiddleware) {
|
function configureSocketIO(server, sessionMiddleware) {
|
||||||
const io = socketIo(server, {
|
const io = socketIo(server, {
|
||||||
serveClient: false,
|
serveClient: false,
|
||||||
path: '/ssh/socket.io',
|
path: "/ssh/socket.io",
|
||||||
pingTimeout: 60000, // 1 minute
|
pingTimeout: 60000, // 1 minute
|
||||||
pingInterval: 25000, // 25 seconds
|
pingInterval: 25000, // 25 seconds
|
||||||
cors: getCorsConfig()
|
cors: getCorsConfig()
|
||||||
});
|
})
|
||||||
|
|
||||||
// Share session with io sockets
|
// Share session with io sockets
|
||||||
io.use(sharedsession(sessionMiddleware, {
|
io.use(
|
||||||
autoSave: true
|
sharedsession(sessionMiddleware, {
|
||||||
}));
|
autoSave: true
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
return io;
|
return io
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,8 +96,8 @@ function configureSocketIO(server, sessionMiddleware) {
|
||||||
*/
|
*/
|
||||||
function getCorsConfig() {
|
function getCorsConfig() {
|
||||||
return {
|
return {
|
||||||
origin: config.origin || ['*.*'],
|
origin: config.origin || ["*.*"],
|
||||||
methods: ['GET', 'POST'],
|
methods: ["GET", "POST"],
|
||||||
credentials: true
|
credentials: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,10 +124,12 @@ function startServer() {
|
||||||
|
|
||||||
// Start the server
|
// Start the server
|
||||||
server.listen(config.listen.port, config.listen.ip, () => {
|
server.listen(config.listen.port, config.listen.ip, () => {
|
||||||
console.log(`WebSSH2 service listening on ${config.listen.ip}:${config.listen.port}`)
|
console.log(
|
||||||
|
`WebSSH2 service listening on ${config.listen.ip}:${config.listen.port}`
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
server.on('error', handleServerError)
|
server.on("error", handleServerError)
|
||||||
|
|
||||||
return { server, io, app }
|
return { server, io, app }
|
||||||
}
|
}
|
||||||
|
@ -128,8 +139,8 @@ function startServer() {
|
||||||
* @param {Error} err - The error object
|
* @param {Error} err - The error object
|
||||||
*/
|
*/
|
||||||
function handleServerError(err) {
|
function handleServerError(err) {
|
||||||
console.error('WebSSH2 server.listen ERROR:', err.code)
|
console.error("WebSSH2 server.listen ERROR:", err.code)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't start the server immediately, export the function instead
|
// Don't start the server immediately, export the function instead
|
||||||
module.exports = { startServer, config }
|
module.exports = { startServer, config }
|
||||||
|
|
222
app/config.js
222
app/config.js
|
@ -1,12 +1,12 @@
|
||||||
// server
|
// server
|
||||||
// app/config.js
|
// app/config.js
|
||||||
'use strict'
|
"use strict"
|
||||||
|
|
||||||
const path = require('path')
|
const path = require("path")
|
||||||
const fs = require('fs')
|
const fs = require("fs")
|
||||||
const readConfig = require('read-config-ng')
|
const readConfig = require("read-config-ng")
|
||||||
const Ajv = require('ajv')
|
const Ajv = require("ajv")
|
||||||
const crypto = require('crypto')
|
const crypto = require("crypto")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} Config
|
* @typedef {Object} Config
|
||||||
|
@ -54,11 +54,11 @@ const crypto = require('crypto')
|
||||||
*/
|
*/
|
||||||
const defaultConfig = {
|
const defaultConfig = {
|
||||||
listen: {
|
listen: {
|
||||||
ip: '0.0.0.0',
|
ip: "0.0.0.0",
|
||||||
port: 2222
|
port: 2222
|
||||||
},
|
},
|
||||||
http: {
|
http: {
|
||||||
origins: ['*:*']
|
origins: ["*:*"]
|
||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
name: null,
|
name: null,
|
||||||
|
@ -67,7 +67,7 @@ const defaultConfig = {
|
||||||
ssh: {
|
ssh: {
|
||||||
host: null,
|
host: null,
|
||||||
port: 22,
|
port: 22,
|
||||||
term: 'xterm-color',
|
term: "xterm-color",
|
||||||
readyTimeout: 20000,
|
readyTimeout: 20000,
|
||||||
keepaliveInterval: 120000,
|
keepaliveInterval: 120000,
|
||||||
keepaliveCountMax: 10
|
keepaliveCountMax: 10
|
||||||
|
@ -76,11 +76,11 @@ const defaultConfig = {
|
||||||
cursorBlink: true,
|
cursorBlink: true,
|
||||||
scrollback: 10000,
|
scrollback: 10000,
|
||||||
tabStopWidth: 8,
|
tabStopWidth: 8,
|
||||||
bellStyle: 'sound'
|
bellStyle: "sound"
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
text: null,
|
text: null,
|
||||||
background: 'green'
|
background: "green"
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
challengeButton: true,
|
challengeButton: true,
|
||||||
|
@ -89,28 +89,28 @@ const defaultConfig = {
|
||||||
},
|
},
|
||||||
algorithms: {
|
algorithms: {
|
||||||
kex: [
|
kex: [
|
||||||
'ecdh-sha2-nistp256',
|
"ecdh-sha2-nistp256",
|
||||||
'ecdh-sha2-nistp384',
|
"ecdh-sha2-nistp384",
|
||||||
'ecdh-sha2-nistp521',
|
"ecdh-sha2-nistp521",
|
||||||
'diffie-hellman-group-exchange-sha256',
|
"diffie-hellman-group-exchange-sha256",
|
||||||
'diffie-hellman-group14-sha1'
|
"diffie-hellman-group14-sha1"
|
||||||
],
|
],
|
||||||
cipher: [
|
cipher: [
|
||||||
'aes128-ctr',
|
"aes128-ctr",
|
||||||
'aes192-ctr',
|
"aes192-ctr",
|
||||||
'aes256-ctr',
|
"aes256-ctr",
|
||||||
'aes128-gcm',
|
"aes128-gcm",
|
||||||
'aes128-gcm@openssh.com',
|
"aes128-gcm@openssh.com",
|
||||||
'aes256-gcm',
|
"aes256-gcm",
|
||||||
'aes256-gcm@openssh.com',
|
"aes256-gcm@openssh.com",
|
||||||
'aes256-cbc'
|
"aes256-cbc"
|
||||||
],
|
],
|
||||||
hmac: ['hmac-sha2-256', 'hmac-sha2-512', 'hmac-sha1'],
|
hmac: ["hmac-sha2-256", "hmac-sha2-512", "hmac-sha1"],
|
||||||
compress: ['none', 'zlib@openssh.com', 'zlib']
|
compress: ["none", "zlib@openssh.com", "zlib"]
|
||||||
},
|
},
|
||||||
session: {
|
session: {
|
||||||
secret: generateSecureSecret(),
|
secret: generateSecureSecret(),
|
||||||
name: 'webssh2.sid'
|
name: "webssh2.sid"
|
||||||
},
|
},
|
||||||
serverlog: {
|
serverlog: {
|
||||||
client: false,
|
client: false,
|
||||||
|
@ -124,115 +124,134 @@ const defaultConfig = {
|
||||||
* Schema for validating the config
|
* Schema for validating the config
|
||||||
*/
|
*/
|
||||||
const configSchema = {
|
const configSchema = {
|
||||||
type: 'object',
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
listen: {
|
listen: {
|
||||||
type: 'object',
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
ip: { type: 'string', format: 'ipv4' },
|
ip: { type: "string", format: "ipv4" },
|
||||||
port: { type: 'integer', minimum: 1, maximum: 65535 }
|
port: { type: "integer", minimum: 1, maximum: 65535 }
|
||||||
},
|
},
|
||||||
required: ['ip', 'port']
|
required: ["ip", "port"]
|
||||||
},
|
},
|
||||||
http: {
|
http: {
|
||||||
type: 'object',
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
origins: {
|
origins: {
|
||||||
type: 'array',
|
type: "array",
|
||||||
items: { type: 'string' }
|
items: { type: "string" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
required: ['origins']
|
required: ["origins"]
|
||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
type: 'object',
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
name: { type: ['string', 'null'] },
|
name: { type: ["string", "null"] },
|
||||||
password: { type: ['string', 'null'] }
|
password: { type: ["string", "null"] }
|
||||||
},
|
},
|
||||||
required: ['name', 'password']
|
required: ["name", "password"]
|
||||||
},
|
},
|
||||||
ssh: {
|
ssh: {
|
||||||
type: 'object',
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
host: { type: ['string', 'null'] },
|
host: { type: ["string", "null"] },
|
||||||
port: { type: 'integer', minimum: 1, maximum: 65535 },
|
port: { type: "integer", minimum: 1, maximum: 65535 },
|
||||||
term: { type: 'string' },
|
term: { type: "string" },
|
||||||
readyTimeout: { type: 'integer' },
|
readyTimeout: { type: "integer" },
|
||||||
keepaliveInterval: { type: 'integer' },
|
keepaliveInterval: { type: "integer" },
|
||||||
keepaliveCountMax: { type: 'integer' }
|
keepaliveCountMax: { type: "integer" }
|
||||||
},
|
},
|
||||||
required: ['host', 'port', 'term', 'readyTimeout', 'keepaliveInterval', 'keepaliveCountMax']
|
required: [
|
||||||
|
"host",
|
||||||
|
"port",
|
||||||
|
"term",
|
||||||
|
"readyTimeout",
|
||||||
|
"keepaliveInterval",
|
||||||
|
"keepaliveCountMax"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
terminal: {
|
terminal: {
|
||||||
type: 'object',
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
cursorBlink: { type: 'boolean' },
|
cursorBlink: { type: "boolean" },
|
||||||
scrollback: { type: 'integer' },
|
scrollback: { type: "integer" },
|
||||||
tabStopWidth: { type: 'integer' },
|
tabStopWidth: { type: "integer" },
|
||||||
bellStyle: { type: 'string' }
|
bellStyle: { type: "string" }
|
||||||
},
|
},
|
||||||
required: ['cursorBlink', 'scrollback', 'tabStopWidth', 'bellStyle']
|
required: ["cursorBlink", "scrollback", "tabStopWidth", "bellStyle"]
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
type: 'object',
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
text: { type: ['string', 'null'] },
|
text: { type: ["string", "null"] },
|
||||||
background: { type: 'string' }
|
background: { type: "string" }
|
||||||
},
|
},
|
||||||
required: ['text', 'background']
|
required: ["text", "background"]
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
type: 'object',
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
challengeButton: { type: 'boolean' },
|
challengeButton: { type: "boolean" },
|
||||||
allowReauth: { type: 'boolean' },
|
allowReauth: { type: "boolean" },
|
||||||
allowReplay: { type: 'boolean' }
|
allowReplay: { type: "boolean" }
|
||||||
},
|
},
|
||||||
required: ['challengeButton', 'allowReauth', 'allowReplay']
|
required: ["challengeButton", "allowReauth", "allowReplay"]
|
||||||
},
|
},
|
||||||
algorithms: {
|
algorithms: {
|
||||||
type: 'object',
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
kex: {
|
kex: {
|
||||||
type: 'array',
|
type: "array",
|
||||||
items: { type: 'string' }
|
items: { type: "string" }
|
||||||
},
|
},
|
||||||
cipher: {
|
cipher: {
|
||||||
type: 'array',
|
type: "array",
|
||||||
items: { type: 'string' }
|
items: { type: "string" }
|
||||||
},
|
},
|
||||||
hmac: {
|
hmac: {
|
||||||
type: 'array',
|
type: "array",
|
||||||
items: { type: 'string' }
|
items: { type: "string" }
|
||||||
},
|
},
|
||||||
compress: {
|
compress: {
|
||||||
type: 'array',
|
type: "array",
|
||||||
items: { type: 'string' }
|
items: { type: "string" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
required: ['kex', 'cipher', 'hmac', 'compress']
|
required: ["kex", "cipher", "hmac", "compress"]
|
||||||
},
|
},
|
||||||
session: {
|
session: {
|
||||||
type: 'object',
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
secret: { type: 'string' },
|
secret: { type: "string" },
|
||||||
name: { type: 'string' }
|
name: { type: "string" }
|
||||||
},
|
},
|
||||||
required: ['secret', 'name']
|
required: ["secret", "name"]
|
||||||
},
|
},
|
||||||
serverlog: {
|
serverlog: {
|
||||||
type: 'object',
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
client: { type: 'boolean' },
|
client: { type: "boolean" },
|
||||||
server: { type: 'boolean' }
|
server: { type: "boolean" }
|
||||||
},
|
},
|
||||||
required: ['client', 'server']
|
required: ["client", "server"]
|
||||||
},
|
},
|
||||||
accesslog: { type: 'boolean' },
|
accesslog: { type: "boolean" },
|
||||||
verify: { type: 'boolean' }
|
verify: { type: "boolean" }
|
||||||
},
|
},
|
||||||
required: ['listen', 'http', 'user', 'ssh', 'terminal', 'header', 'options', 'algorithms', 'serverlog', 'accesslog', 'verify']
|
required: [
|
||||||
|
"listen",
|
||||||
|
"http",
|
||||||
|
"user",
|
||||||
|
"ssh",
|
||||||
|
"terminal",
|
||||||
|
"header",
|
||||||
|
"options",
|
||||||
|
"algorithms",
|
||||||
|
"serverlog",
|
||||||
|
"accesslog",
|
||||||
|
"verify"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -241,7 +260,7 @@ const configSchema = {
|
||||||
*/
|
*/
|
||||||
function getConfigPath() {
|
function getConfigPath() {
|
||||||
const nodeRoot = path.dirname(require.main.filename)
|
const nodeRoot = path.dirname(require.main.filename)
|
||||||
return path.join(nodeRoot, 'config.json')
|
return path.join(nodeRoot, "config.json")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -250,7 +269,7 @@ function getConfigPath() {
|
||||||
* @returns {Config} The configuration object
|
* @returns {Config} The configuration object
|
||||||
*/
|
*/
|
||||||
function readConfigFile(configPath) {
|
function readConfigFile(configPath) {
|
||||||
console.log('WebSSH2 service reading config from: ' + configPath)
|
console.log("WebSSH2 service reading config from: " + configPath)
|
||||||
return readConfig(configPath)
|
return readConfig(configPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,9 +283,11 @@ function validateConfig(config) {
|
||||||
const ajv = new Ajv()
|
const ajv = new Ajv()
|
||||||
const validate = ajv.compile(configSchema)
|
const validate = ajv.compile(configSchema)
|
||||||
const valid = validate(config)
|
const valid = validate(config)
|
||||||
console.log('WebSSH2 service validating config')
|
console.log("WebSSH2 service validating config")
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
throw new Error('Config validation error: ' + ajv.errorsText(validate.errors))
|
throw new Error(
|
||||||
|
"Config validation error: " + ajv.errorsText(validate.errors)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
@ -279,7 +300,7 @@ function validateConfig(config) {
|
||||||
function logError(message, error) {
|
function logError(message, error) {
|
||||||
console.error(message)
|
console.error(message)
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('ERROR:\n\n ' + error)
|
console.error("ERROR:\n\n " + error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,26 +314,29 @@ function loadConfig() {
|
||||||
try {
|
try {
|
||||||
if (fs.existsSync(configPath)) {
|
if (fs.existsSync(configPath)) {
|
||||||
const providedConfig = readConfigFile(configPath)
|
const providedConfig = readConfigFile(configPath)
|
||||||
|
|
||||||
// Deep merge the provided config with the default config
|
// Deep merge the provided config with the default config
|
||||||
const mergedConfig = deepMerge(JSON.parse(JSON.stringify(defaultConfig)), providedConfig)
|
const mergedConfig = deepMerge(
|
||||||
|
JSON.parse(JSON.stringify(defaultConfig)),
|
||||||
|
providedConfig
|
||||||
|
)
|
||||||
|
|
||||||
const validatedConfig = validateConfig(mergedConfig)
|
const validatedConfig = validateConfig(mergedConfig)
|
||||||
console.log('Merged and validated configuration')
|
console.log("Merged and validated configuration")
|
||||||
return validatedConfig
|
return validatedConfig
|
||||||
} else {
|
} else {
|
||||||
logError(
|
logError(
|
||||||
'\n\nERROR: Missing config.json for webssh. Using default config: ' +
|
"\n\nERROR: Missing config.json for webssh. Using default config: " +
|
||||||
JSON.stringify(defaultConfig) +
|
JSON.stringify(defaultConfig) +
|
||||||
'\n\n See config.json.sample for details\n\n'
|
"\n\n See config.json.sample for details\n\n"
|
||||||
)
|
)
|
||||||
return defaultConfig
|
return defaultConfig
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logError(
|
logError(
|
||||||
'\n\nERROR: Problem loading config.json for webssh. Using default config: ' +
|
"\n\nERROR: Problem loading config.json for webssh. Using default config: " +
|
||||||
JSON.stringify(defaultConfig) +
|
JSON.stringify(defaultConfig) +
|
||||||
'\n\n See config.json.sample for details\n\n',
|
"\n\n See config.json.sample for details\n\n",
|
||||||
err
|
err
|
||||||
)
|
)
|
||||||
return defaultConfig
|
return defaultConfig
|
||||||
|
@ -324,7 +348,7 @@ function loadConfig() {
|
||||||
* @returns {string} A random 32-byte hex string
|
* @returns {string} A random 32-byte hex string
|
||||||
*/
|
*/
|
||||||
function generateSecureSecret() {
|
function generateSecureSecret() {
|
||||||
return crypto.randomBytes(32).toString('hex')
|
return crypto.randomBytes(32).toString("hex")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,48 +1,65 @@
|
||||||
// server
|
// server
|
||||||
// app/connectionHandler.js
|
// app/connectionHandler.js
|
||||||
var path = require('path');
|
var path = require("path")
|
||||||
var fs = require('fs');
|
var fs = require("fs")
|
||||||
var extend = require('util')._extend;
|
var extend = require("util")._extend
|
||||||
|
|
||||||
function handleConnection(req, res, urlParams) {
|
function handleConnection(req, res, urlParams) {
|
||||||
urlParams = urlParams || {};
|
urlParams = urlParams || {}
|
||||||
|
|
||||||
// The path to the client directory of the webssh2 module.
|
|
||||||
var clientPath = path.resolve(__dirname, '..', 'node_modules', 'webssh2_client', 'client', 'public');
|
|
||||||
|
|
||||||
// Combine URL parameters, query parameters, and form data
|
// The path to the client directory of the webssh2 module.
|
||||||
var connectionParams = extend({}, urlParams);
|
var clientPath = path.resolve(
|
||||||
extend(connectionParams, req.query);
|
__dirname,
|
||||||
extend(connectionParams, req.body || {});
|
"..",
|
||||||
|
"node_modules",
|
||||||
// Inject configuration
|
"webssh2_client",
|
||||||
var config = {
|
"client",
|
||||||
socket: {
|
"public"
|
||||||
url: req.protocol + '://' + req.get('host'),
|
)
|
||||||
path: '/ssh/socket.io'
|
|
||||||
},
|
// Combine URL parameters, query parameters, and form data
|
||||||
ssh: {
|
var connectionParams = extend({}, urlParams)
|
||||||
host: urlParams.host || '',
|
extend(connectionParams, req.query)
|
||||||
port: urlParams.port || 22
|
extend(connectionParams, req.body || {})
|
||||||
},
|
|
||||||
autoConnect: !!req.session.sshCredentials
|
// Inject configuration
|
||||||
};
|
var config = {
|
||||||
|
socket: {
|
||||||
// Read the client.htm file
|
url: req.protocol + "://" + req.get("host"),
|
||||||
fs.readFile(path.join(clientPath, 'client.htm'), 'utf8', function(err, data) {
|
path: "/ssh/socket.io"
|
||||||
|
},
|
||||||
|
ssh: {
|
||||||
|
host: urlParams.host || "",
|
||||||
|
port: urlParams.port || 22
|
||||||
|
},
|
||||||
|
autoConnect: !!req.session.sshCredentials
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the client.htm file
|
||||||
|
fs.readFile(
|
||||||
|
path.join(clientPath, "client.htm"),
|
||||||
|
"utf8",
|
||||||
|
function (err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return res.status(500).send('Error loading client file');
|
return res.status(500).send("Error loading client file")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace relative paths with the correct path
|
// Replace relative paths with the correct path
|
||||||
var modifiedHtml = data.replace(/(src|href)="(?!http|\/\/)/g, '$1="/ssh/assets/');
|
var modifiedHtml = data.replace(
|
||||||
|
/(src|href)="(?!http|\/\/)/g,
|
||||||
|
'$1="/ssh/assets/'
|
||||||
|
)
|
||||||
|
|
||||||
// Inject the configuration into the HTML
|
// Inject the configuration into the HTML
|
||||||
modifiedHtml = modifiedHtml.replace('window.webssh2Config = null;', 'window.webssh2Config = ' + JSON.stringify(config) + ';');
|
modifiedHtml = modifiedHtml.replace(
|
||||||
|
"window.webssh2Config = null;",
|
||||||
|
"window.webssh2Config = " + JSON.stringify(config) + ";"
|
||||||
|
)
|
||||||
|
|
||||||
// Send the modified HTML
|
// Send the modified HTML
|
||||||
res.send(modifiedHtml);
|
res.send(modifiedHtml)
|
||||||
});
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = handleConnection;
|
module.exports = handleConnection
|
||||||
|
|
|
@ -1,34 +1,34 @@
|
||||||
// server
|
// server
|
||||||
// /app/routes.js
|
// /app/routes.js
|
||||||
const createDebug = require('debug')
|
const createDebug = require("debug")
|
||||||
const debug = createDebug('webssh2:routes')
|
const debug = createDebug("webssh2:routes")
|
||||||
const express = require('express');
|
const express = require("express")
|
||||||
const router = express.Router();
|
const router = express.Router()
|
||||||
const handleConnection = require('./connectionHandler');
|
const handleConnection = require("./connectionHandler")
|
||||||
const basicAuth = require('basic-auth');
|
const basicAuth = require("basic-auth")
|
||||||
|
|
||||||
function auth(req, res, next) {
|
function auth(req, res, next) {
|
||||||
debug('Authenticating user with HTTP Basic Auth');
|
debug("Authenticating user with HTTP Basic Auth")
|
||||||
var credentials = basicAuth(req);
|
var credentials = basicAuth(req)
|
||||||
if (!credentials) {
|
if (!credentials) {
|
||||||
res.setHeader('WWW-Authenticate', 'Basic realm="WebSSH2"');
|
res.setHeader("WWW-Authenticate", 'Basic realm="WebSSH2"')
|
||||||
return res.status(401).send('Authentication required.');
|
return res.status(401).send("Authentication required.")
|
||||||
}
|
}
|
||||||
// Store credentials in session
|
// Store credentials in session
|
||||||
req.session.sshCredentials = credentials;
|
req.session.sshCredentials = credentials
|
||||||
next();
|
next()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scenario 1: No auth required, uses websocket authentication instead
|
// Scenario 1: No auth required, uses websocket authentication instead
|
||||||
router.get('/', function(req, res) {
|
router.get("/", function (req, res) {
|
||||||
debug('Accessed /ssh route');
|
debug("Accessed /ssh route")
|
||||||
handleConnection(req, res);
|
handleConnection(req, res)
|
||||||
});
|
})
|
||||||
|
|
||||||
// Scenario 2: Auth required, uses HTTP Basic Auth
|
// Scenario 2: Auth required, uses HTTP Basic Auth
|
||||||
router.get('/host/:host', auth, function(req, res) {
|
router.get("/host/:host", auth, function (req, res) {
|
||||||
debug(`Accessed /ssh/host/${req.params.host} route`);
|
debug(`Accessed /ssh/host/${req.params.host} route`)
|
||||||
handleConnection(req, res, { host: req.params.host });
|
handleConnection(req, res, { host: req.params.host })
|
||||||
});
|
})
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router
|
||||||
|
|
230
app/socket.js
230
app/socket.js
|
@ -1,11 +1,11 @@
|
||||||
// server
|
// server
|
||||||
// app/socket.js
|
// app/socket.js
|
||||||
'use strict'
|
"use strict"
|
||||||
|
|
||||||
const createDebug = require('debug')
|
const createDebug = require("debug")
|
||||||
const { header } = require('./config')
|
const { header } = require("./config")
|
||||||
const debug = createDebug('webssh2:socket')
|
const debug = createDebug("webssh2:socket")
|
||||||
const SSH = require('ssh2').Client
|
const SSH = require("ssh2").Client
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles WebSocket connections for SSH
|
* Handles WebSocket connections for SSH
|
||||||
|
@ -13,7 +13,7 @@ const SSH = require('ssh2').Client
|
||||||
* @param {Object} config - The configuration object
|
* @param {Object} config - The configuration object
|
||||||
*/
|
*/
|
||||||
module.exports = function (io, config) {
|
module.exports = function (io, config) {
|
||||||
io.on('connection', (socket) => handleConnection(socket, config))
|
io.on("connection", (socket) => handleConnection(socket, config))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,20 +22,22 @@ module.exports = function (io, config) {
|
||||||
* @param {Object} config - The configuration object
|
* @param {Object} config - The configuration object
|
||||||
*/
|
*/
|
||||||
function handleConnection(socket, config) {
|
function handleConnection(socket, config) {
|
||||||
let conn = null;
|
let conn = null
|
||||||
let stream = null;
|
let stream = null
|
||||||
let authenticated = false;
|
let authenticated = false
|
||||||
let isConnectionClosed = false;
|
let isConnectionClosed = false
|
||||||
|
|
||||||
debug(`CONNECT: ${socket.id}, URL: ${socket.handshake.url}`);
|
debug(`CONNECT: ${socket.id}, URL: ${socket.handshake.url}`)
|
||||||
|
|
||||||
removeExistingListeners(socket)
|
removeExistingListeners(socket)
|
||||||
setupInitialSocketListeners(socket, config)
|
setupInitialSocketListeners(socket, config)
|
||||||
|
|
||||||
// Emit an event to the client to request authentication
|
// Emit an event to the client to request authentication
|
||||||
if (!authenticated) {
|
if (!authenticated) {
|
||||||
debug(`Requesting authentication for ${socket.id} and authenticated is ${authenticated}`);
|
debug(
|
||||||
socket.emit('authentication', { action: 'request_auth' });
|
`Requesting authentication for ${socket.id} and authenticated is ${authenticated}`
|
||||||
|
)
|
||||||
|
socket.emit("authentication", { action: "request_auth" })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,9 +45,11 @@ function handleConnection(socket, config) {
|
||||||
* @param {import('socket.io').Socket} socket - The Socket.IO socket
|
* @param {import('socket.io').Socket} socket - The Socket.IO socket
|
||||||
*/
|
*/
|
||||||
function removeExistingListeners(socket) {
|
function removeExistingListeners(socket) {
|
||||||
['authenticate', 'data', 'resize', 'disconnect', 'control'].forEach(event => {
|
;["authenticate", "data", "resize", "disconnect", "control"].forEach(
|
||||||
socket.removeAllListeners(event)
|
(event) => {
|
||||||
})
|
socket.removeAllListeners(event)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,41 +58,47 @@ function handleConnection(socket, config) {
|
||||||
* @param {Object} config - The configuration object
|
* @param {Object} config - The configuration object
|
||||||
*/
|
*/
|
||||||
function setupInitialSocketListeners(socket, config) {
|
function setupInitialSocketListeners(socket, config) {
|
||||||
socket.on('error', (error) => console.error(`Socket error for ${socket.id}:`, error));
|
socket.on("error", (error) =>
|
||||||
socket.on('authenticate', creds => handleAuthentication(socket, creds, config))
|
console.error(`Socket error for ${socket.id}:`, error)
|
||||||
socket.on('disconnect', (reason) => {
|
)
|
||||||
debug(`Client ${socket.id} disconnected. Reason: ${reason}`);
|
socket.on("authenticate", (creds) =>
|
||||||
debug('Socket state at disconnect:', socket.conn.transport.readyState);
|
handleAuthentication(socket, creds, config)
|
||||||
|
)
|
||||||
|
socket.on("disconnect", (reason) => {
|
||||||
|
debug(`Client ${socket.id} disconnected. Reason: ${reason}`)
|
||||||
|
debug("Socket state at disconnect:", socket.conn.transport.readyState)
|
||||||
if (conn) {
|
if (conn) {
|
||||||
conn.end();
|
conn.end()
|
||||||
conn = null;
|
conn = null
|
||||||
}
|
}
|
||||||
if (stream) {
|
if (stream) {
|
||||||
stream.end();
|
stream.end()
|
||||||
stream = null;
|
stream = null
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles authentication attempts
|
* Handles authentication attempts
|
||||||
* @param {import('socket.io').Socket} socket - The Socket.IO socket
|
* @param {import('socket.io').Socket} socket - The Socket.IO socket
|
||||||
*
|
*
|
||||||
* @param {Credentials} creds - The credentials for authentication
|
* @param {Credentials} creds - The credentials for authentication
|
||||||
* @param {Object} config - The configuration object
|
* @param {Object} config - The configuration object
|
||||||
*/
|
*/
|
||||||
function handleAuthentication(socket, creds, config) {
|
function handleAuthentication(socket, creds, config) {
|
||||||
debug(`AUTHENTICATE: ${socket.id}, Host: ${creds.host}`);
|
debug(`AUTHENTICATE: ${socket.id}, Host: ${creds.host}`)
|
||||||
|
|
||||||
if (isValidCredentials(creds)) {
|
|
||||||
debug(`CREDENTIALS VALID: ${socket.id}, Host: ${creds.host}`);
|
|
||||||
initializeConnection(socket, creds, config);
|
|
||||||
} else {
|
|
||||||
debug(`CREDENTIALS INVALID: ${socket.id}, Host: ${creds.host}`);
|
|
||||||
socket.emit('authentication', { success: false, message: 'Invalid credentials format' });
|
|
||||||
|
|
||||||
|
if (isValidCredentials(creds)) {
|
||||||
|
debug(`CREDENTIALS VALID: ${socket.id}, Host: ${creds.host}`)
|
||||||
|
initializeConnection(socket, creds, config)
|
||||||
|
} else {
|
||||||
|
debug(`CREDENTIALS INVALID: ${socket.id}, Host: ${creds.host}`)
|
||||||
|
socket.emit("authentication", {
|
||||||
|
success: false,
|
||||||
|
message: "Invalid credentials format"
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes an SSH connection
|
* Initializes an SSH connection
|
||||||
|
@ -97,42 +107,51 @@ function handleAuthentication(socket, creds, config) {
|
||||||
* @param {Object} config - The configuration object
|
* @param {Object} config - The configuration object
|
||||||
*/
|
*/
|
||||||
function initializeConnection(socket, creds, config) {
|
function initializeConnection(socket, creds, config) {
|
||||||
debug(`INITIALIZING SSH CONNECTION: ${socket.id}, Host: ${creds.host}`);
|
debug(`INITIALIZING SSH CONNECTION: ${socket.id}, Host: ${creds.host}`)
|
||||||
if (conn) {
|
if (conn) {
|
||||||
conn.end()
|
conn.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
conn = new SSH()
|
conn = new SSH()
|
||||||
|
|
||||||
conn.on('ready', () => {
|
conn.on("ready", () => {
|
||||||
authenticated = true;
|
authenticated = true
|
||||||
debug(`SSH CONNECTION READY: ${socket.id}, Host: ${creds.host}`);
|
debug(`SSH CONNECTION READY: ${socket.id}, Host: ${creds.host}`)
|
||||||
socket.emit('authentication', { action: 'auth_result', success: true });
|
socket.emit("authentication", { action: "auth_result", success: true })
|
||||||
|
|
||||||
// Emit consolidated permissions
|
// Emit consolidated permissions
|
||||||
socket.emit('permissions', {
|
socket.emit("permissions", {
|
||||||
allowReplay: config.options.allowReplay || false,
|
allowReplay: config.options.allowReplay || false,
|
||||||
allowReauth: config.options.allowReauth || false
|
allowReauth: config.options.allowReauth || false
|
||||||
});
|
})
|
||||||
|
|
||||||
if (config.header && config.header.text !== null) {
|
if (config.header && config.header.text !== null) {
|
||||||
debug('header:', config.header)
|
debug("header:", config.header)
|
||||||
socket.emit('updateUI', { header: config.header } || { header: { text: '', background: '' } })
|
socket.emit(
|
||||||
|
"updateUI",
|
||||||
|
{ header: config.header } || { header: { text: "", background: "" } }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
setupSSHListeners(socket, creds)
|
setupSSHListeners(socket, creds)
|
||||||
initializeShell(socket, creds)
|
initializeShell(socket, creds)
|
||||||
})
|
})
|
||||||
|
|
||||||
conn.on('error', err => {
|
conn.on("error", (err) => {
|
||||||
console.error(`SSH CONNECTION ERROR: ${socket.id}, Host: ${creds.host}, Error: ${err.message}`);
|
console.error(
|
||||||
if (err.level === 'client-authentication') {
|
`SSH CONNECTION ERROR: ${socket.id}, Host: ${creds.host}, Error: ${err.message}`
|
||||||
socket.emit('authentication', { action: 'auth_result', success: false, message: 'Authentication failed' })
|
)
|
||||||
|
if (err.level === "client-authentication") {
|
||||||
|
socket.emit("authentication", {
|
||||||
|
action: "auth_result",
|
||||||
|
success: false,
|
||||||
|
message: "Authentication failed"
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
handleError(socket, 'SSH CONNECTION ERROR', err)
|
handleError(socket, "SSH CONNECTION ERROR", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
conn.connect(getSSHConfig(creds, config))
|
conn.connect(getSSHConfig(creds, config))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,13 +161,15 @@ function handleAuthentication(socket, creds, config) {
|
||||||
* @param {Credentials} creds - The user credentials
|
* @param {Credentials} creds - The user credentials
|
||||||
*/
|
*/
|
||||||
function setupSSHListeners(socket, creds) {
|
function setupSSHListeners(socket, creds) {
|
||||||
conn.on('banner', data => handleBanner(socket, data))
|
conn.on("banner", (data) => handleBanner(socket, data))
|
||||||
conn.on('end', () => handleSSHEnd(socket))
|
conn.on("end", () => handleSSHEnd(socket))
|
||||||
conn.on('close', () => handleSSHClose(socket))
|
conn.on("close", () => handleSSHClose(socket))
|
||||||
|
|
||||||
socket.on('data', data => handleData(socket, stream, data))
|
socket.on("data", (data) => handleData(socket, stream, data))
|
||||||
socket.on('resize', data => handleResize(stream, data))
|
socket.on("resize", (data) => handleResize(stream, data))
|
||||||
socket.on('control', controlData => handleControl(socket, stream, creds, controlData, config))
|
socket.on("control", (controlData) =>
|
||||||
|
handleControl(socket, stream, creds, controlData, config)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -165,17 +186,18 @@ function handleAuthentication(socket, creds, config) {
|
||||||
},
|
},
|
||||||
(err, str) => {
|
(err, str) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return handleError(socket, 'EXEC ERROR', err)
|
return handleError(socket, "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) => {
|
stream.on("close", (code, signal) => {
|
||||||
handleError(socket, 'STREAM CLOSE', {
|
handleError(socket, "STREAM CLOSE", {
|
||||||
message: code || signal ? `CODE: ${code} SIGNAL: ${signal}` : undefined
|
message:
|
||||||
|
code || signal ? `CODE: ${code} SIGNAL: ${signal}` : undefined
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
stream.stderr.on('data', data => debug('STDERR: ' + data))
|
stream.stderr.on("data", (data) => debug("STDERR: " + data))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -186,7 +208,7 @@ function handleAuthentication(socket, creds, config) {
|
||||||
* @param {string} data - The banner data
|
* @param {string} data - The banner data
|
||||||
*/
|
*/
|
||||||
function handleBanner(socket, data) {
|
function handleBanner(socket, data) {
|
||||||
socket.emit('data', data.replace(/\r?\n/g, '\r\n'))
|
socket.emit("data", data.replace(/\r?\n/g, "\r\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -221,7 +243,7 @@ function handleAuthentication(socket, creds, config) {
|
||||||
conn.end()
|
conn.end()
|
||||||
conn = null
|
conn = null
|
||||||
}
|
}
|
||||||
socket.emit('connection_closed')
|
socket.emit("connection_closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -245,12 +267,12 @@ function handleAuthentication(socket, creds, config) {
|
||||||
try {
|
try {
|
||||||
stream.write(data)
|
stream.write(data)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
debug('Error writing to stream:', error.message)
|
debug("Error writing to stream:", error.message)
|
||||||
handleConnectionClose(socket)
|
handleConnectionClose(socket)
|
||||||
}
|
}
|
||||||
} else if (isConnectionClosed) {
|
} else if (isConnectionClosed) {
|
||||||
debug('Attempted to write to closed connection')
|
debug("Attempted to write to closed connection")
|
||||||
socket.emit('connection_closed')
|
socket.emit("connection_closed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,15 +298,15 @@ function handleAuthentication(socket, creds, config) {
|
||||||
* @param {Object} config - The configuration object
|
* @param {Object} config - The configuration object
|
||||||
*/
|
*/
|
||||||
function handleControl(socket, stream, credentials, controlData, config) {
|
function handleControl(socket, stream, credentials, controlData, config) {
|
||||||
debug(`Received control data: ${controlData}`);
|
debug(`Received control data: ${controlData}`)
|
||||||
|
|
||||||
if (controlData === 'replayCredentials' && stream && credentials) {
|
if (controlData === "replayCredentials" && stream && credentials) {
|
||||||
replayCredentials(socket, stream, credentials, config);
|
replayCredentials(socket, stream, credentials, config)
|
||||||
} else if (controlData === 'reauth' && config.options.allowReauth) {
|
} else if (controlData === "reauth" && config.options.allowReauth) {
|
||||||
handleReauth(socket);
|
handleReauth(socket)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replays the user credentials to the SSH stream
|
* Replays the user credentials to the SSH stream
|
||||||
* @param {import('socket.io').Socket} socket - The Socket.IO socket
|
* @param {import('socket.io').Socket} socket - The Socket.IO socket
|
||||||
|
@ -293,13 +315,13 @@ function handleAuthentication(socket, creds, config) {
|
||||||
* @param {Object} config - The configuration object
|
* @param {Object} config - The configuration object
|
||||||
*/
|
*/
|
||||||
function replayCredentials(socket, stream, credentials, config) {
|
function replayCredentials(socket, stream, credentials, config) {
|
||||||
let allowReplay = config.options.allowReplay || false;
|
let allowReplay = config.options.allowReplay || false
|
||||||
|
|
||||||
if (allowReplay) {
|
if (allowReplay) {
|
||||||
debug(`Replaying credentials for ${socket.id}`);
|
debug(`Replaying credentials for ${socket.id}`)
|
||||||
stream.write(credentials.password + '\n');
|
stream.write(credentials.password + "\n")
|
||||||
} else {
|
} else {
|
||||||
debug(`Credential replay not allowed for ${socket.id}`);
|
debug(`Credential replay not allowed for ${socket.id}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,9 +330,9 @@ function handleAuthentication(socket, creds, config) {
|
||||||
* @param {import('socket.io').Socket} socket - The Socket.IO socket
|
* @param {import('socket.io').Socket} socket - The Socket.IO socket
|
||||||
*/
|
*/
|
||||||
function handleReauth(socket) {
|
function handleReauth(socket) {
|
||||||
debug(`Reauthentication requested for ${socket.id}`);
|
debug(`Reauthentication requested for ${socket.id}`)
|
||||||
socket.emit('authentication', { action: 'reauth' });
|
socket.emit("authentication", { action: "reauth" })
|
||||||
handleConnectionClose(socket);
|
handleConnectionClose(socket)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -320,9 +342,9 @@ function handleAuthentication(socket, creds, config) {
|
||||||
* @param {Error} [err] - The error object
|
* @param {Error} [err] - The error object
|
||||||
*/
|
*/
|
||||||
function handleError(socket, context, err) {
|
function handleError(socket, context, err) {
|
||||||
const errorMessage = err ? `: ${err.message}` : ''
|
const errorMessage = err ? `: ${err.message}` : ""
|
||||||
debug(`WebSSH2 error: ${context}${errorMessage}`)
|
debug(`WebSSH2 error: ${context}${errorMessage}`)
|
||||||
socket.emit('ssherror', `SSH ${context}${errorMessage}`)
|
socket.emit("ssherror", `SSH ${context}${errorMessage}`)
|
||||||
handleConnectionClose(socket)
|
handleConnectionClose(socket)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,11 +355,13 @@ function handleAuthentication(socket, creds, config) {
|
||||||
*/
|
*/
|
||||||
function isValidCredentials(credentials) {
|
function isValidCredentials(credentials) {
|
||||||
// Basic format validation
|
// Basic format validation
|
||||||
return credentials &&
|
return (
|
||||||
typeof credentials.username === 'string' &&
|
credentials &&
|
||||||
typeof credentials.password === 'string' &&
|
typeof credentials.username === "string" &&
|
||||||
typeof credentials.host === 'string' &&
|
typeof credentials.password === "string" &&
|
||||||
typeof credentials.port === 'number'
|
typeof credentials.host === "string" &&
|
||||||
|
typeof credentials.port === "number"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -357,7 +381,7 @@ function handleAuthentication(socket, creds, config) {
|
||||||
readyTimeout: credentials.readyTimeout,
|
readyTimeout: credentials.readyTimeout,
|
||||||
keepaliveInterval: credentials.keepaliveInterval,
|
keepaliveInterval: credentials.keepaliveInterval,
|
||||||
keepaliveCountMax: credentials.keepaliveCountMax,
|
keepaliveCountMax: credentials.keepaliveCountMax,
|
||||||
debug: createDebug('webssh2:ssh')
|
debug: createDebug("webssh2:ssh")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
6
index.js
6
index.js
|
@ -1,4 +1,4 @@
|
||||||
'use strict'
|
"use strict"
|
||||||
// server
|
// server
|
||||||
// index.js
|
// index.js
|
||||||
/**
|
/**
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
* Bill Church - https://github.com/billchurch/WebSSH2 - May 2017
|
* Bill Church - https://github.com/billchurch/WebSSH2 - May 2017
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { startServer, config } = require('./app/app')
|
const { startServer, config } = require("./app/app")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main function to start the application
|
* Main function to start the application
|
||||||
|
@ -23,4 +23,4 @@ main()
|
||||||
// For testing purposes, export the functions
|
// For testing purposes, export the functions
|
||||||
module.exports = {
|
module.exports = {
|
||||||
startServer
|
startServer
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue