From 4b92b670c99d5b12f341d0c49deb0983f6783270 Mon Sep 17 00:00:00 2001 From: checktheroads Date: Sat, 6 Jun 2020 01:22:14 -0700 Subject: [PATCH] add structured data configuration options & external RPKI validation --- docs/docs/configuration.mdx | 17 +++--- docs/docs/structured-data.mdx | 59 +++++++++++++++++++ docs/sidebars.js | 1 + hyperglass/configuration/models/params.py | 2 + hyperglass/configuration/models/structured.py | 31 ++++++++++ hyperglass/external/rpki.py | 35 +++++++++++ 6 files changed, 137 insertions(+), 8 deletions(-) create mode 100644 docs/docs/structured-data.mdx create mode 100644 hyperglass/configuration/models/structured.py create mode 100644 hyperglass/external/rpki.py diff --git a/docs/docs/configuration.mdx b/docs/docs/configuration.mdx index 1bdb014..746ace0 100644 --- a/docs/docs/configuration.mdx +++ b/docs/docs/configuration.mdx @@ -79,14 +79,15 @@ cors_origins: [localhost:3000, 192.0.2.1] From the top level, the following subsections may be defined and configured: -| Section | Description | All Options | -| :--------- | :-------------------------------------------------- | :-----------------------------------------: | -| `cache` | Redis server & cache timeout settings. | ➡️ | -| `docs` | API documentation settings. | ➡️ | -| `logging` | File, syslog, and webhook settings. | ➡️ | -| `messages` | Customize almost all user-facing UI & API messages. | ➡️ | -| `queries` | Enable, disable, or configure query types. | ➡️ | -| `web` | Web UI & branding settings. | ➡️ | +| Section | Description | All Options | +| :----------- | :-------------------------------------------------- | :------------------------------------------------: | +| `cache` | Redis server & cache timeout settings. | ➡️ | +| `docs` | API documentation settings. | ➡️ | +| `logging` | File, syslog, and webhook settings. | ➡️ | +| `messages` | Customize almost all user-facing UI & API messages. | ➡️ | +| `queries` | Enable, disable, or configure query types. | ➡️ | +| `structured` | Configure structured data features. | ➡️ | +| `web` | Web UI & branding settings. | ➡️ | ## Adding Devices diff --git a/docs/docs/structured-data.mdx b/docs/docs/structured-data.mdx new file mode 100644 index 0000000..5d96b97 --- /dev/null +++ b/docs/docs/structured-data.mdx @@ -0,0 +1,59 @@ +--- +id: structured-data +title: Structured Data +sidebar_label: Structured Data +description: Configure structured data parameters +--- + +
+ +The `structured` subsection contains multiple subsections of its own: + +| Section | Description | All Options | +| :------------ | :--------------------------------- | :---------------------------------------: | +| `communities` | Include or exclude BGP communities | ➡️ | +| `rpki` | Configure RPKI validation | ➡️ | + +## `communities` + +| Parameter | Type | Default | Description | +| :-------- | :----: | :------: | :------------------------------------------------------------------------------------------------------------------------------------ | +| `mode` | String | `'deny'` | `'deny'` denies any matching patterns and permits anything else. `'permit'` only permits matching patterns and denies everything else | +| `items` | List | `[]` | List of regular expression patterns to match | + +:::note Regular Expressions with YAML +If you're using any regex patterns with special characters, you'll probably need to wrap the patterns in single quotes, to denote a raw string. For example: + +```yaml +normal_string: Normal String +raw_string: '^Raw\sString$' +``` + +::: + +## `rpki` + +| Parameter | Type | Default | +| :-------- | :----: | :--------: | +| `mode` | String | `'router'` | + +### `router` mode + +`router` mode uses the RPKI state from the perspective of the router. This means if your network is not using Origin Validation, the RPKI state will be shown as "Not Verified". Otherwise, the corresponding RPKI state will be shown. + +### `external` mode + +`external` mode takes each prefix, the last ASN in the `AS_PATH` and requests the RPKI validation state via the [Cloudflare RPKI Portal](https://rpki.cloudflare.com/). + +## Example + +```yaml title="hyperglass.yaml" +structured: + communities: + mode: permit + items: + - '65000:\d+' + - '65[1-4]00:\d+' + rpki: + mode: external +``` diff --git a/docs/sidebars.js b/docs/sidebars.js index 699d0f6..aac90e2 100755 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -18,6 +18,7 @@ module.exports = { "ui", "api", "messages", + "structured-data", ], }, { diff --git a/hyperglass/configuration/models/params.py b/hyperglass/configuration/models/params.py index 96687a6..caf4f7d 100644 --- a/hyperglass/configuration/models/params.py +++ b/hyperglass/configuration/models/params.py @@ -23,6 +23,7 @@ from hyperglass.configuration.models.cache import Cache from hyperglass.configuration.models.logging import Logging from hyperglass.configuration.models.queries import Queries from hyperglass.configuration.models.messages import Messages +from hyperglass.configuration.models.structured import Structured class Params(HyperglassModel): @@ -113,6 +114,7 @@ class Params(HyperglassModel): logging: Logging = Logging() messages: Messages = Messages() queries: Queries = Queries() + structured: Structured = Structured() web: Web = Web() class Config: diff --git a/hyperglass/configuration/models/structured.py b/hyperglass/configuration/models/structured.py new file mode 100644 index 0000000..a2fa1a6 --- /dev/null +++ b/hyperglass/configuration/models/structured.py @@ -0,0 +1,31 @@ +"""Structured data configuration variables.""" + +# Standard Library +from typing import List + +# Third Party +from pydantic import StrictInt, StrictStr, constr + +# Project +from hyperglass.models import HyperglassModel + + +class StructuredCommunities(HyperglassModel): + """Control structured data response for BGP communties.""" + + mode: constr(regex=r"(permit|deny)") = "deny" + items: List[StrictStr] = [] + + +class StructuredRpki(HyperglassModel): + """Control structured data response for RPKI state.""" + + mode: constr(regex=r"(router|external)") = "router" + max_age: StrictInt = 24 + + +class Structured(HyperglassModel): + """Control structured data responses.""" + + communities: StructuredCommunities = StructuredCommunities() + rpki: StructuredRpki = StructuredRpki() diff --git a/hyperglass/external/rpki.py b/hyperglass/external/rpki.py new file mode 100644 index 0000000..5e035b4 --- /dev/null +++ b/hyperglass/external/rpki.py @@ -0,0 +1,35 @@ +"""Validate RPKI state via Cloudflare GraphQL API.""" + +from hyperglass.log import log +from hyperglass.external._base import BaseExternal + +RPKI_STATE_MAP = {"Invalid": 0, "Valid": 1, "NotFound": 2, "DEFAULT": 3} +RPKI_NAME_MAP = {v: k for k, v in RPKI_STATE_MAP.items()} + + +def rpki_state(prefix, asn): + """Get RPKI state and map to expected integer.""" + log.debug("Validating RPKI State for {p} via AS{a}", p=prefix, a=asn) + + state = 3 + query = 'query GetValidation {{ validation(prefix: "{prefix}", asn: {asn}) {{ state }} }}'.format( # noqa: E501 + prefix=prefix, asn=asn + ) + + try: + with BaseExternal(base_url="https://rpki.cloudflare.com") as client: + response = client._post("/api/graphql", data={"query": query}) + validation_state = ( + response.get("data", {}).get("validation", {}).get("state", "DEFAULT") + ) + state = RPKI_STATE_MAP[validation_state] + except Exception: + state = 3 + + log.debug( + "RPKI Validation State for {p} via AS{a} is {s}", + p=prefix, + a=asn, + s=RPKI_NAME_MAP[state], + ) + return state