mirror of
https://github.com/thatmattlove/hyperglass.git
synced 2026-02-07 17:58:24 +00:00
78 lines
2.5 KiB
Python
78 lines
2.5 KiB
Python
"""Validate RPKI state via Cloudflare GraphQL API."""
|
|
|
|
# Standard Library
|
|
import typing as t
|
|
import requests
|
|
|
|
# Project
|
|
from hyperglass.log import log
|
|
from hyperglass.state import use_state
|
|
from hyperglass.external._base import BaseExternal
|
|
|
|
if t.TYPE_CHECKING:
|
|
from ipaddress import IPv4Address, IPv6Address
|
|
|
|
RPKI_STATE_MAP = {
|
|
"Invalid": 0,
|
|
"invalid": 0,
|
|
"Valid": 1,
|
|
"valid": 1,
|
|
"NotFound": 2,
|
|
"notfound": 2,
|
|
"not_found": 2,
|
|
"not-found": 2,
|
|
"Unknown": 2,
|
|
"unknown": 2,
|
|
"DEFAULT": 3,
|
|
}
|
|
RPKI_NAME_MAP = {v: k for k, v in RPKI_STATE_MAP.items()}
|
|
CACHE_KEY = "hyperglass.external.rpki"
|
|
|
|
|
|
def rpki_state(
|
|
prefix: t.Union["IPv4Address", "IPv6Address", str],
|
|
asn: t.Union[int, str],
|
|
backend: str = "cloudflare",
|
|
rpki_server_url: str = "",
|
|
) -> int:
|
|
"""Get RPKI state and map to expected integer."""
|
|
_log = log.bind(prefix=prefix, asn=asn)
|
|
_log.debug("Validating RPKI State")
|
|
|
|
cache = use_state("cache")
|
|
state = 3
|
|
ro = f"{prefix!s}@{asn!s}"
|
|
|
|
cached = cache.get_map(CACHE_KEY, ro)
|
|
if cached is not None:
|
|
state = cached
|
|
else:
|
|
try:
|
|
if backend == "cloudflare":
|
|
ql = 'query GetValidation {{ validation(prefix: "{}", asn: {}) {{ state }} }}'
|
|
query = ql.format(prefix, asn)
|
|
_log.bind(query=query).debug("Cloudflare RPKI GraphQL Query")
|
|
with BaseExternal(base_url="https://rpki.cloudflare.com") as client:
|
|
response = client._post("/api/graphql", data={"query": query})
|
|
validation_state = response["data"]["validation"]["state"]
|
|
elif backend == "routinator":
|
|
url = f"{rpki_server_url.rstrip('/')}/validity?asn={asn}&prefix={prefix}"
|
|
_log.bind(url=url).debug("Routinator RPKI HTTP Query")
|
|
response = requests.get(url, timeout=5)
|
|
response.raise_for_status()
|
|
data = response.json()
|
|
validation_state = data["validated_route"]["validity"]["state"]
|
|
else:
|
|
raise ValueError(f"Unknown RPKI backend: {backend}")
|
|
|
|
state = RPKI_STATE_MAP.get(validation_state, 3)
|
|
cache.set_map_item(CACHE_KEY, ro, state)
|
|
except Exception as err:
|
|
log.error(err)
|
|
state = 3
|
|
|
|
msg = "RPKI Validation State for {} via AS{} is {}".format(prefix, asn, RPKI_NAME_MAP[state])
|
|
if cached is not None:
|
|
msg += " [CACHED]"
|
|
log.debug(msg)
|
|
return state
|