Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions agents/agentrecoverycore.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ require('MeshAgent').AddCommandHandler(function (data)
if (xurl != null) {
var woptions = http.parseUri(xurl);
woptions.rejectUnauthorized = 0;
// Add custom relay headers if provided by the server
if (data.relayHeaders != null) { woptions.headers = data.relayHeaders; }
//sendConsoleText(JSON.stringify(woptions));
var tunnel = http.request(woptions);
tunnel.on('upgrade', function (response, s, head)
Expand Down
3 changes: 3 additions & 0 deletions agents/meshcore.js
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,9 @@ function handleServerCommand(data) {
woptions.checkServerIdentity = tunnel_checkServerIdentity;
woptions.checkServerIdentity.servertlshash = data.servertlshash;

// Add custom relay headers if provided by the server
if (data.relayHeaders != null) { woptions.headers = data.relayHeaders; }

//sendConsoleText(JSON.stringify(woptions));
//sendConsoleText('TUNNEL: ' + JSON.stringify(data, null, 2));

Expand Down
2 changes: 2 additions & 0 deletions agents/recoverycore.js
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,8 @@ require('MeshAgent').AddCommandHandler(function (data)
}
woptions.checkServerIdentity.servertlshash = data.servertlshash;

// Add custom relay headers if provided by the server
if (data.relayHeaders != null) { woptions.headers = data.relayHeaders; }

//sendConsoleText(JSON.stringify(woptions));
var tunnel = http.request(woptions);
Expand Down
4 changes: 3 additions & 1 deletion meshagent.js
Original file line number Diff line number Diff line change
Expand Up @@ -2152,7 +2152,9 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
obj.RequestCoreDump = function (agenthashhex, corehashhex) {
if (agenthashhex.length > 16) { agenthashhex = agenthashhex.substring(0, 16); }
const cookie = parent.parent.encodeCookie({ a: 'aft', b: 'coredump', c: obj.agentInfo.agentId + '-' + agenthashhex + '-' + obj.nodeid + '.dmp' }, parent.parent.loginCookieEncryptionKey);
obj.send('{"action":"msg","type":"tunnel","value":"*/' + (((domain.dns == null) && (domain.id != '')) ? (domain.id + '/') : '') + 'agenttransfer.ashx?c=' + cookie + '","rights":"4294967295"}');
const cmd = { action: 'msg', type: 'tunnel', value: '*/' + (((domain.dns == null) && (domain.id != '')) ? (domain.id + '/') : '') + 'agenttransfer.ashx?c=' + cookie, rights: '4294967295' };
if (typeof domain.agentrelayheaders == 'object') { cmd.relayHeaders = domain.agentrelayheaders; }
obj.send(JSON.stringify(cmd));
}

// Return true if we need to ignore the agent hash check
Expand Down
7 changes: 7 additions & 0 deletions meshcentral-config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2807,6 +2807,13 @@
},
"description": "This is used to add custom HTTP headers to all responses. For example setting \"httpHeaders\": { \"X-Frame-Options\":\"DENY\" } will add the X-Frame-Options header to all responses."
},
"agentRelayHeaders": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Custom HTTP headers added to agent relay/tunnel WebSocket connections. Useful when MeshCentral is behind a gateway that requires specific headers on agent relay paths."
},
"agentConfig": {
"type": "array",
"uniqueItems": true,
Expand Down
6 changes: 3 additions & 3 deletions meshcentral.js
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,7 @@ function CreateMeshCentralServer(config, args) {
obj.args = args = config2.settings;

// Lower case all keys in the config file
obj.common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders', 'telegram/proxy']);
obj.common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders', 'agentrelayheaders', 'telegram/proxy']);

// Grad some of the values from the original config.json file if present.
if ((config.settings.vault != null) && (config2.settings != null)) { config2.settings.vault = config.settings.vault; }
Expand Down Expand Up @@ -1375,7 +1375,7 @@ function CreateMeshCentralServer(config, args) {
for (i in args) { config2.settings[i] = args[i]; }

// Lower case all keys in the config file
common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders', 'telegram/proxy']);
common.objKeysToLower(config2, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders', 'agentrelayheaders', 'telegram/proxy']);

// Grab some of the values from the original config.json file if present.
config2['mysql'] = config['mysql'];
Expand Down Expand Up @@ -4062,7 +4062,7 @@ function getConfig(createSampleConfig) {

// Lower case all keys in the config file
try {
require('./common.js').objKeysToLower(config, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders', 'telegram/proxy']);
require('./common.js').objKeysToLower(config, ['ldapoptions', 'defaultuserwebstate', 'forceduserwebstate', 'httpheaders', 'agentrelayheaders', 'telegram/proxy']);
} catch (ex) {
console.log('CRITICAL ERROR: Unable to access the file \"./common.js\".\r\nCheck folder & file permissions.');
process.exit();
Expand Down
4 changes: 4 additions & 0 deletions meshdesktopmultiplex.js
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,7 @@ function CreateMeshRelayEx2(parent, ws, req, domain, user, cookie) {
const rcookie = parent.parent.encodeCookie(rcookieData, parent.parent.loginCookieEncryptionKey);
if (obj.id == undefined) { obj.id = ('' + Math.random()).substring(2); } // If there is no connection id, generate one.
const command = { nodeid: node._id, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id + '&rauth=' + rcookie, tcpport: cookie.tcpport, tcpaddr: cookie.tcpaddr };
if (typeof domain.agentrelayheaders == 'object') { command.relayHeaders = domain.agentrelayheaders; }
parent.parent.debug('relay', 'Relay: Sending agent tunnel command: ' + JSON.stringify(command));
if (obj.sendAgentMessage(command, user ? user._id : null, cookie.domainid) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + obj.req.clientIp + ')'); }
performRelay(0);
Expand All @@ -1314,10 +1315,12 @@ function CreateMeshRelayEx2(parent, ws, req, domain, user, cookie) {

if (obj.req.query.tcpport != null) {
const command = { nodeid: node._id, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id + '&rauth=' + rcookie, tcpport: obj.req.query.tcpport, tcpaddr: ((obj.req.query.tcpaddr == null) ? '127.0.0.1' : obj.req.query.tcpaddr) };
if (typeof domain.agentrelayheaders == 'object') { command.relayHeaders = domain.agentrelayheaders; }
parent.parent.debug('relay', 'Relay: Sending agent TCP tunnel command: ' + JSON.stringify(command));
if (obj.sendAgentMessage(command, user ? user._id : null, domain.id) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + obj.req.clientIp + ')'); }
} else if (obj.req.query.udpport != null) {
const command = { nodeid: node._id, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id + '&rauth=' + rcookie, udpport: obj.req.query.udpport, udpaddr: ((obj.req.query.udpaddr == null) ? '127.0.0.1' : obj.req.query.udpaddr) };
if (typeof domain.agentrelayheaders == 'object') { command.relayHeaders = domain.agentrelayheaders; }
parent.parent.debug('relay', 'Relay: Sending agent UDP tunnel command: ' + JSON.stringify(command));
if (obj.sendAgentMessage(command, user ? user._id : null, domain.id) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + obj.req.clientIp + ')'); }
}
Expand All @@ -1340,6 +1343,7 @@ function CreateMeshRelayEx2(parent, ws, req, domain, user, cookie) {
if (user != null) { rcookieData.ruserid = user._id; } else if (obj.nouser === true) { rcookieData.nouser = 1; }
const rcookie = parent.parent.encodeCookie(rcookieData, parent.parent.loginCookieEncryptionKey);
const command = { nodeid: node._id, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?p=2&id=' + obj.id + '&rauth=' + rcookie + '&nodeid=' + node._id, soptions: {}, usage: 2, rights: cookie.r, guestuserid: user._id, guestname: cookie.gn, consent: cookie.cf, remoteaddr: cleanRemoteAddr(obj.req.clientIp) };
if (typeof domain.agentrelayheaders == 'object') { command.relayHeaders = domain.agentrelayheaders; }
if (typeof domain.consentmessages == 'object') {
if (typeof domain.consentmessages.title == 'string') { command.soptions.consentTitle = domain.consentmessages.title; }
if (typeof domain.consentmessages.desktop == 'string') { command.soptions.consentMsgDesktop = domain.consentmessages.desktop; }
Expand Down
1 change: 1 addition & 0 deletions meshdevicefile.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ module.exports.CreateMeshDeviceFile = function (parent, ws, res, req, domain, us
// Send connection request to agent
const rcookie = parent.parent.encodeCookie({ ruserid: user._id }, parent.parent.loginCookieEncryptionKey);
const command = { nodeid: nodeid, action: 'msg', type: 'tunnel', userid: user._id, value: '*/devicefile.ashx?id=' + obj.id + '&rauth=' + rcookie, soptions: {} };
if (typeof domain.agentrelayheaders == 'object') { command.relayHeaders = domain.agentrelayheaders; }
parent.parent.debug('relay', 'FileRelay: Sending agent tunnel command: ' + JSON.stringify(command));
if (obj.sendAgentMessage(command, user, domain.id) == false) { delete obj.id; parent.parent.debug('relay', 'FileRelay: Unable to contact this agent (' + obj.req.clientIp + ')'); }
}
Expand Down
7 changes: 6 additions & 1 deletion meshrelay.js
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
const rcookie = parent.parent.encodeCookie(rcookieData, parent.parent.loginCookieEncryptionKey);
if (obj.id == null) { obj.id = parent.crypto.randomBytes(9).toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); } // If there is no connection id, generate one.
const command = { nodeid: cookie.nodeid, action: 'msg', type: 'tunnel', value: '*/' + xdomain + 'meshrelay.ashx?' + (obj.req.query.p != null ? ('p=' + obj.req.query.p + '&') : '') + 'id=' + obj.id + '&rauth=' + rcookie, tcpport: cookie.tcpport, tcpaddr: cookie.tcpaddr, soptions: {} };
if (typeof domain.agentrelayheaders == 'object') { command.relayHeaders = domain.agentrelayheaders; }
if (user) { command.userid = user._id; }
if (typeof domain.consentmessages == 'object') {
if (typeof domain.consentmessages.title == 'string') { command.soptions.consentTitle = domain.consentmessages.title; }
Expand Down Expand Up @@ -934,6 +935,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
const rcookie = parent.parent.encodeCookie({ ruserid: user._id }, parent.parent.loginCookieEncryptionKey);
if (obj.req.query.tcpport != null) {
const command = { nodeid: obj.req.query.nodeid, action: 'msg', type: 'tunnel', userid: user._id, value: '*/' + xdomain + 'meshrelay.ashx?' + (obj.req.query.p != null ? ('p=' + obj.req.query.p + '&') : '') + 'id=' + obj.id + '&rauth=' + rcookie, tcpport: obj.req.query.tcpport, tcpaddr: ((obj.req.query.tcpaddr == null) ? '127.0.0.1' : obj.req.query.tcpaddr), soptions: {} };
if (typeof domain.agentrelayheaders == 'object') { command.relayHeaders = domain.agentrelayheaders; }
if (typeof domain.consentmessages == 'object') {
if (typeof domain.consentmessages.title == 'string') { command.soptions.consentTitle = domain.consentmessages.title; }
if (typeof domain.consentmessages.desktop == 'string') { command.soptions.consentMsgDesktop = domain.consentmessages.desktop; }
Expand All @@ -953,7 +955,9 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
parent.parent.debug('relay', 'Relay: Sending agent TCP tunnel command: ' + JSON.stringify(command));
if (obj.sendAgentMessage(command, user._id, domain.id) == false) { delete obj.id; parent.parent.debug('relay', 'Relay: Unable to contact this agent (' + obj.req.clientIp + ')'); }
} else if (obj.req.query.udpport != null) {
const command = { nodeid: obj.req.query.nodeid, action: 'msg', type: 'tunnel', userid: user._id, value: '*/' + xdomain + 'meshrelay.ashx?' + (obj.req.query.p != null ? ('p=' + obj.req.query.p + '&') : '') + 'id=' + obj.id + '&rauth=' + rcookie, udpport: obj.req.query.udpport, udpaddr: ((obj.req.query.udpaddr == null) ? '127.0.0.1' : obj.req.query.udpaddr), soptions: {} }; if (typeof domain.consentmessages == 'object') {
const command = { nodeid: obj.req.query.nodeid, action: 'msg', type: 'tunnel', userid: user._id, value: '*/' + xdomain + 'meshrelay.ashx?' + (obj.req.query.p != null ? ('p=' + obj.req.query.p + '&') : '') + 'id=' + obj.id + '&rauth=' + rcookie, udpport: obj.req.query.udpport, udpaddr: ((obj.req.query.udpaddr == null) ? '127.0.0.1' : obj.req.query.udpaddr), soptions: {} };
if (typeof domain.agentrelayheaders == 'object') { command.relayHeaders = domain.agentrelayheaders; }
if (typeof domain.consentmessages == 'object') {
if (typeof domain.consentmessages.title == 'string') { command.soptions.consentTitle = domain.consentmessages.title; }
if (typeof domain.consentmessages.desktop == 'string') { command.soptions.consentMsgDesktop = domain.consentmessages.desktop; }
if (typeof domain.consentmessages.terminal == 'string') { command.soptions.consentMsgTerminal = domain.consentmessages.terminal; }
Expand Down Expand Up @@ -994,6 +998,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
if (user != null) { rcookieData.ruserid = user._id; } else if (obj.nouser === true) { rcookieData.nouser = 1; }
const rcookie = parent.parent.encodeCookie(rcookieData, parent.parent.loginCookieEncryptionKey);
const command = { nodeid: node._id, action: 'msg', type: 'tunnel', value: '*/' + xdomain + 'meshrelay.ashx?p=' + obj.req.query.p + '&id=' + obj.id + '&rauth=' + rcookie + '&nodeid=' + node._id, soptions: {}, rights: cookie.r, guestuserid: user._id, guestname: cookie.gn, consent: cookie.cf, remoteaddr: cleanRemoteAddr(obj.req.clientIp) };
if (typeof domain.agentrelayheaders == 'object') { command.relayHeaders = domain.agentrelayheaders; }
obj.guestname = cookie.gn;

// Limit what this relay connection can do
Expand Down
3 changes: 3 additions & 0 deletions meshuser.js
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use

// Add tunnel pre-message deflate
if (typeof parent.parent.config.settings.agentwscompression == 'boolean') { command.perMessageDeflate = parent.parent.config.settings.agentwscompression; }

// Add agent relay headers
if (typeof domain.agentrelayheaders == 'object') { command.relayHeaders = domain.agentrelayheaders; }
}

// If a response is needed, set a callback function
Expand Down
Loading