Added support for MeshCmd routing.
This commit is contained in:
parent
951e6236f9
commit
e75adafb05
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,170 @@
|
|||
var fs = require('fs');
|
||||
var os = require('os');
|
||||
var net = require('net');
|
||||
var http = require('http');
|
||||
var dgram = require('dgram');
|
||||
var httpHeaders = require('http-headers');
|
||||
var tcpserver = null;
|
||||
var broadcastSockets = {};
|
||||
var multicastSockets = {};
|
||||
var discoveryInterval = null;
|
||||
var membershipIPv4 = '239.255.255.235';
|
||||
var membershipIPv6 = 'FF02:0:0:0:0:0:0:FE';
|
||||
|
||||
/*
|
||||
// Route Settings
|
||||
var settings = {
|
||||
action: 'route',
|
||||
localPort: 1234,
|
||||
remoteName: 'AmtMachine7',
|
||||
remoteNodeId: 'node//nmiPnDhT3vHKu$zg296YC5RjK53Trgh3Cimx3K8GVrFh$xch0UAAett2rbJpeddc',
|
||||
remotePort: 3389,
|
||||
username: 'a',
|
||||
password: 'a',
|
||||
serverUrl: 'wss://devbox.mesh.meshcentral.com:443/meshrelay.ashx',
|
||||
serverId: 'D99362D5ED8BAEA8BF9E743B34B242256370C460FD66CB62373C6CFCB204D6D707403E396CF0EF6DC2B3A42F735135FD', // SHA384 of server HTTPS public key
|
||||
serverHttpsHash: 'D9DE9E27A229B5355708A3672FB23237CC994A680B3570D242A91E36B4AE5BC9', // SHA256 of server HTTPS certificate
|
||||
debugLevel: 0
|
||||
}
|
||||
*/
|
||||
|
||||
// Check the server certificate fingerprint
|
||||
function onVerifyServer(clientName, certs) {
|
||||
try { for (var i in certs) { if (certs[i].fingerprint.replace(/:/g, '') == settings.serverHttpsHash) { return; } } } catch (e) { }
|
||||
if (serverhash != null) { console.log('Error: Failed to verify server certificate.'); return false; }
|
||||
}
|
||||
|
||||
// Print a debug message
|
||||
function debug(level, message) { if ((settings.debugLevel != null) && (settings.debugLevel >= level)) { console.log(message); } }
|
||||
|
||||
// Start the router, start by listening to the local port
|
||||
function run(argv) {
|
||||
console.log('MeshCentral Command v1.0');
|
||||
var actionpath = 'meshaction.txt';
|
||||
if (argv.length >= 2) { actionpath = argv[1]; }
|
||||
|
||||
// Load the action file
|
||||
var actionfile = null;
|
||||
try { actionfile = fs.readFileSync(actionpath); } catch (e) { }
|
||||
if (actionfile == null) { console.log('Unable to load \"' + actionpath + '\". Create this file or specify the location as the first argument.'); process.exit(1); }
|
||||
try { settings = JSON.parse(actionfile); } catch (e) { console.log(actionpath, e); process.exit(1); }
|
||||
|
||||
// Validate meshaction.txt
|
||||
if (settings.action == null) { console.log('No \"action\" specified.'); process.exit(1); }
|
||||
settings.action = settings.action.toLowerCase();
|
||||
if (settings.action == 'route') {
|
||||
if ((settings.localPort == null) || (typeof settings.localPort != 'number') || (settings.localPort < 0) || (settings.localPort > 65535)) { console.log('No or invalid \"localPort\" specified.'); process.exit(1); }
|
||||
if ((settings.remoteNodeId == null) || (typeof settings.remoteNodeId != 'string')) { console.log('No or invalid \"remoteNodeId\" specified.'); process.exit(1); }
|
||||
if ((settings.username == null) || (typeof settings.username != 'string')) { console.log('No or invalid \"username\" specified.'); process.exit(1); }
|
||||
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified.'); process.exit(1); }
|
||||
if ((settings.serverId == null) || (typeof settings.serverId != 'string') || (settings.serverId.length != 96)) { console.log('No or invalid \"serverId\" specified.'); process.exit(1); }
|
||||
if ((settings.serverHttpsHash == null) || (typeof settings.serverHttpsHash != 'string') || (settings.serverHttpsHash.length != 96)) { console.log('No or invalid \"serverHttpsHash\" specified.'); process.exit(1); }
|
||||
if ((settings.remotePort == null) || (typeof settings.remotePort != 'number') || (settings.remotePort < 0) || (settings.remotePort > 65535)) { console.log('No or invalid \"remotePort\" specified.'); process.exit(1); }
|
||||
} else {
|
||||
console.log('Invalid \"action\" specified.'); process.exit(1);
|
||||
}
|
||||
|
||||
debug(1, "Settings: " + JSON.stringify(settings));
|
||||
if (settings.serverUrl != null) { startRouter(); } else { discoverMeshServer(); }
|
||||
}
|
||||
|
||||
// Starts the router
|
||||
function startRouter() {
|
||||
tcpserver = net.createServer(OnTcpClientConnected);
|
||||
tcpserver.on('error', function (err) { console.log(err); process.exit(0); });
|
||||
tcpserver.listen(settings.localPort, function () {
|
||||
// We started listening.
|
||||
if (settings.remoteName == null) {
|
||||
console.log('Redirecting local port ' + settings.localPort + ' to remote port ' + settings.remotePort + '.');
|
||||
} else {
|
||||
console.log('Redirecting local port ' + settings.localPort + ' to ' + settings.remoteName + ':' + settings.remotePort + '.');
|
||||
}
|
||||
console.log('Press ctrl-c to terminal.');
|
||||
|
||||
// If settings has a "cmd", run it now.
|
||||
//process.exec("notepad.exe");
|
||||
});
|
||||
}
|
||||
|
||||
// Called when a TCP connect is received on the local port. Launch a tunnel.
|
||||
function OnTcpClientConnected(c) {
|
||||
try {
|
||||
// 'connection' listener
|
||||
debug(1, 'Client connected');
|
||||
c.on('end', function () { disconnectTunnel(this, this.websocket, 'Client closed'); });
|
||||
c.pause();
|
||||
|
||||
try {
|
||||
options = http.parseUri(settings.serverUrl + '?user=' + settings.username + '&pass=' + settings.password + '&nodeid=' + settings.remoteNodeId + '&tcpport=' + settings.remotePort);
|
||||
} catch (e) { console.log('Unable to parse \"serverUrl\".'); process.exit(1); }
|
||||
options.checkServerIdentity = onVerifyServer;
|
||||
c.websocket = http.request(options);
|
||||
c.websocket.tcp = c;
|
||||
c.websocket.tunneling = false;
|
||||
c.websocket.upgrade = OnWebSocket;
|
||||
c.websocket.on('error', function (msg) { console.log(msg); });
|
||||
c.websocket.end();
|
||||
} catch (e) { debug(2, e); }
|
||||
}
|
||||
|
||||
// Disconnect both TCP & WebSocket connections and display a message.
|
||||
function disconnectTunnel(tcp, ws, msg) {
|
||||
if (ws != null) { try { ws.end(); } catch (e) { debug(2, e); } }
|
||||
if (tcp != null) { try { tcp.end(); } catch (e) { debug(2, e); } }
|
||||
debug(1, 'Tunnel disconnected: ' + msg);
|
||||
}
|
||||
|
||||
// Called when the web socket gets connected
|
||||
function OnWebSocket(msg, s, head) {
|
||||
debug(1, 'Websocket connected');
|
||||
s.on('data', function (msg) {
|
||||
if (this.parent.tunneling == false) {
|
||||
msg = msg.toString();
|
||||
if (msg == 'c') {
|
||||
this.parent.tunneling = true; this.pipe(this.parent.tcp); this.parent.tcp.pipe(this); debug(1, 'Tunnel active');
|
||||
} else if ((msg.length > 6) && (msg.substring(0, 6) == 'error:')) {
|
||||
console.log(msg.substring(6));
|
||||
disconnectTunnel(this.tcp, this, msg.substring(6));
|
||||
}
|
||||
}
|
||||
});
|
||||
s.on('error', function (msg) { disconnectTunnel(this.tcp, this, 'Websocket error'); });
|
||||
s.on('close', function (msg) { disconnectTunnel(this.tcp, this, 'Websocket closed'); });
|
||||
s.parent = this;
|
||||
}
|
||||
|
||||
// Try to discover the location of the mesh server
|
||||
function discoverMeshServer() { console.log('Looking for server...'); discoveryInterval = setInterval(discoverMeshServerOnce, 5000); discoverMeshServerOnce(); }
|
||||
|
||||
// Try to discover the location of the mesh server only once
|
||||
function discoverMeshServerOnce() {
|
||||
var interfaces = os.networkInterfaces();
|
||||
for (var adapter in interfaces) {
|
||||
if (interfaces.hasOwnProperty(adapter)) {
|
||||
for (var i = 0 ; i < interfaces[adapter].length; ++i) {
|
||||
var addr = interfaces[adapter][i];
|
||||
multicastSockets[i] = dgram.createSocket({ type: (addr.family == "IPv4" ? "udp4" : "udp6") });
|
||||
multicastSockets[i].bind({ address: addr.address, exclusive: false });
|
||||
if (addr.family == "IPv4") {
|
||||
multicastSockets[i].addMembership(membershipIPv4);
|
||||
//multicastSockets[i].setMulticastLoopback(true);
|
||||
multicastSockets[i].once('message', OnMulticastMessage);
|
||||
multicastSockets[i].send(settings.serverId, 16989, membershipIPv4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called when a multicast packet is received
|
||||
function OnMulticastMessage(msg, rinfo) {
|
||||
var m = msg.toString().split('|');
|
||||
if ((m.length == 3) && (m[0] == 'MeshCentral2') && (m[1] == settings.serverId)) {
|
||||
settings.serverUrl = m[2].replace('%s', rinfo.address).replace('/agent.ashx', '/meshrelay.ashx');
|
||||
console.log('Found server at ' + settings.serverUrl + '.');
|
||||
if (discoveryInterval != null) { clearInterval(discoveryInterval); discoveryInterval = null; }
|
||||
startRouter();
|
||||
}
|
||||
}
|
||||
|
||||
try { run(process.argv); } catch (e) { /*console.log(e);*/ }
|
|
@ -59,7 +59,7 @@ CheckInstallAgent() {
|
|||
DownloadAgent $url $meshid $machineid
|
||||
fi
|
||||
else
|
||||
echo "MeshID is not correct, must be 64 HEX characters long."
|
||||
echo "MeshID is not correct, must be 64 characters long."
|
||||
fi
|
||||
else
|
||||
echo "URI and/or MeshID have not been specified, must be passed in as arguments."
|
||||
|
@ -78,7 +78,7 @@ DownloadAgent() {
|
|||
wget $url/meshagents?id=$machineid -q --no-check-certificate -O /usr/local/mesh/meshagent
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
echo "Mesh agent download."
|
||||
echo "Mesh agent downloaded."
|
||||
# TODO: We could check the meshagent sha256 hash, but best to authenticate the server.
|
||||
chmod 755 /usr/local/mesh/meshagent
|
||||
wget $url/meshsettings?id=$meshid -q --no-check-certificate -O /usr/local/mesh/meshagent.msh
|
||||
|
@ -97,6 +97,7 @@ DownloadAgent() {
|
|||
ln -s /usr/local/mesh/meshagent /etc/rc3.d/S20mesh
|
||||
ln -s /usr/local/mesh/meshagent /etc/rc5.d/S20mesh
|
||||
fi
|
||||
echo "Mesh agent started."
|
||||
else
|
||||
echo "Unable to download mesh settings at: $url/meshsettings?id=$meshid."
|
||||
fi
|
||||
|
|
|
@ -131,7 +131,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
|||
if (len == agentUpdateBlockSize) { obj.ws.send(obj.agentUpdate.buf); } else { obj.ws.send(obj.agentUpdate.buf.slice(0, len + 4)); } // Command 14, mesh agent next data block
|
||||
|
||||
if (len < agentUpdateBlockSize) {
|
||||
console.log("Agent update sent");
|
||||
//console.log("Agent update sent");
|
||||
obj.send(obj.common.ShortToStr(13) + obj.common.ShortToStr(0) + obj.common.hex2rstr(obj.agentExeInfo.hash)); // Command 13, end mesh agent download, send agent SHA384 hash
|
||||
obj.fs.close(obj.agentUpdate.fd);
|
||||
obj.agentUpdate = null;
|
||||
|
|
143
meshrelay.js
143
meshrelay.js
|
@ -4,7 +4,7 @@
|
|||
* @version v0.0.1
|
||||
*/
|
||||
|
||||
module.exports.CreateMeshRelay = function (parent, ws, req) {
|
||||
module.exports.CreateMeshRelay = function (parent, ws, req, domain) {
|
||||
var obj = {};
|
||||
obj.ws = ws;
|
||||
obj.req = req;
|
||||
|
@ -12,6 +12,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req) {
|
|||
obj.parent = parent;
|
||||
obj.id = req.query.id;
|
||||
obj.remoteaddr = obj.ws._socket.remoteAddress;
|
||||
obj.domain = domain;
|
||||
if (obj.remoteaddr.startsWith('::ffff:')) { obj.remoteaddr = obj.remoteaddr.substring(7); }
|
||||
|
||||
// Disconnect this agent
|
||||
|
@ -60,7 +61,27 @@ module.exports.CreateMeshRelay = function (parent, ws, req) {
|
|||
|
||||
if (req.query.auth == null) {
|
||||
// Use ExpressJS session, check if this session is a logged in user, at least one of the two connections will need to be authenticated.
|
||||
try { if ((req.session) && (req.session.userid) || (req.session.domainid == getDomain(req).id)) { obj.authenticated = true; } } catch (e) { }
|
||||
try { if ((req.session) && (req.session.userid) || (req.session.domainid == obj.domain.id)) { obj.authenticated = true; } } catch (e) { }
|
||||
if ((obj.authenticated != true) && (req.query.user != null) && (req.query.pass != null)) {
|
||||
// Check user authentication
|
||||
obj.parent.authenticate(req.query.user, req.query.pass, obj.domain, function (err, userid, passhint) {
|
||||
if (userid != null) {
|
||||
obj.authenticated = true;
|
||||
// Check is we have agent routing instructions, process this here.
|
||||
if ((req.query.nodeid != null) && (req.query.tcpport != null)) {
|
||||
if (obj.id == undefined) { obj.id = ('' + Math.random()).substring(2); } // If there is no connection id, generate one.
|
||||
var command = { nodeid: req.query.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id, tcpport: req.query.tcpport, tcpaddr: ((req.query.tcpaddr == null) ? '127.0.0.1' : req.query.tcpaddr) };
|
||||
if (obj.sendAgentMessage(command, userid, obj.domain.id) == false) { obj.id = null; obj.parent.parent.debug(1, 'Relay: Unable to contact this agent (' + obj.remoteaddr + ')'); }
|
||||
}
|
||||
} else {
|
||||
obj.parent.parent.debug(1, 'Relay: User authentication failed (' + obj.remoteaddr + ')');
|
||||
obj.ws.send('error:Authentication failed');
|
||||
}
|
||||
performRelay();
|
||||
});
|
||||
} else {
|
||||
performRelay();
|
||||
}
|
||||
} else {
|
||||
// Get the session from the cookie
|
||||
var cookie = obj.parent.parent.webserver.decodeCookie(req.query.auth);
|
||||
|
@ -76,74 +97,78 @@ module.exports.CreateMeshRelay = function (parent, ws, req) {
|
|||
} else {
|
||||
obj.id = null;
|
||||
obj.parent.parent.debug(1, 'Relay: invalid cookie (' + obj.remoteaddr + ')');
|
||||
obj.ws.send('error:Invalid cookie');
|
||||
}
|
||||
performRelay();
|
||||
}
|
||||
|
||||
if (obj.id == null) { try { obj.close(); } catch (e) { } return null; } // Attempt to connect without id, drop this.
|
||||
ws._socket.setKeepAlive(true, 240000); // Set TCP keep alive
|
||||
function performRelay() {
|
||||
if (obj.id == null) { try { obj.close(); } catch (e) { } return null; } // Attempt to connect without id, drop this.
|
||||
ws._socket.setKeepAlive(true, 240000); // Set TCP keep alive
|
||||
|
||||
// Validate that the id is valid, we only need to do this on non-authenticated sessions.
|
||||
// TODO: Figure out when this needs to be done.
|
||||
/*
|
||||
if (!parent.args.notls) {
|
||||
// Check the identifier, if running without TLS, skip this.
|
||||
var ids = obj.id.split(':');
|
||||
if (ids.length != 3) { obj.ws.close(); obj.id = null; return null; } // Invalid ID, drop this.
|
||||
if (parent.crypto.createHmac('SHA384', parent.relayRandom).update(ids[0] + ':' + ids[1]).digest('hex') != ids[2]) { obj.ws.close(); obj.id = null; return null; } // Invalid HMAC, drop this.
|
||||
if ((Date.now() - parseInt(ids[1])) > 120000) { obj.ws.close(); obj.id = null; return null; } // Expired time, drop this.
|
||||
obj.id = ids[0];
|
||||
}
|
||||
*/
|
||||
// Validate that the id is valid, we only need to do this on non-authenticated sessions.
|
||||
// TODO: Figure out when this needs to be done.
|
||||
/*
|
||||
if (!parent.args.notls) {
|
||||
// Check the identifier, if running without TLS, skip this.
|
||||
var ids = obj.id.split(':');
|
||||
if (ids.length != 3) { obj.ws.close(); obj.id = null; return null; } // Invalid ID, drop this.
|
||||
if (parent.crypto.createHmac('SHA384', parent.relayRandom).update(ids[0] + ':' + ids[1]).digest('hex') != ids[2]) { obj.ws.close(); obj.id = null; return null; } // Invalid HMAC, drop this.
|
||||
if ((Date.now() - parseInt(ids[1])) > 120000) { obj.ws.close(); obj.id = null; return null; } // Expired time, drop this.
|
||||
obj.id = ids[0];
|
||||
}
|
||||
*/
|
||||
|
||||
// Check the peer connection status
|
||||
{
|
||||
var relayinfo = parent.wsrelays[obj.id];
|
||||
if (relayinfo) {
|
||||
if (relayinfo.state == 1) {
|
||||
// Check that at least one connection is authenticated
|
||||
if ((obj.authenticated != true) && (relayinfo.peer1.authenticated != true)) {
|
||||
// Check the peer connection status
|
||||
{
|
||||
var relayinfo = parent.wsrelays[obj.id];
|
||||
if (relayinfo) {
|
||||
if (relayinfo.state == 1) {
|
||||
// Check that at least one connection is authenticated
|
||||
if ((obj.authenticated != true) && (relayinfo.peer1.authenticated != true)) {
|
||||
obj.id = null;
|
||||
obj.ws.close();
|
||||
obj.parent.parent.debug(1, 'Relay without-auth: ' + obj.id + ' (' + obj.remoteaddr + ')');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Connect to peer
|
||||
obj.peer = relayinfo.peer1;
|
||||
obj.peer.peer = obj;
|
||||
relayinfo.peer2 = obj;
|
||||
relayinfo.state = 2;
|
||||
obj.ws.send('c'); // Send connect to both peers
|
||||
relayinfo.peer1.ws.send('c');
|
||||
relayinfo.peer1.ws.resume(); // Release the traffic
|
||||
|
||||
relayinfo.peer1.ws.peer = relayinfo.peer2.ws;
|
||||
relayinfo.peer2.ws.peer = relayinfo.peer1.ws;
|
||||
|
||||
obj.parent.parent.debug(1, 'Relay connected: ' + obj.id + ' (' + obj.remoteaddr + ' --> ' + obj.peer.remoteaddr + ')');
|
||||
} else {
|
||||
// Connected already, drop (TODO: maybe we should re-connect?)
|
||||
obj.id = null;
|
||||
obj.ws.close();
|
||||
obj.parent.parent.debug(1, 'Relay without-auth: ' + obj.id + ' (' + obj.remoteaddr + ')');
|
||||
obj.parent.parent.debug(1, 'Relay duplicate: ' + obj.id + ' (' + obj.remoteaddr + ')');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Connect to peer
|
||||
obj.peer = relayinfo.peer1;
|
||||
obj.peer.peer = obj;
|
||||
relayinfo.peer2 = obj;
|
||||
relayinfo.state = 2;
|
||||
obj.ws.send('c'); // Send connect to both peers
|
||||
relayinfo.peer1.ws.send('c');
|
||||
relayinfo.peer1.ws.resume(); // Release the traffic
|
||||
|
||||
relayinfo.peer1.ws.peer = relayinfo.peer2.ws;
|
||||
relayinfo.peer2.ws.peer = relayinfo.peer1.ws;
|
||||
|
||||
obj.parent.parent.debug(1, 'Relay connected: ' + obj.id + ' (' + obj.remoteaddr + ' --> ' + obj.peer.remoteaddr + ')');
|
||||
} else {
|
||||
// Connected already, drop (TODO: maybe we should re-connect?)
|
||||
obj.id = null;
|
||||
obj.ws.close();
|
||||
obj.parent.parent.debug(1, 'Relay duplicate: ' + obj.id + ' (' + obj.remoteaddr + ')');
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// Wait for other relay connection
|
||||
ws.pause(); // Hold traffic until the other connection
|
||||
parent.wsrelays[obj.id] = { peer1: obj, state: 1 };
|
||||
obj.parent.parent.debug(1, 'Relay holding: ' + obj.id + ' (' + obj.remoteaddr + ')');
|
||||
// Wait for other relay connection
|
||||
ws.pause(); // Hold traffic until the other connection
|
||||
parent.wsrelays[obj.id] = { peer1: obj, state: 1 };
|
||||
obj.parent.parent.debug(1, 'Relay holding: ' + obj.id + ' (' + obj.remoteaddr + ')');
|
||||
|
||||
// Check if a peer server has this connection
|
||||
if (parent.parent.multiServer != null) {
|
||||
var rsession = obj.parent.wsPeerRelays[obj.id];
|
||||
if ((rsession != null) && (rsession.serverId > obj.parent.parent.serverId)) {
|
||||
// We must initiate the connection to the peer
|
||||
parent.parent.multiServer.createPeerRelay(ws, req, rsession.serverId, req.session.userid);
|
||||
delete parent.wsrelays[obj.id];
|
||||
} else {
|
||||
// Send message to other peers that we have this connection
|
||||
parent.parent.multiServer.DispatchMessage(JSON.stringify({ action: 'relay', id: obj.id }));
|
||||
// Check if a peer server has this connection
|
||||
if (parent.parent.multiServer != null) {
|
||||
var rsession = obj.parent.wsPeerRelays[obj.id];
|
||||
if ((rsession != null) && (rsession.serverId > obj.parent.parent.serverId)) {
|
||||
// We must initiate the connection to the peer
|
||||
parent.parent.multiServer.createPeerRelay(ws, req, rsession.serverId, req.session.userid);
|
||||
delete parent.wsrelays[obj.id];
|
||||
} else {
|
||||
// Send message to other peers that we have this connection
|
||||
parent.parent.multiServer.DispatchMessage(JSON.stringify({ action: 'relay', id: obj.id }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -338,7 +338,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
|
|||
// TODO: Right now, we only create type 1 Agent-less Intel AMT mesh, or type 2 Agent mesh
|
||||
if ((command.meshtype == 1) || (command.meshtype == 2)) {
|
||||
// Create a type 1 agent-less Intel AMT mesh.
|
||||
obj.crypto.randomBytes(48, function (err, buf) {
|
||||
obj.parent.crypto.randomBytes(48, function (err, buf) {
|
||||
var meshid = 'mesh/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$');;
|
||||
var links = {}
|
||||
links[user._id] = { name: user.name, rights: 0xFFFFFFFF };
|
||||
|
@ -492,7 +492,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
|
|||
if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return;
|
||||
|
||||
// Create a new nodeid
|
||||
obj.crypto.randomBytes(48, function (err, buf) {
|
||||
obj.parent.crypto.randomBytes(48, function (err, buf) {
|
||||
// create the new node
|
||||
var nodeid = 'node/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$');;
|
||||
var device = { type: 'node', mtype: 1, _id: nodeid, meshid: command.meshid, name: command.devicename, host: command.hostname, domain: domain.id, intelamt: { user: command.amtusername, pass: command.amtpassword, tls: parseInt(command.amttls) } };
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "meshcentral",
|
||||
"version": "0.0.8-w",
|
||||
"version": "0.1.0-d",
|
||||
"keywords": [
|
||||
"Remote Management",
|
||||
"Intel AMT",
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
<script type="text/javascript" src="scripts/ol3-contextmenu.js"></script>
|
||||
<title>MeshCentral</title>
|
||||
</head>
|
||||
|
||||
<body onload="javascript:if (typeof(startup) !== 'undefined') startup();" oncontextmenu="handleContextMenu(event)">
|
||||
<!-- right click menu -->
|
||||
<div id="contextMenu" class="contextMenu" style="display: none">
|
||||
|
@ -1494,10 +1493,10 @@
|
|||
setDialogMode(2, "Add Mesh Agent", 1, null, x);
|
||||
|
||||
if (serverinfo.https == true) {
|
||||
Q('agins_linux_area').value = "wget -q https://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh https://" + serverinfo.name + ":" + serverinfo.port + " " + meshid.split('/')[2] + "\r\n";
|
||||
Q('agins_linux_area').value = "wget -q https://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh https://" + serverinfo.name + ":" + serverinfo.port + " " + meshid.split('/')[2].replace(/\$/g, '\\$') + "\r\n";
|
||||
Q('agins_linux_area_un').value = "wget -q https://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n";
|
||||
} else {
|
||||
Q('agins_linux_area').value = "wget -q http://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh http://" + serverinfo.name + ":" + serverinfo.port + " " + meshid.split('/')[2] + "\r\n";
|
||||
Q('agins_linux_area').value = "wget -q http://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh http://" + serverinfo.name + ":" + serverinfo.port + " " + meshid.split('/')[2].replace(/\$/g, '\\$') + "\r\n";
|
||||
Q('agins_linux_area_un').value = "wget -q http://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n";
|
||||
}
|
||||
}
|
||||
|
@ -2415,6 +2414,8 @@
|
|||
if (mesh.mtype == 2) x += '<a style=cursor:pointer onclick=p10showNodeNetInfoDialog("' + node._id + '") title="Show device network interface information">Interfaces</a> ';
|
||||
if (xxmap != null) x += '<a style=cursor:pointer onclick=p10showNodeLocationDialog("' + node._id + '") title="Show device locations information">Location</a> ';
|
||||
|
||||
if (mesh.mtype == 2) x += '<a style=cursor:pointer onclick=p10showRouterDialog("' + node._id + '") title="Traffic router used to connect to a device thru this server.">Router</a> ';
|
||||
|
||||
// RDP link, show this link only of the remote machine is Windows.
|
||||
if (((connectivity & 1) != 0) && (clickOnce == true) && (mesh.mtype == 2) && ((meshrights & 8) != 0)) {
|
||||
if ((node.agent.id > 0) && (node.agent.id < 5)) { x += '<a style=cursor:pointer onclick=p10clickOnce("' + node._id + '","RDP2",3389) title="Requires Microsoft ClickOnce support in your browser.">RDP</a> '; }
|
||||
|
@ -2689,6 +2690,39 @@
|
|||
meshserver.Send({ action: 'getnetworkinfo', nodeid: currentNode._id });
|
||||
}
|
||||
|
||||
// Show router dialog
|
||||
function p10showRouterDialog() {
|
||||
if (xxdialogMode) return;
|
||||
var y = "<select id=aginsSelect onclick=meshCmdOsClick() style=width:236px>";
|
||||
y += "<option value=1>Windows (32bit)</option>";
|
||||
y += "<option value=2>Windows (64bit)</option>";
|
||||
y += "<option value=5>Linux x86 (32bit)</option>";
|
||||
y += "<option value=6>Linux x86 (64bit)</option>";
|
||||
y += "<option value=25>Linux ARM, Raspberry Pi (32bit)</option>";
|
||||
y += "</select>";
|
||||
|
||||
var x = "";
|
||||
x += "<div>Download \"meshcmd\" with an action file to route traffic thru this server to this device. Make sure to edit meshaction.txt and add your account password or make any changes needed.<br /><br />";
|
||||
x += addHtmlValue('Operating System', y);
|
||||
x += addHtmlValue('Mesh Command', '<a id="meshcmddownloadid" href="meshagents?meshcmd=1" target="_blank"></a>');
|
||||
x += addHtmlValue('Action File', '<a href="meshagents?meshaction=route&nodeid=' + currentNode._id + '" target="_blank">MeshAction (.txt)</a>');
|
||||
x += "</div>";
|
||||
|
||||
setDialogMode(2, "Network Router", 1, null, x, currentNode._id);
|
||||
meshCmdOsClick();
|
||||
}
|
||||
|
||||
function meshCmdOsClick() {
|
||||
var os = Q('aginsSelect').value, osn = '';
|
||||
Q('meshcmddownloadid').href = "meshagents?meshcmd=" + os;
|
||||
if (os == 1) { osn = 'MeshCmd (Win32 executable)'; }
|
||||
if (os == 2) { osn = 'MeshCmd (Win64 executable)'; }
|
||||
if (os == 5) { osn = 'MeshCmd (Linux x86, 32bit)'; }
|
||||
if (os == 6) { osn = 'MeshCmd (Linux x86, 64bit)'; }
|
||||
if (os == 25) { osn = 'MeshCmd (Linux ARM, 32bit)'; }
|
||||
QH('meshcmddownloadid', osn);
|
||||
}
|
||||
|
||||
function p10showiconselector() {
|
||||
if (xxdialogMode) return;
|
||||
var mesh = meshes[currentNode.meshid];
|
||||
|
|
47
webserver.js
47
webserver.js
|
@ -88,6 +88,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
|
|||
// Perform hash on web certificate and agent certificate
|
||||
obj.webCertificateHash = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.web.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' });
|
||||
obj.webCertificateHashBase64 = new Buffer(parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.web.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
|
||||
obj.agentCertificateHashHex = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.agent.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'hex' });
|
||||
obj.agentCertificateHashBase64 = new Buffer(parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.agent.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
|
||||
obj.agentCertificateAsn1 = parent.certificateOperations.forge.asn1.toDer(parent.certificateOperations.forge.pki.certificateToAsn1(parent.certificateOperations.forge.pki.certificateFromPem(parent.certificates.agent.cert))).getBytes();
|
||||
|
||||
|
@ -1119,6 +1120,48 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
|
|||
if (scriptInfo == null) { res.sendStatus(404); return; }
|
||||
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'text/plain', 'Content-Disposition': 'attachment; filename=' + scriptInfo.rname });
|
||||
res.sendFile(scriptInfo.path);
|
||||
} else if (req.query.meshcmd != null) {
|
||||
// Send meshcmd for a specific platform back
|
||||
var argentInfo = obj.parent.meshAgentBinaries[req.query.meshcmd];
|
||||
if (argentInfo == null) { res.sendStatus(404); return; }
|
||||
// Load the agent
|
||||
obj.fs.readFile(argentInfo.path, function (err, agentexe) {
|
||||
if (err != null) { res.sendStatus(404); return; }
|
||||
// Load meshcmd.js
|
||||
obj.fs.readFile(obj.path.join(__dirname, 'agents', 'meshcmd.js'), function (err, meshcmdjs) {
|
||||
if (err != null) { res.sendStatus(404); return; }
|
||||
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=meshcmd' + ((req.query.meshcmd <= 4) ? '.exe' : '') });
|
||||
var tail = new Buffer(8);
|
||||
tail.writeInt32BE(meshcmdjs.length, 0);
|
||||
tail.writeInt32BE(agentexe.length + meshcmdjs.length + 8, 4);
|
||||
res.send(Buffer.concat([agentexe, meshcmdjs, tail]));
|
||||
});
|
||||
});
|
||||
} else if (req.query.meshaction != null) {
|
||||
var domain = checkUserIpAddress(req, res);
|
||||
var user = obj.users[req.session.userid];
|
||||
if (domain == null || req.query.nodeid == null) { res.sendStatus(404); return; }
|
||||
obj.db.Get(req.query.nodeid, function (err, nodes) {
|
||||
if (nodes.length != 1) { res.sendStatus(401); return; }
|
||||
var node = nodes[0];
|
||||
// Create the meshaction.txt file for meshcmd.exe
|
||||
var meshaction = {
|
||||
action: req.query.meshaction,
|
||||
localPort: 1234,
|
||||
remoteName: node.name,
|
||||
remoteNodeId: node._id,
|
||||
remotePort: 3389,
|
||||
username: '',
|
||||
password: '',
|
||||
serverId: obj.agentCertificateHashHex.toUpperCase(), // SHA384 of server HTTPS public key
|
||||
serverHttpsHash: new Buffer(obj.webCertificateHash, 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate
|
||||
debugLevel: 0
|
||||
}
|
||||
if (user != null) { meshaction.username = user.name; }
|
||||
if (obj.args.lanonly != true) { meshaction.serverUrl = ((obj.args.notls == true) ? 'ws://' : 'wss://') + obj.certificates.CommonName + ':' + obj.args.port + '/' + ((domain.id == '') ? '' : ('/' + domain.id)) + 'meshrelay.ashx'; }
|
||||
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'text/plain', 'Content-Disposition': 'attachment; filename=meshaction.txt' });
|
||||
res.send(JSON.stringify(meshaction, null, ' '));
|
||||
});
|
||||
} else {
|
||||
// Send a list of available mesh agents
|
||||
var response = '<html><head><title>Mesh Agents</title><style>table,th,td { border:1px solid black;border-collapse:collapse;padding:3px; }</style></head><body><table>';
|
||||
|
@ -1140,7 +1183,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
|
|||
if (domain == null) return;
|
||||
//if ((domain.id !== '') || (!req.session) || (req.session == null) || (!req.session.userid)) { res.sendStatus(401); return; }
|
||||
|
||||
// Delete a mesh and all computers within it
|
||||
// Query the meshid
|
||||
obj.db.Get('mesh/' + domain.id + '/' + req.query.id, function (err, meshes) {
|
||||
if (meshes.length != 1) { res.sendStatus(401); return; }
|
||||
var mesh = meshes[0];
|
||||
|
@ -1205,7 +1248,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
|
|||
obj.app.post(url + 'uploadmeshcorefile.ashx', handleUploadMeshCoreFile);
|
||||
obj.app.get(url + 'userfiles/*', handleDownloadUserFiles);
|
||||
obj.app.ws(url + 'echo.ashx', handleEchoWebSocket);
|
||||
obj.app.ws(url + 'meshrelay.ashx', function (ws, req) { try { obj.meshRelayHandler.CreateMeshRelay(obj, ws, req); } catch (e) { console.log(e); } });
|
||||
obj.app.ws(url + 'meshrelay.ashx', function (ws, req) { try { obj.meshRelayHandler.CreateMeshRelay(obj, ws, req, getDomain(req)); } catch (e) { console.log(e); } });
|
||||
|
||||
// Receive mesh agent connections
|
||||
obj.app.ws(url + 'agent.ashx', function (ws, req) { try { var domain = getDomain(req); obj.meshAgentHandler.CreateMeshAgent(obj, obj.db, ws, req, obj.args, domain); } catch (e) { console.log(e); } });
|
||||
|
|
Loading…
Reference in New Issue