new backend 'multi' for #14

This commit is contained in:
ziirish 2014-09-13 14:33:59 +02:00
parent bfdf3d7efd
commit 268b2fa756
23 changed files with 534 additions and 119 deletions

View file

@ -20,6 +20,8 @@ if __name__ == '__main__':
(options, args) = parser.parse_args()
d = options.log
app.config['DEBUG'] = d
if d:
app.config['TESTING'] = True
if options.config:
if os.path.isfile(options.config):

View file

@ -10,7 +10,7 @@ __license__ = 'BSD 3-clause'
from flask import Flask
from flask.ext.login import LoginManager
from burpui.server import Server as BurpUI
from burpui.server import BUIServer as BurpUI
# First, we setup the app
app = Flask(__name__)

View file

@ -57,8 +57,8 @@ class LdapLoader:
query = 'uid={0}'.format(uid)
self.app.logger.info('query: %s | base: %s', query, self.base)
r = self.ldap.search(query, base_dn=self.base)
except:
self.app.logger.info('Ooops, LDAP lookup failed')
except Exception, e:
self.app.logger.error('Ooops, LDAP lookup failed: %s', str(e))
return None
return r[0]['uid'][0]

View file

@ -145,7 +145,7 @@ class Burp(BUIbackend):
Utilities functions
"""
def status(self, query='\n'):
def status(self, query='\n', agent=None):
"""
status connects to the burp status port, ask the given 'question' and
parses the output in an array
@ -178,7 +178,7 @@ class Burp(BUIbackend):
self.logger('error', 'Cannot contact burp server at %s:%s', self.host, self.port)
raise BUIserverException('Cannot contact burp server at {0}:{1}'.format(self.host, self.port))
def parse_backup_log(self, f, n, c=None):
def parse_backup_log(self, f, n, c=None, agent=None):
"""
parse_backup_log parses the log.gz of a given backup and returns a dict
containing different stats used to render the charts in the reporting view
@ -269,7 +269,7 @@ class Burp(BUIbackend):
break
return backup
def get_counters(self, name=None):
def get_counters(self, name=None, agent=None):
"""
get_counters parses the stats of the live status for a given client and
returns a dict
@ -310,7 +310,7 @@ class Burp(BUIbackend):
r['timeleft'] = -1
return r
def is_backup_running(self, name=None):
def is_backup_running(self, name=None, agent=None):
"""
is_backup_running returns True if the given client is currently running a
backup
@ -327,7 +327,7 @@ class Burp(BUIbackend):
return True
return False
def is_one_backup_running(self):
def is_one_backup_running(self, agent=None):
"""
is_one_backup_running returns a list of clients name that are currently
running a backup
@ -343,7 +343,7 @@ class Burp(BUIbackend):
self.running = r
return r
def get_all_clients(self):
def get_all_clients(self, agent=None):
"""
get_all_clients returns a list of dict representing each clients with their
name, state and last backup date
@ -370,7 +370,7 @@ class Burp(BUIbackend):
j.append(c)
return j
def get_client(self, name=None):
def get_client(self, name=None, agent=None):
"""
get_client returns a list of dict representing the backups (with its number
and date) of a given client
@ -399,7 +399,7 @@ class Burp(BUIbackend):
r.reverse()
return r
def get_tree(self, name=None, backup=None, root=None):
def get_tree(self, name=None, backup=None, root=None, agent=None):
"""
get_tree returns a list of dict representing files/dir (with their attr)
within a given path
@ -442,7 +442,7 @@ class Burp(BUIbackend):
r.append(t)
return r
def restore_files(self, name=None, backup=None, files=None):
def restore_files(self, name=None, backup=None, files=None, agent=None):
if not name or not backup or not files:
return None
flist = json.loads(files)

View file

@ -6,28 +6,28 @@ class BUIbackend:
self.host = host
self.port = port
def status(self, query='\n'):
def status(self, query='\n', agent=None):
raise NotImplementedError("Sorry, the current Backend does not implement this method!")
def parse_backup_log(self, f, n, c=None):
def parse_backup_log(self, f, n, c=None, agent=None):
raise NotImplementedError("Sorry, the current Backend does not implement this method!")
def get_counters(self, name=None):
def get_counters(self, name=None, agent=None):
raise NotImplementedError("Sorry, the current Backend does not implement this method!")
def is_backup_running(self, name=None):
def is_backup_running(self, name=None, agent=None):
raise NotImplementedError("Sorry, the current Backend does not implement this method!")
def is_one_backup_running(self):
def is_one_backup_running(self, agent=None):
raise NotImplementedError("Sorry, the current Backend does not implement this method!")
def get_all_clients(self):
def get_all_clients(self, agent=None):
raise NotImplementedError("Sorry, the current Backend does not implement this method!")
def get_client(self, name=None):
def get_client(self, name=None, agent=None):
raise NotImplementedError("Sorry, the current Backend does not implement this method!")
def get_tree(self, name=None, backup=None, root=None):
def get_tree(self, name=None, backup=None, root=None, agent=None):
raise NotImplementedError("Sorry, the current Backend does not implement this method!")
class BUIserverException(Exception):

View file

@ -0,0 +1,249 @@
# -*- coding: utf8 -*-
import re
import socket
import sys
import json
import struct
import ConfigParser
from burpui.misc.backend.interface import BUIbackend, BUIserverException
class Burp(BUIbackend):
def __init__(self, app=None, conf=None):
self.app = app
self.servers = {}
self.servers_status = {}
if conf:
config = ConfigParser.ConfigParser()
with open(conf) as fp:
config.readfp(fp)
for sec in config.sections():
r = re.match('^Agent:(.+)$', sec)
if r:
try:
host = config.get(sec, 'host')
port = config.getint(sec, 'port')
password = config.get(sec, 'password')
ssl = config.getboolean(sec, 'ssl')
except Exception, e:
self.app.logger.error(str(e))
self.servers[r.group(1)] = NClient(app, host, port, password, ssl)
self.app.logger.debug(self.servers)
for key, serv in self.servers.iteritems():
self.servers_status[key] = {'clients': [], 'alive': serv.connected}
if not serv.connected:
continue
for c in serv.get_all_clients(key):
self.servers_status[key]['clients'].append(c['name'])
"""
Utilities functions
"""
def status(self, query='\n', agent=None):
"""
status connects to the burp status port, ask the given 'question' and
parses the output in an array
"""
return self.servers[agent].status(query)
def parse_backup_log(self, f, n, c=None, agent=None):
"""
parse_backup_log parses the log.gz of a given backup and returns a dict
containing different stats used to render the charts in the reporting view
"""
return self.servers[agent].parse_backup_log(f, n, c)
def get_counters(self, name=None, agent=None):
"""
get_counters parses the stats of the live status for a given client and
returns a dict
"""
return self.servers[agent].get_counters(name)
def is_backup_running(self, name=None, agent=None):
"""
is_backup_running returns True if the given client is currently running a
backup
"""
return self.servers[agent].is_backup_running(name)
def is_one_backup_running(self, agent=None):
"""
is_one_backup_running returns a list of clients name that are currently
running a backup
"""
return self.servers[agent].is_one_backup_running()
def get_all_clients(self, agent=None):
"""
get_all_clients returns a list of dict representing each clients with their
name, state and last backup date
"""
return self.servers[agent].get_all_clients()
def get_client(self, name=None, agent=None):
"""
get_client returns a list of dict representing the backups (with its number
and date) of a given client
"""
return self.servers[agent].get_client(name)
def get_tree(self, name=None, backup=None, root=None, agent=None):
"""
get_tree returns a list of dict representing files/dir (with their attr)
within a given path
"""
return self.servers[agent].get_tree(name, backup, root)
def restore_files(self, name=None, backup=None, files=None, agent=None):
pass
class NClient(BUIbackend):
def __init__(self, app=None, host=None, port=None, password=None, ssl=None):
self.host = host
self.port = port
self.password = password
self.ssl = ssl
self.nok = False
self.connected = False
self.app = app
self.conn()
def conn(self):
try:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((self.host, self.port))
self.connected = True
self.app.logger.debug('OK, connected to agent %s:%s', self.host, self.port)
except Exception, e:
self.connected = False
self.app.logger.error('Could not connect to %s:%s => %s', self.host, self.port, str(e))
def send_command(self, data=None):
if not data:
return
old_data = data
try:
data['password'] = self.password
raw = json.dumps(data)
length = len(raw)
self.sock.sendall(struct.pack('!Q', length))
self.sock.sendall(raw)
self.app.logger.debug("Sending: %s", raw)
res = self.sock.recv(10)
self.app.logger.debug("recv: '%s'", res)
if 'OK Result:' != res:
self.app.logger.debug('Ooops, unsuccessful!')
self.nok = True
return
self.app.logger.debug("Data sent successfully")
self.nok = False
except Exception, e:
self.app.logger.error(str(e))
self.conn()
self.nok = True
self.send_command(old_data)
def get_result(self):
if self.nok:
return None
self.app.logger.debug('What now?')
lengthbuf = self.sock.recv(8)
length, = struct.unpack('!Q', lengthbuf)
return self.recvall(length)
def recvall(self, length):
buf = b''
bsize = 1024
while length:
newbuf = self.sock.recv(1024)
if not newbuf: return None
buf += newbuf
length -= len(newbuf)
self.app.logger.debug('result: %s', buf)
return buf
"""
Utilities functions
"""
def status(self, query='\n', agent=None):
"""
status connects to the burp status port, ask the given 'question' and
parses the output in an array
"""
data = {'func': 'status', 'args': {'query': query}}
self.send_command(data)
return json.loads(self.get_result())
def parse_backup_log(self, f, n, c=None, agent=None):
"""
parse_backup_log parses the log.gz of a given backup and returns a dict
containing different stats used to render the charts in the reporting view
"""
data = {'func': 'parse_backup_log', 'args': {'f': f, 'n': n, 'c': c}}
self.send_command(data)
return json.loads(self.get_result())
def get_counters(self, name=None, agent=None):
"""
get_counters parses the stats of the live status for a given client and
returns a dict
"""
data = {'func': 'get_counters', 'args': {'name': name}}
self.send_command(data)
return json.loads(self.get_result())
def is_backup_running(self, name=None, agent=None):
"""
is_backup_running returns True if the given client is currently running a
backup
"""
data = {'func': 'is_backup_running', 'args': {'name': name}}
self.send_command(data)
return json.loads(self.get_result())
def is_one_backup_running(self, agent=None):
"""
is_one_backup_running returns a list of clients name that are currently
running a backup
"""
data = {'func': 'is_one_backup_running', 'args': None}
self.send_command(data)
return json.loads(self.get_result())
def get_all_clients(self, agent=None):
"""
get_all_clients returns a list of dict representing each clients with their
name, state and last backup date
"""
data = {'func': 'get_all_clients', 'args': None}
self.send_command(data)
return json.loads(self.get_result())
def get_client(self, name=None, agent=None):
"""
get_client returns a list of dict representing the backups (with its number
and date) of a given clientm
"""
data = {'func': 'get_client', 'args': {'name': name}}
self.send_command(data)
return json.loads(self.get_result())
def get_tree(self, name=None, backup=None, root=None, agent=None):
"""
get_tree returns a list of dict representing files/dir (with their attr)
within a given path
"""
data = {'func': 'get_tree', 'args': {'name': name, 'backup': backup, 'root': root}}
self.send_command(data)
return json.loads(self.get_result())
def restore_files(self, name=None, backup=None, files=None, agent=None):
return None

View file

@ -15,15 +15,6 @@ def load_user(userid):
return bui.uhandler.user(userid)
return None
@app.route('/test/download')
def test_download():
try:
resp = send_file('/tmp/monfichierr', as_attachment=True)
resp.set_cookie('fileDownload', 'true')
return resp
except Exception, e:
abort(500)
@app.route('/api/restore/<name>/<int:backup>', methods=['POST'])
@login_required
def restore(name=None, backup=None):
@ -47,25 +38,32 @@ The whole API returns JSON-formated data
"""
@app.route('/api/running-clients.json')
@app.route('/api/<server>/running-clients.json')
@login_required
def running_clients():
def running_clients(server=None):
"""
API: running_clients
:returns: a list of running clients
"""
r = bui.cli.is_one_backup_running()
if not server:
server = request.args.get('server')
r = bui.cli.is_one_backup_running(agent=server)
return jsonify(results=r)
@app.route('/api/render-live-template', methods=['GET'])
@app.route('/api/<server>/render-live-template', methods=['GET'])
@app.route('/api/render-live-template/<name>')
@app.route('/api/<server>/render-live-template/<name>')
@login_required
def render_live_tpl(name=None):
def render_live_tpl(server=None, name=None):
"""
API: render_live_tpl
:param name: the client name if any. You can also use the GET parameter
'name' to achieve the same thing
:returns: HTML that should be included directly into the page
"""
if not server:
server = request.args.get('server')
c = request.args.get('name')
if not name and not c:
abort(500)
@ -74,136 +72,166 @@ def render_live_tpl(name=None):
if name not in bui.cli.running:
abort(404)
try:
counters = bui.cli.get_counters(name)
counters = bui.cli.get_counters(name, agent=server)
except BUIserverException:
counters = []
return render_template('live-monitor-template.html', cname=name, counters=counters)
return render_template('live-monitor-template.html', cname=name, counters=counters, server=server)
@app.route('/api/servers.json')
@login_required
def servers_json():
r = []
for serv in bui.cli.servers_status:
r.append({'name': serv, 'clients': len(bui.cli.servers_status[serv]['clients']), 'connected': bui.cli.servers_status[serv]['alive']})
return jsonify(results=r)
@app.route('/api/live.json')
@app.route('/api/<server>/live.json')
@login_required
def live():
def live(server=None):
"""
API: live
:returns: the live status of the server
"""
if not server:
server = request.args.get('server')
r = []
for c in bui.cli.is_one_backup_running():
for c in bui.cli.is_one_backup_running(agent=server):
s = {}
s['client'] = c
try:
s['status'] = bui.cli.get_counters(c)
s['status'] = bui.cli.get_counters(c, agent=server)
except BUIserverException:
s['status'] = []
r.append(s)
return jsonify(results=r)
@app.route('/api/running.json')
@app.route('/api/<server>/running.json')
@login_required
def backup_running():
def backup_running(server=None):
"""
API: backup_running
:returns: true if at least one backup is running
"""
j = bui.cli.is_one_backup_running()
if not server:
server = request.args.get('server')
j = bui.cli.is_one_backup_running(agent=server)
r = len(j) > 0
return jsonify(results=r)
@app.route('/api/client-tree.json/<name>/<int:backup>', methods=['GET'])
@app.route('/api/<server>/client-tree.json/<name>/<int:backup>', methods=['GET'])
@login_required
def client_tree(name=None, backup=None):
def client_tree(server=None, name=None, backup=None):
"""
WebService: return a specific client files tree
:param name: the client name (mandatory)
:param backup: the backup number (mandatory)
"""
if not server:
server = request.args.get('server')
j = []
if not name or not backup:
return jsonify(results=j)
root = request.args.get('root')
try:
j = bui.cli.get_tree(name, backup, root)
j = bui.cli.get_tree(name, backup, root, agent=server)
except BUIserverException, e:
err = [[2, str(e)]]
return jsonify(notif=err)
return jsonify(results=j)
@app.route('/api/clients-report.json')
@app.route('/api/<server>/clients-report.json')
@login_required
def clients_report_json():
def clients_report_json(server=None):
"""
WebService: return a JSON with global stats
"""
if not server:
server = request.args.get('server')
j = []
try:
clients = bui.cli.get_all_clients()
clients = bui.cli.get_all_clients(agent=server)
except BUIserverException, e:
err = [[2, str(e)]]
return jsonify(notif=err)
cl = []
ba = []
for c in clients:
client = bui.cli.get_client(c['name'])
client = bui.cli.get_client(c['name'], agent=server)
if not client:
continue
f = bui.cli.status('c:{0}:b:{1}:f:log.gz\n'.format(c['name'], client[-1]['number']))
cl.append( { 'name': c['name'], 'stats': bui.cli.parse_backup_log(f, client[-1]['number']) } )
f = bui.cli.status('c:{0}:b:{1}:f:log.gz\n'.format(c['name'], client[-1]['number']), agent=server)
cl.append( { 'name': c['name'], 'stats': bui.cli.parse_backup_log(f, client[-1]['number'], agent=server) } )
for b in client:
f = bui.cli.status('c:{0}:b:{1}:f:log.gz\n'.format(c['name'], b['number']))
ba.append(bui.cli.parse_backup_log(f, b['number'], c['name']))
f = bui.cli.status('c:{0}:b:{1}:f:log.gz\n'.format(c['name'], b['number']), agent=server)
ba.append(bui.cli.parse_backup_log(f, b['number'], c['name']), agent=server)
j.append( { 'clients': cl, 'backups': sorted(ba, key=lambda k: k['end']) } )
return jsonify(results=j)
@app.route('/api/client-stat.json/<name>')
@app.route('/api/<server>/client-stat.json/<name>')
@app.route('/api/client-stat.json/<name>/<int:backup>')
@app.route('/api/<server>/client-stat.json/<name>/<int:backup>')
@login_required
def client_stat_json(name=None, backup=None):
def client_stat_json(server=None, name=None, backup=None):
"""
WebService: return a specific client detailed report
"""
if not server:
server = request.args.get('server')
j = []
if not name:
err = [[1, 'No client defined']]
return jsonify(notif=err)
if backup:
try:
f = bui.cli.status('c:{0}:b:{1}:f:log.gz\n'.format(name, backup))
f = bui.cli.status('c:{0}:b:{1}:f:log.gz\n'.format(name, backup), agent=server)
except BUIserverException, e:
err = [[2, str(e)]]
return jsonify(notif=err)
j = bui.cli.parse_backup_log(f, backup)
j = bui.cli.parse_backup_log(f, backup, agent=server)
else:
try:
cl = bui.cli.get_client(name)
cl = bui.cli.get_client(name, agent=server)
except BUIserverException, e:
err = [[2, str(e)]]
return jsonify(notif=err)
for c in cl:
f = bui.cli.status('c:{0}:b:{1}:f:log.gz\n'.format(name, c['number']))
j.append(bui.cli.parse_backup_log(f, c['number']))
f = bui.cli.status('c:{0}:b:{1}:f:log.gz\n'.format(name, c['number']), agent=server)
j.append(bui.cli.parse_backup_log(f, c['number']), agent=server)
return jsonify(results=j)
@app.route('/api/client.json/<name>')
@app.route('/api/<server>/client.json/<name>')
@login_required
def client_json(name=None):
def client_json(server=None, name=None):
"""
WebService: return a specific client backups overview
"""
if not server:
server = request.args.get('server')
try:
j = bui.cli.get_client(name)
j = bui.cli.get_client(name, agent=server)
except BUIserverException, e:
err = [[2, str(e)]]
return jsonify(notif=err)
return jsonify(results=j)
@app.route('/api/clients.json')
@app.route('/api/<server>/clients.json')
@login_required
def clients():
def clients_json(server=None):
"""
WebService: return a JSON listing all clients
"""
if not server:
server = request.args.get('server')
try:
j = bui.cli.get_all_clients()
j = bui.cli.get_all_clients(agent=server)
except BUIserverException, e:
err = [[2, str(e)]]
return jsonify(notif=err)
@ -240,63 +268,83 @@ And here is the main site
"""
@app.route('/live-monitor')
@app.route('/<server>/live-monitor')
@app.route('/live-monitor/<name>')
@app.route('/<server>/live-monitor/<name>')
@login_required
def live_monitor(name=None):
def live_monitor(server=None, name=None):
"""
Live status monitor view
"""
if not server:
server = request.get.args('server')
if not bui.cli.running:
flash('Sorry, there are no running backups', 'warning')
return redirect(url_for('home'))
return render_template('live-monitor.html', live=True, cname=name)
return render_template('live-monitor.html', live=True, cname=name, server=server)
@app.route('/client-browse/<name>', methods=['GET'])
@app.route('/<server>/client-browse/<name>', methods=['GET'])
@app.route('/client-browse/<name>/<int:backup>')
@app.route('/<server>/client-browse/<name>/<int:backup>')
@login_required
def client_browse(name=None, backup=None):
def client_browse(server=None, name=None, backup=None):
"""
Browse a specific backup of a specific client
"""
if not server:
server = request.args.get('server')
bkp = request.args.get('backup')
if bkp and not backup:
return redirect(url_for('client_browse', name=name, backup=bkp))
return render_template('client-browse.html', tree=True, backup=True, overview=True, cname=name, nbackup=backup)
return redirect(url_for('client_browse', name=name, backup=bkp, server=server))
return render_template('client-browse.html', tree=True, backup=True, overview=True, cname=name, nbackup=backup, server=server)
@app.route('/client-report/<name>')
@app.route('/<server>/client-report/<name>')
@login_required
def client_report(name=None):
def client_report(server=None, name=None):
"""
Specific client report
"""
l = bui.cli.get_client(name)
if not server:
server = request.args.get('server')
l = bui.cli.get_client(name, agent=server)
if len(l) == 1:
return redirect(url_for('backup_report', name=name, backup=l[0]['number']))
return render_template('client-report.html', client=True, report=True, cname=name)
return redirect(url_for('backup_report', name=name, backup=l[0]['number']), server=server)
return render_template('client-report.html', client=True, report=True, cname=name, server=server)
@app.route('/clients-report')
@app.route('/<server>/clients-report')
@login_required
def clients_report():
def clients_report(server=None):
"""
Global report
"""
return render_template('clients-report.html', clients=True, report=True)
if not server:
server = request.args.get('server')
return render_template('clients-report.html', clients=True, report=True, server=server)
@app.route('/backup-report/<name>', methods=['GET'])
@app.route('/<server>/backup-report/<name>', methods=['GET'])
@app.route('/backup-report/<name>/<int:backup>', methods=['GET'])
@app.route('/<server>/backup-report/<name>/<int:backup>', methods=['GET'])
@login_required
def backup_report(name=None, backup=None):
def backup_report(server=None, name=None, backup=None):
"""
Backup specific report
"""
if not backup:
backup = request.args.get('backup')
return render_template('backup-report.html', client=True, backup=True, report=True, cname=name, nbackup=backup)
if not server:
server = request.args.get('server')
return render_template('backup-report.html', client=True, backup=True, report=True, cname=name, nbackup=backup, server=server)
@app.route('/client', methods=['GET'])
@app.route('/<server>/client', methods=['GET'])
@app.route('/client/<name>')
@app.route('/<server>/client/<name>')
@login_required
def client(name=None):
def client(server=None, name=None):
"""
Specific client overview
"""
@ -304,9 +352,24 @@ def client(name=None):
c = name
else:
c = request.args.get('name')
if bui.cli.is_backup_running(c):
return redirect(url_for('live_monitor', name=name))
return render_template('client.html', client=True, overview=True, cname=c)
if not server:
server = request.args.get('server')
if bui.cli.is_backup_running(c, agent=server):
return redirect(url_for('live_monitor', name=name, server=server))
return render_template('client.html', client=True, overview=True, cname=c, server=server)
@app.route('/clients', methods=['GET'])
@app.route('/<server>/clients', methods=['GET'])
@login_required
def clients(server=None):
if not server:
server = request.args.get('server')
return render_template('clients.html', clients=True, overview=True, server=server)
@app.route('/servers', methods=['GET'])
@login_required
def servers():
return render_template('servers.html', servers=True, overview=True)
@app.route('/login', methods=['POST', 'GET'])
def login():
@ -317,6 +380,8 @@ def login():
login_user(user, remember=form.remember.data)
flash('Logged in successfully', 'success')
return redirect(request.args.get("next") or url_for('home'))
else:
flash('Wrong username or password', 'danger')
return render_template('login.html', form=form, login=True)
@app.route('/logout')
@ -331,4 +396,7 @@ def home():
"""
Home page
"""
return render_template('clients.html', clients=True, overview=True)
if bui.standalone:
return redirect(url_for('clients'))
else:
return redirect(url_for('servers'))

View file

@ -6,18 +6,19 @@ g_port = 5000
g_bind = '::'
g_refresh = 15
g_ssl = False
g_standalone = True
g_sslcert = ''
g_sslkey = ''
g_version = 1
g_auth = 'basic'
class Server:
class BUIServer:
def __init__(self, app=None):
self.init = False
self.app = app
def setup(self, conf=None):
global g_refresh, g_port, g_bind, g_ssl, g_sslcert, g_sslkey, g_version, g_auth
global g_refresh, g_port, g_bind, g_ssl, g_sslcert, g_sslkey, g_version, g_auth, g_standalone
self.sslcontext = None
if not conf:
conf = self.app.config['CFG']
@ -27,7 +28,8 @@ class Server:
config = ConfigParser.ConfigParser({'port': g_port,'bind': g_bind,
'refresh': g_refresh, 'ssl': g_ssl, 'sslcert': g_sslcert,
'sslkey': g_sslkey, 'version': g_version, 'auth': g_auth})
'sslkey': g_sslkey, 'version': g_version, 'auth': g_auth,
'standalone': g_standalone})
with open(conf) as fp:
config.readfp(fp)
try:
@ -39,6 +41,11 @@ class Server:
except ValueError:
self.app.logger.error("Wrong value for 'ssl' key! Assuming 'false'")
self.ssl = False
try:
self.standalone = config.getboolean('Global', 'standalone')
except ValueError:
self.app.logger.error("Wrong value for 'standalone' key! Assuming 'True'")
self.standalone = True
self.sslcert = config.get('Global', 'sslcert')
self.sslkey = config.get('Global', 'sslkey')
self.auth = config.get('Global', 'auth')
@ -53,21 +60,28 @@ class Server:
else:
# I know that's ugly, but hey, I need it!
self.app.login_manager._login_disabled = True
except ConfigParser.NoOptionError:
self.app.logger.error("Missing option")
except ConfigParser.NoOptionError, e:
self.app.logger.error(str(e))
self.app.config['REFRESH'] = config.getint('UI', 'refresh')
self.app.config['STANDALONE'] = self.standalone
self.app.logger.info('burp version: %d', self.vers)
self.app.logger.info('listen port: %d', self.port)
self.app.logger.info('bind addr: %s', self.bind)
self.app.logger.info('use ssl: %s', self.ssl)
self.app.logger.info('standalone: %s', self.standalone)
self.app.logger.info('sslcert: %s', self.sslcert)
self.app.logger.info('sslkey: %s', self.sslkey)
self.app.logger.info('refresh: %d', self.app.config['REFRESH'])
if self.standalone:
module = 'burpui.misc.backend.burp{0}'.format(self.vers)
else:
module = 'burpui.misc.backend.multi'
try:
mod = __import__('burpui.misc.backend.burp{0}'.format(self.vers), fromlist=['Burp'])
mod = __import__(module, fromlist=['Burp'])
Client = mod.Burp
self.cli = Client(self.app, conf=conf)
except Exception, e:
@ -87,6 +101,7 @@ class Server:
self.sslcontext.use_certificate_file(self.sslcert)
if self.sslcontext:
self.app.config['SSL'] = True
self.app.run(host=self.bind, port=self.port, debug=debug, ssl_context=self.sslcontext)
else:
self.app.run(host=self.bind, port=self.port, debug=debug)

View file

@ -5,7 +5,7 @@
{% include "small_topbar.html" %}
<ul class="breadcrumb" style="margin-bottom: 5px;">
<li><a href="{{ url_for('home') }}">Home</a></li>
<li><a href="{{ url_for('client', name=cname) }}">{{ cname }} overview</a></li>
<li><a href="{{ url_for('client', name=cname, server=server) }}">{{ cname }} overview</a></li>
<li class="active">Backup n°{{ nbackup|mypad }} overview</li>
</ul>
<br />

View file

@ -34,8 +34,9 @@ var notif = function(type, message) {
};
{% if not login -%}
{% if not (servers and overview) -%}
var _check_running = function() {
url = '{{ url_for("backup_running") }}';
url = '{{ url_for("backup_running", server=server) }}';
$.getJSON(url, function(data) {
if (data.results) {
$('#toblink').addClass('blink');
@ -44,6 +45,11 @@ var _check_running = function() {
}
});
};
{% else -%}
var _check_running = function() {
return false;
};
{% endif -%}
{% endif -%}
/***
@ -54,7 +60,7 @@ var _clients_bh = new Bloodhound({
queryTokenizer: Bloodhound.tokenizers.whitespace,
limit: 10,
prefetch: {
url: '{{ url_for("clients") }}',
url: '{{ url_for("clients_json", server=server) }}',
filter: function(list) {
if (list.results) {
return list.results;
@ -75,9 +81,12 @@ $('#input-client').typeahead(null, {
source: _clients_bh.ttAdapter()
});
{% if servers and overview -%}
{% include "js/servers.js" %}
{% endif -%}
{% if clients and overview -%}
{% include "js/home.js" %}
{% include "js/clients.js" %}
{% endif -%}
{% if clients and report -%}
@ -148,6 +157,9 @@ $(function() {
{% if not login -%}
_check_running();
{% endif -%}
{% if servers and overview -%}
_servers();
{% endif -%}
});
/***
@ -156,7 +168,7 @@ $(function() {
var search = $('input[id="input-client"]');
search.keypress(function(e) {
if (e.which == 13) {
window.location = '{{ url_for("client") }}?name='+search.val();
window.location = '{{ url_for("client", server=server) }}?name='+search.val();
}
});
@ -182,6 +194,9 @@ $(function() {
{% if live -%}
_live();
{% endif -%}
{% if servers and overview -%}
_servers();
{% endif -%}
{% if not report and not login -%}
/***
@ -197,6 +212,9 @@ $(function() {
{% if live -%}
_live();
{% endif -%}
{% if servers and overview -%}
_servers();
{% endif -%}
return;
}, {{ config.REFRESH * 1000 }});
{% endif -%}

View file

@ -34,7 +34,7 @@ var _client = function() {
chart_unified.yAxis.tickFormat(d3.format(',.0f'));
}
url = '{{ url_for("client_stat_json", name=cname, backup=nbackup) }}';
url = '{{ url_for("client_stat_json", name=cname, backup=nbackup, server=server) }}';
$.getJSON(url, function(d) {
var _fields = [ 'dir', 'files', 'hardlink', 'softlink' ];
j = d.results;

View file

@ -56,7 +56,7 @@
},
source: function() {
r = [];
$.getJSON('{{ url_for("client_tree", name=cname, backup=nbackup) }}', function(data) {
$.getJSON('{{ url_for("client_tree", name=cname, backup=nbackup, server=server) }}', function(data) {
if (!data.results) {
if (data.notif) {
$.each(data.notif, function(i, n) {
@ -84,7 +84,7 @@
r = [];
p = node.key;
if (p !== "/") p += '/';
$.getJSON('{{ url_for("client_tree", name=cname, backup=nbackup) }}?root='+p, function(data) {
$.getJSON('{{ url_for("client_tree", name=cname, backup=nbackup, server=server) }}?root='+p, function(data) {
if (!data.results) {
if (data.notif) {
$.each(data.notif, function(i, n) {

View file

@ -73,7 +73,7 @@ var _client = function() {
_chart_stats.bars.forceY([0]);
}
url = '{{ url_for("client_stat_json", name=cname) }}';
url = '{{ url_for("client_stat_json", name=cname, server=server) }}';
$.getJSON(url, function(d) {
var _fields = [ 'dir', 'files', 'hardlink', 'softlink', 'files_enc', 'meta', 'meta_enc', 'special', 'efs', 'vssheader', 'vssheader_enc', 'vssfooter', 'vssfooter_enc' ];
var stats = true;

View file

@ -22,7 +22,7 @@
* The JSON is then parsed into a table
*/
var _client = function() {
url = '{{ url_for("client_json", name=cname) }}';
url = '{{ url_for("client_json", name=cname, server=server) }}';
$.getJSON(url, function(data) {
$('#table-client >tbody:last').empty();
$('#client-alert').hide();
@ -41,7 +41,7 @@ var _client = function() {
$('#client-alert').show();
} else {
$.each(data.results.reverse(), function(j, c) {
$('#table-client> tbody:last').append('<tr class="clickable" style="cursor: pointer;"><td><a href="{{ url_for("client_browse", name=cname) }}?backup='+c.number+'" style="color: inherit; text-decoration: inherit;">'+pad(c.number, 7)+'</a></td><td>'+c.date+'</td></tr>');
$('#table-client> tbody:last').append('<tr class="clickable" style="cursor: pointer;"><td><a href="{{ url_for("client_browse", name=cname, server=server) }}?backup='+c.number+'" style="color: inherit; text-decoration: inherit;">'+pad(c.number, 7)+'</a></td><td>'+c.date+'</td></tr>');
});
}
});

View file

@ -35,7 +35,7 @@ var _clients = function() {
_charts_obj.push({ 'key': 'chart_'+j, 'obj': tmp, 'data': [] });
});
}
url = '{{ url_for("clients_report_json") }}';
url = '{{ url_for("clients_report_json", server=server) }}';
$.getJSON(url, function(d) {
rep = [];
size = [];

View file

@ -33,7 +33,7 @@ var __status = {
* The JSON is then parsed into a table
*/
var _clients = function() {
url = '{{ url_for("clients") }}';
url = '{{ url_for("clients_json", server=server) }}';
$.getJSON(url, function(data) {
$('#table-clients > tbody:last').empty();
if (!data.results) {
@ -49,7 +49,7 @@ var _clients = function() {
if (__status[c.state] != undefined) {
clas = ' '+__status[c.state];
}
$('#table-clients > tbody:last').append('<tr class="clickable'+clas+'" style="cursor: pointer;"><td><a href="{{ url_for("client") }}?name='+c.name+'" style="color: inherit; text-decoration: inherit;">'+c.name+'</a></td><td>'+c.state+'</td><td>'+c.last+'</td></tr>');
$('#table-clients > tbody:last').append('<tr class="clickable'+clas+'" style="cursor: pointer;"><td><a href="{{ url_for("client", server=server) }}?name='+c.name+'" style="color: inherit; text-decoration: inherit;">'+c.name+'</a></td><td>'+c.state+'</td><td>'+c.last+'</td></tr>');
});
});
};

View file

@ -1,13 +1,13 @@
_live = function() {
url = '{{ url_for("running_clients") }}';
url = '{{ url_for("running_clients", server=server) }}';
html = ''
$.getJSON(url, function(data) {
if (!data.results || data.results.length === 0) {
document.location = '{{ url_for("home") }}';
document.location = '{{ url_for("home", server=server) }}';
}
$.each(data.results, function(i, c) {
u = '{{ url_for("render_live_tpl") }}?name='+c;
u = '{{ url_for("render_live_tpl", server=server) }}?name='+c;
$.get(u, function(d) {
html += d;
});

View file

@ -0,0 +1,33 @@
/***
* Here is the 'servers' part
* It is available on the global clients view
*/
/***
* _servers: function that retrieve up-to-date informations from the burp server
* The JSON is then parsed into a table
*/
var _servers = function() {
url = '{{ url_for("servers_json") }}';
$.getJSON(url, function(data) {
$('#table-servers > tbody:last').empty();
if (!data.results) {
if (data.notif) {
$.each(data.notif, function(i, n) {
notif(n[0], n[1]);
});
}
return;
}
$.each(data.results, function(j, c) {
cl = '';
glyph = 'glyphicon-ok';
if (!c.connected) {
cl = ' danger';
glyph = 'glyphicon-remove';
}
$('#table-servers > tbody:last').append('<tr class="clickable'+cl+'" style="cursor: pointer;"><td><a href="{{ url_for("clients") }}?server='+c.name+'" style="color: inherit; text-decoration: inherit;">'+c.name+'</a></td><td>'+c.clients+'</td><td><span class="glyphicon '+glyph+'"></span></td></tr>');
});
});
};

View file

@ -0,0 +1,26 @@
{% extends "layout.html" %}
{% block body %}
{% include "notifications.html" %}
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
{% include "small_topbar.html" %}
<ul class="breadcrumb" style="margin-bottom: 5px;">
<li class="active">Home</li>
</ul>
<br />
<h1 class="page-header">Servers</h1>
<div class="table-responsive">
<table class="table table-striped table-hover" id="table-servers">
<thead>
<tr>
<th>Name</th>
<th>Clients</th>
<th>Status</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
{% endblock %}

View file

@ -2,9 +2,9 @@
<ul class="nav nav-sidebar">
<li {% if overview %}class="active"{% endif %}>
{% if backup %}
<a href="{{ url_for('client_browse', name=cname) }}?backup={{ nbackup }}"><span class="glyphicon glyphicon-th"></span>&nbsp;Overview</a>
<a href="{{ url_for('client_browse', name=cname, server=server) }}?backup={{ nbackup }}"><span class="glyphicon glyphicon-th"></span>&nbsp;Overview</a>
{% elif client %}
<a href="{{ url_for('client', name=cname) }}"><span class="glyphicon glyphicon-th"></span>&nbsp;Overview</a>
<a href="{{ url_for('client', name=cname, server=server) }}"><span class="glyphicon glyphicon-th"></span>&nbsp;Overview</a>
{% elif clients %}
<a href="{{ url_for('home') }}"><span class="glyphicon glyphicon-th"></span>&nbsp;Overview</a>
{% else %}
@ -13,11 +13,11 @@
</li>
<li {% if report %}class="active"{% endif %}>
{% if client and not backup %}
<a href="{{ url_for('client_report', name=cname) }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
<a href="{{ url_for('client_report', name=cname, server=server) }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
{% elif clients %}
<a href="{{ url_for('clients_report') }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
<a href="{{ url_for('clients_report', server=server) }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
{% elif backup %}
<a href="{{ url_for('backup_report', name=cname, backup=nbackup) }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
<a href="{{ url_for('backup_report', name=cname, backup=nbackup, server=server) }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
{% else %}
<a href="#"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
{% endif %}

View file

@ -1,19 +1,19 @@
<div class="btn-group btn-group-justified hidden-md hidden-lg">
{% if backup %}
<a class="btn btn-default {% if overview %}active{% endif %}" href="{{ url_for('client_browse', name=cname) }}?backup={{ nbackup }}"><span class="glyphicon glyphicon-th"></span>&nbsp;Overview</a>
<a class="btn btn-default {% if overview %}active{% endif %}" href="{{ url_for('client_browse', name=cname, server=server) }}?backup={{ nbackup }}"><span class="glyphicon glyphicon-th"></span>&nbsp;Overview</a>
{% elif client %}
<a class="btn btn-default {% if overview %}active{% endif %}" href="{{ url_for('client', name=cname) }}"><span class="glyphicon glyphicon-th"></span>&nbsp;Overview</a>
<a class="btn btn-default {% if overview %}active{% endif %}" href="{{ url_for('client', name=cname, server=server) }}"><span class="glyphicon glyphicon-th"></span>&nbsp;Overview</a>
{% elif clients %}
<a class="btn btn-default {% if overview %}active{% endif %}" href="{{ url_for('home') }}"><span class="glyphicon glyphicon-th"></span>&nbsp;Overview</a>
{% else %}
<a class="btn btn-default {% if overview %}active{% endif %}" href="#"><span class="glyphicon glyphicon-th"></span>&nbsp;Overview</a>
{% endif %}
{% if client and not backup %}
<a class="btn btn-default {% if report %}active{% endif %}" href="{{ url_for('client_report', name=cname) }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
<a class="btn btn-default {% if report %}active{% endif %}" href="{{ url_for('client_report', name=cname, server=server) }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
{% elif clients %}
<a class="btn btn-default {% if report %}active{% endif %}" href="{{ url_for('clients_report') }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
<a class="btn btn-default {% if report %}active{% endif %}" href="{{ url_for('clients_report', server=server) }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
{% elif backup %}
<a class="btn btn-default {% if report %}active{% endif %}" href="{{ url_for('backup_report', name=cname, backup=nbackup) }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
<a class="btn btn-default {% if report %}active{% endif %}" href="{{ url_for('backup_report', name=cname, backup=nbackup, server=server) }}"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
{% else %}
<a class="btn btn-default {% if report %}active{% endif %}" href="#"><span class="glyphicon glyphicon-stats"></span>&nbsp;Reports</a>
{% endif %}

View file

@ -10,13 +10,16 @@
<a class="navbar-brand" href="{{ url_for('home') }}">Burp Server Dashboard</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li class="detail{% if clients %} active{% endif %}"><a href="{{ url_for('home') }}"><span class="glyphicon glyphicon-home"></span><span class="dtl hidden-md hidden-lg">&nbsp;Clients</span></a></li>
<ul class="nav navbar-nav navbar-right">
{% if not config.STANDALONE -%}
<li class="detail"><a href="{{ url_for('home') }}"><span class="glyphicon glyphicon-hdd"></span><span class="dtl hidden-md hidden-lg">&nbsp;Servers</span></a></li>
{% endif -%}
<li class="detail{% if clients %} active{% endif %}"><a href="{{ url_for('home') }}"><span class="glyphicon glyphicon-tasks"></span><span class="dtl hidden-md hidden-lg">&nbsp;Clients</span></a></li>
<li class="detail disabled"><a href="#"><span class="glyphicon glyphicon-wrench"></span><span class="dtl hidden-md hidden-lg">&nbsp;Settings</span></a></li>
<li class="detail{% if live %} active{% endif %}"><a href="{{ url_for('live_monitor') }}"><span id="toblink" class="glyphicon glyphicon-screenshot"></span><span class="dtl hidden-md hidden-lg">&nbsp;Live monitor</span></a></li>
{% if current_user and current_user.is_authenticated() %}
<li class="detail{% if live %} active{% endif %}"><a href="{{ url_for('live_monitor', server=server) }}"><span id="toblink" class="glyphicon glyphicon-screenshot"></span><span class="dtl hidden-md hidden-lg">&nbsp;Live monitor</span></a></li>
{% if current_user and current_user.is_authenticated() -%}
<li class="detail"><a href="{{ url_for('logout') }}"><span class="glyphicon glyphicon-log-out"></span><span class="dtl hidden-md hidden-lg">&nbsp;Logout<small>({{ current_user.name }})</small></span></a></li>
{% endif %}
{% endif -%}
<li><a id="refresh" href="#"><span class="glyphicon glyphicon-refresh"></span><span class="hidden-md hidden-lg">&nbsp;Refresh</span></a></li>
</ul>
<form class="navbar-form navbar-right" id="search">

View file

@ -13,6 +13,7 @@ sslkey: /etc/burp/ssl_cert-server.key
# burp server version (currently only burp 1.x is implemented)
version: 1
# Handle multiple bui-servers or not
# /!\ Do not set it to false now as it is experimental /!\
standalone: true
# authentication plugin (mandatory)
# list the misc/auth directory to see the available backends
@ -56,13 +57,13 @@ refresh: 15
#admin: password
#user1: otherpassword
## If you set standalone to false, add one section like this one per bui-server
#[Server:serv1]
## bui-server address
## If you set standalone to false, add one section like this one per bui-agent
#[Agent:agent1]
## bui-agent address
#host: 192.168.1.1
## bui-server port
## bui-agent port
#port: 10000
## bui-server password
## bui-agent password
#password: azerty
## enable SSL
#ssl: true