Zaufanie do GNSS jako telemetria bezpieczeństwa
kshana trust-telemetry zamienia strumień zaufania do GNSS dla każdej epoki (wskaźnik 0-100, pasmo, powody) na sygnały, które centrum operacyjne lub bezpieczeństwa już przyjmuje: metryki Prometheus, zdarzenia syslog w CEF lub LEEF dla SIEM oraz opcjonalnie OpenTelemetry. Czyta strumień; nigdy nie zmienia oceny zaufania i nie nadaje niczego poza telemetrią.
Uruchom#
# live stream on stdin -> /metrics on localhost:9464
kshana receiver-trust live <args> | kshana trust-telemetry
# replay a batch result (the score is the epoch's own for a vessel platform, absent otherwise)
kshana trust-telemetry --result session.result.json --print-metrics
kshana trust-telemetry --result session.result.json --print-syslog --format leef
# SIEM over UDP, one event per band change
kshana trust-telemetry --input stream.jsonl --syslog-udp siem.example:514 --format cef --host ops-gw1
Wejściem jest schemat JSON-lines na żywo v1.1, opisany w docs/MARITIME-TRUST.md. Odczytywane: t_s; state (calibrating|nominal|degraded|untrusted, pasmo); score (0-100 lub null); time (czas epoki w podanej postaci); gate (off|passed|withheld); position (lat_deg, lon_deg, height_m lub null); powody to monitory w deductions, a potem wszelkie dalsze w alarms; od schematu 1,3 także geometry (status, hdop, pdop, n_used) oraz class każdego odliczenia. Pozostałe klucze są ignorowane, ponieważ schemat rośnie wyłącznie przez dopisywanie kluczy. Linia, która się nie parsuje, jest liczona w kshana_trust_input_errors_total i pomijana; wyniki poza zakresem 0-100 są odrzucane, a nie przycinane, aby zmiana formatu była zauważona. Jedynym kodem, który zna ten format, jest src/telemetry/sample.rs.
/metrics jest serwowane bez uwierzytelniania i domyślnie nasłuchuje na 127.0.0.1:9464. Powiązanie z innym adresem wypisuje ostrzeżenie; umieść je za własną kontrolą dostępu. Punkt końcowy obsługuje jedno połączenie naraz z limitem odczytu 2 s, więc bezczynny klient może opóźnić pobranie nawet o tyle; jest przeznaczony dla jednego skrobacza, a nie dla otwartej sieci.
Home Assistant i urządzenia NTP (MQTT)#
--print-mqtt drukuje, a --mqtt-tcp <host:port> publikuje dokument stanu JSON i komunikaty wykrywania MQTT dla Home Assistant. Wykrywanie (zachowywane, pod homeassistant/...) tworzy trzy encje: wskaźnik zaufania, pasmo zaufania (z powodami jako atrybutami) i czujnik problemu untrusted. Dokument stanu trafia do <prefix>/state (domyślnie kshana/trust/state, zachowywany): state, score (null, gdy brak), untrusted, t_s, time, reasons, gate i oświadczenie doradcze; pozycja nigdy nie jest dołączana. Opcje: --mqtt-prefix, --mqtt-node, --mqtt-discovery.
Wydawca mówi MQTT 3.1.1 z QoS 0 i czyta tylko CONNACK brokera. Nie ma poświadczeń ani TLS, więc użyj brokera w sieci lokalnej (lub lokalnego przekaźnika, który je dodaje). Dostarczanie działa we własnym wątku za kolejką o długości 1024: martwy broker powoduje porzucanie wiadomości, które są liczone i zgłaszane na końcu, i nigdy nie blokuje strumienia. Urządzenie NTP lub dowolny inny odbiorca, który chce zwykłego JSON, może czytać tę samą linię stanu. Nic nie jest zapisywane do odbiornika ani zegara. Python, WebAssembly i MCP przyjmują format = "mqtt" i zwracają te same linie topic<TAB>payload (nic tam nie jest publikowane).
Z Pythona, WebAssembly i MCP#
Kodery są też dostępne jako wywołania tekst na wejściu, tekst na wyjściu, bez procesu, gniazda i zegara: Python kshana.trust_telemetry(input, format, host="", input_kind="jsonl", expose_position=False, syslog_all=False, unix_nano=0), eksport WebAssembly trust_telemetry(input, format, host, input_kind, options_json) i narzędzie MCP trust_telemetry (polecenie wtyczki /kshana-trust-telemetry). format to prometheus, cef, leef lub otlp; input_kind to jsonl (strumień) lub result (wynik wsadowy). Zwracają ten sam tekst, który drukuje wiersz poleceń (--print-metrics, --print-syslog), z tymi samymi limitami: wiersze syslog nie mają znacznika czasu, host ma co najwyżej 255 drukowalnych znaków ASCII bez spacji (w przeciwnym razie jest odrzucane, tu i w wierszu poleceń), a odpowiedź większa niż limit powierzchni (4 MiB w MCP) jest odrzucana, ponieważ CEF i LEEF z syslog_all zapisują jedno zdarzenie na epokę. Nic nie jest wysyłane, serwowane ani zapisywane; działające eksportery pozostają procesem w wierszu poleceń.
Dostarczanie nigdy nie blokuje oceny#
Dostarczanie syslog i OpenTelemetry działa we własnych wątkach. Pętla wczytywania tylko przekazuje im wiadomość lub migawkę i nigdy nie czeka, więc martwy, wolny lub niereagujący kolektor nie może wywołać przeciwciśnienia na strumieniu na żywo ani zamrozić /metrics:
- każde połączenie i zapis mają limit 2 s; nieudane połączenie jest porzucane i nawiązywane ponownie leniwie, z wykładniczym wycofywaniem od 1 s do 60 s;
- wiadomość, której nie da się dostarczyć (kolektor nie działa, przekroczono czas zapisu, kolejka 1024 pełna), jest porzucana i liczona w
kshana_trust_syslog_send_failures_total; ustaw alert na jej częstość; - eksporter OpenTelemetry dostaje kopię rejestru, więc podczas operacji sieciowych nie jest trzymana żadna blokada; jeśli eksport wciąż trwa, nowa migawka jest pomijana (następna niesie te same sumy skumulowane).
Po końcu wejścia wątki nadawcze dostają pozostały czas limitów na dokończenie.
Metryki Prometheus#
| Metryka | Typ | Etykiety | Znaczenie |
|---|---|---|---|
kshana_build_info |
gauge | version |
stała 1 |
kshana_trust_score |
gauge | ostatni wskaźnik 0-100; brak, jeśli źródło go nie podaje | |
kshana_trust_band |
gauge | band |
1 dla bieżącego pasma, 0 dla pozostałych |
kshana_trust_gate |
gauge | gate |
off, passed, withheld, unknown: 1 dla bieżącego stanu, 0 dla pozostałych; brak bez bramki |
kshana_trust_position_latitude_degrees, ..._longitude_degrees, ..._height_meters |
gauge | pozycja zgłoszona przez odbiornik; tylko z --expose-position (punkt końcowy jest nieuwierzytelniony, a pozycja może zidentyfikować miejsce lub statek) |
|
kshana_trust_reason_active |
gauge | reason |
1, jeśli występuje w ostatniej epoce |
kshana_trust_epochs_total |
licznik | band |
odebrane epoki |
kshana_trust_reason_epochs_total |
licznik | reason |
epoki, w których wystąpił powód |
kshana_trust_input_errors_total |
licznik | nieparsowalne linie wejścia | |
kshana_trust_reason_overflow_total |
licznik | wystąpienia powodów zwinięte do reason="other" (zob. niżej) |
|
kshana_trust_syslog_send_failures_total |
licznik | niedostarczone zdarzenia syslog | |
kshana_trust_epoch_offset_seconds |
gauge | przesunięcie ostatniej epoki od początku strumienia | |
kshana_trust_last_sample_timestamp_seconds |
gauge | czas odbioru według zegara ściennego; ustaw alert na jego wiek | |
kshana_trust_geometry |
gauge | status |
geometria satelitów w ostatniej epoce, good, degraded, unknown: 1 dla bieżącej (dane wejściowe ze schematu 1,3) |
kshana_trust_geometry_degraded |
gauge | 1 degraded, 0 good; brak, gdy geometria jest nieznana (1.3) | |
kshana_trust_geometry_signal_loss |
gauge | 1, gdy widoczne satelity nie są śledzone, mimo że ich geometria jest dobra (utracono sygnał, a nie niebo; geometry_degraded nadal wskazuje 1), w przeciwnym razie 0; brak, gdy geometria jest nieznana lub wejście o tym nie mówi (1.3) |
|
kshana_trust_geometry_hdop, ..._pdop, ..._used_satellites |
gauge | HDOP, PDOP i liczba użytych satelitów w ostatniej epoce; każda wartość nieobecna, gdy jej brak (1.3) | |
kshana_trust_geometry_epochs_total |
licznik | status |
epoki według statusu geometrii (1.3) |
kshana_trust_geometry_label |
gauge | label |
etykieta geometrii w ostatniej epoce, good, fair, poor, unknown: 1 dla bieżącej; nigdy nie jest oceniana (wskaźnik odejmuje punkty tylko dla poor); z danych wejściowych, które niosą etykietę (0,36) |
kshana_trust_geometry_vdop |
gauge | VDOP w ostatniej epoce; brak, gdy go nie ma (0.36) |
Etykieta geometrii jest najważniejsza. Rysuj wykresy i ustawiaj alerty na kshana_trust_geometry_label (OTLP kshana.geometry.label, CEF trzecie pole cs6, LEEF geoLabel): poor to jedyna etykieta, za którą wskaźnik odejmuje punkty. Status geometrii jest opisowy: degraded obejmuje informacyjny przypadek sky-blocked, który może występować przy dobrym lub średnim DOP, więc nie wywołuj alarmu na samym kshana_trust_geometry_degraded.
Od danych wejściowych schematu 1,3 kshana_trust_reason_active niesie też etykietę class (geometry, environment, inconsistency) przy każdym powodzie, który ją miał: klasa odliczenia w ostatniej epoce. Serie geometrii następują po wszystkich pozostałych i tylko gdy epoka wejściowa niesie geometry (schemat na żywo 1,3, wynik z silnika 0,36 lub nowszego), więc strumień lub wynik 0,35 jest renderowany bajt po bajcie tak jak dawniej. Wartości etykiet są ustalonymi zbiorami (nieznany status daje unknown; nieznana klasa jest odrzucana). Ustaw alert na kshana_trust_reason_active{class="inconsistency"}, a nie tylko na pasmo, aby słabe niebo nie wywoływało alarmu jak atak; klasa geometry to stwierdzenie o niebie, a nie ustalenie, że nie doszło do ataku (zob. docs/RECEIVER-TRUST.md).
Etykieta class jest częścią tożsamości serii: gdy klasa powodu zmienia się między epokami (loss-of-lock z environment na geometry, gdy utracone satelity były nisko, albo geometry na inconsistency, gdy podany HDOP nie zgadza się z podanym niebem), stara seria nie jest już eksportowana i zaczyna się nowa. Zapytaj według reason z max by (reason), gdy klasa nie ma znaczenia, i spodziewaj się, że taka zmiana wygląda jak zakończenie jednej serii i początek drugiej. Serie schematu 1,3, etykieta class i pola geometrii CEF/LEEF są sprawdzane własnymi testami silnika i wyroczniami CEF (pycef, Logstash); zewnętrzne wyrocznie Prometheus i OTLP nie mają jeszcze strumienia 1,3.
Nazwy powodów są obcinane do 128 znaków, a co najwyżej 64 różnych powodów dostaje własną serię; dalsze są liczone pod reason="other" i w kshana_trust_reason_overflow_total, więc wrogie lub błędne wejście nie może wytworzyć nieograniczonej liczności etykiet.
band to jedno z calibrating, nominal, degraded, untrusted, unknown. Nazwy powodów to nazwy monitorów strumienia (kinematic, heading-course, ...; dla wyniku wsadowego cn0-drop, agc, raim).
Przykładowy pulpit jest w deploy/grafana/kshana-gnss-trust.json (zaimportuj go i wybierz swoje źródło danych Prometheus).
Syslog: CEF i LEEF#
Zdarzenia są wysyłane w kopercie RFC 5424 (facility local0, aplikacja kshana, identyfikator wiadomości GNSSTRUST) przez UDP (jeden datagram na zdarzenie; IPv4 lub IPv6) albo TCP, domyślnie zakończone znakiem nowej linii lub, z --syslog-octet-counting, w ramce <length> <message> jak w RFC 6587. Domyślnie wysyła się jedno zdarzenie na zmianę pasma; --syslog-all wysyła jedno na epokę. Dostawca jest zapisywany jako Ashforde OU (ASCII), aby żaden SIEM nie zdekodował go błędnie.
| Pojęcie | Waga syslog | Waga CEF | LEEF sev |
|---|---|---|---|
| calibrating | 6 info | 0 | 1 |
| nominal | 6 info | 1 | 1 |
| z pogorszoną jakością | 4 warning | 5 | 5 |
| untrusted | 2 critical | 9 | 9 |
| unknown | 5 notice | 3 | 3 |
Mapowanie pól (identyfikator zdarzenia to gnss-trust.<band>):
| Znaczenie | CEF | LEEF 2,0 (ogranicznik ^) |
|---|---|---|
| kategoria | cat=gnss-trust |
cat |
| host wysyłający | dvchost |
devHost |
| pasmo | cs1 (etykieta band) |
band |
| powody, oddzielone przecinkami | cs2 (reasons) |
reasons |
| poprzednie pasmo | cs3 (previousBand) |
previousBand |
| wskaźnik | cfp1 (trustScore), pomijane, jeśli brak |
trustScore |
| przesunięcie epoki, s | cfp2 (epochOffsetSeconds) |
epochOffsetSeconds |
| etykieta czasu logu | cs4 (logTime) |
logTime |
| stan bramki | cs5 (gate) |
gate |
status geometrii, następnie ; i klasy odliczeń epoki, oddzielone przecinkami (na przykład degraded;environment,geometry) (dane wejściowe 1,3); z danych wejściowych, które niosą etykietę (0,36), trzecie pole, ; i etykieta, przy czym pole klas jest wtedy zawsze obecne (na przykład degraded;;poor, good;;fair) |
cs6 (geometry) |
geoStatus (tylko status), geoClass (klasy) i geoLabel (etykieta) |
| HDOP (1.3) | cfp3 (geometryHdop) |
geoHdop |
| PDOP (1.3) | cfp4 (geometryPdop) |
geoPdop (0,36, z etykietą) |
| VDOP (0,36, z etykietą) | niesione: nie; zajęte są cztery niestandardowe pola zmiennoprzecinkowe | geoVdop |
| liczba użytych satelitów (0,36, z etykietą) | cn1 (geometryUsedSatellites), liczba całkowita |
geoUsed |
| utrata sygnału: widoczne satelity nie są śledzone, mimo że ich geometria jest dobra (1.3) | nie jest niesione jako pole (żaden klucz słownika CEF nie jest wolny): status to degraded, a klasa odliczenia geometrii to environment w cs6 |
geoSignalLoss=1, tylko gdy zachodzi |
W CEF geometria używa wyłącznie niestandardowych pól ze słownika (żadnych wymyślonych kluczy); etykieta jedzie w cs6, a nie w flexString1, ponieważ kodek CEF w Logstash nie mapuje pól flexString. Zdarzenia geometrii 1,3, w tym przypadki z etykietą, są częścią wiersza CEF o poziomie VALIDATED: oba parsery wyroczni odczytują je z powrotem tak, jak zapisano (tests/cef_reference.rs, poprawki 4 i 5, każda zarejestrowana, zanim któryś parser uruchomiono na jej przypadkach). Pola etykiety, VDOP i liczby satelitów są dopisywane po wszystkich wcześniejszych polach i seriach i tylko dla danych wejściowych, które niosą etykietę, więc wejście bez niej renderuje się dokładnie tak jak dawniej.
Puste pola są pomijane (bez cs2=, gdy nie ma powodów). Nazwa hosta jest obcinana do 255 znaków. Wartości nagłówka CEF poprzedza się znakiem ucieczki dla \ i |; wartości rozszerzeń dla \, = i podziałów wierszy. W wartościach LEEF ^ i podziały wierszy są zastępowane spacjami.
OpenTelemetry (domyślnie wyłączone)#
Zbuduj z --features otlp i podaj --otlp http://host:4318/v1/metrics. Eksporter wysyła metodą POST JSON OTLP/HTTP (kshana.trust.score, kshana.trust.band, kshana.trust.epochs, kshana.trust.reason.epochs oraz dla danych wejściowych schematu 1,3 kshana.geometry.status, kshana.geometry.degraded, kshana.geometry.signal_loss, kshana.geometry.hdop, kshana.geometry.pdop, kshana.geometry.used_satellites i, z danych wejściowych, które niosą etykietę (0,36), kshana.geometry.label (atrybut label) i kshana.geometry.vdop; sumy skumulowane niosą startTimeUnixNano) co --otlp-every epok (domyślnie 10) i na końcu wejścia. Nie dodaje żadnej zależności i nie ma TLS: wysyłaj do kolektora na tym samym hoście lub w zaufanym segmencie, który przekazuje dane dalej przez TLS. Punkty końcowe https:// są odrzucane.