Chrony-Time-Service(at)Work

Chrony-Time-Service(at)Work / Teknisk beskrivelse

Chrony-Time-Service(at)Work

Arkitektur, tidsstyring, sikkerhet, installasjon og drift for Chrony-Time-Service(at)Work – en NTP-server med web-GUI for testklokker i lukkede nett

Versjon 9.0.0Revidert 2026-10-03Server og web-GUI

Chrony-Time-Service(at)Work er en NTP-server med web-grensesnitt for testing i lukkede nett. Klokka på serveren kan settes til en vilkårlig tid eller forskyves, og alle klienter som synkroniserer mot den, følger etter. Dokumentet beskriver arkitekturen, hvordan klokka styres, sikkerhetsmekanismene, installasjonsmodellen og grensesnittene, og er skrevet for drift, sikkerhetsgjennomgang og videreutvikling.

1. Formål og omfang

Produkt

Chrony-Time-Service(at)Work 9.0.0

Komponenter

Web-GUI (app.py, i18n.py, maler), privilegert hjelper (ntpctl.py), installasjonsskript (install.sh, uninstall.sh, check-prereqs.sh), verktøy (ntpcheck.py, selftest.py)

Bruksområde

Testing av tidsavhengig oppførsel: utløp av sertifikater og lisenser, sommertid, skuddår, år 2038, alarmer for tidsavvik og bortfall av tidsserver

Ikke formål

Presis tidsserver for produksjon. I intern modus er klokka så riktig som du stiller den.

Målplattformer

Rocky Linux 9 / RHEL 9 / AlmaLinux 9 / Oracle Linux 9 (primær) og VMware Photon OS, som VM i VMware eller Proxmox VE, eller på fysisk maskin

Kjøremodell

chronyd fra operativsystemet og en Flask-applikasjon under gunicorn, begge styrt av systemd. Ingen containere, ingen pip, ingen internettilgang.

Språk

Engelsk og norsk (bokmål) i web-grensesnitt, meldinger, brukermanual og installasjonsskript

Lisens

MIT

2. Arkitektur

2.1. Oversikt

Komponenter og dataflyt
Nettleser (administrator)
   │ HTTP :5032, Basic Auth, CSRF-token på alle POST
   ▼
gunicorn 23 + Flask 3.1 (app.py, i18n.py)       bruker chronygui (ikke root)
   │  lesing:    chronyc -c via localhost:323 (uten sudo)
   │  endringer: sudo -n /usr/local/sbin/ntpctl …   (den eneste sudo-regelen)
   ▼
ntpctl (ntpctl.py, root, python3 -I)            validerer all input på nytt
   ├─ clock_settime(CLOCK_REALTIME)              stiller systemklokka
   ├─ hwclock --systohc                          valgfritt: skriver RTC
   ├─ /var/lib/ntpgui/settings.json → /etc/chrony.conf
   └─ systemctl start|stop|restart chronyd
   ▼
chronyd (fra OS-et)  ◀── UDP 123 ── NTP-klienter i testnettet

2.2. Kodemoduler

Fil Innhold

app.py

Flask-applikasjonen: ruter, autentisering, CSRF, sikkerhetshoder, parsing av chronyc -c, revisjonslogg, språkvalg, lisens- og manualsider

i18n.py

Norske oversettelser (engelsk er nøkkelen), oversettelse av meldinger fra ntpctl, plattformnavn, tekster til JavaScript

ntpctl.py

Privilegert hjelper. Underkommandoene overview, settings, configure, render, service, settime, shift, resync, logs. Svarer alltid med én JSON-linje.

templates/

base.html (topplinje, EN | NO, bunnlinje), index.html, logs.html, manual.html, licenses.html

static/

Bootstrap 5.3.7 (CSS), app.css, app.js (live-klokke og status), theme.js (mørk modus), ikon, skjermbilder til manualen

manual/

