Ping(at)Work

Ping(at)Work / Teknisk beskrivelse

Ping(at)Work

Arkitektur, kjøreflyt, validering, datamodell, HTTP-grensesnitt og sikkerhet i Ping(at)Work, maler for nettverks-PING med web-GUI.

Versjon 1.0.0Revidert 2026-10-07Web-GUI og worker

1. Formål

Ping(at)Work er en webapplikasjon for å definere maler for nettverks-PING og kjøre dem manuelt eller etter plan. Resultatene lagres med status (OK, advarsel, kritisk, feil) beregnet fra terskler for latens og pakketap, og statusendringer kan varsles på e-post eller webhook.

Løsningen leveres som en offline-pakke for Rocky Linux 9 (x86_64) og kjører likt på fysisk server, VMware og Proxmox VE.

2. Arkitektur

  Nettleser ──HTTPS:443──▶ Caddy (pingforge-caddy)
  (HTTP:80 → 301 HTTPS)        │  reverse_proxy, TLS, HSTS
                               ▼
                     gunicorn + Flask (pingforge-web)       127.0.0.1:8000
                       │  maler, brukere, kø, visning
                       ▼
                SQLite  /var/lib/pingforge/pingforge.db  (WAL)
                       ▲
                       │  henter kø, planlegger, lagrer resultater
              Worker (pingforge-worker) ──▶ /usr/bin/ping (CAP_NET_RAW)
                       │
                       └──▶ SMTP / webhook ved statusendring
Table 1. Komponenter
Komponent Teknologi Ansvar

pingforge-caddy

Caddy 2.11.7 (statisk Go-binær)

TLS-terminering, HTTP→HTTPS, sikkerhetshoder, komprimering, proxy til gunicorn

pingforge-web

Python 3.9, Flask 3.1, gunicorn 23 (2 prosesser × 4 tråder)

Brukergrensesnitt, innlogging, CRUD for maler, legger kjøringer i kø, viser resultater, dokumenter

pingforge-worker

Python 3.9, én prosess med tråder

Utfører køen, planlegger maler, kjører ping parallelt, evaluerer og lagrer, sender varsler, sletter gammel historikk

Database

SQLite 3 i WAL-modus

Maler, mål, kjøringer, resultater, brukere, innloggingsfeil, nøkkel/verdi (worker-puls)

Skillet mellom web og worker er bevisst: web-prosessen starter aldri underprosesser og trenger ingen ekstra rettigheter. Bare worker har CAP_NET_RAW.

3. Kjøreflyt

  1. Brukeren trykker Kjør eller Lagre og kjør. Web lager en rad i runs med state = queued og sender brukeren til kjøringssiden.

  2. Worker leser køen hvert 2. sekund og starter én tråd per kjøring. state blir running.

  3. For hvert mål og hver IP-versjon bygges en argumentliste (se Kommandobygging), og ping kjøres parallelt med inntil max_parallel samtidige prosesser.

  4. Utdata tolkes (sendt, mottatt, tap, min/snitt/maks/mdev). Hvert resultat får status etter tersklene. Kjøringens status er det verste resultatet.

  5. Resultatene lagres i én transaksjon. Malen får last_status og last_run_at.

  6. Hvis status har endret seg siden forrige kjøring, sendes varsel.

  7. Kjøringssiden spør /api/runs/<id> hvert 1,5 sekund og laster seg på nytt når kjøringen er ferdig.

Planlagte maler: hvert 15. sekund legger worker i kø de aktiverte malene der last_run_at + schedule_minutes er passert og som ikke allerede har en kjøring i kø eller i gang.

3.1. Kommandobygging

/usr/bin/ping -<4|6> -n -c <count> -i <interval> -s <size> -W <timeout> -w <deadline>
              [-t <ttl>] [-M do] [-Q <dscp<<2>] [-I <interface>] -- <host>
  • deadline = ceil(count × interval + timeout) + 1 sekunder. Prosessen avbrytes etter deadline + 10 sekunder.

  • -Q får hele TOS-byten, dvs. DSCP-verdien skiftet to bit (EF 46 → 184).

  • Kommandoen kjøres med subprocess.run(liste), uten shell, med LC_ALL=C og minimal PATH.

3.2. Statusregler

Status Regel (første som slår til)

Feil

ping ga ingen statistikk (ukjent vertsnavn, ugyldig grensesnitt, programfeil)

Kritisk

0 svar, eller tap ≥ kritisk tapsterskel, eller snittlatens ≥ kritisk latensterskel

Advarsel

tap ≥ advarsel tapsterskel, eller snittlatens ≥ advarsel latensterskel

OK

ellers

Varsel sendes når status endres. Den aller første kjøringen varsler bare hvis den ikke er OK.

4. Validering

Felt Regel

Mål

IPv4/IPv6 (ipaddress), RFC 1123-vertsnavn (etiketter 1–63 tegn, ikke ledende/avsluttende -, ≤ 253 tegn), eller CIDR utvidet til vertsadresser. Maks max_targets (256) totalt. Duplikater fjernes

