diff --git a/CHANGELOG.rst b/CHANGELOG.rst index aa7ffde6..bb9960dc 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,6 +7,7 @@ Current - **BREAKING**: the *single* and *version* options within the ``[Global]`` section have been removed in favor of a new unified *backend* option - **BREAKING**: a change introduced by `#284 `_ may return wrong timestamps for backups made with burp-server <= 2.1.10 if your current burp-server is >= 2.1.10 - **BREAKING**: the authentication backends section have been renamed with the ``:AUTH`` suffix +- **BREAKING**: the ``prefix`` option has been moved from the ``[Global]`` configuration section to the ``[Production]`` one - Add: new `audit logging `_ system - Add: new ``bui-monitor`` processes pool + ``async`` backend to parallelize some requests `#278 `_ - Add: new `listen` and `listen_status` options in burp-2.2.10 `#279 `_ diff --git a/burpui/app.py b/burpui/app.py index 1fe1998c..fa1506b2 100644 --- a/burpui/app.py +++ b/burpui/app.py @@ -10,6 +10,7 @@ jQuery/Bootstrap .. moduleauthor:: Ziirish """ import os +import json import time import logging @@ -148,9 +149,14 @@ def create_app(conf=None, verbose=0, logfile=None, **kwargs): # Manage reverse_proxy special tricks & improvements if reverse_proxy: # pragma: no cover - from werkzeug.contrib.fixers import ProxyFix + from werkzeug.middleware.proxy_fix import ProxyFix - app.wsgi_app = ProxyFix(app.wsgi_app) + kwargs = {} + if app.config['NUM_PROXIES'] > 0: + kwargs = app.config['PROXY_FIX_ARGS'].format(num_proxies=app.config['NUM_PROXIES']) + kwargs = json.loads(kwargs) + logger.debug(f"Using {kwargs} as ProxyFix parameters") + app = ProxyFix(app, **kwargs) if app.storage and app.storage.lower() == 'redis': try: @@ -373,10 +379,10 @@ def create_app(conf=None, verbose=0, logfile=None, **kwargs): g.date_format = session.get('dateFormat', 'llll') # make sure to store secure cookie if required if app.config['BUI_SCOOKIE']: - criteria = [ + criteria = ( request.is_secure, request.headers.get('X-Forwarded-Proto', 'http') == 'https' - ] + ) app.config['SESSION_COOKIE_SECURE'] = \ app.config['REMEMBER_COOKIE_SECURE'] = any(criteria) if '_extra' in request.args: diff --git a/burpui/engines/server.py b/burpui/engines/server.py index dd6c062d..d5e350ee 100644 --- a/burpui/engines/server.py +++ b/burpui/engines/server.py @@ -11,6 +11,7 @@ import os import re import sys import logging # noqa +import warnings from ..tools.logging import logger from ..misc.auth.handler import UserAuthHandler @@ -25,8 +26,8 @@ from flask import Flask BUI_DEFAULTS = { 'Global': { - 'port': 5000, - 'bind': '::', + 'port': 0, + 'bind': '', 'ssl': False, 'sslcert': '', 'sslkey': '', @@ -64,6 +65,8 @@ BUI_DEFAULTS = { 'database': '', 'limiter': False, 'ratio': '60/minute', + 'num_proxies': 0, + 'proxy_fix_args': "{'x_for': {num_proxies}, 'x_host': {num_proxies}, 'x_prefix': {num_proxies}}", }, 'WebSocket': { 'enabled': True, @@ -137,40 +140,41 @@ class BUIServer(Flask): self.conf.parse(conf, True, BUI_DEFAULTS) self.conf.default_section('Global') - self.config['BUI_PORT'] = self.conf.safe_get( - 'port', - 'integer' - ) - self.demo = self.config['BUI_DEMO'] = self.conf.safe_get( - 'demo', - 'boolean' - ) + self.config['BUI_BIND'] = self.conf.safe_get('bind') + self.config['BUI_PORT'] = self.conf.safe_get('port', 'integer') + if self.config['BUI_BIND'] or self.config['BUI_PORT']: + warnings.warn( + "The 'bind' and 'port' configuration options are now deprecated and " + "have no effect on burp-ui anymore unless you use the 'burp-ui-legacy' " + "command.\n" + "Please use the '-h' and '-p' command line flags instead.", + UserWarning + ) + self.demo = self.config['BUI_DEMO'] = self.conf.safe_get('demo', 'boolean') self.config['BUI_DSN'] = self.conf.safe_get('dsn') self.config['BUI_PIWIK_URL'] = self.conf.safe_get('piwik_url') self.config['BUI_PIWIK_SCRIPT'] = self.conf.safe_get('piwik_script') self.config['BUI_PIWIK_ID'] = self.conf.safe_get('piwik_id', 'integer') - self.config['BUI_BIND'] = self.conf.safe_get('bind') self.config['BACKEND'] = self.conf.safe_get('backend') + # FIXME: this sucks, we need to test the burp2 backend as well! if unittest: self.config['BACKEND'] = 'burp1' - self.config['BUI_SSL'] = self.conf.safe_get( - 'ssl', - 'boolean' - ) self.config['STANDALONE'] = self.config['BACKEND'] != 'multi' - self.config['BUI_SSLCERT'] = self.conf.safe_get( - 'sslcert' - ) - self.config['BUI_SSLKEY'] = self.conf.safe_get( - 'sslkey' - ) - prefix = self.config['BUI_PREFIX'] = self.conf.safe_get( - 'prefix' - ) - if prefix and not prefix.startswith('/'): - if prefix.lower() != 'none': - self.logger.warning("'prefix' must start with a '/'!") - self.config['BUI_PREFIX'] = '' + self.config['BUI_SSL'] = self.conf.safe_get('ssl', 'boolean') + self.config['BUI_SSLCERT'] = self.conf.safe_get('sslcert') + self.config['BUI_SSLKEY'] = self.conf.safe_get('sslkey') + if self.config['BUI_SSL'] or self.config['BUI_SSLCERT'] or \ + self.config['BUI_SSLKEY']: + warnings.warn( + "The 'ssl', 'sslcert' and 'sslkey' configuration options are deprecated " + "as of v0.4.0. It means they have no effect on burp-ui anymore. " + "If you really need them, consider using the 'burp-ui-legacy' command " + "instead.", + UserWarning + ) + + # TODO: remove in 0.8.0 + old_prefix = self.conf.safe_get('prefix') self.plugins = self.config['BUI_PLUGINS'] = self.conf.safe_get( 'plugins', @@ -274,6 +278,33 @@ class BUIServer(Flask): else: self.config['WITH_CELERY'] = self.use_celery and \ self.use_celery.lower() != 'none' + self.config['NUM_PROXIES'] = self.conf.safe_get( + 'num_proxies', + 'integer', + section='Production' + ) + self.config['PROXY_FIX_ARGS'] = self.conf.safe_get( + 'proxy_fix_args', + section='Production' + ) + prefix = self.conf.safe_get( + 'prefix', + section='Production' + ) + if not prefix and old_prefix: + # TODO: remove in a later version + prefix = old_prefix + warnings.warn( + "The 'prefix' option has been moved from the '[Global]' section to the " + "'[Production]' section", + UserWarning + ) + if prefix and not prefix.startswith('/'): + if prefix.lower() != 'none': + self.logger.warning("'prefix' must start with a '/'!") + self.config['BUI_PREFIX'] = '' + elif prefix: + self.config['BUI_PREFIX'] = prefix # WebSocket options self.ws_enabled = self.config['WS_ENABLED'] = self.conf.safe_get( diff --git a/docs/advanced_usage.rst b/docs/advanced_usage.rst index bfb70506..2e0de24b 100644 --- a/docs/advanced_usage.rst +++ b/docs/advanced_usage.rst @@ -55,12 +55,6 @@ The `burpui.cfg`_ configuration file contains a ``[Global]`` section as follow: # list the misc/audit directory to see the available backends # default is no audit log audit = basic - # You can change the prefix if you are behind a reverse-proxy under a custom - # root path. For example: /burpui - # You can also configure your reverse-proxy to announce the prefix through the - # 'X-Script-Name' header. In this case, the bellow prefix will be ignored in - # favour of the one announced by your reverse-proxy - prefix = none # list of paths to look for external plugins plugins = none @@ -78,8 +72,6 @@ Each option is commented, but here is a more detailed documentation: - *auth*: What `Authentication`_ backend to use. - *acl*: What `ACL`_ module to use. - *audit*: What `Audit`_ module to use. -- *prefix*: You can host `Burp-UI`_ behind a sub-root path. See the `gunicorn - `__ page for details. - *plugins*: Specify a list of paths to look for external plugins. See the `Plugins `_ page for details on how to write plugins. @@ -161,6 +153,30 @@ follow: # limiter ratio # see https://flask-limiter.readthedocs.io/en/stable/#ratelimit-string ratio = 60/minute + # you can change the prefix if you are behind a reverse-proxy under a custom + # root path. For example: /burpui + # You can also configure your reverse-proxy to announce the prefix through the + # 'X-Script-Name' header. In this case, the bellow prefix will be ignored in + # favour of the one announced by your reverse-proxy + prefix = none + # ProxyFix + # number of reverse-proxy to trust in order to retrieve some HTTP headers + # All the details can be found here: + # https://werkzeug.palletsprojects.com/en/0.15.x/middleware/proxy_fix/#module-werkzeug.middleware.proxy_fix + num_proxies = 0 + # alternatively, you can specify your own ProxyFix args. + # The default is: "{'x_for': {num_proxies}, 'x_host': {num_proxies}, 'x_prefix': {num_proxies}}" + # if num_proxies > 0, else it defaults to ProxyFix defaults + proxy_fix_args = "{'x_for': {num_proxies}, 'x_host': {num_proxies}, 'x_prefix': {num_proxies}}" + + +- *prefix*: You can host `Burp-UI`_ behind a sub-root path. See the `gunicorn + `__ page for details. +- *num_proxies*: This is useful only if you host `Burp-UI`_ behind a + `reverse-proxy `__. +- *proxy_fix_args*: Same as above. Please refer to `Werkzeug's documentation + `_ + for details. WebSocket diff --git a/docs/gunicorn.rst b/docs/gunicorn.rst index b1852880..2a8243e0 100644 --- a/docs/gunicorn.rst +++ b/docs/gunicorn.rst @@ -257,10 +257,10 @@ Sub-root path You can host `Burp-UI`_ behind a sub-root path. For instance ``/burpui``. To accomplish this, you can either setup your reverse-proxy to announce the desired *prefix*, or you can use the ``prefix`` option in your `Burp-UI`_ -configuration file (see `usage `_ for details). +configuration file (see `usage `_ for details). If you want to configure this reverse-proxy side, you need to announce the HTTP -Header ``X-Script-Name``. +Header ``X-Script-Name``. Alternatively, you can use the ``X_Forwarded_Prefix``. Here is a sample configuration for Nginx: @@ -280,6 +280,7 @@ Here is a sample configuration for Nginx: proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $remote_addr; # Our service is hosted behind the "/burpui" prefix + # Alternatively you can use the "X_FORWARDED_PREFIX" instead proxy_set_header X-Script-Name /burpui; proxy_read_timeout 300; diff --git a/docs/upgrading.rst b/docs/upgrading.rst index 13b6dd06..24b6fa53 100644 --- a/docs/upgrading.rst +++ b/docs/upgrading.rst @@ -36,6 +36,9 @@ v0.7.0 and the burp-client used by burp-ui must be able to reach the burp-server). A new timeout has been added though in order for ``bui-agent`` to wait for the burp-server to be ready. +- **Breaking** - the ``prefix`` option has been moved from the ``[Global]`` + configuration section to the ``[Production]`` one for consistency with the new + *Production* options introduced. v0.6.0 ------ diff --git a/requirements.txt b/requirements.txt index 0806da7d..8df24c40 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,3 +14,4 @@ pyOpenSSL==19.0.0 configobj==5.0.6 async_generator Click==7.0 +werkzeug>=0.15.0 diff --git a/share/burpui/etc/burpui.sample.cfg b/share/burpui/etc/burpui.sample.cfg index 2ef8e629..d2c5da01 100644 --- a/share/burpui/etc/burpui.sample.cfg +++ b/share/burpui/etc/burpui.sample.cfg @@ -24,12 +24,6 @@ acl = basic # list the misc/audit directory to see the available backends # default is no audit log audit = basic -# you can change the prefix if you are behind a reverse-proxy under a custom -# root path. For example: /burpui -# You can also configure your reverse-proxy to announce the prefix through the -# 'X-Script-Name' header. In this case, the bellow prefix will be ignored in -# favour of the one announced by your reverse-proxy -prefix = none # list of paths to look for external plugins plugins = none @@ -88,6 +82,21 @@ limiter = false # limiter ratio # see https://flask-limiter.readthedocs.io/en/stable/#ratelimit-string ratio = 60/minute +# you can change the prefix if you are behind a reverse-proxy under a custom +# root path. For example: /burpui +# You can also configure your reverse-proxy to announce the prefix through the +# 'X-Script-Name' header. In this case, the bellow prefix will be ignored in +# favour of the one announced by your reverse-proxy +prefix = none +# ProxyFix +# number of reverse-proxy to trust in order to retrieve some HTTP headers +# All the details can be found here: +# https://werkzeug.palletsprojects.com/en/0.15.x/middleware/proxy_fix/#module-werkzeug.middleware.proxy_fix +num_proxies = 0 +# alternatively, you can specify your own ProxyFix args. +# The default is: "{'x_for': {num_proxies}, 'x_host': {num_proxies}, 'x_prefix': {num_proxies}}" +# if num_proxies > 0, else it defaults to ProxyFix defaults +proxy_fix_args = "{'x_for': {num_proxies}, 'x_host': {num_proxies}, 'x_prefix': {num_proxies}}" [WebSocket] ## This section contains WebSocket server specific options.