Brukermanualen som HTML, bygget fra docs/*.md med tools/build-manuals.py

lib/common.sh

Felles skallfunksjoner: språk, utskrift, plattformgjenkjenning, CAP_SYS_TIME-kontroll

2.3. Prosessmodell og rettigheter

Prosess Bruker Rettigheter

chronyd

chrony (dropper root etter oppstart)

CAP_SYS_TIME for å justere klokka. Kjøres med -x der klokka ikke kan stilles.

gunicorn (ntpgui.service)

chronygui

Ingen. Én worker med fire tråder, --timeout 0.

ntpctl

root via sudo

Startes for hver handling og avslutter etterpå. python3 -I (isolert: ignorerer miljøvariabler og brukerens site-packages).

--timeout 0 er nødvendig fordi gunicorns overvåking av workeren bruker veggklokka. Når klokka stilles fram, ville workeren ellers bli drept midt i forespørselen. Hver underkommando har i stedet sin egen tidsgrense.

3. Tidsstyring

3.1. Moduser og chrony.conf

ntpctl lager hele /etc/chrony.conf fra settings.json. Bare disse direktivene kan forekomme:

Modus Direktiver

Intern

local stratum N (1–15)

Ekstern

server <vert> iburst, pool <vert> iburst maxsources 4, makestep 1.0 3, valgfritt local stratum N orphan

Begge

allow 127.0.0.1, allow ::1, allow <nett> (maks 32), ratelimit interval 1 burst 16, driftfile, logdir, log tracking measurements statistics, keyfile hvis den finnes

Fila skrives atomisk (midlertidig fil og rename), og SELinux-merkingen gjenopprettes med restorecon.

3.2. Stille og forskyve klokka

  • Sett tid og Synk til denne PC-ens tid sender et epoketall til ntpctl settime. Forskyv sender sekunder til ntpctl shift, som legger dem til gjeldende tid.

  • ntpctl kaller time.clock_settime(CLOCK_REALTIME, …). Klokka hopper; chronyd ser hoppet og serverer den nye tiden fra neste svar. I intern modus er systemklokka selv referansen (LOCAL, ref-ID 7F7F0101).

  • Gyldig område: 2000-01-01 til 2099-12-31 UTC. Største forskyvning: 10 år.

  • I ekstern modus trekker chronyd en manuelt endret klokke tilbake mot oppstrømsserverne. Resynk fra oppstrøms kjører chronyc burst 4/4, venter, kontrollerer at en oppstrømskilde er valgt og kjører chronyc makestep. Uten nåbar kilde endres ikke klokka.

  • Med Skriv også til RTC kjøres hwclock --systohc etterpå.

3.3. Statusavlesning

Siden henter /api/status hvert 5. sekund. app.py leser chronyc -c -n tracking og sources som brukeren chronygui gjennom chronyds kommandoport (UDP 323 på localhost), uten sudo. Klientlisten (chronyc clients) krever root og hentes gjennom ntpctl overview ved sidevisning. Live-klokka i nettleseren beregner forskjellen mellom serverens og nettleserens tid ut fra midtpunktet av forespørselen, og tikker lokalt mellom avlesningene.

3.4. Tidssynk fra hypervisor

ntpctl ser etter agenter som kan stille klokka utenom chrony, og GUI-et viser en advarsel for hver:

  • qemu-guest-agent, vboxadd-service, systemd-timesyncd, ntpd, ptp4l, phc2sys (systemd-enheter som kjører)

  • periodisk tidssynk i VMware Tools (vmware-toolbox-cmd timesync status)

  • Hyper-V (hv_utils)

install.sh slår av den periodiske synken i VMware Tools og skriver ut .vmx-parametrene som også må settes. For Proxmox VE 9.2.5 og nyere er det agent-valget sync-time-on-resume som avgjør om Proxmox stiller gjesteklokka etter gjenopptak, migrering og tilbakerulling. Det står av som standard.

4. Web-grensesnitt

4.1. Sider og HTTP-API

Sti Metode Innhold

/

GET

Hovedsiden: klokke, tjeneste, system, still klokka, konfigurasjon, tabeller

/action

POST

Alle handlinger (action=start|stop|restart|set_time|shift|sync_browser|resync|configure). Svarer med redirect til / og en melding.

/api/status

GET

JSON: epoch, tz, utc_offset, service, tracking, sources

/logs

GET

Revisjonslogg, chronyd-journal, tracking.log, aktiv chrony.conf

/manual

GET

Brukermanualen på valgt språk

/licenses

GET

LICENSE.txt (#license) og THIRD_PARTY_NOTICES.txt (#third-party)

/licenses/third_party_inventory.json

GET

Maskinlesbar liste over tredjepartskomponenter

/lang/<en|no>?next=<sti>

GET

Lagrer språkvalget i informasjonskapselen ntpgui_lang og går tilbake til next (bare lokale stier)

/healthz

GET

200 ok når chronyd kjører og svarer, ellers 503. Krever ikke innlogging.

4.2. Språk (i18n)

  • Engelsk tekst er nøkkelen. i18n.NO holder den norske oversettelsen. Malene bruker _("…"), og Python-koden tr("…"). Mangler en oversettelse, vises engelsk.

  • Språket for en forespørsel er informasjonskapselen ntpgui_lang, ellers DEFAULT_LANG fra ntpgui.env (install.sh --lang).

  • Meldinger fra ntpctl er alltid på engelsk og oversettes med en liste regulære uttrykk i i18n.MESSAGE_PATTERNS.

  • Tekster til app.js sendes i <body data-i18n="…">, siden Content Security Policy ikke tillater innebygde skript.

  • Testen tests/test_i18n.py finner alle tekster i malene og app.py og feiler hvis noen mangler norsk oversettelse eller har ulike plassholdere.

4.3. Lisenser og manual

Bunnlinjen på alle sider har lenkene Lisenser · Tredjepartsmerknader · Brukermanual. Lisensfilene leses fra NTPGUI_DOC_DIR (/opt/ntpgui). Brukermanualen er ferdig HTML i manual/manual.no.html og manual.en.html, laget med pandoc fra docs/Brukermanual.md og docs/en/User-Manual.md. Bildene ligger i static/img/.

5. Sikkerhet

5.1. Tilgangskontroll

  • Alle sider unntatt /healthz og /static/ krever HTTP Basic Auth. Brukernavn og passord sammenlignes med hmac.compare_digest. Uten passord i ntpgui.env slipper ingen inn.

  • Alle POST krever et CSRF-token som ligger i en signert øktcookie (SameSite=Strict, HttpOnly).

  • Sikkerhetshoder: Content-Security-Policy: default-src 'self'; style-src 'self'; script-src 'self'; img-src 'self' data:; frame-ancestors 'none'; form-action 'self', X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy: same-origin, Cache-Control: no-store.

  • Språkbyttet godtar bare lokale stier i next (ikke //vert, ikke https://…), så det kan ikke brukes til å sende brukeren videre til et annet nettsted.

  • --admin-net begrenser GUI-porten i firewalld til ett nett.

5.2. Validering i ntpctl

ntpctl stoler ikke på GUI-et og kontrollerer alt på nytt:

  • tid som tall mellom 2000 og 2099 (UTC), forskyvning høyst 10 år, ikke NaN

  • modus internal/external, stratum 1–15

  • servere: IP-adresse (ipaddress) eller vertsnavn etter et strengt mønster (RFC 1123), valgfritt med `pool ` foran, høyst 8

  • nett: ipaddress.ip_network eller all, høyst 32

  • chrony.conf bygges fra en hviteliste, så det er ikke mulig å injisere egne direktiver

  • alle kommandoer kjøres som argumentlister uten skall

5.3. Kjente begrensninger

  • Basic Auth over HTTP sender passordet i klartekst. GUI-et er ment for et lukket testnett. Bruk --admin-net, eller --bind 127.0.0.1 med SSH-tunnel.

  • NTP er uautentisert. Alle i de tillatte nettene kan spørre serveren, og en angriper i nettet kan forfalske svar. Det er akseptabelt for en testklokke.

  • ntpgui.service kan ikke bruke NoNewPrivileges eller ProtectSystem, siden ntpctl startes gjennom sudo.

  • click 8.1.8 har et kjent funn (PYSEC-2026-2132) i click.edit(), som ikke brukes. Rettet versjon krever Python 3.10 (se Revisjonsrapporten).

6. Installasjon og filstruktur

6.1. Installasjonsprogrammet

install.sh er idempotent: det kan kjøres på nytt for å oppgradere eller endre passord, port og språk. Rekkefølgen er beskrevet i Installasjonsveiledningen, kapittel 5.1. Viktige detaljer:

  • SHA256SUMS kontrolleres før noe endres.

  • Plattformen finnes med /etc/os-release og systemd-detect-virt. CAP_SYS_TIME leses fra CapBnd i /proc/self/status. Mangler den, legges en drop-in for chronyd inn med -x.

  • Programmet byttes ut som en helhet (app.new → app), så filer som er fjernet i en ny versjon, forsvinner.

  • Sudo-regelen kontrolleres med visudo -cf før den tas i bruk.

  • Til slutt kontrolleres /healthz og et NTP-svar på UDP 123 (ntpcheck.py). Avslutningskoden er 2 hvis selvtesten feiler.

6.2. Installert struktur

Sti Innhold

/opt/ntpgui/app/

app.py, i18n.py, templates/, static/, manual/

/opt/ntpgui/lib/python/

Python-pakkene, utpakket fra hjulene

/opt/ntpgui/lib/sh/common.sh

Brukes av uninstall.sh

/opt/ntpgui/docs/

Dokumentene

/opt/ntpgui/{LICENSE.txt,THIRD_PARTY_NOTICES.txt,third_party_inventory.json,VERSION}

Lisenser og versjon

/opt/ntpgui/uninstall.sh, /opt/ntpgui/ntpcheck.py

Avinstallering og NTP-kontroll

/usr/local/sbin/ntpctl

Hjelperen (root:root 0755)

/etc/sudoers.d/ntpgui

chronygui ALL=(root) NOPASSWD: /usr/local/sbin/ntpctl

/etc/systemd/system/ntpgui.service

Tjenesten

/etc/ntpgui/ntpgui.env

Bruker, passord, SECRET_KEY, adresse, port, språk, PYTHONPATH (root:chronygui 0640)

/etc/ntpgui/runtime

host (eller container i Docker-varianten)

/var/lib/ntpgui/settings.json

Innstillingene fra GUI-et

/var/log/ntpgui/audit.log

Revisjonslogg (tid, monoton tid, IP, bruker, handling)

/etc/chrony.conf.ntpgui-orig

Original chrony.conf, legges tilbake ved avinstallering

6.3. Python-pakker

Hjulene i vendor/wheels pakkes ut med python3 -m zipfile -e i /opt/ntpgui/lib/python. Rene Python-hjul (py3-none-any) virker overalt. For MarkupSafe velges hjulet for gjeldende Python-versjon og arkitektur (Python 3.9 og 3.11, x86_64 og aarch64). Finnes det ikke, brukes et hvilket som helst MarkupSafe-hjul: C-utvidelsen lastes da ikke, og MarkupSafe bruker sin rene Python-utgave. Etter utpakking kontrolleres at flask, gunicorn, jinja2 og markupsafe kan importeres, før den gamle mappa byttes ut.

Pakke Versjon Formål

Flask

3.1.3

Web-rammeverk

Werkzeug

3.1.9

HTTP og ruting

Jinja2

3.1.6

Maler

MarkupSafe

3.0.4

HTML-escaping

itsdangerous

2.2.0

Signert øktcookie

click

8.1.8

Krav fra Flask, brukes ikke

blinker

1.9.0

Krav fra Flask

gunicorn

23.0.0

WSGI-server, nyeste med Python 3.9

packaging

26.3

Krav fra gunicorn

importlib_metadata

8.7.1

Krav fra Flask på Python 3.9

zipp

3.23.1

Krav fra importlib_metadata

6.4. Oppgradering fra v8

v8 installerte Python-pakkene i en venv (/opt/ntpgui/venv). 9.0.0 bruker samme stier for innstillinger, logger, bruker og hjelper. install.sh kjenner igjen v8, beholder ntpgui.env (passord, port), settings.json og /etc/chrony.conf.ntpgui-orig, legger til DEFAULT_LANG, GUI_LISTEN og PYTHONPATH, og fjerner venv-mappa.

7. Nettverk og eksterne ressurser

7.1. Porter

Port Protokoll Bruk

123

UDP

NTP. Åpnes i firewalld (tjenesten ntp). chrony svarer bare de tillatte nettene.

5032

TCP

Web-GUI-et (--port). Åpnes i firewalld, eventuelt bare for --admin-net.

323

UDP

chronyds kommandoport, bare på localhost

7.2. Eksterne ressurser

Situasjon Eksterne ressurser

install.sh

Ingen. Hjulene pakkes ut lokalt.

install.sh når chrony, python3 eller sudo mangler

dnf/tdnf fra maskinens pakkebrønner (eller montert ISO)

Drift i intern modus (standard)

Ingen. Serveren tar ikke kontakt med noe utenfor maskinen.

Drift i ekstern modus

NTP (UDP 123) til serverne som er lagt inn, og DNS-oppslag på navnene

Nettleseren

Ingen. Alt hentes fra serveren selv (CSP default-src 'self').

Docker-varianten (valgfri)

python:3.11-slim, Debian-pakker og eventuelt pypi.org ved bygging

8. Drift

Oppgave Kommando

Status

systemctl status ntpgui chronyd, chronyc tracking

Logger

journalctl -u ntpgui -f, journalctl -u chronyd -f, /var/log/ntpgui/audit.log

Helsesjekk

curl -s http://127.0.0.1:5032/healthz

NTP-kontroll

python3 /opt/ntpgui/ntpcheck.py <server>

Selvtest

sudo python3 tools/selftest.py (endrer klokka) eller --readonly

Passord, port, språk

sudo ./install.sh --password … --port … --lang …

Fra skallet

sudo ntpctl shift --seconds 3600, sudo ntpctl settime --epoch …

Avinstallering

sudo /opt/ntpgui/uninstall.sh [--purge]

Revisjonsloggen skriver både systemtiden (som kan være endret) og mono= (sekunder siden oppstart). Rekkefølgen og avstanden mellom hendelser kan dermed leses selv etter store hopp.

9. Kvalitetssikring

Kontroll Innhold

Enhetstester (pytest, Python 3.9)

53 tester: innlogging, CSRF, alle handlinger, validering, språkbytte, åpen redirect, lisenssider, manual på begge språk, oversettelse av meldinger, ntpctl-validering og chrony.conf

tests/test_i18n.py

Alle tekster har norsk oversettelse med samme plassholdere

Selvtest (tools/selftest.py)

37 kontroller mot en installert server med ekte chronyd, sudo og klokkestyring

Installasjonstester

Ny installasjon, oppgradering fra v8, ny kjøring, avinstallering, VMware- og KVM-gjenkjenning, LXC uten CAP_SYS_TIME

Statisk analyse

shellcheck på alle skript, pip-audit på alle hjul

Byggekontroll

tools/build-package.sh kjører testene, kontrollerer tredjepartsfilene og lager SHA256SUMS

Resultatene står i Revisjonsrapporten.

Skjermbilder, installasjon og testscenarier: Tilbake til forsiden