Complete rework of the ZeroMQ implementation

- python server running in the background doing the publishing
- MISP -> python script communication via redis
- configurable / controllable via the admin UI
pull/567/head
Iglocska 2015-06-29 08:56:45 +02:00
parent f00f74ddb3
commit 3f215743f0
15 changed files with 457 additions and 65 deletions

3
.gitignore vendored
View File

@ -23,6 +23,9 @@
/app/files/scripts/python-cybox/
/app/files/scripts/*.pyc
/app/files/scripts/*.py~
/app/files/scripts/mispzmq/*
!/app/files/scripts/mispzmq/mispzmq.py
!/app/files/scripts/mispzmq/mispzmqtest.py
/app/files/scripts/tmp/*
!/app/files/scripts/tmp/empty
/app/tmp/files/*

View File

@ -215,11 +215,5 @@ Recommended actions
Optional features
-------------------
# MISP has a new pub/sub feature, using ZeroMQ. To enable it, follow the following instructions
apt-get install libzmq-dev libtool pkg-config build-essential autoconf automake
pecl install zmq-beta
# after this is done add the following line to your php.ini
# Do this for both the apache and the CLI php.ini, for ubuntu this would be in /etc/php5/apache2/php.ini and /etc/php5/cli/php.ini
extension=zmq.so
service apache2 restart
# You can now enable the feature in the server settings
# MISP has a new pub/sub feature, using ZeroMQ. To enable it, simply run the following command
pip install pyzmq

View File

@ -58,10 +58,4 @@ su www-data -c 'bash /var/www/MISP/app/Console/worker/start.sh'
# 7. Add any new dependencies that might have been added since you've last updated (shown below)
# 7.a requirements for the pubsub optional feature
apt-get install libzmq-dev libtool pkg-config build-essential autoconf automake
pecl install zmq-beta
# after this is done add the following line to your php.ini
# Do this for both the apache and the CLI php.ini, for ubuntu this would be in /etc/php5/apache2/php.ini and /etc/php5/cli/php.ini
extension=zmq.so
service apache2 restart
# You can now enable the feature in the server settings
pip install pyzmq

View File

@ -1 +1 @@
{"major":2, "minor":3, "hotfix":87}
{"major":2, "minor":3, "hotfix":88}

View File

@ -282,6 +282,25 @@ class ServersController extends AppController {
if ($result) $this->Server->save($s);
}
public function serverSettingsReloadSetting($setting, $id) {
if (!$this->_isSiteAdmin()) throw new MethodNotAllowedException();
$pathToSetting = explode('.', $setting);
$settingObject = $this->Server->serverSettings;
foreach ($pathToSetting as $key) {
if (!isset($settingObject[$key])) throw new MethodNotAllowedException();
$settingObject = $settingObject[$key];
}
$result = $this->Server->serverSettingReadSingle($settingObject, $setting, $key);
$this->set('setting', $result);
$priorityErrorColours = array(0 => 'red', 1 => 'yellow', 2 => 'green');
$this->set('priorityErrorColours', $priorityErrorColours);
$priorities = array(0 => 'Critical', 1 => 'Recommended', 2 => 'Optional', 3 => 'Deprecated');
$this->set('priorities', $priorities);
$this->set('k', $id);
$this->layout = false;
$this->render('/Elements/healthElements/settings_row');
}
public function serverSettings($tab=false) {
if (!$this->_isSiteAdmin()) throw new MethodNotAllowedException();
if ($this->request->is('Get')) {
@ -296,7 +315,7 @@ class ServersController extends AppController {
$writeableErrors = array(0 => 'OK', 1 => 'Directory doesn\'t exist', 2 => 'Directory is not writeable');
$gpgErrors = array(0 => 'OK', 1 => 'FAIL: settings not set', 2 => 'FAIL: bad GnuPG.*', 3 => 'FAIL: encrypt failed');
$proxyErrors = array(0 => 'OK', 1 => 'not configured (so not tested)', 2 => 'Getting URL via proxy failed');
$zmqErrors = array(0 => 'OK', 1 => 'not enabled (so not tested)', 2 => 'zmq extension not installed correctly.');
$zmqErrors = array(0 => 'OK', 1 => 'not enabled (so not tested)', 2 => 'Python ZeroMQ library not installed correctly.', 3 => 'ZeroMQ script not running.');
$stixOperational = array(0 => 'STIX or CyBox library not installed correctly', 1 => 'OK');
$stixVersion = array(0 => 'Incorrect STIX version installed, found $current, expecting $expected', 1 => 'OK');
$cyboxVersion = array(0 => 'Incorrect CyBox version installed, found $current, expecting $expected', 1 => 'OK');
@ -337,39 +356,40 @@ class ServersController extends AppController {
$diagnostic_errors = 0;
App::uses('File', 'Utility');
App::uses('Folder', 'Utility');
$additionalViewVars = array();
// Only run this check on the diagnostics tab
if ($tab == 'diagnostics') {
if ($tab == 'diagnostics' || $tab == 'download') {
// check if the current version of MISP is outdated or not
$version = $this->__checkVersion();
$this->set('version', $version);
if ($version && (!$version['upToDate'] || $version['upToDate'] == 'older')) $diagnostic_errors++;
}
if ($tab == 'files') {
$files = $this->__manageFiles();
$this->set('files', $files);
}
if ($tab == 'files') {
$files = $this->__manageFiles();
$this->set('files', $files);
}
// check if the STIX and Cybox libraries are working and the correct version using the test script stixtest.py
$stix = $this->Server->stixDiagnostics($diagnostic_errors, $stixVersion, $cyboxVersion);
// if GPG is set up in the settings, try to encrypt a test message
$gpgStatus = $this->Server->gpgDiagnostics($diagnostic_errors);
// if the message queue pub/sub is enabled, check whether the extension works
$zmqStatus = $this->Server->zmqDiagnostics($diagnostic_errors);
// if Proxy is set up in the settings, try to connect to a test URL
$proxyStatus = $this->Server->proxyDiagnostics($diagnostic_errors);
$additionalViewVars = array('gpgStatus', 'proxyStatus', 'zmqStatus', 'stixVersion', 'cyboxVersion','gpgErrors', 'proxyErrors', 'zmqErrors', 'stixOperational', 'stix');
}
// check whether the files are writeable
$writeableDirs = $this->Server->writeableDirsDiagnostics($diagnostic_errors);
// check if the STIX and Cybox libraries are working and the correct version using the test script stixtest.py
$stix = $this->Server->stixDiagnostics($diagnostic_errors, $stixVersion, $cyboxVersion);
// if GPG is set up in the settings, try to encrypt a test message
$gpgStatus = $this->Server->gpgDiagnostics($diagnostic_errors);
// if the message queue pub/sub is enabled, check whether the extension works
$zmqStatus = $this->Server->zmqDiagnostics($diagnostic_errors);
// if Proxy is set up in the settings, try to connect to a test URL
$proxyStatus = $this->Server->proxyDiagnostics($diagnostic_errors);
$viewVars = array(
'gpgStatus', 'proxyStatus', 'zmqStatus', 'diagnostic_errors', 'tabs', 'tab', 'issues', 'finalSettings', 'writeableErrors','gpgErrors', 'proxyErrors', 'zmqErrors', 'stixOperational', 'stixVersion', 'cyboxVersion', 'stix', 'writeableDirs'
'diagnostic_errors', 'tabs', 'tab', 'issues', 'finalSettings', 'writeableErrors', 'writeableDirs'
);
$viewVars = array_merge($viewVars, $additionalViewVars);
foreach ($viewVars as $viewVar) $this->set($viewVar, ${$viewVar});
if (Configure::read('MISP.background_jobs')) {
@ -410,10 +430,11 @@ class ServersController extends AppController {
$this->response->download('MISP.report.json');
return $this->response;
}
$priorities = array(0 => 'Critical', 1 => 'Recommended', 2 => 'Optional', 3 => 'Deprecated');
$priorityErrorColours = array(0 => 'red', 1 => 'yellow', 2 => 'green');
$this->set('priorities', $priorities);
$this->set('workerIssueCount', $workerIssueCount);
$priorityErrorColours = array(0 => 'red', 1 => 'yellow', 2 => 'green');
$this->set('priorityErrorColours', $priorityErrorColours);
}
}
@ -476,6 +497,25 @@ class ServersController extends AppController {
$this->render('ajax/server_settings_edit');
}
if ($this->request->is('post')) {
$this->autoRender = false;
$this->loadModel('Log');
if (isset($found['beforeHook'])) {
$beforeResult = call_user_func_array(array($this->Server, $found['beforeHook']), array($setting, $this->request->data['Server']['value']));
if ($afterResult !== true) {
$this->Log->create();
$result = $this->Log->save(array(
'org' => $this->Auth->user('org'),
'model' => 'Server',
'model_id' => 0,
'email' => $this->Auth->user('email'),
'action' => 'serverSettingsEdit',
'user_id' => $this->Auth->user('id'),
'title' => 'Server setting issue',
'change' => 'There was an issue witch changing ' . $setting . ' to ' . $this->request->data['Server']['value'] . '. The error message returned is: ' . $beforeResult . 'No changes were made.',
));
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $afterResult)),'status'=>200));
}
}
if ($found['type'] == 'boolean') {
$this->request->data['Server']['value'] = ($this->request->data['Server']['value'] ? true : false);
}
@ -490,7 +530,6 @@ class ServersController extends AppController {
} else {
$oldValue = Configure::read($setting);
$this->Server->serverSettingsSaveValue($setting, $this->request->data['Server']['value']);
$this->loadModel('Log');
$this->Log->create();
$result = $this->Log->save(array(
'org' => $this->Auth->user('org'),
@ -502,7 +541,24 @@ class ServersController extends AppController {
'title' => 'Server setting changed',
'change' => $setting . ' (' . $oldValue . ') => (' . $this->request->data['Server']['value'] . ')',
));
$this->autoRender = false;
// execute after hook
if (isset($found['afterHook'])) {
$afterResult = call_user_func_array(array($this->Server, $found['afterHook']), array($setting, $this->request->data['Server']['value']));
if ($afterResult !== true) {
$this->Log->create();
$result = $this->Log->save(array(
'org' => $this->Auth->user('org'),
'model' => 'Server',
'model_id' => 0,
'email' => $this->Auth->user('email'),
'action' => 'serverSettingsEdit',
'user_id' => $this->Auth->user('id'),
'title' => 'Server setting issue',
'change' => 'There was an issue after setting a new setting. The error message returned is: ' . $afterResult,
));
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $afterResult)),'status'=>200));
}
}
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Field updated.')),'status'=>200));
}
}
@ -573,4 +629,34 @@ class ServersController extends AppController {
}
$this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'files'));
}
public function startZeroMQServer() {
if (!$this->_isSiteAdmin()) throw new MethodNotAllowedException();
App::uses('PubSubTool', 'Tools');
$pubSubTool = new PubSubTool();
$result = $pubSubTool->restartServer();
if ($result === true) return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'ZeroMQ server successfully started.')),'status'=>200));
else return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $result)),'status'=>200));
}
public function stopZeroMQServer() {
if (!$this->_isSiteAdmin()) throw new MethodNotAllowedException();
App::uses('PubSubTool', 'Tools');
$pubSubTool = new PubSubTool();
$result = $pubSubTool->killService();
if ($result === true) return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'ZeroMQ server successfully killed.')),'status'=>200));
else return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Could not kill the previous instance of the ZeroMQ script.')),'status'=>200));
}
public function statusZeroMQServer() {
App::uses('PubSubTool', 'Tools');
$pubSubTool = new PubSubTool();
$result = $pubSubTool->statusCheck();
if (!empty($result)) {
$this->set('events', $result['publishCount']);
$this->set('time', date('Y/m/d H:i:s', $result['timestamp']));
$this->set('time2', date('Y/m/d H:i:s', $result['timestampSettings']));
}
$this->render('ajax/zeromqstatus');
}
}

View File

@ -1,29 +1,110 @@
<?php
class PubSubTool {
private function __setupPub() {
$context = new ZMQContext();
$pub = $context->getSocket(ZMQ::SOCKET_PUB);
$port = Configure::read('Plugin.ZeroMQ_port');
if (empty($port)) $port = 50000;
$pub->bind("tcp://*:" . $port);
return $pub;
private function __getSetSettings() {
$settings = array(
'redis_host' => 'localhost',
'redis_port' => '6379',
'redis_password' => '',
'redis_database' => '1',
'redis_namespace' => 'mispq',
'port' => '50000',
);
foreach ($settings as $key => &$setting) {
$temp = Configure::read('Plugin.ZeroMQ_' . $key);
if ($temp) $setting = $temp;
}
$settingsFile = new File(APP . 'files' . DS . 'scripts' . DS . 'mispzmq' . DS . 'settings.json', true, 0644);
$settingsFile->write(json_encode($settings, true));
$settingsFile->close();
return $settings;
}
// read the pid file, if it exists, check if the process is actually running
// if either the pid file doesn't exists or the process is not running return false
// otherwise return the pid
public function checkIfRunning() {
$pidFile = new File(APP . 'files' . DS . 'scripts' . DS . 'mispzmq' . DS . 'mispzmq.pid');
$pid = $pidFile->read(true, 'r');
if ($pid === false || $pid === '') return false;
if (!is_numeric($pid)) throw new Exception('Internal error (invalid PID file for the MISP zmq script)');
$result = trim(shell_exec('ps aux | awk \'{print $2}\' | grep ' . $pid));
if (empty($result)) return false;
return $pid;
}
public function statusCheck() {
$redis = new Redis();
$settings = $this->__getSetSettings();
$redis->connect($settings['redis_host'], $settings['redis_port']);
$redis->select($settings['redis_database']);
$redis->rPush($settings['redis_namespace'] . ':command', 'status');
sleep(1);
$response = trim($redis->lPop($settings['redis_namespace'] . ':status'));
return json_decode($response, true);
}
public function checkIfPythonLibInstalled() {
$result = trim(shell_exec('python ' . APP . 'files' . DS . 'scripts' . DS . 'mispzmq' . DS . 'mispzmqtest.py'));
if ($result === "OK") return true;
return false;
}
private function __setupPubServer() {
App::uses('File', 'Utility');
$settings = $this->__getSetSettings();
if ($this->checkIfRunning() === false) {
shell_exec('python ' . APP . 'files' . DS . 'scripts' . DS . 'mispzmq' . DS . 'mispzmq.py > ' . APP . 'tmp' . DS . 'logs' . DS . 'mispzmq.log 2> ' . APP . 'tmp' . DS . 'logs' . DS . 'mispzmq.error.log &');
}
return $settings;
}
public function publishEvent($event) {
$pub = $this->__setupPub();
$settings = $this->__setupPubServer();
App::uses('JSONConverterTool', 'Tools');
$jsonTool = new JSONConverterTool();
$json = $jsonTool->event2JSON($event);
sleep(1);
$pub->send('misp_json ' . $json);
$redis = new Redis();
$redis->connect($settings['redis_host'], $settings['redis_port']);
$redis->select($settings['redis_database']);
$redis->rPush($settings['redis_namespace'] . ':misp_json', $json);
return true;
}
public function testZMQ() {
try {
$context = new ZMQContext();
} catch (Exception $e) {
return false;
public function killService($settings = false) {
$redis = new Redis();
if ($settings == false) $settings = $this->__getSetSettings();
$redis->connect($settings['redis_host'], $settings['redis_port']);
$redis->select($settings['redis_database']);
$redis->rPush($settings['redis_namespace'] . ':command', 'kill');
$continue = true;
$counter = 0;
sleep(1);
if ($this->checkIfRunning()) return false;
return true;
}
// reload the server if it is running, if not, start it
public function reloadServer() {
if (!$this->checkIfRunning()) {
$settings = $this->__setupPubServer();
} else {
$settings = $this->__getSetSettings();
$redis = new Redis();
$redis->connect($settings['redis_host'], $settings['redis_port']);
$redis->select($settings['redis_database']);
$redis->rPush($settings['redis_namespace'] . ':command', 'reload');
}
if (!$this->checkIfRunning()) return 'Setting saved, but something is wrong with the ZeroMQ server. Please check the diagnostics page for more information.';
return true;
}
public function restartServer() {
if (!$this->killService()) {
return 'Could not kill the previous instance of the ZeroMQ script.';
}
$this->__setupPubServer();
if (!is_numeric($this->checkIfRunning())) return 'Failed starting the ZeroMQ script.';
return true;
}
}

View File

@ -109,8 +109,7 @@ class Post extends AppModel {
$bodyDetail .= $message . "\n";
$subject = "[" . Configure::read('MISP.org') . " MISP] New post in discussion " . $post['Post']['thread_id'] . " - TLP Amber";
foreach ($orgMembers as &$recipient) {
$result = $this->User->sendEmail($recipient, $bodyDetail, $body, $subject);
$this->User->sendEmail($recipient, $bodyDetail, $body, $subject);
}
return $result;
}
}

View File

@ -647,6 +647,7 @@ class Server extends AppModel {
'errorMessage' => '',
'test' => 'testBool',
'type' => 'boolean',
'afterHook' => 'zmqAfterHook',
),
'ZeroMQ_port' => array(
'level' => 2,
@ -655,7 +656,54 @@ class Server extends AppModel {
'errorMessage' => '',
'test' => 'testForPortNumber',
'type' => 'numeric',
'afterHook' => 'zmqAfterHook',
),
'ZeroMQ_redis_host' => array(
'level' => 2,
'description' => 'Location of the Redis db used by MISP and the Python PUB script to queue data to be published.',
'value' => 'localhost',
'errorMessage' => '',
'test' => 'testForEmpty',
'type' => 'string',
'afterHook' => 'zmqAfterHook',
),
'ZeroMQ_redis_port' => array(
'level' => 2,
'description' => 'The port that Redis is listening on.',
'value' => 6379,
'errorMessage' => '',
'test' => 'testForPortNumber',
'type' => 'numeric',
'afterHook' => 'zmqAfterHook',
),
'ZeroMQ_redis_password' => array(
'level' => 2,
'description' => 'The password, if set for Redis.',
'value' => '',
'errorMessage' => '',
'test' => 'testForEmpty',
'type' => 'string',
'afterHook' => 'zmqAfterHook',
),
'ZeroMQ_redis_database' => array(
'level' => 2,
'description' => 'The database to be used for queuing messages for the pub/sub functionality.',
'value' => '1',
'errorMessage' => '',
'test' => 'testForEmpty',
'type' => 'string',
'afterHook' => 'zmqAfterHook',
),
'ZeroMQ_redis_namespace' => array(
'level' => 2,
'description' => 'The namespace to be used for queuing messages for the pub/sub functionality.',
'value' => 'mispq',
'errorMessage' => '',
'test' => 'testForEmpty',
'type' => 'string',
'afterHook' => 'zmqAfterHook',
),
),
'debug' => array(
'level' => 0,
@ -1120,6 +1168,13 @@ class Server extends AppModel {
return $finalSettings;
}
public function serverSettingReadSingle($settingObject, $settingName, $leafKey) {
$setting = Configure::read($settingName);
$result = $this->__evaluateLeaf($settingObject, $leafKey, $setting);
$result['setting'] = $settingName;
return $result;
}
private function __evaluateLeaf($leafValue, $leafKey, $setting) {
if (isset($setting)) {
$result = $this->{$leafValue['test']}($setting);
@ -1239,6 +1294,23 @@ class Server extends AppModel {
return true;
}
public function zmqAfterHook($setting, $value) {
App::uses('PubSubTool', 'Tools');
$pubSubTool = new PubSubTool();
// If we are trying to change the enable setting to false, we don't need to test anything, just kill the server and return true.
if ($setting == 'Plugin.ZeroMQ_enable') {
if ($value == false || $value == 0) {
$pubSubTool->killService();
return true;
}
} elseif (!Configure::read('Plugin.ZeroMQ_enable')) {
// If we are changing any other ZeroMQ settings but the feature is disabled, don't reload the service
return true;
}
$pubSubTool->reloadServer();
return true;
}
// never come here directly, always go through a secondary check like testForTermsFile in order to also pass along the expected file path
private function __testForFile($value, $path) {
@ -1420,9 +1492,13 @@ class Server extends AppModel {
if (!Configure::read('Plugin.ZeroMQ_enable')) return 1;
App::uses('PubSubTool', 'Tools');
$pubSubTool = new PubSubTool();
if ($pubSubTool->testZMQ()) return 0;
if (!$pubSubTool->checkIfPythonLibInstalled()) {
$diagnostic_errors++;
return 2;
}
if ($pubSubTool->checkIfRunning()) return 0;
$diagnostic_errors++;
return 2;
return 3;
}
public function proxyDiagnostics(&$diagnostic_errors) {

View File

@ -64,7 +64,7 @@
<p>Mitre's STIX and Cybox python libraries have to be installed in order for MISP's STIX export to work. Make sure that you install them (as described in the MISP installation instructions) if you receive an error below.<br />
If you run into any issues here, make sure that both STIX and CyBox are installed as described in the INSTALL.txt file. The required versions are:<br /><b>STIX</b>: <?php echo $stix['stix']['expected'];?><br /><b>CyBox</b>: <?php echo $stix['cybox']['expected'];?><br />
Other versions might work but are not tested / recommended.</p>
<div style="background-color:#f7f7f9;width:600px;">
<div style="background-color:#f7f7f9;width:300px;">
<?php
$colour = 'green';
if ($stix['operational'] == 0) $colour = 'red';
@ -103,9 +103,14 @@
if ($zmqStatus > 1) {
$colour = 'red';
}
echo 'Proxy settings....<span style="color:' . $colour . ';">' . $message . '</span>';
echo 'ZeroMQ settings....<span style="color:' . $colour . ';">' . $message . '</span>';
?>
</div>
<div>
<span class="btn btn-inverse" style="padding-top:1px;padding-bottom:1px;" onClick = "zeroMQServerAction('start')">Start / Restart</span>
<span class="btn btn-inverse" style="padding-top:1px;padding-bottom:1px;" onClick = "zeroMQServerAction('stop')">Stop</span>
<span class="btn btn-inverse" style="padding-top:1px;padding-bottom:1px;" onClick = "zeroMQServerAction('status')">Status</span>
</div>
<h3>
Proxy
</h3>

View File

@ -0,0 +1,22 @@
<?php
$bgColour = '';
if (isset($setting['error']) && $setting['level'] < 3) {
$bgColour = 'background-color:' . $priorityErrorColours[$setting['level']] . ';';
if ($setting['level'] == 0 || $setting['level'] == 2) $bgColour .= 'color:white;';
}
if ($setting['level'] == 3) $bgColour = 'background-color:gray;color:white;';
if ($setting['type'] == 'boolean') $setting['value'] = ($setting['value'] === true ? 'true' : 'false');
if (isset($setting['options'])) $setting['value'] = ($setting['options'][$setting['value']]);
?>
<tr id ="<?php echo h($k); ?>_row">
<td class="short" style="<?php echo $bgColour; ?>"><?php echo h($priorities[$setting['level']]);?></td>
<td class="short" style="<?php echo $bgColour; ?>"><?php echo h($setting['setting']);?></td>
<?php if ((isset($setting['editable']) && !$setting['editable']) || $setting['level'] == 3): ?>
<td id="setting_<?php echo $k; ?>_passive" class="inline-field-solid" style="<?php echo $bgColour; ?>width:500px;"><?php echo nl2br(h($setting['value']));?></td>
<?php else: ?>
<td id="setting_<?php echo $k; ?>_solid" class="inline-field-solid" ondblclick="serverSettingsActivateField('<?php echo $setting['setting'];?>', '<?php echo $k;?>')" style="<?php echo $bgColour; ?>width:500px;"><?php echo h($setting['value']);?></td>
<td id="setting_<?php echo $k; ?>_placeholder" class="short hidden inline-field-placeholder" style="<?php echo $bgColour; ?>width:500px;"></td>
<?php endif; ?>
<td style="<?php echo $bgColour; ?>"><?php echo h($setting['description']);?></td>
<td style="<?php echo $bgColour; ?>"><?php if (isset($setting['error']) && $setting['level'] != 3) echo h($setting['errorMessage']); ?></td>
</tr>

View File

@ -17,7 +17,7 @@
if ($setting['type'] == 'boolean') $setting['value'] = ($setting['value'] === true ? 'true' : 'false');
if (isset($setting['options'])) $setting['value'] = ($setting['options'][$setting['value']]);
?>
<tr>
<tr id ="<?php echo h($k); ?>_row">
<td class="short" style="<?php echo $bgColour; ?>"><?php echo h($priorities[$setting['level']]);?></td>
<td class="short" style="<?php echo $bgColour; ?>"><?php echo h($setting['setting']);?></td>
<?php if ((isset($setting['editable']) && !$setting['editable']) || $setting['level'] == 3): ?>

View File

@ -0,0 +1,13 @@
<div class="confirmation">
<legend>ZeroMQ Server Status</legend>
<div style="padding-left:5px;padding-right:5px;padding-bottom:5px;">
<?php if (isset($time)): ?>
<p><b>Start time</b>: <?php echo h($time); ?><br />
<b>Settings read at</b>: <?php echo h($time2); ?><br />
<b>Events processed</b>: <?php echo h($events); ?></p>
<?php else: ?>
<p>The ZeroMQ server is unreachable.</p>
<?php endif; ?>
<span class="btn btn-inverse" id="PromptNoButton" onClick="cancelPrompt();">OK</span>
</div>
</div>

View File

@ -0,0 +1,78 @@
import zmq
import sys
import redis
import json
import os
import time
socket = None
r = None
namespace = None
settings = None
current_location = os.path.dirname(os.path.realpath(__file__))
pidfile = current_location + "/mispzmq.pid"
timestamp = time.time()
timestampSettings = timestamp
publishCount = 0
def setup():
global namespace
global socket
global r
global settings
global timestampSettings
with open(current_location + '/settings.json') as settings_file:
settings = json.load(settings_file)
namespace = settings["redis_namespace"]
r = redis.StrictRedis(host=settings["redis_host"], db=settings["redis_database"], password=settings["redis_password"], port=settings["redis_port"])
timestampSettings = time.time()
def handleCommand(command):
if command == "kill":
print "Kill command received, shutting down.\n"
removePidFile()
sys.exit()
if command == "reload":
print "Reload command received, reloading settings from file.\n"
setup()
if command == "status":
print "Status command received, responding with latest stats.\n"
r.delete(namespace + ":status")
r.lpush(namespace + ":status", json.dumps({"timestamp": timestamp, "timestampSettings": timestampSettings, "publishCount": publishCount}))
return
def removePidFile():
os.unlink(pidfile)
def createPidFile():
pid = str(os.getpid())
file(pidfile, 'w').write(pid)
def pubMessage(data):
context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.bind("tcp://*:%s" % settings["port"])
print "Sending " + data
time.sleep(1)
socket.send("%s %s" % ('misp_json', data))
socket.close()
context.term()
global publishCount
publishCount = publishCount + 1
def main(args):
setup()
createPidFile()
while True:
time.sleep(1)
command = r.lpop(namespace + ":command")
if command is not None:
handleCommand(command)
topic = "misp_json"
data = r.lpop(namespace + ":misp_json")
if data is None:
continue
pubMessage(data)
if __name__ == "__main__":
main(sys.argv)

View File

@ -0,0 +1,5 @@
try:
import zmq
except ImportError:
print "ZeroMQ library could not be imported."
print "OK"

View File

@ -1329,8 +1329,21 @@ function serverSettingSubmitForm(name, setting, id) {
$.ajax({
data: formData,
cache: false,
beforeSend: function (XMLHttpRequest) {
$(".loading").show();
},
success:function (data, textStatus) {
window.location.reload();
$.ajax({
type:"get",
url:"/servers/serverSettingsReloadSetting/" + setting + "/" + id,
success:function (data2, textStatus2) {
$('#' + id + '_row').replaceWith(data2);
$(".loading").hide();
},
error:function() {
showMessage('fail', 'Could not refresh the table.');
}
});
},
error:function() {
showMessage('fail', 'Request failed for an unknown reason.');
@ -1451,3 +1464,26 @@ function lookupPGPKey(emailFieldName) {
}
});
}
function zeroMQServerAction(action) {
$.ajax({
type: "get",
url: "/servers/" + action + "ZeroMQServer/",
beforeSend: function (XMLHttpRequest) {
$(".loading").show();
},
success: function (data) {
$(".loading").hide();
if (action !== 'status') {
window.location.reload();
} else {
$("#confirmation_box").html(data);
$("#confirmation_box").fadeIn();
$("#gray_out").fadeIn();
}
},
error: function (data, textStatus, errorThrown) {
showMessage('fail', textStatus + ": " + errorThrown);
}
});
}