Antall

1–1000

Intervall

0,2–60 s (komma og punktum godtas)

Størrelse

0–65507 byte

Timeout

1–60 s

TTL

1–255 eller tom

Grensesnitt

^[A-Za-z0-9][A-Za-z0-9_.:@%-]{0,63}$ eller tom

DSCP

0, 8, 10, 18, 26, 34, 40, 46, 48

Plan

0, 5, 15, 60 eller egendefinert 1–10080 min

Terskler

Tall ≥ 0 (tap ≤ 100). Advarsel ≤ kritisk

Varsle

E-postadresser

5. Datamodell

users(id, username UNIQUE, pw_hash, created_at)
templates(id, name, description, ip_version, count, interval, size, timeout, ttl,
          interface, dscp, df, schedule_minutes, warn_latency, crit_latency,
          warn_loss, crit_loss, notify, enabled, last_status, last_run_at,
          created_at, updated_at)
targets(id, template_id → templates ON DELETE CASCADE, host, label, position)
runs(id, template_id → templates ON DELETE SET NULL, template_name, trigger,
     requested_by, state, status, queued_at, started_at, finished_at, message)
results(id, run_id → runs ON DELETE CASCADE, host, label, ip_version, command,
        sent, received, loss, rtt_min, rtt_avg, rtt_max, rtt_mdev, status,
        output, exit_code)
login_failures(key, ts)
kv(key PRIMARY KEY, value)            -- worker_heartbeat

Tidsstempler lagres som ISO 8601 i UTC og vises i serverens lokale tidssone. PRAGMA user_version holder skjemaversjonen (1).

6. HTTP-grensesnitt

Metode Sti Funksjon

GET/POST

/login

Innlogging

POST

/logout

Utlogging

GET

/templates

Oversikt over maler

GET

/templates/new, /templates/<id>

Ny / rediger mal, med siste kjøring

POST

/templates/save, /templates/<id>/save

Lagre (action=save_run lagrer og kjører)

POST

/templates/<id>/run | /duplicate | /delete

Kjør, dupliser, slett

GET

/runs, /runs/<id>

Historikk og detaljer

GET

/runs/<id>/export.csv

Resultater som CSV

GET

/api/runs/<id>

JSON {id, state, status} for oppdatering av siden

GET

/settings

Innstillinger og systemstatus

POST

/settings/password, /settings/import

Bytt passord, importer maler

GET

/settings/export.json

Eksporter maler

GET

/lang/<en|no>

Bytt språk (cookie lang)

GET

/docs/<manual|licenses|notices>

Dokumenter (offentlig)

GET

/healthz

Helsesjekk {status, version} (offentlig)

Alle POST krever CSRF-token. Alle sider unntatt innlogging, dokumenter og helsesjekk krever innlogging.

6.1. Eksportformat for maler

{
  "format": "pingforge-templates",
  "version": 1,
  "exported": "2026-10-07T17:43:00+00:00",
  "templates": [
    {
      "name": "Kjernerutere – latens", "description": "", "ip_version": "4",
      "count": 10, "interval": 0.2, "size": 56, "timeout": 1, "ttl": 64,
      "interface": "", "dscp": 46, "df": true, "schedule_minutes": 5,
      "warn_latency": 30.0, "crit_latency": 80.0, "warn_loss": 1.0, "crit_loss": 5.0,
      "notify": "noc@example.com", "enabled": true,
      "targets": [ { "host": "10.10.0.1", "label": "core-osl-01" } ]
    }
  ]
}

6.2. Webhook

POST med Content-Type: application/json:

{ "text": "[PingForge] CRITICAL: WAN-linker ...", "template": "WAN-linker", "template_id": 3,
  "run_id": 42, "status": "critical", "previous_status": "ok",
  "results": [ { "host": "192.0.2.10", "label": "wan-b", "ip_version": "4", "status": "critical",
                 "loss": 100.0, "rtt_min": null, "rtt_avg": null, "rtt_max": null, "rtt_mdev": null } ] }

Feltet text gjør at innkommende webhooks i Slack, Mattermost og Rocket.Chat viser meldingen direkte.

7. Brukergrensesnitt

  • Server-rendret HTML (Jinja2) som fungerer uten JavaScript. static/js/app.js legger til dynamiske mål-rader, levende kommando-forhåndsvisning, kopiering, søk, temabytte og oppdatering av kjøringssiden.

  • Språk: alle tekster ligger i i18n.py som par (engelsk, norsk). Valget lagres i cookien lang. default_language styrer standarden. Brukermanual og lisensside vises på valgt språk.

  • Mørk/lys modus: CSS-variabler under [data-theme="dark"] og [data-theme="light"]. Valget lagres i cookien theme slik at siden rendres i riktig tema uten blink.

  • Skrifter: IBM Plex Sans og Mono, levert lokalt (woff2). Ingen eksterne ressurser lastes.

  • Tilgjengelighet: semantiske knapper og skjemaelementer, synlig fokus, «hopp til innhold», status som tekst (ikke bare farge), kontrast ≥ 4,5:1, mobiltilpasset fra 360 px.

