mirror of https://github.com/MISP/misp-dashboard
Added support of named ZMQ + automatic Led colors in dashboard
parent
604951ab1d
commit
6425fc711e
|
@ -78,11 +78,12 @@ class EventMessage():
|
||||||
jsonMsg = { 'name': "undefined" ,'log': json.loads(msg) }
|
jsonMsg = { 'name': "undefined" ,'log': json.loads(msg) }
|
||||||
|
|
||||||
self.feedName = jsonMsg['name']
|
self.feedName = jsonMsg['name']
|
||||||
|
self.zmqName = jsonMsg['zmqName']
|
||||||
self.feed = json.loads(jsonMsg['log'])
|
self.feed = json.loads(jsonMsg['log'])
|
||||||
self.feed = LogItem(self.feed).get_row()
|
self.feed = LogItem(self.feed).get_row()
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
to_ret = { 'log': self.feed, 'feedName': self.feedName }
|
to_ret = { 'log': self.feed, 'feedName': self.feedName, 'zmqName': self.zmqName }
|
||||||
return 'data: {}\n\n'.format(json.dumps(to_ret))
|
return 'data: {}\n\n'.format(json.dumps(to_ret))
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
|
|
|
@ -7,6 +7,68 @@ for(i=0; i<maxNumPoint; i++) {
|
||||||
emptyArray.push([i, 0]);
|
emptyArray.push([i, 0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LedManager {
|
||||||
|
constructor() {
|
||||||
|
this._feedLedsTimeout = setTimeout(function(){ this.manageColors(); }, feedStatusFreqCheck);
|
||||||
|
this._feedLedKeepAlive = {};
|
||||||
|
this._allFeedName = [];
|
||||||
|
this._ledNum = 0;
|
||||||
|
this._nameToNumMapping = {}; //avoid bad ID if zmqname contains spaces
|
||||||
|
}
|
||||||
|
|
||||||
|
add_new_led(zmqname) {
|
||||||
|
this._allFeedName.push(zmqname);
|
||||||
|
this._nameToNumMapping[zmqname] = this._ledNum;
|
||||||
|
this._ledNum += 1;
|
||||||
|
this.add_new_html_led(zmqname);
|
||||||
|
this._feedLedKeepAlive[zmqname] = new Date().getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
add_new_html_led(zmqname) {
|
||||||
|
var ID = this._nameToNumMapping[zmqname]
|
||||||
|
var text = document.createElement('b');
|
||||||
|
text.innerHTML = zmqname;
|
||||||
|
var div = document.createElement('DIV');
|
||||||
|
div.id = "status_led_"+ID;
|
||||||
|
div.classList.add("led_green");
|
||||||
|
var sepa = document.createElement('DIV');
|
||||||
|
sepa.classList.add("leftSepa");
|
||||||
|
sepa.classList.add("textTopHeader");
|
||||||
|
sepa.appendChild(text);
|
||||||
|
sepa.appendChild(div);
|
||||||
|
$('#ledsHolder').append(sepa);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateKeepAlive(zmqname) {
|
||||||
|
if (this._allFeedName.indexOf(zmqname) == -1) {
|
||||||
|
this.add_new_led(zmqname);
|
||||||
|
}
|
||||||
|
this._feedLedKeepAlive[zmqname] = new Date().getTime();
|
||||||
|
this.resetTimeoutAndRestart(zmqname);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetTimeoutAndRestart(zmqName) {
|
||||||
|
clearTimeout(this._feedLedsTimeout); //cancel current leds timeout
|
||||||
|
this.manageColors();
|
||||||
|
}
|
||||||
|
|
||||||
|
manageColors() {
|
||||||
|
for (var feed in this._feedLedKeepAlive) {
|
||||||
|
var feedID = this._nameToNumMapping[feed];
|
||||||
|
var htmlLed = $("#status_led_"+feedID);
|
||||||
|
if(new Date().getTime() - this._feedLedKeepAlive[feed] > feedStatusFreqCheck) { // no feed
|
||||||
|
htmlLed.removeClass("led_green");
|
||||||
|
htmlLed.addClass("led_red");
|
||||||
|
} else {
|
||||||
|
htmlLed.removeClass("led_red");
|
||||||
|
htmlLed.addClass("led_green");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._feedLedsTimeout = setTimeout(function(){ ledmanager.manageColors(); }, feedStatusFreqCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
class Sources {
|
class Sources {
|
||||||
constructor() {
|
constructor() {
|
||||||
this._sourcesArray = {};
|
this._sourcesArray = {};
|
||||||
|
@ -101,6 +163,7 @@ class Sources {
|
||||||
|
|
||||||
var sources = new Sources();
|
var sources = new Sources();
|
||||||
sources.addSource('global');
|
sources.addSource('global');
|
||||||
|
var ledmanager = new LedManager();
|
||||||
|
|
||||||
var curNumLog = 0;
|
var curNumLog = 0;
|
||||||
var curMaxDataNumLog = 0;
|
var curMaxDataNumLog = 0;
|
||||||
|
@ -120,7 +183,7 @@ function connect_source_log() {
|
||||||
|
|
||||||
source_log.onmessage = function(event) {
|
source_log.onmessage = function(event) {
|
||||||
var json = jQuery.parseJSON( event.data );
|
var json = jQuery.parseJSON( event.data );
|
||||||
updateLogTable(json.feedName, json.log);
|
updateLogTable(json.feedName, json.log, json.zmqName);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,25 +219,15 @@ $(document).ready(function () {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function ledColorManager() {
|
|
||||||
$("#status_led").removeClass("led_orange");
|
|
||||||
if(new Date().getTime() - keepaliveTime > feedStatusFreqCheck) { // no feed
|
|
||||||
$("#status_led").removeClass("led_green");
|
|
||||||
$("#status_led").addClass("led_red");
|
|
||||||
} else {
|
|
||||||
$("#status_led").removeClass("led_red");
|
|
||||||
$("#status_led").addClass("led_green");
|
|
||||||
}
|
|
||||||
_timeoutLed = setTimeout(function(){ ledColorManager(); }, feedStatusFreqCheck);
|
|
||||||
}
|
|
||||||
_timeoutLed = setTimeout(function(){ ledColorManager(); }, feedStatusFreqCheck);
|
|
||||||
|
|
||||||
|
|
||||||
// LOG TABLE
|
// LOG TABLE
|
||||||
function updateLogTable(feedName, log) {
|
function updateLogTable(feedName, log, zmqName) {
|
||||||
if (log.length == 0)
|
if (log.length == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// update keepAlives
|
||||||
|
ledmanager.updateKeepAlive(zmqName);
|
||||||
|
|
||||||
// Create new row
|
// Create new row
|
||||||
tableBody = document.getElementById('table_log_body');
|
tableBody = document.getElementById('table_log_body');
|
||||||
|
|
||||||
|
@ -200,13 +253,11 @@ function updateLogTable(feedName, log) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (feedName == "Keepalive") {
|
} else if (feedName == "Keepalive") {
|
||||||
keepaliveTime = new Date().getTime();
|
// do nothing
|
||||||
clearTimeout(_timeoutLed); //cancel current led timeout
|
|
||||||
ledColorManager();
|
|
||||||
} else {
|
} else {
|
||||||
// do nothing
|
// do nothing
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function slideAndMax(orig, newData) {
|
function slideAndMax(orig, newData) {
|
||||||
|
|
|
@ -30,36 +30,33 @@ table {
|
||||||
}
|
}
|
||||||
|
|
||||||
.led_green {
|
.led_green {
|
||||||
float: right;
|
|
||||||
margin: auto auto;
|
margin: auto auto;
|
||||||
margin-top: 12.5px;
|
margin-top: 7.5px;
|
||||||
width: 24px;
|
width: 15px;
|
||||||
height: 24px;
|
height: 15px;
|
||||||
background-color: #ABFF00;
|
background-color: #ABFF00;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #304701 0 -1px 9px, #89FF00 0 2px 12px;
|
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #304701 0 -1px 9px, #89FF00 0 2px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.led_red {
|
.led_red {
|
||||||
float: right;
|
|
||||||
margin: auto auto;
|
margin: auto auto;
|
||||||
margin-top: 12.5px;
|
margin-top: 7.5px;
|
||||||
width: 24px;
|
width: 15px;
|
||||||
height: 24px;
|
height: 15px;
|
||||||
background-color: #F82222;
|
background-color: #F82222;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #304701 0 -1px 9px, #FF0303 0 2px 12px;
|
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #304701 0 -1px 9px, #FF0303 0 2px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.led_orange {
|
.led_orange {
|
||||||
float: right;
|
|
||||||
margin: auto auto;
|
margin: auto auto;
|
||||||
margin-top: 12.5px;
|
margin-top: 7.5px;
|
||||||
width: 24px;
|
width: 15px;
|
||||||
height: 24px;
|
height: 15px;
|
||||||
background-color: #FFB400;
|
background-color: #FFB400;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #304701 0 -1px 9px, #FF9000 0 2px 12px;
|
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #304701 0 -1px 9px, #FF9000 0 2px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.marker_animation {
|
.marker_animation {
|
||||||
|
@ -93,6 +90,13 @@ table {
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.textTopHeader {
|
||||||
|
height: 50px;
|
||||||
|
text-align: center;
|
||||||
|
margin-left: 8px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
small {
|
small {
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
@ -108,7 +112,7 @@ small {
|
||||||
<nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0; padding-left: 15px;">
|
<nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0; padding-left: 15px;">
|
||||||
<div class="navbar-header">
|
<div class="navbar-header">
|
||||||
<a class="navbar-brand" href="{{ url_for('index') }}">Misp feed dashboard</a>
|
<a class="navbar-brand" href="{{ url_for('index') }}">Misp feed dashboard</a>
|
||||||
<div id="status_led" class="led_orange"></div>
|
<div id="ledsHolder" style="float: right; height: 50px;"></div>
|
||||||
</div>
|
</div>
|
||||||
<!-- /.navbar-header -->
|
<!-- /.navbar-header -->
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import zmq
|
||||||
import redis
|
import redis
|
||||||
import random
|
import random
|
||||||
import configparser
|
import configparser
|
||||||
|
import argparse
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
|
@ -35,8 +36,8 @@ reader = geoip2.database.Reader(path_to_db)
|
||||||
|
|
||||||
channel_proc = "CoordToProcess"
|
channel_proc = "CoordToProcess"
|
||||||
|
|
||||||
def publish_log(name, content):
|
def publish_log(zmq_name, name, content):
|
||||||
to_send = { 'name': name, 'log': json.dumps(content) }
|
to_send = { 'name': name, 'log': json.dumps(content), 'zmqName': zmq_name }
|
||||||
redis_server.publish(channel, json.dumps(to_send))
|
redis_server.publish(channel, json.dumps(to_send))
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,7 +51,7 @@ def ip_to_coord(ip):
|
||||||
lon_corrected = float("{:.4f}".format(lon))
|
lon_corrected = float("{:.4f}".format(lon))
|
||||||
return { 'coord': {'lat': lat_corrected, 'lon': lon_corrected}, 'full_rep': resp }
|
return { 'coord': {'lat': lat_corrected, 'lon': lon_corrected}, 'full_rep': resp }
|
||||||
|
|
||||||
def getCoordAndPublish(supposed_ip, categ):
|
def getCoordAndPublish(zmq_name, supposed_ip, categ):
|
||||||
try:
|
try:
|
||||||
rep = ip_to_coord(supposed_ip)
|
rep = ip_to_coord(supposed_ip)
|
||||||
coord = rep['coord']
|
coord = rep['coord']
|
||||||
|
@ -77,17 +78,17 @@ def getCoordAndPublish(supposed_ip, categ):
|
||||||
## HANDLERS ##
|
## HANDLERS ##
|
||||||
##############
|
##############
|
||||||
|
|
||||||
def handler_log(jsonevent):
|
def handler_log(zmq_name, jsonevent):
|
||||||
print('sending', 'log')
|
print('sending', 'log')
|
||||||
return
|
return
|
||||||
|
|
||||||
def handler_keepalive(jsonevent):
|
def handler_keepalive(zmq_name, jsonevent):
|
||||||
print('sending', 'keepalive')
|
print('sending', 'keepalive')
|
||||||
to_push = [ jsonevent['uptime'] ]
|
to_push = [ jsonevent['uptime'] ]
|
||||||
publish_log('Keepalive', to_push)
|
publish_log(zmq_name, 'Keepalive', to_push)
|
||||||
|
|
||||||
def handler_event(jsonevent):
|
def handler_event(zmq_name, jsonevent):
|
||||||
print(jsonevent)
|
#print(jsonevent)
|
||||||
#fields: threat_level_id, id, info
|
#fields: threat_level_id, id, info
|
||||||
jsonevent = jsonevent['Event']
|
jsonevent = jsonevent['Event']
|
||||||
#redirect to handler_attribute
|
#redirect to handler_attribute
|
||||||
|
@ -95,16 +96,17 @@ def handler_event(jsonevent):
|
||||||
attributes = jsonevent['Attribute']
|
attributes = jsonevent['Attribute']
|
||||||
if attributes is list:
|
if attributes is list:
|
||||||
for attr in attributes:
|
for attr in attributes:
|
||||||
handler_attribute(attr)
|
handler_attribute(zmq_name, attr)
|
||||||
else:
|
else:
|
||||||
handler_attribute(jsonevent)
|
handler_attribute(zmq_name, jsonevent)
|
||||||
|
|
||||||
|
|
||||||
def handler_attribute(jsonattr):
|
def handler_attribute(zmq_name, jsonattr):
|
||||||
print(jsonattr)
|
|
||||||
jsonattr = jsonattr['Attribute']
|
jsonattr = jsonattr['Attribute']
|
||||||
|
print(jsonattr)
|
||||||
to_push = []
|
to_push = []
|
||||||
for field in json.loads(cfg.get('Log', 'fieldname_order')):
|
for field in json.loads(cfg.get('Log', 'fieldname_order')):
|
||||||
|
print(field)
|
||||||
if type(field) is list:
|
if type(field) is list:
|
||||||
to_add = cfg.get('Log', 'char_separator').join([ jsonattr[subField] for subField in field ])
|
to_add = cfg.get('Log', 'char_separator').join([ jsonattr[subField] for subField in field ])
|
||||||
else:
|
else:
|
||||||
|
@ -113,23 +115,24 @@ def handler_attribute(jsonattr):
|
||||||
|
|
||||||
#try to get coord
|
#try to get coord
|
||||||
if jsonattr['category'] == "Network activity":
|
if jsonattr['category'] == "Network activity":
|
||||||
getCoordAndPublish(jsonattr['value'], jsonattr['category'])
|
getCoordAndPublish(zmq_name, jsonattr['value'], jsonattr['category'])
|
||||||
|
|
||||||
publish_log('Attribute', to_push)
|
publish_log(zmq_name, 'Attribute', to_push)
|
||||||
|
|
||||||
|
|
||||||
def process_log(event):
|
def process_log(zmq_name, event):
|
||||||
event = event.decode('utf8')
|
event = event.decode('utf8')
|
||||||
topic, eventdata = event.split(' ', maxsplit=1)
|
topic, eventdata = event.split(' ', maxsplit=1)
|
||||||
jsonevent = json.loads(eventdata)
|
jsonevent = json.loads(eventdata)
|
||||||
dico_action[topic](jsonevent)
|
dico_action[topic](zmq_name, jsonevent)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main(zmqName):
|
||||||
while True:
|
while True:
|
||||||
content = socket.recv()
|
content = socket.recv()
|
||||||
content.replace(b'\n', b'') # remove \n...
|
content.replace(b'\n', b'') # remove \n...
|
||||||
process_log(content)
|
zmq_name = zmqName
|
||||||
|
process_log(zmq_name, content)
|
||||||
|
|
||||||
|
|
||||||
dico_action = {
|
dico_action = {
|
||||||
|
@ -144,6 +147,11 @@ dico_action = {
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
|
||||||
|
parser = argparse.ArgumentParser(description='A zmq subscriber. It subscribe to a ZNQ then redispatch it to the misp-dashboard')
|
||||||
|
parser.add_argument('-n', '--name', required=False, dest='zmqname', help='The ZMQ feed name', default="Misp Standard ZMQ")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
main(args.zmqname)
|
||||||
reader.close()
|
reader.close()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue