Added retry logic to self-update

This commit is contained in:
Bryan Roe 2021-01-18 22:08:12 -08:00
parent d8dc375d22
commit 05573d9bb9
2 changed files with 85 additions and 32 deletions

View File

@ -3892,13 +3892,14 @@ function createMeshCore(agent) {
// If this value is null // If this value is null
var sessionid = (updateoptions != null) ? updateoptions.sessionid : null; // If this is null, messages will be broadcast. Otherwise they will be unicasted var sessionid = (updateoptions != null) ? updateoptions.sessionid : null; // If this is null, messages will be broadcast. Otherwise they will be unicasted
if (this._selfupdate != null) if (agentUpdate_Start._selfupdate != null)
{ {
// We were already called, so we will ignore this duplicate request // We were already called, so we will ignore this duplicate request
if (sessionid != null) { sendConsoleText('Self update already in progress...', sessionid); } if (sessionid != null) { sendConsoleText('Self update already in progress...', sessionid); }
} }
else else
{ {
if (agentUpdate_Start._retryCount == null) { agentUpdate_Start._retryCount = 0; }
if (require('MeshAgent').ARCHID == null && updateurl == null) if (require('MeshAgent').ARCHID == null && updateurl == null)
{ {
// This agent doesn't have the ability to tell us which ARCHID it is, so we don't know which agent to pull // This agent doesn't have the ability to tell us which ARCHID it is, so we don't know which agent to pull
@ -3956,13 +3957,13 @@ function createMeshCore(agent) {
} }
} }
options.checkServerIdentity.servertlshash = (updateoptions != null ? updateoptions.tlshash : null); options.checkServerIdentity.servertlshash = (updateoptions != null ? updateoptions.tlshash : null);
this._selfupdate = require('https').get(options); agentUpdate_Start._selfupdate = require('https').get(options);
this._selfupdate.on('error', function (e) agentUpdate_Start._selfupdate.on('error', function (e)
{ {
sendConsoleText('Self Update failed, because there was a problem trying to download the update', sessionid); sendConsoleText('Self Update failed, because there was a problem trying to download the update', sessionid);
sendAgentMessage('Self Update failed, because there was a problem trying to download the update', 3); sendAgentMessage('Self Update failed, because there was a problem trying to download the update', 3);
}); });
this._selfupdate.on('response', function (img) agentUpdate_Start._selfupdate.on('response', function (img)
{ {
this._file = require('fs').createWriteStream(agentfilename + '.update', { flags: 'wb' }); this._file = require('fs').createWriteStream(agentfilename + '.update', { flags: 'wb' });
this._filehash = require('SHA384Stream').create(); this._filehash = require('SHA384Stream').create();
@ -3976,8 +3977,22 @@ function createMeshCore(agent) {
} }
else else
{ {
sendConsoleText('Self Update FAILED because the downloaded agent FAILED hash check', sessionid); agentUpdate_Start._retryCount++;
sendAgentMessage('Self Update FAILED because the downloaded agent FAILED hash check', 3); sendConsoleText('Self Update FAILED because the downloaded agent FAILED hash check (' + agentUpdate_Start._retryCount + ')', sessionid);
sendAgentMessage('Self Update FAILED because the downloaded agent FAILED hash check (' + agentUpdate_Start._retryCount + ')', 3);
agentUpdate_Start._selfupdate = null;
if (agentUpdate_Start._retryCount < 4)
{
// Retry the download again
sendConsoleText('Self Update will try again in 60 seconds...', sessionid);
agentUpdate_Start._timeout = setTimeout(agentUpdate_Start, 60000, updateurl, updateoptions);
}
else
{
sendConsoleText('Self Update giving up, too many failures...', sessionid);
sendAgentMessage('Self Update giving up, too many failures...', 3);
}
return; return;
} }
} }

View File

@ -228,33 +228,42 @@ function windows_execve(name, agentfilename, sessionid) {
} }
// Start a JavaScript based Agent Self-Update // Start a JavaScript based Agent Self-Update
function agentUpdate_Start(updateurl, updateoptions) { function agentUpdate_Start(updateurl, updateoptions)
{
// If this value is null // If this value is null
var sessionid = (updateoptions != null) ? updateoptions.sessionid : null; // If this is null, messages will be broadcast. Otherwise they will be unicasted var sessionid = (updateoptions != null) ? updateoptions.sessionid : null; // If this is null, messages will be broadcast. Otherwise they will be unicasted
if (this._selfupdate != null) { if (agentUpdate_Start._selfupdate != null)
{
// We were already called, so we will ignore this duplicate request // We were already called, so we will ignore this duplicate request
if (sessionid != null) { sendConsoleText('Self update already in progress...', sessionid); } if (sessionid != null) { sendConsoleText('Self update already in progress...', sessionid); }
} }
else { else
if (require('MeshAgent').ARCHID == null && updateurl == null) { {
if (agentUpdate_Start._retryCount == null) { agentUpdate_Start._retryCount = 0; }
if (require('MeshAgent').ARCHID == null && updateurl == null)
{
// This agent doesn't have the ability to tell us which ARCHID it is, so we don't know which agent to pull // This agent doesn't have the ability to tell us which ARCHID it is, so we don't know which agent to pull
sendConsoleText('Unable to initiate update, agent ARCHID is not defined', sessionid); sendConsoleText('Unable to initiate update, agent ARCHID is not defined', sessionid);
} }
else { else
{
var agentfilename = process.execPath.split(process.platform == 'win32' ? '\\' : '/').pop(); // Local File Name, ie: MeshAgent.exe var agentfilename = process.execPath.split(process.platform == 'win32' ? '\\' : '/').pop(); // Local File Name, ie: MeshAgent.exe
var name = require('MeshAgent').serviceName; var name = require('MeshAgent').serviceName;
if (name == null) { name = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'; } // This is an older agent that doesn't expose the service name, so use the default if (name == null) { name = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'; } // This is an older agent that doesn't expose the service name, so use the default
try { try
{
var s = require('service-manager').manager.getService(name); var s = require('service-manager').manager.getService(name);
if (!s.isMe()) { if (!s.isMe())
{
if (process.platform == 'win32') { s.close(); } if (process.platform == 'win32') { s.close(); }
sendConsoleText('Self Update cannot continue, this agent is not an instance of (' + name + ')', sessionid); sendConsoleText('Self Update cannot continue, this agent is not an instance of (' + name + ')', sessionid);
return; return;
} }
if (process.platform == 'win32') { s.close(); } if (process.platform == 'win32') { s.close(); }
} }
catch (zz) { catch (zz)
{
sendConsoleText('Self Update Failed because this agent is not an instance of (' + name + ')', sessionid); sendConsoleText('Self Update Failed because this agent is not an instance of (' + name + ')', sessionid);
sendAgentMessage('Self Update Failed because this agent is not an instance of (' + name + ')', 3); sendAgentMessage('Self Update Failed because this agent is not an instance of (' + name + ')', 3);
return; return;
@ -265,13 +274,15 @@ function agentUpdate_Start(updateurl, updateoptions) {
options.protocol = 'https:'; options.protocol = 'https:';
if (updateurl == null) { options.path = ('/meshagents?id=' + require('MeshAgent').ARCHID); } if (updateurl == null) { options.path = ('/meshagents?id=' + require('MeshAgent').ARCHID); }
options.rejectUnauthorized = false; options.rejectUnauthorized = false;
options.checkServerIdentity = function checkServerIdentity(certs) { options.checkServerIdentity = function checkServerIdentity(certs)
{
// If the tunnel certificate matches the control channel certificate, accept the connection // If the tunnel certificate matches the control channel certificate, accept the connection
try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { } try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { }
try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { } try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { }
// Check that the certificate is the one expected by the server, fail if not. // Check that the certificate is the one expected by the server, fail if not.
if (checkServerIdentity.servertlshash == null) { if (checkServerIdentity.servertlshash == null)
{
if (require('MeshAgent').ServerInfo == null || require('MeshAgent').ServerInfo.ControlChannelCertificate == null) { return; } if (require('MeshAgent').ServerInfo == null || require('MeshAgent').ServerInfo.ControlChannelCertificate == null) { return; }
sendConsoleText('Self Update failed, because the url cannot be verified', sessionid); sendConsoleText('Self Update failed, because the url cannot be verified', sessionid);
@ -279,33 +290,55 @@ function agentUpdate_Start(updateurl, updateoptions) {
throw new Error('BadCert'); throw new Error('BadCert');
} }
if (certs[0].digest == null) { return; } if (certs[0].digest == null) { return; }
if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) { if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase()))
{
sendConsoleText('Self Update failed, because the supplied certificate does not match', sessionid); sendConsoleText('Self Update failed, because the supplied certificate does not match', sessionid);
sendAgentMessage('Self Update failed, because the supplied certificate does not match', 3); sendAgentMessage('Self Update failed, because the supplied certificate does not match', 3);
throw new Error('BadCert') throw new Error('BadCert')
} }
} }
options.checkServerIdentity.servertlshash = (updateoptions != null ? updateoptions.tlshash : null); options.checkServerIdentity.servertlshash = (updateoptions != null ? updateoptions.tlshash : null);
this._selfupdate = require('https').get(options); agentUpdate_Start._selfupdate = require('https').get(options);
this._selfupdate.on('error', function (e) { agentUpdate_Start._selfupdate.on('error', function (e)
{
sendConsoleText('Self Update failed, because there was a problem trying to download the update', sessionid); sendConsoleText('Self Update failed, because there was a problem trying to download the update', sessionid);
sendAgentMessage('Self Update failed, because there was a problem trying to download the update', 3); sendAgentMessage('Self Update failed, because there was a problem trying to download the update', 3);
}); });
this._selfupdate.on('response', function (img) { agentUpdate_Start._selfupdate.on('response', function (img)
{
this._file = require('fs').createWriteStream(agentfilename + '.update', { flags: 'wb' }); this._file = require('fs').createWriteStream(agentfilename + '.update', { flags: 'wb' });
this._filehash = require('SHA384Stream').create(); this._filehash = require('SHA384Stream').create();
this._filehash.on('hash', function (h) { this._filehash.on('hash', function (h)
if (updateoptions != null && updateoptions.hash != null) { {
if (updateoptions.hash.toLowerCase() == h.toString('hex').toLowerCase()) { if (updateoptions != null && updateoptions.hash != null)
{
if (updateoptions.hash.toLowerCase() == h.toString('hex').toLowerCase())
{
if (sessionid != null) { sendConsoleText('Download complete. HASH verified.', sessionid); } if (sessionid != null) { sendConsoleText('Download complete. HASH verified.', sessionid); }
} }
else { else
sendConsoleText('Self Update FAILED because the downloaded agent FAILED hash check', sessionid); {
sendAgentMessage('Self Update FAILED because the downloaded agent FAILED hash check', 3); agentUpdate_Start._retryCount++;
sendConsoleText('Self Update FAILED because the downloaded agent FAILED hash check (' + agentUpdate_Start._retryCount + ')', sessionid);
sendAgentMessage('Self Update FAILED because the downloaded agent FAILED hash check (' + agentUpdate_Start._retryCount + ')', 3);
agentUpdate_Start._selfupdate = null;
if (agentUpdate_Start._retryCount < 4)
{
// Retry the download again
sendConsoleText('Self Update will try again in 60 seconds...', sessionid);
agentUpdate_Start._timeout = setTimeout(agentUpdate_Start, 60000, updateurl, updateoptions);
}
else
{
sendConsoleText('Self Update giving up, too many failures...', sessionid);
sendAgentMessage('Self Update giving up, too many failures...', 3);
}
return; return;
} }
} }
else { else
{
sendConsoleText('Download complete. HASH=' + h.toString('hex'), sessionid); sendConsoleText('Download complete. HASH=' + h.toString('hex'), sessionid);
} }
@ -313,11 +346,13 @@ function agentUpdate_Start(updateurl, updateoptions) {
try { require('MeshAgent').SendCommand({ action: 'agentupdatedownloaded' }); } catch (e) { } try { require('MeshAgent').SendCommand({ action: 'agentupdatedownloaded' }); } catch (e) { }
if (sessionid != null) { sendConsoleText('Updating and restarting agent...', sessionid); } if (sessionid != null) { sendConsoleText('Updating and restarting agent...', sessionid); }
if (process.platform == 'win32') { if (process.platform == 'win32')
{
// Use _wexecve() equivalent to perform the update // Use _wexecve() equivalent to perform the update
windows_execve(name, agentfilename, sessionid); windows_execve(name, agentfilename, sessionid);
} }
else { else
{
var m = require('fs').statSync(process.execPath).mode; var m = require('fs').statSync(process.execPath).mode;
require('fs').chmodSync(process.cwd() + agentfilename + '.update', m); require('fs').chmodSync(process.cwd() + agentfilename + '.update', m);
@ -331,7 +366,8 @@ function agentUpdate_Start(updateurl, updateoptions) {
// erase update // erase update
require('fs').unlinkSync(process.cwd() + agentfilename + '.update'); require('fs').unlinkSync(process.cwd() + agentfilename + '.update');
switch (process.platform) { switch (process.platform)
{
case 'freebsd': case 'freebsd':
bsd_execv(name, agentfilename, sessionid); bsd_execv(name, agentfilename, sessionid);
break; break;
@ -339,12 +375,14 @@ function agentUpdate_Start(updateurl, updateoptions) {
linux_execv(name, agentfilename, sessionid); linux_execv(name, agentfilename, sessionid);
break; break;
default: default:
try { try
{
// restart service // restart service
var s = require('service-manager').manager.getService(name); var s = require('service-manager').manager.getService(name);
s.restart(); s.restart();
} }
catch (zz) { catch (zz)
{
sendConsoleText('Self Update encountered an error trying to restart service', sessionid); sendConsoleText('Self Update encountered an error trying to restart service', sessionid);
sendAgentMessage('Self Update encountered an error trying to restart service', 3); sendAgentMessage('Self Update encountered an error trying to restart service', 3);
} }