8. Sikkerhet

Tiltak Gjennomføring

Transport

Caddy med TLS 1.2/1.3, HSTS, HTTP→HTTPS. Selvsignert RSA 3072-sertifikat ved installasjon

Innlogging

Werkzeug scrypt-hash. Sperre etter 5 feil per bruker / 20 per IP i 15 min. Sesjon 12 t

Sesjon

Signert cookie (itsdangerous) med Secure, HttpOnly, SameSite=Lax

CSRF

Token per sesjon på alle POST

Hoder

CSP default-src 'self' (ingen inline-skript eller -stil), X-Frame-Options: DENY, nosniff, Referrer-Policy: same-origin, Cache-Control: no-store

Kommandoer

Ingen shell, validerte argumenter, -- foran mål

Prosesser

Egne systembrukere. systemd-herding: NoNewPrivileges, ProtectSystem=strict, ProtectHome, PrivateTmp, PrivateDevices, CapabilityBoundingSet (web: ingen, worker: CAP_NET_RAW, Caddy: CAP_NET_BIND_SERVICE)

Nettverk

Bare Caddy lytter eksternt. Caddys admin-API er slått av

9. Filer og rettigheter

Sti Eier / modus Innhold

/opt/pingforge/app/

root, 0644/0755

Python-pakken pingforge

/opt/pingforge/venv/

root

Python-miljø (fra wheels)

/opt/pingforge/bin/caddy

root, 0755

Caddy

/opt/pingforge/Dokumenter/

root

Dokumentasjon (vises i appen)

/etc/pingforge/pingforge.conf

root:pingforge 0640

Konfigurasjon og hemmeligheter

/etc/pingforge/Caddyfile

root 0644

Proxy-oppsett

/etc/pingforge/tls/

root:pingforge-caddy 0750

Sertifikat (0644) og nøkkel (0640)

/var/lib/pingforge/

pingforge 0750

Database

/var/lib/pingforge-caddy/

pingforge-caddy 0750

Caddys arbeidsdata

/etc/systemd/system/pingforge-*.service

root 0644

Tjenester

/usr/local/bin/pingforge-admin

root 0755

Administrasjon

10. Kapasitet

  • Én kjøring bruker høyst max_parallel (16) ping-prosesser. Flere maler kan kjøre samtidig.

  • Varighet per mål ≈ count × interval sekunder (5 × 1 s = 5 s). 256 mål med 16 parallelle tar ca. 16 × varigheten for ett mål.

  • Databasevekst ≈ 1–3 kB per resultat (inkludert rå utdata). 50 mål hvert 5. minutt i 90 dager gir ca. 1,3 mill. resultater, i størrelsesorden 2–4 GB. Senk retention_days ved store volumer.

11. Bygging og pakking

build.sh setter sammen pakken fra kildekoden og vendor/:

  • vendor/wheels/ – wheels for CPython 3.9 manylinux x86_64 (låst i requirements.txt) og pip.

  • vendor/caddy/ – Caddy-binær og lisens (SHA-512 kontrollert ved nedlasting).

  • vendor/fonts/ – OFL-lisensene for IBM Plex.

Resultatet er dist/pingforge-<versjon>-el9-x86_64.tar.gz med SHA256SUMS inni og en .sha256-fil ved siden av. scripts/gen_notices.py lager Tredjepartsmerknader og scripts/gen_inventory.py lager third_party_inventory.json fra de samme filene; build.sh kjører begge.

Pakkens innhold
pingforge-1.0.0-el9-x86_64/
├── install.sh  uninstall.sh  VERSION  LICENSE  README.md  requirements.txt  SHA256SUMS
├── third_party_inventory.json
├── app/pingforge/        Python-pakken (app, worker, cli, maler, static)
├── wheels/               13 wheel-filer
├── caddy/                caddy, LICENSE
├── config/               Caddyfile, pingforge.conf.example
├── systemd/              pingforge-web/-worker/-caddy.service
└── Dokumenter/           all dokumentasjon

12. Kildekode

Fil Innhold

app.py

Flask-applikasjon: ruter, innlogging, CSRF, hoder, dokumentvisning

worker.py

Kø, planlegging, utførelse, opprydding

runner.py

Bygging av ping-kommando, kjøring, tolking og statusregler

validation.py

Validering av skjema og import, CIDR-utvidelse

db.py

Skjema og databasefunksjoner

notify.py

E-post og webhook

i18n.py

Engelske og norske tekster

cli.py

pingforge-admin

config.py

Lesing av pingforge.conf

templates/

Jinja2-maler

static/

CSS, JavaScript, fonter, ikon

Skjermbilder, installasjon og eksempelmaler: Tilbake til forsiden