From ac201314adae549115506023dce7828de71ef8cf Mon Sep 17 00:00:00 2001 From: Peter Hansen Date: Sat, 28 Dec 2019 11:40:04 +0100 Subject: [PATCH 01/19] + add blacklist for commands to config + custom start page with DEFAULT_TEMPLATE --- lg.py | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/lg.py b/lg.py index b8f829c..101b5ac 100644 --- a/lg.py +++ b/lg.py @@ -37,7 +37,7 @@ from toolbox import mask_is_valid, ipv6_is_valid, ipv4_is_valid, resolve, save_c import pydot -from flask import Flask, render_template, jsonify, redirect, session, request, abort, Response, Markup +from flask import Flask, render_template, render_template_string, jsonify, redirect, session, request, abort, Response, Markup app = Flask(__name__) app.config.from_pyfile('lg.cfg') @@ -171,6 +171,8 @@ def inject_commands(): ("adv", "show route ..."), ("adv_bgpmap", "show route ... (bgpmap)"), ] + + commands = [i for i in commands if i[0] not in app.config.get("BLACKLIST_COMMANDS", [])] commands_dict = {} for id, text in commands: commands_dict[id] = text @@ -184,6 +186,12 @@ def inject_all_host(): @app.route("/") def hello(): + if app.config.get("DEFAULT_TEMPLATE", False): + first_command = next(iter(inject_commands()['commands_dict'])) + set_session(first_command, "+".join(app.config["PROXY"].keys()), "ipv4", "") + with open(app.config.get("DEFAULT_TEMPLATE"), 'r') as filehandle: + filecontent = filehandle.read() + return render_template_string(filecontent) return redirect("/summary/%s/ipv4" % "+".join(app.config["PROXY"].keys())) @@ -227,6 +235,8 @@ SUMMARY_UNWANTED_PROTOS = ["Kernel", "Static", "Device"] @app.route("/summary/") @app.route("/summary//") def summary(hosts, proto="ipv4"): + if 'summary' not in iter(inject_commands()['commands_dict']): + return render_template('error.html', errors=["Access denied"]), 403 set_session("summary", hosts, proto, "") command = "show protocols" @@ -269,6 +279,9 @@ def summary(hosts, proto="ipv4"): @app.route("/detail//") def detail(hosts, proto): + if 'detail' not in iter(inject_commands()['commands_dict']): + return render_template('error.html', errors=["Access denied"]), 403 + name = get_query() if not name: @@ -298,6 +311,9 @@ def detail(hosts, proto): @app.route("/traceroute//") def traceroute(hosts, proto): + if 'traceroute' not in iter(inject_commands()['commands_dict']): + return render_template('error.html', errors=["Access denied"]), 403 + q = get_query() if not q: @@ -331,41 +347,65 @@ def traceroute(hosts, proto): @app.route("/adv//") def show_route_filter(hosts, proto): + if 'adv' not in iter(inject_commands()['commands_dict']): + return render_template('error.html', errors=["Access denied"]), 403 + return show_route("adv", hosts, proto) @app.route("/adv_bgpmap//") def show_route_filter_bgpmap(hosts, proto): + if 'adv_bgpmap' not in iter(inject_commands()['commands_dict']): + return render_template('error.html', errors=["Access denied"]), 403 + return show_route("adv_bgpmap", hosts, proto) @app.route("/where//") def show_route_where(hosts, proto): + if 'where' not in iter(inject_commands()['commands_dict']): + return render_template('error.html', errors=["Access denied"]), 403 + return show_route("where", hosts, proto) @app.route("/where_detail//") def show_route_where_detail(hosts, proto): + if 'where_detail' not in iter(inject_commands()['commands_dict']): + return render_template('error.html', errors=["Access denied"]), 403 + return show_route("where_detail", hosts, proto) @app.route("/where_bgpmap//") def show_route_where_bgpmap(hosts, proto): + if 'where_bgpmap' not in iter(inject_commands()['commands_dict']): + return render_template('error.html', errors=["Access denied"]), 403 + return show_route("where_bgpmap", hosts, proto) @app.route("/prefix//") def show_route_for(hosts, proto): + if 'prefix' not in iter(inject_commands()['commands_dict']): + return render_template('error.html', errors=["Access denied"]), 403 + return show_route("prefix", hosts, proto) @app.route("/prefix_detail//") def show_route_for_detail(hosts, proto): + if 'prefix_detail' not in iter(inject_commands()['commands_dict']): + return render_template('error.html', errors=["Access denied"]), 403 + return show_route("prefix_detail", hosts, proto) @app.route("/prefix_bgpmap//") def show_route_for_bgpmap(hosts, proto): + if 'prefix_bgpmap' not in iter(inject_commands()['commands_dict']): + return render_template('error.html', errors=["Access denied"]), 403 + return show_route("prefix_bgpmap", hosts, proto) From d3e9d5c6b9c7badfedf95dd7b1b1c962b8a0689c Mon Sep 17 00:00:00 2001 From: Guillaume Marsay Date: Mon, 15 Jun 2020 09:57:59 +0200 Subject: [PATCH 02/19] DOMAIN is now optional --- lg.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/lg.py b/lg.py index 3616c58..3be6307 100644 --- a/lg.py +++ b/lg.py @@ -148,16 +148,23 @@ def bird_proxy(host, proto, service, query): return False, 'Host "%s" invalid' % host elif not path: return False, 'Proto "%s" invalid' % proto - else: - url = "http://%s.%s:%d/%s?q=%s" % (host, app.config["DOMAIN"], port, path, quote(query)) - try: - f = urlopen(url) - resultat = f.read() - status = True # retreive remote status - except IOError: - resultat = "Failed retreive url: %s" % url - status = False - return status, resultat + + url = "http://%s" % (host) + if "DOMAIN" in app.config: + url = "%s.%s" % (url, app.config["DOMAIN"]) + url = "%s:%d/%s?" % (url, port, path) + + url = "%sq=%s" % (url, quote(query)) + + try: + f = urlopen(url) + resultat = f.read() + status = True # retreive remote status + except IOError: + resultat = "Failed retreive url: %s" % url + status = False + + return status, resultat @app.context_processor @@ -459,7 +466,10 @@ def show_bgpmap(): return edges[edge_tuple] for host, asmaps in data.iteritems(): - add_node(host, label= "%s\r%s" % (host.upper(), app.config["DOMAIN"].upper()), shape="box", fillcolor="#F5A9A9") + if "DOMAIN" in app.config: + add_node(host, label= "%s\r%s" % (host.upper(), app.config["DOMAIN"].upper()), shape="box", fillcolor="#F5A9A9") + else: + add_node(host, label= "%s" % (host.upper()), shape="box", fillcolor="#F5A9A9") as_number = app.config["AS_NUMBER"].get(host, None) if as_number: From 96c33da44690adba5c257863ffcf993bcffe7ab5 Mon Sep 17 00:00:00 2001 From: Guillaume Marsay Date: Mon, 15 Jun 2020 13:27:26 +0200 Subject: [PATCH 03/19] Add SHARED_SECRET --- lg.cfg | 3 +++ lg.py | 3 ++- lgproxy.cfg | 9 +++++++++ lgproxy.py | 13 +++++++++---- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/lg.cfg b/lg.cfg index 2cc3388..5ddfc5d 100644 --- a/lg.cfg +++ b/lg.cfg @@ -5,6 +5,9 @@ LOG_LEVEL="WARNING" DOMAIN = "tetaneutral.net" +# Used for restrict access on lgproxy - must be same in lgproxy.cfg +SHARED_SECRET="ThisTokenIsNotSecret" + BIND_IP = "0.0.0.0" BIND_PORT = 5000 diff --git a/lg.py b/lg.py index 3be6307..e9ea3e5 100644 --- a/lg.py +++ b/lg.py @@ -153,7 +153,8 @@ def bird_proxy(host, proto, service, query): if "DOMAIN" in app.config: url = "%s.%s" % (url, app.config["DOMAIN"]) url = "%s:%d/%s?" % (url, port, path) - + if "SHARED_SECRET" in app.config: + url = "%ssecret=%s&" % (url, app.config["SHARED_SECRET"]) url = "%sq=%s" % (url, quote(query)) try: diff --git a/lgproxy.cfg b/lgproxy.cfg index e7a6e8a..7019e52 100644 --- a/lgproxy.cfg +++ b/lgproxy.cfg @@ -1,12 +1,21 @@ DEBUG=False + LOG_FILE="/var/log/lg-proxy/lg-proxy.log" LOG_LEVEL="WARNING" + BIND_IP = "0.0.0.0" BIND_PORT = 5000 + +# Used for restrict access on lgproxy - Empty list = all allowed ACCESS_LIST = ["91.224.149.206", "178.33.111.110", "2a01:6600:8081:ce00::1"] + +# Used for restrict access on lgproxy - Must be same in lg.cfg +SHARED_SECRET="ThisTokenIsNotSecret" + IPV4_SOURCE="" IPV6_SOURCE="" + BIRD_SOCKET="/var/run/bird/bird.ctl" BIRD6_SOCKET="/var/run/bird/bird6.ctl" diff --git a/lgproxy.py b/lgproxy.py index e1b3cdb..a81abb9 100644 --- a/lgproxy.py +++ b/lgproxy.py @@ -54,14 +54,19 @@ def access_log_after(response, *args, **kwargs): app.logger.info("[%s] reponse %s, %s", request.remote_addr, request.url, response.status_code) return response -def check_accesslist(): - if app.config["ACCESS_LIST"] and request.remote_addr not in app.config["ACCESS_LIST"]: +def check_security(): + if app.config["ACCESS_LIST"] and request.remote_addr not in app.config["ACCESS_LIST"]: + app.logger.info("Your remote address is not valid") + abort(401) + + if app.config.get('SHARED_SECRET') and request.args.get("secret") != app.config["SHARED_SECRET"]: + app.logger.info("Your shared secret is not valid") abort(401) @app.route("/traceroute") @app.route("/traceroute6") def traceroute(): - check_accesslist() + check_security() if sys.platform.startswith('freebsd') or sys.platform.startswith('netbsd') or sys.platform.startswith('openbsd'): traceroute4 = [ 'traceroute' ] @@ -100,7 +105,7 @@ def traceroute(): @app.route("/bird") @app.route("/bird6") def bird(): - check_accesslist() + check_security() if request.path == "/bird": b = BirdSocket(file=app.config.get("BIRD_SOCKET")) elif request.path == "/bird6": b = BirdSocket(file=app.config.get("BIRD6_SOCKET")) From f87719956a156e200112b200545590b71133f884 Mon Sep 17 00:00:00 2001 From: Guillaume Marsay Date: Mon, 15 Jun 2020 13:13:05 +0200 Subject: [PATCH 04/19] Add WEBSITE_TITLE - Issue #69 --- lg.cfg | 2 +- templates/layout.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lg.cfg b/lg.cfg index 2cc3388..b40f4d1 100644 --- a/lg.cfg +++ b/lg.cfg @@ -1,4 +1,4 @@ - +WEBSITE_TITLE="Bird-LG / Looking Glass" DEBUG = False LOG_FILE="/var/log/lg.log" LOG_LEVEL="WARNING" diff --git a/templates/layout.html b/templates/layout.html index 34071b0..aa57f29 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -1,6 +1,6 @@ - {{config.DOMAIN|capitalize}} looking glass + {{config.WEBSITE_TITLE|default("Bird-LG / Looking Glass") }} @@ -18,7 +18,7 @@ - {{config.DOMAIN|capitalize}} / Looking Glass + {{config.WEBSITE_TITLE|default("Bird-LG / Looking Glass") }}