diff --git a/docs/docs/configuration.mdx b/docs/docs/configuration.mdx index bbeeac4..d96de00 100644 --- a/docs/docs/configuration.mdx +++ b/docs/docs/configuration.mdx @@ -51,7 +51,7 @@ The following global settings can be set in `hyperglass.yaml`: | `site_description` | String | `'{org_name} Network Looking Glass'` | A short description of your hyperglass site. This field is used in th UI & API documentation to set the `` tag. `{org_name}` may be used to insert the value of the `org_name` field. | | `site_keywords` | List | | Keywords pertaining to your hyperglass site. This field is used to generate `` HTML tags, which helps tremendously with [SEO](https://support.google.com/webmasters/answer/7451184). | | `request_timeout` | Integer | `30` | Global timeout in seconds for all requests. The UI uses this field's exact value when submitting queries. The backend uses this field's value, minus one second, for its own timeout handling. This is to ensure a contextual timeout error is presented to the end user in the event of a backend application timeout. | -| `listen_address` | String | `'localhost'` | Local IP Address or hostname the hyperglass application listens on to serve web traffic. | +| `listen_address` | String | `'localhost'` | Local IPv4/IPv6 Address the hyperglass application listens on to serve web traffic. | | `listen_port` | Integer | `8001` | Local TCP port the hyperglass application listens on to serve web traffic. | | `log_file` | String | | Path to a log file to which hyperglass can write logs. If none is set, hyperglass will write logs to a file located at `/tmp/`, with a uniquely generated name for each time hyperglass is started. | | `cors_origins` | List | `[]` | Allowed [CORS](https://developer.mozilla.org/docs/Web/HTTP/CORS) hosts. By default, no CORS hosts are allowed. | @@ -71,7 +71,7 @@ site_title: hyperglass site_description: "{org_name} Network Looking Glass" site_keywords: [hyperglass, looking glass, routing, bgp] request_timeout: 30 -listen_address: localhost +listen_address: "127.0.0.1" listen_port: 8001 log_file: /tmp/hyperglass.log cors_origins: [localhost:3000, 192.0.2.1] diff --git a/hyperglass/configuration/models/params.py b/hyperglass/configuration/models/params.py index d522760..7dfaeb1 100644 --- a/hyperglass/configuration/models/params.py +++ b/hyperglass/configuration/models/params.py @@ -14,6 +14,7 @@ from pydantic import ( StrictStr, StrictBool, IPvAnyAddress, + constr, validator, ) @@ -87,7 +88,7 @@ class Params(HyperglassModel): title="Request Timeout", description="Global timeout in seconds for all requests. The frontend application (UI) uses this field's exact value when submitting queries. The backend application uses this field's value, minus one second, for its own timeout handling. This is to ensure a contextual timeout error is presented to the end user in the event of a backend application timeout.", ) - listen_address: Optional[Union[IPvAnyAddress, StrictStr]] = Field( + listen_address: Optional[Union[IPvAnyAddress, constr(regex=r"localhost")]] = Field( None, title="Listen Address", description="Local IP Address or hostname the hyperglass application listens on to serve web traffic.", @@ -137,7 +138,7 @@ class Params(HyperglassModel): {str} -- Validated listen_address """ if value is None and not values["debug"]: - listen_address = "localhost" + listen_address = ip_address("127.0.0.1") elif value is None and values["debug"]: listen_address = ip_address("0.0.0.0") # noqa: S104 elif isinstance(value, str) and value != "localhost": @@ -146,7 +147,7 @@ class Params(HyperglassModel): except ValueError: raise ValueError(str(value)) elif isinstance(value, str) and value == "localhost": - listen_address = value + listen_address = ip_address("127.0.0.1") else: raise ValueError(str(value)) return listen_address diff --git a/hyperglass/main.py b/hyperglass/main.py index 9d904b0..9a62464 100644 --- a/hyperglass/main.py +++ b/hyperglass/main.py @@ -32,6 +32,7 @@ from hyperglass.util import ( # isort:skip check_redis, build_frontend, clear_redis_cache, + format_listen_address, ) from hyperglass.compat._asyncio import aiorun # isort:skip @@ -140,7 +141,9 @@ def start(**kwargs): "preload": True, "keepalive": 10, "command": shutil.which("gunicorn"), - "bind": ":".join((str(params.listen_address), str(params.listen_port))), + "bind": ":".join( + (format_listen_address(params.listen_address), str(params.listen_port)) + ), "workers": workers, "loglevel": loglevel, "timeout": math.ceil(params.request_timeout * 1.25), diff --git a/hyperglass/util.py b/hyperglass/util.py index db62f7a..9914078 100644 --- a/hyperglass/util.py +++ b/hyperglass/util.py @@ -618,6 +618,31 @@ def import_public_key(app_path, device_name, keystring): return True +def format_listen_address(listen_address): + """Format a listen_address. + + Wraps IPv6 address in brackets. + + Arguments: + listen_address {str} -- Preformatted listen_address + + Returns: + {str} -- Formatted listen_address + """ + from ipaddress import ip_address, IPv4Address, IPv6Address + + if not isinstance(listen_address, (IPv4Address, IPv6Address)): + try: + listen_address = ip_address(listen_address) + if listen_address.version == 6: + listen_address = f"[{str(listen_address)}]" + except ValueError: + pass + else: + listen_address = str(listen_address) + return listen_address + + def split_on_uppercase(s): """Split characters by uppercase letters.