2016-09-20 03:28:50 +00:00
|
|
|
/* jslint node: true */
|
|
|
|
'use strict';
|
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
// ENiGMA½
|
2022-06-05 20:04:25 +00:00
|
|
|
const Config = require('../../config.js').get;
|
|
|
|
const baseClient = require('../../client.js');
|
|
|
|
const Log = require('../../logger.js').log;
|
2018-06-23 03:26:46 +00:00
|
|
|
const LoginServerModule = require('../../login_server_module.js');
|
2022-06-05 20:04:25 +00:00
|
|
|
const userLogin = require('../../user_login.js').userLogin;
|
|
|
|
const enigVersion = require('../../../package.json').version;
|
|
|
|
const theme = require('../../theme.js');
|
|
|
|
const stringFormat = require('../../string_format.js');
|
|
|
|
const { Errors, ErrorReasons } = require('../../enig_error.js');
|
|
|
|
const User = require('../../user.js');
|
|
|
|
const UserProps = require('../../user_property.js');
|
2018-06-23 03:26:46 +00:00
|
|
|
|
|
|
|
// deps
|
2022-06-05 20:04:25 +00:00
|
|
|
const ssh2 = require('ssh2');
|
|
|
|
const fs = require('graceful-fs');
|
|
|
|
const util = require('util');
|
|
|
|
const _ = require('lodash');
|
|
|
|
const assert = require('assert');
|
|
|
|
|
|
|
|
const ModuleInfo = (exports.moduleInfo = {
|
|
|
|
name: 'SSH',
|
|
|
|
desc: 'SSH Server',
|
|
|
|
author: 'NuSkooler',
|
|
|
|
isSecure: true,
|
|
|
|
packageName: 'codes.l33t.enigma.ssh.server',
|
|
|
|
});
|
2016-09-20 03:28:50 +00:00
|
|
|
|
|
|
|
function SSHClient(clientConn) {
|
2018-06-22 05:15:04 +00:00
|
|
|
baseClient.Client.apply(this, arguments);
|
|
|
|
|
|
|
|
//
|
2018-06-23 03:26:46 +00:00
|
|
|
// WARNING: Until we have emit 'ready', self.input, and self.output and
|
|
|
|
// not yet defined!
|
2018-06-22 05:15:04 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
const self = this;
|
|
|
|
|
|
|
|
clientConn.on('authentication', function authAttempt(ctx) {
|
2022-06-05 20:04:25 +00:00
|
|
|
const username = ctx.username || '';
|
|
|
|
const config = Config();
|
|
|
|
self.isNewUser = (config.users.newUserNames || []).indexOf(username) > -1;
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
self.log.trace(
|
|
|
|
{ method: ctx.method, username: username, newUser: self.isNewUser },
|
|
|
|
'SSH authentication attempt'
|
|
|
|
);
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
const safeContextReject = param => {
|
2018-12-24 22:14:37 +00:00
|
|
|
try {
|
|
|
|
return ctx.reject(param);
|
2022-06-05 20:04:25 +00:00
|
|
|
} catch (e) {
|
2018-12-24 22:14:37 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-12-25 07:18:04 +00:00
|
|
|
const terminateConnection = () => {
|
2018-12-24 22:14:37 +00:00
|
|
|
safeContextReject();
|
2018-06-22 05:15:04 +00:00
|
|
|
return clientConn.end();
|
2018-12-25 07:18:04 +00:00
|
|
|
};
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2018-12-25 07:18:04 +00:00
|
|
|
// slow version to thwart brute force attacks
|
|
|
|
const slowTerminateConnection = () => {
|
2022-06-05 20:04:25 +00:00
|
|
|
setTimeout(() => {
|
2018-12-25 07:18:04 +00:00
|
|
|
return terminateConnection();
|
|
|
|
}, 2000);
|
|
|
|
};
|
|
|
|
|
|
|
|
const promptAndTerm = (msg, method = 'standard') => {
|
2022-06-05 20:04:25 +00:00
|
|
|
if ('keyboard-interactive' === ctx.method) {
|
2018-11-23 06:07:37 +00:00
|
|
|
ctx.prompt(msg);
|
|
|
|
}
|
2018-12-25 07:18:04 +00:00
|
|
|
return 'slow' === method ? slowTerminateConnection() : terminateConnection();
|
|
|
|
};
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
const accountAlreadyLoggedIn = username => {
|
|
|
|
return promptAndTerm(
|
|
|
|
`${username} is already connected to the system. Terminating connection.\n(Press any key to continue)`
|
|
|
|
);
|
2018-12-25 07:18:04 +00:00
|
|
|
};
|
2018-11-23 06:07:37 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
const accountDisabled = username => {
|
2018-11-23 06:07:37 +00:00
|
|
|
return promptAndTerm(`${username} is disabled.\n(Press any key to continue)`);
|
2018-12-25 07:18:04 +00:00
|
|
|
};
|
2018-11-23 06:07:37 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
const accountInactive = username => {
|
|
|
|
return promptAndTerm(
|
|
|
|
`${username} is waiting for +op activation.\n(Press any key to continue)`
|
|
|
|
);
|
2018-12-25 07:18:04 +00:00
|
|
|
};
|
2018-11-23 06:07:37 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
const accountLocked = username => {
|
|
|
|
return promptAndTerm(
|
|
|
|
`${username} is locked.\n(Press any key to continue)`,
|
|
|
|
'slow'
|
|
|
|
);
|
2018-12-25 07:18:04 +00:00
|
|
|
};
|
2018-11-23 06:07:37 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
const isSpecialHandleError = err => {
|
|
|
|
return [
|
|
|
|
ErrorReasons.AlreadyLoggedIn,
|
|
|
|
ErrorReasons.Disabled,
|
|
|
|
ErrorReasons.Inactive,
|
|
|
|
ErrorReasons.Locked,
|
|
|
|
].includes(err.reasonCode);
|
2018-12-25 07:18:04 +00:00
|
|
|
};
|
2018-11-23 06:07:37 +00:00
|
|
|
|
2018-12-25 07:18:04 +00:00
|
|
|
const handleSpecialError = (err, username) => {
|
2022-06-05 20:04:25 +00:00
|
|
|
switch (err.reasonCode) {
|
|
|
|
case ErrorReasons.AlreadyLoggedIn:
|
|
|
|
return accountAlreadyLoggedIn(username);
|
|
|
|
case ErrorReasons.Inactive:
|
|
|
|
return accountInactive(username);
|
|
|
|
case ErrorReasons.Disabled:
|
|
|
|
return accountDisabled(username);
|
|
|
|
case ErrorReasons.Locked:
|
|
|
|
return accountLocked(username);
|
|
|
|
default:
|
|
|
|
return terminateConnection();
|
2018-11-23 06:07:37 +00:00
|
|
|
}
|
2018-12-25 07:18:04 +00:00
|
|
|
};
|
2018-11-23 06:07:37 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
const authWithPasswordOrPubKey = authType => {
|
|
|
|
if (
|
|
|
|
User.AuthFactor1Types.SSHPubKey !== authType ||
|
|
|
|
!self.user.isAuthenticated() ||
|
|
|
|
!ctx.signature
|
|
|
|
) {
|
2019-02-21 06:55:09 +00:00
|
|
|
// step 1: login/auth using PubKey
|
2022-06-05 20:04:25 +00:00
|
|
|
userLogin(self, ctx.username, ctx.password, { authType, ctx }, err => {
|
|
|
|
if (err) {
|
|
|
|
if (isSpecialHandleError(err)) {
|
2019-02-21 06:55:09 +00:00
|
|
|
return handleSpecialError(err, username);
|
|
|
|
}
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (Errors.BadLogin().code === err.code) {
|
2019-02-21 06:55:09 +00:00
|
|
|
return slowTerminateConnection();
|
|
|
|
}
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2019-02-21 06:55:09 +00:00
|
|
|
return safeContextReject(SSHClient.ValidAuthMethods);
|
2018-12-24 22:32:38 +00:00
|
|
|
}
|
|
|
|
|
2019-02-21 06:55:09 +00:00
|
|
|
ctx.accept();
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// step 2: verify signature
|
2022-06-05 20:04:25 +00:00
|
|
|
const pubKeyActual = ssh2.utils.parseKey(
|
|
|
|
self.user.getProperty(UserProps.AuthPubKey)
|
|
|
|
);
|
|
|
|
if (!pubKeyActual || !pubKeyActual.verify(ctx.blob, ctx.signature)) {
|
2019-02-21 06:55:09 +00:00
|
|
|
return slowTerminateConnection();
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
2019-02-21 06:55:09 +00:00
|
|
|
return ctx.accept();
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
2019-02-21 06:55:09 +00:00
|
|
|
};
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2019-02-21 06:55:09 +00:00
|
|
|
const authKeyboardInteractive = () => {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (0 === username.length) {
|
2018-12-24 22:14:37 +00:00
|
|
|
return safeContextReject();
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
const interactivePrompt = {
|
|
|
|
prompt: `${ctx.username}'s password: `,
|
|
|
|
echo: false,
|
|
|
|
};
|
2018-06-22 05:15:04 +00:00
|
|
|
|
|
|
|
ctx.prompt(interactivePrompt, function retryPrompt(answers) {
|
2022-06-05 20:04:25 +00:00
|
|
|
userLogin(self, username, answers[0] || '', err => {
|
|
|
|
if (err) {
|
|
|
|
if (isSpecialHandleError(err)) {
|
2018-11-23 06:07:37 +00:00
|
|
|
return handleSpecialError(err, username);
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (Errors.BadLogin().code === err.code) {
|
2018-12-25 07:18:04 +00:00
|
|
|
return slowTerminateConnection();
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const artOpts = {
|
2022-06-05 20:04:25 +00:00
|
|
|
client: self,
|
|
|
|
name: 'SSHPMPT.ASC',
|
|
|
|
readSauce: false,
|
2018-06-22 05:15:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
theme.getThemeArt(artOpts, (err, artInfo) => {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (err) {
|
2018-06-22 05:15:04 +00:00
|
|
|
interactivePrompt.prompt = `Access denied\n${ctx.username}'s password: `;
|
|
|
|
} else {
|
2022-06-05 20:04:25 +00:00
|
|
|
const newUserNameList =
|
|
|
|
_.has(config, 'users.newUserNames') &&
|
|
|
|
config.users.newUserNames.length > 0
|
|
|
|
? config.users.newUserNames
|
|
|
|
.map(newName => '"' + newName + '"')
|
|
|
|
.join(', ')
|
|
|
|
: '(No new user names enabled!)';
|
|
|
|
|
|
|
|
interactivePrompt.prompt = `Access denied\n${stringFormat(
|
|
|
|
artInfo.data,
|
|
|
|
{ newUserNames: newUserNameList }
|
|
|
|
)}\n${ctx.username}'s password:`;
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
return ctx.prompt(interactivePrompt, retryPrompt);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
ctx.accept();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2019-02-21 06:55:09 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// If the system is open and |isNewUser| is true, the login
|
|
|
|
// sequence is hijacked in order to start the application process.
|
|
|
|
//
|
2022-06-05 20:04:25 +00:00
|
|
|
if (false === config.general.closedSystem && self.isNewUser) {
|
2019-02-21 06:55:09 +00:00
|
|
|
return ctx.accept();
|
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
switch (ctx.method) {
|
|
|
|
case 'password':
|
2019-02-23 05:51:12 +00:00
|
|
|
return authWithPasswordOrPubKey(User.AuthFactor1Types.Password);
|
2022-06-05 20:04:25 +00:00
|
|
|
//return authWithPassword();
|
2019-02-21 06:55:09 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
case 'publickey':
|
2019-04-10 02:07:19 +00:00
|
|
|
return authWithPasswordOrPubKey(User.AuthFactor1Types.SSHPubKey);
|
2022-06-05 20:04:25 +00:00
|
|
|
//return authWithPubKey();
|
2019-02-21 06:55:09 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
case 'keyboard-interactive':
|
2019-02-21 06:55:09 +00:00
|
|
|
return authKeyboardInteractive();
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
default:
|
2019-02-21 06:55:09 +00:00
|
|
|
return safeContextReject(SSHClient.ValidAuthMethods);
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
this.dataHandler = function (data) {
|
2018-06-22 05:15:04 +00:00
|
|
|
self.emit('data', data);
|
|
|
|
};
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
this.updateTermInfo = function (info) {
|
2018-06-22 05:15:04 +00:00
|
|
|
//
|
2018-06-23 03:26:46 +00:00
|
|
|
// From ssh2 docs:
|
|
|
|
// "rows and cols override width and height when rows and cols are non-zero."
|
2018-06-22 05:15:04 +00:00
|
|
|
//
|
|
|
|
let termHeight;
|
|
|
|
let termWidth;
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (info.rows > 0 && info.cols > 0) {
|
|
|
|
termHeight = info.rows;
|
|
|
|
termWidth = info.cols;
|
|
|
|
} else if (info.width > 0 && info.height > 0) {
|
|
|
|
termHeight = info.height;
|
|
|
|
termWidth = info.width;
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
assert(_.isObject(self.term));
|
|
|
|
|
|
|
|
//
|
2018-06-23 03:26:46 +00:00
|
|
|
// Note that if we fail here, connect.js attempts some non-standard
|
|
|
|
// queries/etc., and ultimately will default to 80x24 if all else fails
|
2018-06-22 05:15:04 +00:00
|
|
|
//
|
2022-06-05 20:04:25 +00:00
|
|
|
if (termHeight > 0 && termWidth > 0) {
|
2018-06-22 05:15:04 +00:00
|
|
|
self.term.termHeight = termHeight;
|
2018-06-23 03:26:46 +00:00
|
|
|
self.term.termWidth = termWidth;
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (
|
|
|
|
_.isString(info.term) &&
|
|
|
|
info.term.length > 0 &&
|
|
|
|
'unknown' === self.term.termType
|
|
|
|
) {
|
2018-06-22 05:15:04 +00:00
|
|
|
self.setTermType(info.term);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
clientConn.once('ready', function clientReady() {
|
|
|
|
self.log.info('SSH authentication success');
|
|
|
|
|
|
|
|
clientConn.on('session', accept => {
|
|
|
|
const session = accept();
|
|
|
|
|
|
|
|
session.on('pty', function pty(accept, reject, info) {
|
|
|
|
self.log.debug(info, 'SSH pty event');
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (_.isFunction(accept)) {
|
2018-06-22 05:15:04 +00:00
|
|
|
accept();
|
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (self.input) {
|
|
|
|
// do we have I/O?
|
2018-06-22 05:15:04 +00:00
|
|
|
self.updateTermInfo(info);
|
|
|
|
} else {
|
|
|
|
self.cachedTermInfo = info;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-01-03 02:52:15 +00:00
|
|
|
session.on('env', (accept, reject, info) => {
|
|
|
|
self.log.debug(info, 'SSH env event');
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (_.isFunction(accept)) {
|
2019-01-03 02:52:15 +00:00
|
|
|
accept();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
session.on('shell', accept => {
|
|
|
|
self.log.debug('SSH shell event');
|
|
|
|
|
|
|
|
const channel = accept();
|
|
|
|
|
|
|
|
self.setInputOutput(channel.stdin, channel.stdout);
|
|
|
|
|
|
|
|
channel.stdin.on('data', self.dataHandler);
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (self.cachedTermInfo) {
|
2018-06-22 05:15:04 +00:00
|
|
|
self.updateTermInfo(self.cachedTermInfo);
|
|
|
|
delete self.cachedTermInfo;
|
|
|
|
}
|
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
// we're ready!
|
2022-06-05 20:04:25 +00:00
|
|
|
const firstMenu = self.isNewUser
|
|
|
|
? Config().loginServers.ssh.firstMenuNewUser
|
|
|
|
: Config().loginServers.ssh.firstMenu;
|
|
|
|
self.emit('ready', { firstMenu: firstMenu });
|
2018-06-22 05:15:04 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
session.on('window-change', (accept, reject, info) => {
|
|
|
|
self.log.debug(info, 'SSH window-change event');
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (self.input) {
|
2018-06-22 05:15:04 +00:00
|
|
|
self.updateTermInfo(info);
|
|
|
|
} else {
|
|
|
|
self.cachedTermInfo = info;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2019-02-02 17:20:22 +00:00
|
|
|
clientConn.once('end', () => {
|
2022-06-05 20:04:25 +00:00
|
|
|
return self.emit('end'); // remove client connection/tracking
|
2018-06-22 05:15:04 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
clientConn.on('error', err => {
|
2022-06-05 20:04:25 +00:00
|
|
|
self.log.warn({ error: err.message, code: err.code }, 'SSH connection error');
|
2018-06-22 05:15:04 +00:00
|
|
|
});
|
2019-02-02 17:20:22 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
this.disconnect = function () {
|
2019-02-02 17:20:22 +00:00
|
|
|
return clientConn.end();
|
|
|
|
};
|
2016-09-20 03:28:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
util.inherits(SSHClient, baseClient.Client);
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
SSHClient.ValidAuthMethods = ['password', 'keyboard-interactive', 'publickey'];
|
2016-09-20 03:28:50 +00:00
|
|
|
|
2016-10-25 03:49:45 +00:00
|
|
|
exports.getModule = class SSHServerModule extends LoginServerModule {
|
2018-06-22 05:15:04 +00:00
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
}
|
|
|
|
|
2018-12-27 09:19:26 +00:00
|
|
|
createServer(cb) {
|
2018-06-22 05:15:04 +00:00
|
|
|
const config = Config();
|
2022-06-05 20:04:25 +00:00
|
|
|
if (true != config.loginServers.ssh.enabled) {
|
2018-12-27 09:19:26 +00:00
|
|
|
return cb(null);
|
2018-11-13 05:05:36 +00:00
|
|
|
}
|
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
const serverConf = {
|
2022-06-05 20:04:25 +00:00
|
|
|
hostKeys: [
|
2018-06-22 05:15:04 +00:00
|
|
|
{
|
2022-06-05 20:04:25 +00:00
|
|
|
key: fs.readFileSync(config.loginServers.ssh.privateKeyPem),
|
|
|
|
passphrase: config.loginServers.ssh.privateKeyPass,
|
|
|
|
},
|
2018-06-22 05:15:04 +00:00
|
|
|
],
|
2022-06-05 20:04:25 +00:00
|
|
|
ident: 'enigma-bbs-' + enigVersion + '-srv',
|
2018-06-22 05:15:04 +00:00
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
// Note that sending 'banner' breaks at least EtherTerm!
|
2018-11-05 02:29:51 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
debug: sshDebugLine => {
|
|
|
|
if (true === config.loginServers.ssh.traceConnections) {
|
2018-06-22 05:15:04 +00:00
|
|
|
Log.trace(`SSH: ${sshDebugLine}`);
|
|
|
|
}
|
|
|
|
},
|
2022-06-05 20:04:25 +00:00
|
|
|
algorithms: config.loginServers.ssh.algorithms,
|
2018-06-22 05:15:04 +00:00
|
|
|
};
|
|
|
|
|
2019-01-30 03:36:45 +00:00
|
|
|
//
|
|
|
|
// This is a terrible hack, and we should not have to do it;
|
|
|
|
// However, as of this writing, NetRunner and SyncTERM both
|
|
|
|
// fail to respond to OpenSSH keep-alive pings (keepalive@openssh.com)
|
|
|
|
//
|
2022-04-08 22:41:22 +00:00
|
|
|
// See also #399
|
|
|
|
//
|
|
|
|
ssh2.Server.KEEPALIVE_CLIENT_INTERVAL = 0;
|
2019-01-30 03:36:45 +00:00
|
|
|
|
2022-04-06 16:01:21 +00:00
|
|
|
this.server = new ssh2.Server(serverConf);
|
2018-06-22 05:15:04 +00:00
|
|
|
this.server.on('connection', (conn, info) => {
|
|
|
|
Log.info(info, 'New SSH connection');
|
|
|
|
this.handleNewClient(new SSHClient(conn), conn._sock, ModuleInfo);
|
|
|
|
});
|
2018-12-27 09:19:26 +00:00
|
|
|
|
|
|
|
return cb(null);
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
2018-12-27 09:46:16 +00:00
|
|
|
listen(cb) {
|
2018-06-22 05:15:04 +00:00
|
|
|
const config = Config();
|
2022-06-05 20:04:25 +00:00
|
|
|
if (true != config.loginServers.ssh.enabled) {
|
2018-12-27 09:46:16 +00:00
|
|
|
return cb(null);
|
2018-11-13 05:05:36 +00:00
|
|
|
}
|
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
const port = parseInt(config.loginServers.ssh.port);
|
2022-06-05 20:04:25 +00:00
|
|
|
if (isNaN(port)) {
|
|
|
|
Log.error(
|
|
|
|
{ server: ModuleInfo.name, port: config.loginServers.ssh.port },
|
|
|
|
'Cannot load server (invalid port)'
|
|
|
|
);
|
2018-12-27 09:46:16 +00:00
|
|
|
return cb(Errors.Invalid(`Invalid port: ${config.loginServers.ssh.port}`));
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
2019-04-10 02:25:27 +00:00
|
|
|
this.server.listen(port, config.loginServers.ssh.address, err => {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (!err) {
|
|
|
|
Log.info(
|
|
|
|
{ server: ModuleInfo.name, port: port },
|
|
|
|
'Listening for connections'
|
|
|
|
);
|
2018-12-27 09:46:16 +00:00
|
|
|
}
|
|
|
|
return cb(err);
|
|
|
|
});
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
2016-09-20 03:28:50 +00:00
|
|
|
};
|