diff --git a/docs/blacklist_error.png b/docs/blacklist_error.png
index 08a72b0..f9a5592 100644
Binary files a/docs/blacklist_error.png and b/docs/blacklist_error.png differ
diff --git a/docs/configuration/authentication.md b/docs/configuration/authentication.md
deleted file mode 100644
index d76be8e..0000000
--- a/docs/configuration/authentication.md
+++ /dev/null
@@ -1,16 +0,0 @@
-Authentication parameters are stored in the `devices.toml` file, at `hyperglass/hyperglass/configuration/devices.toml`. The array of tables simply stores the username and password for a device. SSH Key authentication is not yet supported.
-
-Example:
-
-```toml
-[credential.'default']
-username = "hyperglass"
-password = "secret_password"
-
-[credential.'other_credential']
-username = "other_username"
-password = "other_secret_password"
-```
-
-!!! warning "Security Warning"
- These values are stored in plain text. Make sure the accounts are restricted and that the configuration file is stored in a secure location.
diff --git a/docs/configuration/blacklist.md b/docs/configuration/blacklist.md
deleted file mode 100644
index d1572c3..0000000
--- a/docs/configuration/blacklist.md
+++ /dev/null
@@ -1,25 +0,0 @@
-Blacklisted querys are defined in `hyperglass/hyperglass/configuration/blacklist.toml`.
-
-The blacklist is a simple TOML array (list) of host IPs or prefixes that you do not want end users to be able to query. For example, if you want to prevent users from looking up 198.18.0.0/15 or any contained host or prefix, you can add it to the blacklist:
-
-```toml
-blacklist = [
-198.18.0.0/15
-]
-```
-
-If you have multiple hosts/subnets you wish to blacklist, you can do so by adding a comma `,` after each entry (except the last):
-
-```toml
-blacklist = [
-'198.18.0.0/15',
-'10.0.0.0/8',
-'192.168.0.0/16',
-'2001:db8::/32'
-'172.16.0.0/12'
-]
-```
-
-When users attempt to query a matching host/prefix, they will receive the following error message by default:
-
-
diff --git a/docs/configuration/branding.md b/docs/configuration/branding.md
index f4f7885..aef3e38 100644
--- a/docs/configuration/branding.md
+++ b/docs/configuration/branding.md
@@ -10,9 +10,10 @@
}
-From `hyperglass/hyperglass/configuration/config.toml`:
+From `hyperglass/hyperglass/configuration/configuration.toml` `[branding]` table.
-### site_title
+# Site Parameters
+#### site_title
| Type | Default Value |
| ------ | -------------- |
@@ -20,7 +21,7 @@ From `hyperglass/hyperglass/configuration/config.toml`:
HTML `
+
+### max_prefix_length_ipv4
+
+| Type | Default Value |
+| ------- | ------------- |
+| Integer | `24` |
+
+If `enable_max_prefix` is enabled, the maxiumum prefix length allowed for IPv4 BGP Route queries.
+
+### max_prefix_length_ipv6
+
+| Type | Default Value |
+| ------- | ------------- |
+| Integer | `64` |
+
+If `enable_max_prefix` is enabled, the maxiumum prefix length allowed for IPv6 BGP Route queries.
diff --git a/docs/configuration/index.md b/docs/configuration/index.md
index e9e727a..a08eb19 100644
--- a/docs/configuration/index.md
+++ b/docs/configuration/index.md
@@ -11,6 +11,98 @@ hyperglass/configuration/
└── requires_ipv6_cidr.toml
```
-## `requires_ipv6_cidr.toml`
+## Blacklist
+
+Blacklisted querys are defined in `hyperglass/hyperglass/configuration/blacklist.toml`
+
+The blacklist is a simple TOML array (list) of host IPs or prefixes that you do not want end users to be able to query. For example, if you have one or more hosts/subnets you wish to prevent users from looking up (or any contained host or prefix), add them to the list.
+
+#### Example
+
+```toml
+blacklist = [
+'198.18.0.0/15',
+'2001:db8::/32',
+'10.0.0.0/8',
+'192.168.0.0/16',
+'172.16.0.0/12'
+]
+```
+
+When users attempt to query a matching host/prefix, they will receive the following error message by default:
+
+
+
+## Commands
+
+Commands are defined in `hyperglass/hyperglass/configuration/commands.toml`. A table for each NOS (Network Operating System) contains three nested tables: `dual`, `ipv4`, and `ipv6`.
+
+| Table | Function | Commands |
+| --------- | ----------------------------- | ------------------------------- |
+| **dual** | Protocol agnostic commands | `bgp_community` `bgp_aspath` |
+| **ipv4** | IPv4-specific commands | `bgp_route` `ping` `traceroute` |
+| **ipv6** | IPv6-specific commands | `bgp_route` `ping` `traceroute` |
+
+#### Variables
+
+The following variables can be used in the command definitions.
+
+- `{target}` Maps to search box input.
+- `{src_addr_ipv4}` Maps to [src_addr_ipv4](configuration/devices.md/#src_addr_ipv4)
+- `{src_addr_ipv6}` Maps to [src_addr_ipv6](configuration/devices.md/#src_addr_ipv6)
+
+#### Example
+
+```toml
+[[cisco_ios]]
+[cisco_ios.dual]
+bgp_community = "show bgp all community {target}"
+bgp_aspath = 'show bgp all quote-regexp "{target}"'
+[cisco_ios.ipv4]
+bgp_route = "show bgp ipv4 unicast {target} | exclude pathid:|Epoch"
+ping = "ping {target} repeat 5 source {src_addr_ipv4}"
+traceroute = "traceroute {target} timeout 1 probe 2 source {src_addr_ipv4}"
+[cisco_ios.ipv6]
+bgp_route = "show bgp ipv6 unicast {target} | exclude pathid:|Epoch"
+ping = "ping ipv6 {target} repeat 5 source {src_addr_ipv6}"
+traceroute = "traceroute ipv6 {target} timeout 1 probe 2 source {src_addr_ipv6}"
+
+[[cisco_xr]]
+[cisco_xr.dual]
+bgp_community = 'show bgp all unicast community {target} | utility egrep -v "\(BGP |Table |Non-stop\)"'
+bgp_aspath = 'show bgp all unicast regexp {target} | utility egrep -v "\(BGP |Table |Non-stop\)"'
+[cisco_xr.ipv4]
+bgp_route = 'show bgp ipv4 unicast {target} | util egrep "\(BGP routing table entry|Path \#|aggregated by|Origin |Community:|validity| from \)"'
+ping = "ping ipv4 {target} count 5 source {src_addr_ipv4}"
+traceroute = "traceroute ipv4 {target} timeout 1 probe 2 source {src_addr_ipv4}"
+[cisco_xr.ipv6]
+bgp_route = 'show bgp ipv6 unicast {target} | util egrep "\(BGP routing table entry|Path \#|aggregated by|Origin |Community:|validity| from \)"'
+ping = "ping ipv6 {target} count 5 source {src_addr_ipv6}"
+traceroute = "traceroute ipv6 {target} timeout 1 probe 2 source {src_addr_ipv6}"
+
+[[juniper]]
+[juniper.dual]
+bgp_community = "show route protocol bgp community {target}"
+bgp_aspath = "show route protocol bgp aspath-regex {target}"
+[juniper.ipv4]
+bgp_route = "show route protocol bgp table inet.0 {target} detail"
+ping = "ping inet {target} count 5 source {src_addr_ipv4}"
+traceroute = "traceroute inet {target} wait 1 source {src_addr_ipv4}"
+[juniper.ipv6]
+bgp_route = "show route protocol bgp table inet6.0 {target} detail"
+ping = "ping inet6 {target} count 5 source {src_addr_ipv6}"
+traceroute = "traceroute inet6 {target} wait 1 source {src_addr_ipv6}"
+```
+
+## IPv6 CIDR Format Required
Some platforms (namely Cisco IOS) are unable to perform a BGP lookup by IPv6 host address (e.g. 2001:db8::1), but must perform the lookup by prefix (e.g. 2001:db8::/48). `requires_ipv6_cidr.toml` is a list (TOML array) of network operating systems that require this (in Netmiko format).
+
+#### Example
+
+```toml
+requires_ipv6_cidr = [
+"cisco_ios",
+"cisco_nxos"
+]
+```
diff --git a/docs/configuration/proxy.md b/docs/configuration/proxy.md
deleted file mode 100644
index 1455843..0000000
--- a/docs/configuration/proxy.md
+++ /dev/null
@@ -1,45 +0,0 @@
-Proxy servers are defined in `hyperglass/hyperglass/configuration/devices.toml`. Each proxy definition is a unique TOML table, for example:
-
-```toml
-[proxy.'jumpbox1']
-address = "10.1.1.1"
-username = "hyperglass"
-password = "secret_password"
-type = "linux_ssh"
-ssh_command = "ssh -l {username} {host}"
-
-[proxy.'jumpbox2']
-address = "10.1.1.2"
-username = "hyperglass"
-password = "secret_password"
-type = "linux_ssh"
-ssh_command = "ssh -l {username} {host}"
-```
-
-When a proxy server is defined under the `[[router]]` heading in `devices.toml`, the defined proxy name is matched to a configured proxy as shown above. When the connection to the device is initiated, the hyperglass server will first initiate an SSH connection to the proxy, and then initiate a second connection to the target device (router) *from* the proxy server. This can be helpful if you want to secure access to your routers.
-
-#### address
-
-IP address hyperglass will use to connect to the device.
-
-#### username
-
-Username for SSH authentication to the proxy server/jumpbox. SSH Key authentication is not yet supported.
-
-#### password
-
-Plain text password for SSH authentication to the proxy server/jumpbox.
-
-!!! warning "Security Warning"
- These values are stored in plain text. Make sure the accounts are restricted and that the configuration file is stored in a secure location.
-
-#### type
-
-Device type/vendor name as recognized by [Netmiko](https://github.com/ktbyers/netmiko). See [supported device types](#supported-device-types) for a full list.
-
-!!! note "Compatibility"
- Hyperglass has only been tested with `linux_ssh` as of this writing.
-
-#### ssh_command
-
-Command used to initiate an SSH connection *from* the proxy server to the target device. `{username}` will map to the target device (router) username as defined in its associated credential mapping. `{host}` will map to the target device IP address as defined in `devices.toml`.
diff --git a/docs/configuration/securing-router-access.md b/docs/extras/securing-router-access.md
similarity index 97%
rename from docs/configuration/securing-router-access.md
rename to docs/extras/securing-router-access.md
index 23dd180..d420bef 100644
--- a/docs/configuration/securing-router-access.md
+++ b/docs/extras/securing-router-access.md
@@ -1,8 +1,6 @@
More than likely, you'll want to "lock down" what commands can be executed with the credentials you've provided in `hyperglass/hyperglass/configuration/devices.toml`. It is **strongly** recommended to use a low privilege read only account and not your full administrator account. Even though Hyperglass is coded to only run certain commands to begin with, you're more than likely still exposing the server Hyperglass runs on to the internet, and on that server is a plain text file with your router's credentials in it. Take precautions.
-# Creating Restricted Accounts
-
-## Cisco IOS
+# Cisco IOS
On Cisco IOS, **parser views** are the recommended tool to restrict access. Basic instructions for configuring Cisco IOS parser views for the default enabled query types are below:
@@ -21,7 +19,7 @@ username hyperglass privilege 15 view hyperglass secret {lg_cmd} does not allow networks masks."
+ return (msg, code.warning, lg_data)
+
+ # If enable_max_prefix feature enabled, require BGP Route queries be smaller than prefix size limit
+ if lg_cmd == "bgp_route" and general.enable_max_prefix == True:
+ if (
+ IPNetwork(lg_ipprefix).version == 4
+ and IPNetwork(lg_ipprefix).prefixlen > general.max_prefix_length_ipv4
+ ):
+ msg = f"Prefix length must be smaller than /{general.max_prefix_length_ipv4}. {IPNetwork(lg_ipprefix)} is too specific."
+ return (msg, code.warning, lg_data)
+ if (
+ IPNetwork(lg_ipprefix).version == 6
+ and IPNetwork(lg_ipprefix).prefixlen > general.max_prefix_length_ipv6
+ ):
+ msg = f"Prefix length must be smaller than /{general.max_prefix_length_ipv4}. {IPNetwork(lg_ipprefix)} is too specific."
+ return (msg, code.warning, lg_data)
+
if d.type == "frr":
http = params().http()
try:
diff --git a/hyperglass/configuration/__init__.py b/hyperglass/configuration/__init__.py
index d34a586..64cd320 100644
--- a/hyperglass/configuration/__init__.py
+++ b/hyperglass/configuration/__init__.py
@@ -165,7 +165,7 @@ class general:
)
self.enable_max_prefix = g.get("enable_max_prefix", False)
self.max_prefix_length_ipv4 = g.get("max_prefix_length_ipv4", 24)
- self.max_prefix_length_ipv6 = g.get("max_prefix_length_ipv6", 29)
+ self.max_prefix_length_ipv6 = g.get("max_prefix_length_ipv6", 64)
class branding:
@@ -223,9 +223,9 @@ class branding:
"text_limiter_subtitle",
f"You have accessed this site more than {general().rate_limit_site} times in the last minute.",
)
- self.text_415_title = b.get("text_415_title", "Error")
- self.text_415_subtitle = b.get("text_415_subtitle", "Something went wrong.")
- self.text_415_button = b.get("text_415_button", "Home")
+ self.text_500_title = b.get("text_500_title", "Error")
+ self.text_500_subtitle = b.get("text_500_subtitle", "Something went wrong.")
+ self.text_500_button = b.get("text_500_button", "Home")
self.text_help_bgp_route = b.get(
"text_help_bgp_route",
"Performs BGP table lookup based on IPv4/IPv6 prefix.",
diff --git a/hyperglass/configuration/configuration.toml.example b/hyperglass/configuration/configuration.toml.example
index 427f166..95a21a0 100644
--- a/hyperglass/configuration/configuration.toml.example
+++ b/hyperglass/configuration/configuration.toml.example
@@ -44,9 +44,9 @@ text_location = ""
text_cache = ""
text_limiter_title = ""
text_limiter_subtitle = ""
-text_415_title = ""
-text_415_subtitle = ""
-text_415_button = ""
+text_500_title = ""
+text_500_subtitle = ""
+text_500_button = ""
text_help_bgp_route = ""
text_help_bgp_community = ""
text_help_bgp_aspath = ""
diff --git a/hyperglass/file.test b/hyperglass/file.test
new file mode 100755
index 0000000..e69de29
diff --git a/hyperglass/gunicorn_config.py.example b/hyperglass/gunicorn_config.py.example
index f7eba13..cba6ac7 100644
--- a/hyperglass/gunicorn_config.py.example
+++ b/hyperglass/gunicorn_config.py.example
@@ -3,6 +3,6 @@ import multiprocessing
command = "/usr/local/bin/gunicorn"
pythonpath = "/opt/hyperglass/hyperglass"
bind = "[::1]:8001"
-workers = 1 # multiprocessing.cpu_count() * 2
+workers = multiprocessing.cpu_count() * 2
user = "www-data"
timeout = 60
diff --git a/hyperglass/hyperglass.py b/hyperglass/hyperglass.py
index 5e83f8e..6493008 100644
--- a/hyperglass/hyperglass.py
+++ b/hyperglass/hyperglass.py
@@ -11,7 +11,7 @@ from flask_caching import Cache
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
-# Local Imports
+# Project Imports
import hyperglass.configuration as configuration
from hyperglass.command import execute
from hyperglass import render
@@ -19,44 +19,14 @@ from hyperglass import render
# Main Flask definition
app = Flask(__name__, static_url_path="/static")
+# Initialize general configuration parameters for reuse
general = configuration.general()
+
# Flask-Limiter Config
rate_limit_query = f"{general.rate_limit_query} per minute"
rate_limit_site = f"{general.rate_limit_site} per minute"
limiter = Limiter(app, key_func=get_remote_address, default_limits=[rate_limit_site])
-
-def renderCSS():
- try:
- render.css.renderTemplate()
- except:
- raise
-
-
-# Render Main Flask-Limiter Error Message
-@app.errorhandler(429)
-def error429(e):
- """Renders full error page for too many site queries"""
- html = render.html.renderTemplate("429")
- return html, 429
-
-
-def error415():
- """Renders full error page for generic errors"""
- html = render.html.renderTemplate("415")
- return html, 415
-
-
-def errorQuery():
- """Renders modal error message"""
- return 429
-
-
-def errorGeneral(id):
- """Renders notification error message with an ID number"""
- return "An unknown error occurred." + "\s" + id, 415
-
-
# Flask-Caching Config
cache = Cache(
app,
@@ -68,6 +38,19 @@ cache = Cache(
)
+@app.errorhandler(429)
+def error429(e):
+ """Renders full error page for too many site queries"""
+ html = render.html.renderTemplate("429")
+ return html, 429
+
+
+def error500():
+ """Renders full error page for generic errors"""
+ html = render.html.renderTemplate("500")
+ return html, 500
+
+
def clearCache():
"""Function to clear the Flask-Caching cache"""
with app.app_context():
@@ -77,7 +60,6 @@ def clearCache():
raise
-# Main / Flask route where html is rendered via Jinja2
@app.route("/", methods=["GET"])
@limiter.limit(rate_limit_site)
def site():
@@ -86,27 +68,26 @@ def site():
return html
-# Test route for various tests
@app.route("/test", methods=["GET"])
def testRoute():
- html = render.html.renderTemplate("test")
+ """Test route for various tests"""
+ html = render.html.renderTemplate("500")
return html
-# Flask GET route provides a JSON list of all routers for the selected network/ASN
@app.route("/routers/