Довіра до GNSS як телеметрія безпеки
kshana trust-telemetry перетворює потік довіри до GNSS для кожної епохи (оцінка 0–100, смуга, причини) на сигнали, які центр експлуатації чи безпеки вже приймає: метрики Prometheus, події syslog у CEF або LEEF для SIEM і, за бажання, OpenTelemetry. Вона читає потік; вона ніколи не змінює оцінку довіри і не передає нічого, крім телеметрії.
Запуск#
# 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
Вхід — це живий формат JSON-рядків версії схеми 1.1, задокументований у docs/MARITIME-TRUST.md. Читаються: t_s; state (calibrating|nominal|degraded|untrusted, смуга); score (0–100 або null); time (час епохи, як заявлено); gate (off|passed|withheld); position (lat_deg, lon_deg, height_m або null); причини — це монітори в deductions, потім будь-які інші в alarms; зі схеми 1,3 — geometry (status, hdop, pdop, n_used) і class кожного відрахування. Інші ключі ігноруються, бо схема зростає лише додаванням ключів. Рядок, що не розбирається, зараховується в kshana_trust_input_errors_total і пропускається; оцінки поза 0–100 відхиляються, а не обрізаються, щоб зміну формату було помічено. Єдиний код, що знає цей формат, — src/telemetry/sample.rs.
/metrics віддається без автентифікації й типово прив'язаний до 127.0.0.1:9464. Прив'язка в іншому місці друкує попередження; поставте його за власним контролем доступу. Кінцева точка обробляє одне з'єднання за раз із обмеженням читання 2 s, тож неактивний клієнт може затримати скрейпінг на цей час; вона призначена для одного скрейпера, а не для відкритої мережі.
Home Assistant і пристрої NTP (MQTT)#
--print-mqtt друкує, а --mqtt-tcp <host:port> публікує документ стану JSON і повідомлення виявлення MQTT для Home Assistant. Виявлення (збережене, під homeassistant/...) створює три сутності: оцінку довіри, смугу довіри (з причинами як атрибутами) і датчик проблеми untrusted. Документ стану йде до <prefix>/state (типово kshana/trust/state, збережений): state, score (null, поки немає), untrusted, t_s, time, reasons, gate і консультативне твердження; позиція ніколи не включається. Параметри: --mqtt-prefix, --mqtt-node, --mqtt-discovery.
Публікатор говорить MQTT 3.1.1 із QoS 0 і читає лише CONNACK брокера. Він не має облікових даних і TLS, тож використовуйте брокера в локальній мережі (або локальний ретранслятор, що їх додає). Доставка працює у власному потоці за чергою 1024: мертвий брокер призводить до втрати повідомлень, які підраховуються і повідомляються наприкінці, і ніколи не зупиняє потік. Пристрій NTP або будь-який інший споживач, якому потрібен простий JSON, може читати той самий рядок стану. Нічого не записується в приймач чи годинник. Python, WebAssembly і MCP приймають format = "mqtt" і повертають ті самі рядки topic<TAB>payload (там нічого не публікується).
З Python, WebAssembly і MCP#
Кодувальники також доступні як виклики «текст на вході, текст на виході» без процесу, сокета чи годинника: Python kshana.trust_telemetry(input, format, host="", input_kind="jsonl", expose_position=False, syslog_all=False, unix_nano=0), експорт WebAssembly trust_telemetry(input, format, host, input_kind, options_json) та інструмент MCP trust_telemetry (команда плагіна /kshana-trust-telemetry). format — це prometheus, cef, leef або otlp; input_kind — це jsonl (потік) або result (пакетний результат). Вони повертають той самий текст, який друкує командний рядок (--print-metrics, --print-syslog), із тими самими обмеженнями: рядки syslog не мають мітки часу, host — щонайбільше 255 друкованих символів ASCII без пробілів (інакше відхиляється, тут і в командному рядку), а відповідь, більша за обмеження поверхні (4 MiB в MCP), відхиляється, бо CEF і LEEF з syslog_all записують одну подію на епоху. Нічого не надсилається, не віддається і не записується; запущені експортери залишаються процесом у командному рядку.
Доставка ніколи не зупиняє оцінювання#
Доставка syslog і OpenTelemetry працює у власних потоках. Цикл прийому лише передає їм повідомлення чи знімок і ніколи не чекає, тож мертвий, повільний або нечитаючий збирач не може чинити зворотного тиску на живий потік чи заморозити /metrics:
- кожне з'єднання й запис мають обмеження 2 s; невдале з'єднання скидається й відновлюється відкладено, з експоненційною затримкою від 1 s до 60 s;
- повідомлення, яке не можна доставити (збирач недоступний, час запису вичерпано, черга 1024 заповнена), відкидається і підраховується в
kshana_trust_syslog_send_failures_total; налаштуйте сповіщення за його частотою; - експортеру OpenTelemetry передається копія реєстру, тож під час мережевого вводу-виводу жодне блокування не утримується; якщо експорт ще триває, новий знімок пропускається (наступний міститиме ті самі кумулятивні підсумки).
Наприкінці входу потокам відправників надається решта їхніх обмежень часу для завершення.
Метрики Prometheus#
| Метрика | Тип | Мітки | Значення |
|---|---|---|---|
kshana_build_info |
gauge | version |
стала 1 |
kshana_trust_score |
gauge | остання оцінка 0–100; відсутня, якщо джерело її не дає | |
kshana_trust_band |
gauge | band |
1 для поточної смуги, 0 для інших |
kshana_trust_gate |
gauge | gate |
off, passed, withheld, unknown: 1 для поточного стану, 0 для інших; відсутня без шлюзу |
kshana_trust_position_latitude_degrees, ..._longitude_degrees, ..._height_meters |
gauge | позиція, повідомлена приймачем; лише з --expose-position (кінцева точка без автентифікації, а позиція може ідентифікувати об'єкт чи судно) |
|
kshana_trust_reason_active |
gauge | reason |
1 якщо є в останній епосі |
kshana_trust_epochs_total |
лічильник | band |
отримані епохи |
kshana_trust_reason_epochs_total |
лічильник | reason |
епохи, у яких причина була присутня |
kshana_trust_input_errors_total |
лічильник | рядки входу, що не розбираються | |
kshana_trust_reason_overflow_total |
лічильник | входження причин, згорнуті в reason="other" (див. нижче) |
|
kshana_trust_syslog_send_failures_total |
лічильник | події syslog, що не доставлені | |
kshana_trust_epoch_offset_seconds |
gauge | зміщення останньої епохи від початку потоку | |
kshana_trust_last_sample_timestamp_seconds |
gauge | час отримання за настінним годинником; налаштуйте сповіщення за його віком | |
kshana_trust_geometry |
gauge | status |
геометрія супутників в останній епосі, good, degraded, unknown: 1 для поточної (входи схеми 1,3) |
kshana_trust_geometry_degraded |
gauge | 1 погіршено, 0 добре; відсутня, коли геометрія невідома (1.3) | |
kshana_trust_geometry_signal_loss |
gauge | 1, коли супутники у видимості не відстежуються, хоча їхня геометрія добра (сигнал втрачено, а не небо; geometry_degraded усе ще дорівнює 1), інакше 0; відсутня, коли геометрія невідома або вхід цього не вказує (1.3) |
|
kshana_trust_geometry_hdop, ..._pdop, ..._used_satellites |
gauge | HDOP, PDOP і використані супутники в останній епосі; кожна відсутня, якщо немає (1.3) | |
kshana_trust_geometry_epochs_total |
лічильник | status |
епохи за станом геометрії (1.3) |
kshana_trust_geometry_label |
gauge | label |
мітка геометрії в останній епосі, good, fair, poor, unknown: 1 для поточної; ніколи не оцінюється (оцінка зменшується лише за poor); із входів, що несуть мітку (0,36) |
kshana_trust_geometry_vdop |
gauge | VDOP в останній епосі; відсутня, якщо немає (0.36) |
Мітка геометрії — головна. Будуйте графіки й налаштовуйте сповіщення за kshana_trust_geometry_label (OTLP kshana.geometry.label, CEF третє поле cs6, LEEF geoLabel): poor — єдина мітка, за якою оцінка зменшується. Стан геометрії описовий: degraded включає інформаційний випадок sky-blocked, який може супроводжуватися добрим або задовільним DOP, тож не надсилайте виклик лише за kshana_trust_geometry_degraded.
Для входів схеми 1,3 kshana_trust_reason_active також несе мітку class (geometry, environment, inconsistency) на кожній причині, що мала її: клас відрахування в останній епосі. Ряди геометрії йдуть після всіх інших і лише тоді, коли епоха входу несе geometry (жива схема 1,3, результат рушія 0,36 або новішого), тож потік чи результат 0,35 відображається байт у байт як раніше. Значення міток — фіксовані набори (невідомий стан читається як unknown; невідомий клас відкидається). Налаштовуйте сповіщення за kshana_trust_reason_active{class="inconsistency"}, а не лише за смугою, щоб поганий небозвід не сприймався як атака; клас geometry — це твердження про небо, а не висновок, що атаки не було (див. docs/RECEIVER-TRUST.md).
Мітка class входить до ідентичності ряду: коли клас причини змінюється між епохами (loss-of-lock з environment на geometry, коли втрачені супутники були низькими, або geometry на inconsistency, коли заявлений HDOP суперечить заявленому небу), старий ряд більше не експортується, а починається новий. Запитуйте за reason із max by (reason), коли клас не має значення, і очікуйте, що такий перехід виглядатиме як завершення одного ряду та початок іншого. Ряди схеми 1,3, мітка class і поля геометрії CEF/LEEF перевіряються власними тестами рушія та оракулами CEF (pycef, Logstash); зовнішні оракули Prometheus і OTLP ще не мають потоку 1,3.
Назви причин обрізаються до 128 символів, і щонайбільше 64 різних причин дістають власні ряди; пізніші підраховуються під reason="other" і в kshana_trust_reason_overflow_total, тож ворожий або помилковий вхід не може створити необмежену кардинальність міток.
band — одне з таких значень: calibrating, nominal, degraded, untrusted, unknown. Назви причин — це назви моніторів потоку (kinematic, heading-course, ...; для пакетного результату — cn0-drop, agc, raim).
Приклад панелі моніторингу міститься в deploy/grafana/kshana-gnss-trust.json (імпортуйте його й оберіть своє джерело даних Prometheus).
Syslog: CEF і LEEF#
Події надсилаються в оболонці RFC 5424 (facility local0, застосунок kshana, ідентифікатор повідомлення GNSSTRUST) через UDP (по одній датаграмі на подію; IPv4 або IPv6) або TCP: за замовчуванням із завершенням символом нового рядка, а з --syslog-octet-counting — із кадруванням <length> <message> за RFC 6587. За замовчуванням надсилається одна подія на кожну зміну band; --syslog-all надсилає по одній на кожну епоху. Постачальник записується як Ashforde OU (ASCII), щоб жодна система SIEM не декодувала його неправильно.
| Поняття | Серйозність у Syslog | Серйозність у CEF | LEEF sev |
|---|---|---|---|
| calibrating | 6 info | 0 | 1 |
| номінальний | 6 info | 1 | 1 |
| деградовані | 4 warning | 5 | 5 |
| untrusted | 2 critical | 9 | 9 |
| unknown | 5 notice | 3 | 3 |
Відповідність полів (ідентифікатор події — gnss-trust.<band>):
| Значення | CEF | LEEF 2,0 (роздільник ^) |
|---|---|---|
| категорія | cat=gnss-trust |
cat |
| хост-відправник | dvchost |
devHost |
| band | cs1 (мітка band) |
band |
| причини, через кому | cs2 (reasons) |
reasons |
| попередній band | cs3 (previousBand) |
previousBand |
| оцінка | cfp1 (trustScore), пропускається, якщо значення немає |
trustScore |
| зсув епохи, с | cfp2 (epochOffsetSeconds) |
epochOffsetSeconds |
| мітка часу журналу | cs4 (logTime) |
logTime |
| стан шлюзу | cs5 (gate) |
gate |
стан геометрії, далі ; і класи вирахувань епохи, через кому (наприклад, degraded;environment,geometry) (вхідні дані 1,3); для вхідних даних із міткою (0,36) — третє поле, ; і мітка; тоді поле класів завжди присутнє (наприклад, degraded;;poor, good;;fair) |
cs6 (geometry) |
geoStatus (лише стан), geoClass (класи) і geoLabel (мітка) |
| HDOP (1.3) | cfp3 (geometryHdop) |
geoHdop |
| PDOP (1.3) | cfp4 (geometryPdop) |
geoPdop (0,36, з міткою) |
| VDOP (0,36, з міткою) | не передається: використано чотири власні поля з рухомою комою | geoVdop |
| використані супутники (0,36, з міткою) | cn1 (geometryUsedSatellites), ціле число |
geoUsed |
| втрата сигналу: супутники в зоні видимості не відстежуються, хоча їхня геометрія хороша (1.3) | не передається як поле (для нього немає вільного ключа словника CEF): стан має значення degraded, а клас вирахування геометрії — environment у cs6 |
geoSignalLoss=1, лише поки умова виконується |
У CEF геометрія використовує лише користувацькі поля зі словника (без вигаданих ключів); мітка передається в cs6, а не в flexString1, бо кодек Logstash CEF не зіставляє поля flexString. Події геометрії 1,3, зокрема випадки з міткою, належать до рядка CEF з рівнем VALIDATED: обидва оракульні розбирачі зчитують їх назад так, як їх записано (tests/cef_reference.rs, поправки 4 і 5, кожну зареєстровано до того, як будь-який із розбирачів було запущено на їхніх випадках). Поля мітки, VDOP і кількості супутників додаються після всіх попередніх полів і рядів, і лише для вхідних даних із міткою, тож вхідні дані без неї відтворюються точно так, як раніше.
Порожні поля пропускаються (немає cs2= без причин). Ім'я хоста обрізається до 255 символів. Значення в заголовку CEF екранують \ і |; значення в розширенні екранують \, = і розриви рядків. У значеннях LEEF ^ і розриви рядків замінюються пробілами.
OpenTelemetry (типово вимкнено)#
Зберіть із --features otlp і передайте --otlp http://host:4318/v1/metrics. Експортер надсилає OTLP/HTTP JSON (kshana.trust.score, kshana.trust.band, kshana.trust.epochs, kshana.trust.reason.epochs, а для вхідних даних зі схемою 1,3 — kshana.geometry.status, kshana.geometry.degraded, kshana.geometry.signal_loss, kshana.geometry.hdop, kshana.geometry.pdop, kshana.geometry.used_satellites і, для вхідних даних із міткою (0,36), — kshana.geometry.label (атрибут label) та kshana.geometry.vdop; кумулятивні суми містять startTimeUnixNano) кожні --otlp-every епох (типово 10) і наприкінці вхідних даних. Він не додає жодних залежностей і не підтримує TLS: надсилайте на колектор на тому самому хості або в довіреному сегменті, який пересилатиме далі через TLS. Кінцеві точки https:// відхиляються.