mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-11-26 07:57:56 +03:00
SSH remember credentials.
This commit is contained in:
parent
b963bb8175
commit
2416545aa4
62
apprelays.js
62
apprelays.js
@ -165,6 +165,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
|
||||
|
||||
// Construct a SSH Relay object, called upon connection
|
||||
module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
|
||||
console.log('CreateSshRelay');
|
||||
const Net = require('net');
|
||||
const WebSocket = require('ws');
|
||||
|
||||
@ -293,6 +294,7 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
|
||||
// When data is received from the web socket
|
||||
// SSH default port is 22
|
||||
ws.on('message', function (msg) {
|
||||
console.log('message', msg);
|
||||
try {
|
||||
if (typeof msg != 'string') return;
|
||||
if (msg[0] == '{') {
|
||||
@ -400,6 +402,28 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
|
||||
delete obj.ws;
|
||||
};
|
||||
|
||||
// Save SSH credentials into device
|
||||
function saveSshCredentials() {
|
||||
console.log('Save SSH credentials', obj.username, obj.password, obj.nodeid);
|
||||
parent.parent.db.Get(obj.nodeid, function (err, nodes) {
|
||||
if ((err != null) || (nodes == null) || (nodes.length != 1)) return;
|
||||
const node = nodes[0];
|
||||
const changed = (node.ssh == null);
|
||||
|
||||
// Save the credentials
|
||||
node.ssh = { u: obj.username, p: obj.password };
|
||||
parent.parent.db.Set(node);
|
||||
|
||||
// Event node change if needed
|
||||
if (changed) {
|
||||
// Event the node change
|
||||
var event = { etype: 'node', action: 'changenode', nodeid: obj.nodeid, domain: domain.id, userid: user._id, username: user.name, node: parent.CloneSafeNode(node), msg: "Changed SSH credentials" };
|
||||
if (parent.parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come.
|
||||
parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(node.meshid, [obj.nodeid]), obj, event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Start the looppback server
|
||||
function startRelayConnection(authCookie) {
|
||||
try {
|
||||
@ -423,6 +447,9 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
|
||||
const Client = require('ssh2').Client;
|
||||
obj.sshClient = new Client();
|
||||
obj.sshClient.on('ready', function () { // Authentication was successful.
|
||||
// If requested, save the credentials
|
||||
if (obj.keep === true) saveSshCredentials();
|
||||
|
||||
obj.sshClient.shell(function (err, stream) { // Start a remote shell
|
||||
if (err) { obj.close(); return; }
|
||||
obj.sshShell = stream;
|
||||
@ -433,7 +460,8 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
|
||||
obj.ws.send('c');
|
||||
});
|
||||
obj.sshClient.on('error', function (err) {
|
||||
if (err.level == 'client-authentication') { obj.ws.send(JSON.stringify({ action: 'autherror' })); }
|
||||
if (err.level == 'client-authentication') { try { obj.ws.send(JSON.stringify({ action: 'autherror' })); } catch (ex) { } }
|
||||
if (err.level == 'client-timeout') { try { obj.ws.send(JSON.stringify({ action: 'sessiontimeout' })); } catch (ex) { } }
|
||||
obj.close();
|
||||
});
|
||||
|
||||
@ -442,8 +470,8 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
|
||||
|
||||
// Connect the SSH module to the serial tunnel
|
||||
var connectionOptions = { sock: obj.ser }
|
||||
if (typeof obj.username == 'string') { connectionOptions.username = obj.username; delete obj.username; }
|
||||
if (typeof obj.password == 'string') { connectionOptions.password = obj.password; delete obj.password; }
|
||||
if (typeof obj.username == 'string') { connectionOptions.username = obj.username; }
|
||||
if (typeof obj.password == 'string') { connectionOptions.password = obj.password; }
|
||||
obj.sshClient.connect(connectionOptions);
|
||||
|
||||
// We are all set, start receiving data
|
||||
@ -475,6 +503,7 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
|
||||
if ((typeof msg.username != 'string') || (typeof msg.password != 'string')) break;
|
||||
if ((typeof msg.rows != 'number') || (typeof msg.cols != 'number') || (typeof msg.height != 'number') || (typeof msg.width != 'number')) break;
|
||||
|
||||
obj.keep = msg.keep; // If true, keep store credentials on the server if the SSH tunnel connected succesfully.
|
||||
obj.termSize = msg;
|
||||
obj.username = msg.username;
|
||||
obj.password = msg.password;
|
||||
@ -485,6 +514,19 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
|
||||
startRelayConnection(parent.parent.encodeCookie(cookieContent, parent.parent.loginCookieEncryptionKey));
|
||||
break;
|
||||
}
|
||||
case 'sshautoauth': {
|
||||
// Verify inputs
|
||||
if ((typeof msg.rows != 'number') || (typeof msg.cols != 'number') || (typeof msg.height != 'number') || (typeof msg.width != 'number')) break;
|
||||
obj.termSize = msg;
|
||||
|
||||
if ((obj.username == null) || (obj.password == null)) return;
|
||||
|
||||
// Create a mesh relay authentication cookie
|
||||
var cookieContent = { userid: user._id, domainid: user.domain, nodeid: obj.nodeid, tcpport: obj.tcpport };
|
||||
if (obj.mtype == 3) { cookieContent.lc = 1; } // This is a local device
|
||||
startRelayConnection(parent.parent.encodeCookie(cookieContent, parent.parent.loginCookieEncryptionKey));
|
||||
break;
|
||||
}
|
||||
case 'resize': {
|
||||
// Verify inputs
|
||||
if ((typeof msg.rows != 'number') || (typeof msg.cols != 'number') || (typeof msg.height != 'number') || (typeof msg.width != 'number')) break;
|
||||
@ -531,8 +573,22 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
|
||||
// We are all set, start receiving data
|
||||
ws._socket.resume();
|
||||
|
||||
// Check if we have SSH credentials for this device
|
||||
parent.parent.db.Get(obj.nodeid, function (err, nodes) {
|
||||
if ((err != null) || (nodes == null) || (nodes.length != 1)) return;
|
||||
const node = nodes[0];
|
||||
|
||||
if ((node.ssh == null) || (typeof node.ssh != 'object') || (typeof node.ssh.u != 'string') || (typeof node.ssh.p != 'string')) {
|
||||
// Send a request for SSH authentication
|
||||
try { ws.send(JSON.stringify({ action: 'sshauth' })) } catch (ex) { }
|
||||
} else {
|
||||
// Use our existing credentials
|
||||
obj.username = node.ssh.u;
|
||||
obj.password = node.ssh.p;
|
||||
try { ws.send(JSON.stringify({ action: 'sshautoauth' })) } catch (ex) { }
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
return obj;
|
||||
|
@ -3999,6 +3999,17 @@
|
||||
function p12clearConsoleMsg() { QH('p12TermConsoleMsg', ''); QV('p12TermConsoleMsg', false); if (p12TermConsoleMsgTimer) { clearTimeout(p12TermConsoleMsgTimer); p12TermConsoleMsgTimer = null; } }
|
||||
function p13clearConsoleMsg() { QH('p13FilesConsoleMsg', ''); QV('p13FilesConsoleMsg', false); if (p13FilesConsoleMsgTimer) { clearTimeout(p13FilesConsoleMsgTimer); p13FilesConsoleMsgTimer = null; } }
|
||||
|
||||
function p12setConsoleMsg(msg, timeout) {
|
||||
if (msg) {
|
||||
Q('p12TermConsoleMsg').innerHTML += msg;
|
||||
QV('p12TermConsoleMsg', true);
|
||||
if (p12TermConsoleMsgTimer != null) { clearTimeout(p12TermConsoleMsgTimer); }
|
||||
if (timeout) { p12TermConsoleMsgTimer = setTimeout(p12clearConsoleMsg, timeout); }
|
||||
} else {
|
||||
p12clearConsoleMsg();
|
||||
}
|
||||
}
|
||||
|
||||
function onDesktopStateChange(xdesktop, state) {
|
||||
var xstate = state;
|
||||
if ((xstate == 3) && (xdesktop.contype == 2)) { xstate++; }
|
||||
@ -4567,9 +4578,17 @@
|
||||
var x = '';
|
||||
x += addHtmlValue("Username", '<input id=dp2user style=width:190px maxlength=64 autocomplete=off onkeyup=sshAuthKeyUp(event) />');
|
||||
x += addHtmlValue("Password", '<input type=password id=dp2pass style=width:190px maxlength=64 autocomplete=off onkeyup=sshAuthKeyUp(event) />');
|
||||
x += addHtmlValue('', '<label><input id=dp2keep type=checkbox>' + "Remember credentials" + '</label>');
|
||||
setDialogMode(2, "Authentication", 11, sshConnectEx, x, 'ssh');
|
||||
setTimeout(sshAuthKeyUp, 50);
|
||||
}
|
||||
case 'sshautoauth': {
|
||||
terminal.socket.send(JSON.stringify({ action: 'sshautoauth', cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight }));
|
||||
break;
|
||||
}
|
||||
case 'autherror': { p12setConsoleMsg("Authentication Error", 5000); break; }
|
||||
case 'sessionerror': { p12setConsoleMsg("Session expired", 5000); break; }
|
||||
case 'sessiontimeout': { p12setConsoleMsg("Session timeout", 5000); break; }
|
||||
}
|
||||
} else if (data[0] == '~') { xterm.writeUtf8(data.substring(1)); }
|
||||
}
|
||||
@ -4580,7 +4599,7 @@
|
||||
if (b == 0) {
|
||||
if (terminal != null) { connectTerminal(); } // Disconnect
|
||||
} else {
|
||||
terminal.socket.send(JSON.stringify({ action: 'sshauth', username: Q('dp2user').value, password: Q('dp2pass').value, cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight }));
|
||||
terminal.socket.send(JSON.stringify({ action: 'sshauth', username: Q('dp2user').value, password: Q('dp2pass').value, keep: Q('dp2keep').checked, cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight }));
|
||||
}
|
||||
}
|
||||
|
||||
@ -4648,16 +4667,7 @@
|
||||
terminal.onStateChanged = onTerminalStateChange;
|
||||
terminal.contype = 1;
|
||||
terminal.attemptWebRTC = false; // Never do WebRTC on terminal, because of a race condition we can't do it.
|
||||
terminal.onConsoleMessageChange = function (server, msg) {
|
||||
if (terminal.consoleMessage) {
|
||||
Q('p12TermConsoleMsg').innerHTML += formatAgentConsoleMessage(terminal.consoleMessage, terminal.consoleMessageId, terminal.consoleMessageArgs);
|
||||
QV('p12TermConsoleMsg', true);
|
||||
if (p12TermConsoleMsgTimer != null) { clearTimeout(p12TermConsoleMsgTimer); }
|
||||
if (terminal.consoleMessageTimeout) { p12TermConsoleMsgTimer = setTimeout(p12clearConsoleMsg, terminal.consoleMessageTimeout * 1000); }
|
||||
} else {
|
||||
p12clearConsoleMsg();
|
||||
}
|
||||
};
|
||||
terminal.onConsoleMessageChange = function () { p12setConsoleMsg(terminal.consoleMessage ? formatAgentConsoleMessage(terminal.consoleMessage, terminal.consoleMessageId, terminal.consoleMessageArgs) : null, terminal.consoleMessageTimeout); }
|
||||
} else {
|
||||
terminal.Stop();
|
||||
terminal = null;
|
||||
|
@ -7804,6 +7804,17 @@
|
||||
function p12clearConsoleMsg() { QH('p12TermConsoleMsg', ''); QV('p12TermConsoleMsg', false); if (p12TermConsoleMsgTimer) { clearTimeout(p12TermConsoleMsgTimer); p12TermConsoleMsgTimer = null; } }
|
||||
function p13clearConsoleMsg() { QH('p13FilesConsoleMsg', ''); QV('p13FilesConsoleMsg', false); if (p13FilesConsoleMsgTimer) { clearTimeout(p13FilesConsoleMsgTimer); p13FilesConsoleMsgTimer = null; } }
|
||||
|
||||
function p12setConsoleMsg(msg, timeout) {
|
||||
if (msg) {
|
||||
Q('p12TermConsoleMsg').innerHTML += msg;
|
||||
QV('p12TermConsoleMsg', true);
|
||||
if (p12TermConsoleMsgTimer != null) { clearTimeout(p12TermConsoleMsgTimer); }
|
||||
if (timeout) { p12TermConsoleMsgTimer = setTimeout(p12clearConsoleMsg, timeout); }
|
||||
} else {
|
||||
p12clearConsoleMsg();
|
||||
}
|
||||
}
|
||||
|
||||
var webRtcDesktop = null;
|
||||
function webRtcDesktopReset() {
|
||||
if (webRtcDesktop == null) return;
|
||||
@ -8680,9 +8691,18 @@
|
||||
var x = '';
|
||||
x += addHtmlValue("Username", '<input id=dp2user style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthKeyUp(event) />');
|
||||
x += addHtmlValue("Password", '<input type=password id=dp2pass style=width:230px maxlength=64 autocomplete=off onkeyup=sshAuthKeyUp(event) />');
|
||||
x += addHtmlValue('', '<label><input id=dp2keep type=checkbox>' + "Remember credentials" + '</label>');
|
||||
setDialogMode(2, "Authentication", 11, sshConnectEx, x, 'ssh');
|
||||
setTimeout(sshAuthKeyUp, 50);
|
||||
break;
|
||||
}
|
||||
case 'sshautoauth': {
|
||||
terminal.socket.send(JSON.stringify({ action: 'sshautoauth', cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight }));
|
||||
break;
|
||||
}
|
||||
case 'autherror': { p12setConsoleMsg("Authentication Error", 5000); break; }
|
||||
case 'sessionerror': { p12setConsoleMsg("Session expired", 5000); break; }
|
||||
case 'sessiontimeout': { p12setConsoleMsg("Session timeout", 5000); break; }
|
||||
}
|
||||
} else if (data[0] == '~') { xterm.writeUtf8(data.substring(1)); }
|
||||
}
|
||||
@ -8693,7 +8713,7 @@
|
||||
if (b == 0) {
|
||||
if (terminal != null) { connectTerminal(); } // Disconnect
|
||||
} else {
|
||||
terminal.socket.send(JSON.stringify({ action: 'sshauth', username: Q('dp2user').value, password: Q('dp2pass').value, cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight }));
|
||||
terminal.socket.send(JSON.stringify({ action: 'sshauth', username: Q('dp2user').value, password: Q('dp2pass').value, keep: Q('dp2keep').checked, cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight }));
|
||||
}
|
||||
}
|
||||
|
||||
@ -8796,16 +8816,7 @@
|
||||
terminal.onStateChanged = onTerminalStateChange;
|
||||
terminal.contype = 1;
|
||||
terminal.attemptWebRTC = false; // Never do WebRTC on terminal, because of a race condition we can't do it.
|
||||
terminal.onConsoleMessageChange = function (server, msg) {
|
||||
if (terminal.consoleMessage) {
|
||||
Q('p12TermConsoleMsg').innerHTML += formatAgentConsoleMessage(terminal.consoleMessage, terminal.consoleMessageId, terminal.consoleMessageArgs);
|
||||
QV('p12TermConsoleMsg', true);
|
||||
if (p12TermConsoleMsgTimer != null) { clearTimeout(p12TermConsoleMsgTimer); }
|
||||
if (terminal.consoleMessageTimeout) { p12TermConsoleMsgTimer = setTimeout(p12clearConsoleMsg, terminal.consoleMessageTimeout * 1000); }
|
||||
} else {
|
||||
p12clearConsoleMsg();
|
||||
}
|
||||
};
|
||||
terminal.onConsoleMessageChange = function () { p12setConsoleMsg(terminal.consoleMessage ? formatAgentConsoleMessage(terminal.consoleMessage, terminal.consoleMessageId, terminal.consoleMessageArgs) : null, terminal.consoleMessageTimeout); }
|
||||
} else {
|
||||
QV('termarea3xdiv', false);
|
||||
QV('Term', true);
|
||||
@ -8819,16 +8830,7 @@
|
||||
terminal.m.lineFeed = ([1, 2, 3, 4, 21, 22].indexOf(currentNode.agent.id) >= 0) ? '\r\n' : '\r'; // On windows, send \r\n, on Linux only \r
|
||||
terminal.attemptWebRTC = false; // Never do WebRTC on terminal, because of a race condition we can't do it.
|
||||
terminal.onStateChanged = onTerminalStateChange;
|
||||
terminal.onConsoleMessageChange = function () {
|
||||
if (terminal.consoleMessage) {
|
||||
Q('p12TermConsoleMsg').innerHTML += formatAgentConsoleMessage(terminal.consoleMessage, terminal.consoleMessageId, terminal.consoleMessageArgs);
|
||||
QV('p12TermConsoleMsg', true);
|
||||
if (p12TermConsoleMsgTimer != null) { clearTimeout(p12TermConsoleMsgTimer); }
|
||||
if (terminal.consoleMessageTimeout) { p12TermConsoleMsgTimer = setTimeout(p12clearConsoleMsg, terminal.consoleMessageTimeout * 1000); }
|
||||
} else {
|
||||
p12clearConsoleMsg();
|
||||
}
|
||||
}
|
||||
terminal.onConsoleMessageChange = function () { p12setConsoleMsg(terminal.consoleMessage ? formatAgentConsoleMessage(terminal.consoleMessage, terminal.consoleMessageId, terminal.consoleMessageArgs) : null, terminal.consoleMessageTimeout); }
|
||||
terminal.Start(terminalNode._id);
|
||||
terminal.contype = 1;
|
||||
terminal.m.terminalEmulation = 0;
|
||||
|
@ -6898,6 +6898,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
if ((r.pmt != null) || ((r.intelamt != null) && ((r.intelamt.pass != null) || (r.intelamt.mpspass != null)))) {
|
||||
r = Object.assign({}, r); // Shallow clone
|
||||
if (r.pmt != null) { r.pmt = 1; }
|
||||
if (r.ssh != null) { r.ssh = 1; }
|
||||
if ((r.intelamt != null) && ((r.intelamt.pass != null) || (r.intelamt.mpspass != null))) {
|
||||
r.intelamt = Object.assign({}, r.intelamt); // Shallow clone
|
||||
if (r.intelamt.pass != null) { r.intelamt.pass = 1; }; // Remove the Intel AMT administrator password from the node
|
||||
|
Loading…
Reference in New Issue
Block a user