1. Sammendrag
SNMP Trap Receiver tar imot SNMP-varsler (traps og informs i SNMP v1, v2c og v3) fra nettverksutstyr og servere. Den oversetter de numeriske objekt-ID-ene til navn ved hjelp av MIB-filer, lagrer hvert varsel i loggfiler og viser dem fortløpende i et web-GUI på engelsk eller norsk.
Versjon 2.0 erstatter den tidligere Docker-containeren. Den kjører som en herdet systemd-tjeneste på Rocky Linux 9 (også minimal-installasjonen) og installeres fra én selvstendig fil uten tilgang til internett.
2. Formål og omfang
Tar imot |
SNMPv1 Trap-PDU, SNMPv2c Trap og InformRequest, SNMPv3 Trap og InformRequest (USM: noAuthNoPriv, authNoPriv, authPriv) over UDP/IPv4 og UDP/IPv6 |
Oversetter |
Trap-OID og variabelnavn ved hjelp av opplastede leverandør-MIB-er og de innebygde IETF-MIB-ene |
Lagrer |
Lesbar tekstlogg og JSON Lines-logg, begge roteres |
Viser |
Fortløpende liste med filter, detaljer per trap, CSV-eksport, status for mottakeren, MIB-håndtering, nedlasting av logger og lisensinformasjon |
Gjør ikke |
Videresende traps, sende e-post, polle enheter eller lagre traps i database |
3. Arkitektur
nettverksenheter Rocky Linux 9-server
┌───────────────┐ UDP 162 ┌────────────────────────────────────────────────┐
│ svitsj/ruter ├──────────► │ snmp-trap-receiver.service (bruker snmptrap) │
│ ESXi, UPS ... │ v1/v2c/v3 │ │
└───────────────┘ │ ┌────────────────────┐ ┌─────────────────┐ │
│ │ TrapReceiver │ │ MibManager │ │
│ │ tråd + asyncio ├──►│ MIB-kompilator │ │
│ │ pysnmp 7-motor │ │ OID→navn-oppslag│ │
│ └─────────┬──────────┘ └─────────────────┘ │
│ │ post │
│ ringbuffer (2000) + snmp_traps.log/.jsonl │
│ ▲ │
│ ┌─────────┴──────────┐ │
┌──────────┐ HTTP 8080 │ │ Flask-app på │ │
│ nettleser├───────────────► │ │ Waitress (8 tråder)│ │
└──────────┘ GUI + JSON-API │ └────────────────────┘ │
└────────────────────────────────────────────────┘
Alt kjører i én prosess. SNMP-motoren har en egen tråd med sin egen asyncio-hendelsesløkke. Web-serveren kjører i arbeidstrådene til Waitress og leser fra mottakerens ringbuffer i minnet. De to møtes bare i en låsbeskyttet kø, så en treg nettleser forsinker aldri mottaket av traps.
3.1. Moduler
| Modul | Ansvar |
|---|---|
|
Leser |
|
|
|
|
|
Flask-applikasjonen: innlogging, CSRF, sikkerhetshoder, sider og JSON-API. |
|
Alle GUI-tekster på engelsk og norsk (121 nøkler). Begge språkene må ha de samme nøklene, ellers lastes ikke modulen. |
|
Viser brukermanualen ( |
|
Ende-til-ende-test på en midlertidig port og i midlertidige mapper. |
|
Kommandolinje: |
|
25 standard IETF-MIB-kilder pluss en stubb for |
|
HTML-maler, Bootstrap 5.3.7 og applikasjonens egen JavaScript og CSS. |
4. Behandling av traps
4.1. Mottak
For hver listen-oppføring åpner mottakeren en UDP-transport (IPv4 eller
IPv6) i pysnmp. pysnmp binder socketer asynkront. Mottakeren venter derfor
på hver binding og rapporterer hver feil, for eksempel Address already in
use eller Address family not supported. Kan ingen adresse bindes, forblir
mottakeren stoppet og viser feilen. Kan bare noen bindes, kjører den på dem
og viser en advarsel.
Communities registreres som sikkerhetsoppføringer for SNMPv1/v2c.
SNMPv3-brukere registreres i USM med sine protokoller for autentisering og
kryptering. En v3-trap godtas bare hvis brukeren er registrert for
avsenderens snmpEngineID (engine_id i konfigurasjonen). En v3-inform
bruker mottakerens egen engine-ID og trenger ikke engine_id.
4.2. Kontroll av community
En observatør på pysnmp-punktet rfc2576.processIncomingMsg:writable ser
hver v1/v2c-melding før den autoriseres:
-
Communityen som faktisk ble brukt, huskes til loggen.
-
Er den ukjent og
accept_any_community = false, forkaster pysnmp meldingen. Mottakeren teller den som avvist og skriver enREJECTED-linje med avsenderadressen i tekstloggen. -
Er
accept_any_community = true, kobles communityen til den første konfigurerte, slik at meldingen godtas. Loggen viser likevel communityen enheten faktisk sendte.
4.3. Behandling av varsler
NotificationReceiver i pysnmp kaller mottakeren for hver godtatte trap
eller inform og kvitterer automatisk for informs. Mottakeren gjør så
dette:
-
henter kildeadresse og port fra transportinformasjonen;
-
henter SNMP-versjon og PDU-type (trap eller inform) fra meldingskonteksten;
-
slår opp hver variabelbinding i MIB-visningen (navn og formatering av verdi, for eksempel oppramsinger og
DisplayString); -
trekker ut
snmpTrapOID.0ogsysUpTime.0. For SNMPv1-traps har pysnmp allerede gjort om Trap-PDU-en til SNMPv2-formen, såsnmpTrapAddress.0,snmpTrapCommunity.0ogsnmpTrapEnterprise.0er med som variabler; -
bygger en post, legger den i ringbufferen og skriver den til begge loggfilene.
4.4. Postformat
Hver linje i snmp_traps.jsonl er ett JSON-objekt:
{"id": 17, "time": "2026-10-02 10:45:20", "ts": 1790930720.642,
"source": "192.0.2.31", "source_port": 49052, "version": "v2c",
"kind": "trap", "security": "public",
"trap_oid": "1.3.6.1.6.3.1.1.5.3", "trap_name": "IF-MIB::linkDown",
"uptime": "123456",
"varbinds": [
{"oid": "1.3.6.1.2.1.2.2.1.1.12", "name": "IF-MIB::ifIndex.12",
"value": "12", "type": "Integer"},
{"oid": "1.3.6.1.2.1.2.2.1.8.12", "name": "IF-MIB::ifOperStatus.12",
"value": "down", "type": "Integer"}]}
|
Løpenummer. Fortsetter etter en omstart, fordi bufferen lastes inn igjen fra JSON-loggen |
|
Community (v1/v2c) eller USM-brukernavn (v3) |
|
|
|
Tom hvis ingen lastet MIB definerer OID-en |
Tekstloggen har det samme innholdet i lesbar form:
[2026-10-02 10:45:20] TRAP v2c from 192.0.2.31:49052 (community: public) snmpTrapOID = IF-MIB::linkDown (1.3.6.1.6.3.1.1.5.3) sysUpTime = 123456 IF-MIB::ifIndex.12 = 12 IF-MIB::ifOperStatus.12 = down
5. Håndtering av MIB-er
5.1. Opplasting
-
Filendelsen må være
.mib,.txt,.my,.smieller ingen. -
Fila kan ikke være tom, kan ikke være større enn
mib_max_kbog kan ikke inneholde NUL-byte i de første 4 KB. -
Modulnavnet leses fra innholdet (
NAVN DEFINITIONS ::= BEGIN) og må passe med^[A-Za-z][A-Za-z0-9-]{0,127}$. -
Fila skrives atomisk som
<data_dir>/mibs/source/<NAVN>. Navnet nettleseren sendte, vises bare i lista og brukes aldri som sti. Path traversal er derfor umulig.
5.2. Kompilering
Alle opplastede moduler kompileres med pysmi til pysnmp-moduler i Python i
<data_dir>/mibs/compiled/. Hver modul kompileres for seg. Én ødelagt fil
stopper derfor ikke de andre, og hver modul får sin egen status:
|
Kompilert og lastet |
|
Melding fra parseren, eller |
|
Ikke kompilert ennå |
Kildene for IMPORTS søkes i denne rekkefølgen: mappa med opplastinger,
mappene i mib_search_dirs (for eksempel /usr/share/snmp/mibs hvis
net-snmp-libs er installert) og de innebygde IETF-MIB-ene. HTTP-leseren i
pysmi brukes aldri. Avhengigheten requests er erstattet av en stubb som
avviser nedlasting, så kompileringen skjer bare offline.
5.3. Standard-MIB-er
Første gang en versjon starter, kompileres de innebygde standard-MIB-ene
som definerer vanlige varsler og objekter, én gang til
<data_dir>/mibs/base/ (ca. 1,5 sekunder): IF-MIB, IANAifType-MIB,
HOST-RESOURCES-MIB, IP-MIB, TCP-MIB, UDP-MIB, DISMAN-EVENT-MIB,
NOTIFICATION-LOG-MIB og det de importerer. Sammen med modulene som følger
med pysnmp (SNMPv2-MIB, RFC1213-MIB, …) gjør det at for eksempel
IF-MIB::linkDown og SNMPv2-MIB::coldStart vises med navn uten
opplasting. Opplastede moduler går foran disse.
6. Konfigurasjon
Fil: /etc/snmp-trap-receiver/config.ini (eier root:snmptrap, modus
0640). Start tjenesten på nytt etter en endring.
| Seksjon | Nøkkel | Standard | Betydning |
|---|---|---|---|
|
|
|
Adressen til web-GUI-et |
|
|
TCP-port |
|
|
|
Krev passord |
|
|
(settes av installeren) |
scrypt-hash (Werkzeug). Settes med |
|
|
(tilfeldig) |
Signerer sesjonskapselen |
|
|
|
|
|
|
|
Levetid for sesjonen |
|
|
|
|
|
|
|
Start mottaket når tjenesten starter |
|
|
|
Communities som godtas |
|
|
|
Godta alle communities |
|
|
|
Antall traps i minnet for GUI-et |
|
|
|
|
MIB-kilder, kompilerte moduler og status |
|
|
Trap-logger |
|
|
|
Størrelse per fil før rotasjon |
|
|
|
Antall roterte filer som beholdes per logg |
|
|
|
Største MIB-opplasting |
|
|
|
Ekstra mapper med MIB-kilder |
|
|
|
|
|
|
Passordfrase for autentisering |
||
|
|
|
|
|
Passordfrase for kryptering |
||
|
Avsenderens engine-ID i heks (v3-traps) |
7. Web-GUI og API
7.1. Sider
Traps |
Fortløpende liste med de nyeste øverst, oppdatert hvert 2. sekund. Fritekstfilter (alle ord må finnes i kilde, OID, navn eller verdi), filter på versjon, pause, tømming av visningen og CSV-eksport. Klikk på en rad for å se alle variabler med OID, navn, verdi og type. |
MIB-filer |
Last opp én eller flere filer. Status per modul med feiltekst, nedlasting av kilde, sletting og ny kompilering. |
Mottaker |
Kjører/stoppet, lytteadresser, adresser som feilet, tellere for mottatt, avvist og feil, konfigurerte communities og v3-brukere, og siste feil. Start, stopp og omstart. |
Logger |
Alle loggfiler, også roterte, med størrelse, tid og nedlasting. |
Bunnlinjen |
Versjon og lenker til Lisenser (komponentliste fra |
Valgt fane huskes i nettleseren. Oppsettet virker ned til mobilbredde. På smale skjermer skjules to av kolonnene i trap-lista.
7.2. Endepunkter
| Sti | Metode | Formål |
|---|---|---|
|
GET/POST, POST |
Sesjon |
|
GET |
Setter språk-kapselen |
|
GET |
Hovedsiden |
|
GET |
Lisensvisning |
|
GET |
Brukermanualen på valgt språk, med innholdsfortegnelse og søk. Brukermanual-knappen lenker til |
|
GET |
|
|
GET |
Status og tellere for mottakeren |
|
GET |
Traps med |
|
POST |
Tømmer lista i minnet (loggene endres ikke) |
|
POST |
Styrer mottakeren |
|
GET/POST |
Liste / opplasting ( |
|
POST |
Kompilerer alle på nytt |
|
POST |
Sletter en modul |
|
GET |
Laster ned kilden |
|
GET |
Lister loggfilene |
|
GET |
Laster ned en loggfil (bare trap-loggene og rotasjonene deres) |
|
GET |
CSV med trapsene i minnet |
|
GET |
Lisensdata på valgt språk |
7.3. Sikkerhet
-
Autentisering: ett passord, lagret som scrypt-hash. Etter fem feilede forsøk fra samme adresse innen 15 minutter avvises innlogging fra den adressen i 30 sekunder. Tiden dobles for hvert nye forsøk, opp til 15 minutter.
-
Sesjon: signert kapsel med
HttpOnlyogSameSite=Strict, gyldig i 12 timer. -
CSRF: hver POST krever sesjonens token (hodet
X-CSRF-Tokeneller et skjemafelt). -
Hoder:
Content-Security-Policy: default-src 'self'uten innebygd skript eller stil,X-Frame-Options: DENY,nosniff,Referrer-Policy: no-referrerogCache-Control: no-storepå API-et. -
Utdata: alt innhold fra traps escapes før det vises. I CSV-eksporten får celler som starter med
=,+,-eller@, en'foran, så regneark ikke kan kjøre dem som formler. -
Opplasting: se Opplasting.
8. Språk
Engelsk er standard. Standardspråket kan endres i konfigurasjonen, og hver bruker kan bytte med EN/NO i topplinjen (lagres i en kapsel i ett år). Serveren lager siden på valgt språk, og den samme ordboka sendes til JavaScript, så dynamiske tekster følger språket. Lisensrapporten vises på valgt språk. Third-party notices er på engelsk, fordi det er de originale lisenstekstene.
9. Installasjon og drift
9.1. Filer og brukere
| Sti | Eier, modus | Innhold |
|---|---|---|
|
root, 0755 |
Applikasjonen |
|
root, 0755 |
Python-miljø bygget fra wheel-filene som følger med |
|
root |
Denne dokumentasjonen og lisensfilene |
|
root |
Forrige versjon etter en oppgradering |
|
root:snmptrap, 0640 |
Konfigurasjon (inneholder hemmelig nøkkel og v3-nøkler) |
|
snmptrap, 0750 |
MIB-kilder, kompilerte moduler og |
|
snmptrap, 0750 |
|
|
root, 0755 |
Kommandolinje |
9.2. systemd-enheten
Tjenesten kjører som systembrukeren snmptrap. Den får nøyaktig én
kapabilitet, CAP_NET_BIND_SERVICE, for å kunne binde UDP 162. Enheten
setter også NoNewPrivileges, ProtectSystem=strict (alt skrivebeskyttet
unntatt data- og loggmappene), ProtectHome, PrivateTmp,
PrivateDevices, beskyttelse av kjerne og control groups,
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX, RestrictNamespaces,
LockPersonality og UMask=0027. Den starter automatisk på nytt etter en
feil (Restart=on-failure).
SELinux trenger ingen endringer. Tjenesten kjører i domenet
unconfined_service_t, som andre programmer under /opt.
9.3. Kommandolinje
|
Kjør mottaker og web-GUI (brukes av systemd) |
|
Sett web-passordet (root) |
|
Importer og kompiler MIB-filer |
|
Kompiler alle opplastede MIB-er. Avslutningskode 2 hvis noen feiler |
|
Ende-til-ende-test (se Verifisering) |
|
Versjon |
Som root kjøres alle kommandoer unntatt set-password som brukeren
snmptrap, så filene får riktig eier.
9.4. Offline-installeren
snmp-trap-receiver-offline-installer.run er et bash-skript etterfulgt av
et gzip-komprimert tar-arkiv. Skriptet bruker python3 til å kontrollere
arkivets SHA-256 og pakker det ut med datafilteret i tarfile (ingen
absolutte stier, ingen ..). Deretter startes install.sh. Verken tar
eller pip må være installert på serveren.
install.sh:
-
kontrollerer root, operativsystem, Python 3.9, arkitektur mot pakken, glibc 2.34 eller nyere (kreves av cryptography-wheelen) og sjekksummen til hver fil (
SHA256SUMS); -
stopper en kjørende tjeneste ved oppgradering;
-
oppretter brukeren og mappene;
-
bygger et nytt virtuelt miljø med
python3 -m venv --without-pipog installerer de låste wheel-filene med pip-wheelen som følger med (--no-index --no-deps --only-binary=:all:). Selve pip installeres ikke i miljøet; -
kontrollerer at pakkene kan importeres, bytter inn den nye versjonen og flytter den gamle til
.previous/; -
oppretter konfigurasjonen ved første installasjon (tilfeldig hemmelig nøkkel og generert passord) eller beholder den som finnes, og legger på valgene fra kommandolinja;
-
importerer eventuelt MIB-er;
-
installerer systemd-enheten, åpner porter i firewalld, starter tjenesten på nytt og venter på
/healthz; -
kjører selvtesten.
9.5. Bygge installeren
På en hvilken som helst maskin med internettilgang:
bash build-offline-installer.sh # x86_64 + aarch64
Skriptet laster ned wheel-filene i Application/requirements.lock for
CPython 3.9 på begge arkitekturene (manylinux 2014/2_17/2_28/2_34) og
kontrollerer at hver pakke finnes for hver arkitektur. Det genererer
THIRD_PARTY_NOTICES.txt og third_party_inventory.json fra wheel-filene
(inkludert GPL-skanningen) og kopierer applikasjonen og dokumentasjonen.
Har byggemaskinen Python 3.9, kjøres en offline-installasjon og selvtesten.
pip-audit kjøres hvis det er installert. Til slutt skrives installeren,
en vanlig .tar.gz og sjekksummene til Offline-Installation/.
For å oppdatere avhengigheter endrer du Application/requirements.in,
løser avhengighetene med Python 3.9 og skriver resultatet til
requirements.lock.
10. Verifisering
snmp-trap-receiver selftest bruker en ledig høy UDP-port på 127.0.0.1 og
midlertidige mapper, så den forstyrrer aldri den kjørende tjenesten. Den
kontrollerer dette:
-
at
cryptographyer tilgjengelig (kryptering i SNMPv3); -
at en test-MIB lastet opp med filnavnet
../../evil/selftest.miblagres bare under modulnavnet; -
at MIB-en kompileres offline mot de innebygde IETF-MIB-ene, og at en fil som ikke er en MIB, avvises;
-
at mottakeren binder. En v1-trap, en v2c-trap, en v2c-inform og en v3-trap med authPriv (SHA/AES) sendes, og alle fire mottas med riktig versjon og type;
-
at trap-OID-en og variablene slås opp via den opplastede MIB-en, at kildeadressen registreres, at feil community avvises og telles, og at begge loggene skrives;
-
web: innlogging kreves, feil passord avvises og innlogging virker. Den engelske siden er standard, og en POST uten CSRF-token avvises. Videre testes trap-API-et, MIB-opplasting via API-et, nedlasting av logg, avvisning av path traversal ved nedlasting av logg, CSV-eksport, den brukermanualen på engelsk og norsk, norske siden, sikkerhetshoder, lisensinventaret og begge lisensdokumentene på begge språk, og stopp og start via API-et.
Installeren kjører selvtesten etter hver installasjon.
11. Begrensninger
-
Bare HTTP. For tilgang over nett man ikke stoler på, bind GUI-et til
127.0.0.1og bruk SSH-tunnel, eller sett en omvendt proxy med TLS foran. -
Ett felles passord. Ingen brukerkontoer eller revisjonsspor for handlinger i GUI-et (start, stopp, opplasting og sletting skrives til journalen med klientens adresse).
-
Lista i minnet holder
buffer_sizetraps. Eldre traps finnes bare i loggfilene. -
Målt kapasitet: rundt 700 traps i sekundet på én prosessorkjerne. Salver på 2000 traps ble mottatt uten tap. Mottakeren ber om en mottaksbuffer på 4 MB, men kjernen begrenser den til
net.core.rmem_max(standard i Rocky 9: 208 KB, nok til noen hundre traps i én salve). Der det kan komme store trap-stormer, bør grensen økes:echo net.core.rmem_max=4194304 > /etc/sysctl.d/90-snmp-trap-receiver.conf && sysctl --system.
12. Lisenser
Alle tredjepartskomponentene har tillatende lisenser. Se
Lisensrapport.md, THIRD_PARTY_NOTICES.txt og
third_party_inventory.json.
Vedlegg A: Porter
Port |
Protokoll |
Retning |
Formål |
162 |
UDP |
inn |
SNMP-traps og -informs (kan endres) |
8080 |
TCP |
inn |
Web-GUI (kan endres) |
Vedlegg B: Dokumenter
|
Installasjon, konfigurasjon, drift og oppgradering |
|
Revisjon av Docker-versjonen og hva som er rettet |
|
Lisenser og GPL-skanning |
|
Fullstendige lisenstekster |
|
Maskinlesbar komponentliste |
|
Endringer |