Source code for websnort.web

#!/usr/bin/env python

# Websnort - Web service for analysing pcap files with snort
# Copyright (C) 2013-2015 Steve Henderson
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import
from __future__ import unicode_literals

try:
    from gevent import monkey
    monkey.patch_all()
    SERVER = 'gevent'
except ImportError:
    SERVER = 'wsgiref'

import argparse
from datetime import datetime
import hashlib
import json
import logging
import os
import tempfile

from bottle import request, response, route, run, default_app
from jinja2.environment import Environment
from jinja2.loaders import FileSystemLoader

from websnort import runner
from websnort.version import __version__

# Load templates
root = os.path.join(os.path.dirname(__file__), "html")
env = Environment()
env.loader = FileSystemLoader(root)

jsondate = lambda obj: obj.isoformat() if isinstance(obj, datetime) else None

[docs]@route("/", name="home") def home(): """ Main page, displays a submit file form. """ template = env.get_template("submit.html") return template.render(base)
def analyse_pcap(infile, filename): """ Run IDS across the supplied file. :param infile: File like object containing pcap data. :param filename: Filename of the submitted file. :returns: Dictionary of analysis results. """ tmp = tempfile.NamedTemporaryFile(suffix=".pcap", delete=False) m = hashlib.md5() results = {'filename': filename, 'status': 'Failed', 'apiversion': __version__, } try: size = 0 while True: buf = infile.read(16384) if not buf: break tmp.write(buf) size += len(buf) m.update(buf) tmp.close() results['md5'] = m.hexdigest() results['filesize'] = size results.update(runner.run(tmp.name)) except OSError as ex: results['stderr'] = str(ex) finally: os.remove(tmp.name) return results
[docs]@route("/submit", method="POST", name="submit") def submit_and_render(): """ Blocking POST handler for file submission. Runs snort on supplied file and returns results as rendered html. """ data = request.files.file template = env.get_template("results.html") if not data: pass results = analyse_pcap(data.file, data.filename) results.update(base) return template.render(results)
[docs]@route("/api/submit", method="POST", name="api_submit") def api_submit(): """ Blocking POST handler for file submission. Runs snort on supplied file and returns results as json text. """ data = request.files.file response.content_type = 'application/json' if not data or not hasattr(data, 'file'): return json.dumps({"status": "Failed", "stderr": "Missing form params"}) return json.dumps(analyse_pcap(data.file, data.filename), default=jsondate, indent=4)
@route("/api", name="api") def api(): """ Display an api usage/help page. """ template = env.get_template("api.html") return template.render(base) def main(): """ Main entrypoint for command-line webserver. """ parser = argparse.ArgumentParser() parser.add_argument("-H", "--host", help="Web server Host address to bind to", default="0.0.0.0", action="store", required=False) parser.add_argument("-p", "--port", help="Web server Port to bind to", default=8080, action="store", required=False) args = parser.parse_args() logging.basicConfig() run(host=args.host, port=args.port, reloader=True, server=SERVER) # WSGI and template url support application = default_app() base = {'get_url': application.get_url} if __name__ == '__main__': main()