SNMP-Trap-Tools(at)Work

SNMP-Trap-Tools(at)Work / Teknisk beskrivelse

SNMP Trap Receiver

Mottaker for SNMP-traps og -informs i v1, v2c og v3 med web-GUI for Rocky Linux 9

Versjon 2.1.0Revidert 2026-10-02

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

trapreceiver/config.py

Leser /etc/snmp-trap-receiver/config.ini. Alle nøkler har trygge standardverdier. Oppdaterer også enkeltnøkler og beholder kommentarer og fileierskap.

trapreceiver/receiver.py

TrapReceiver: bygger pysnmp-motoren, binder transporter og kontrollerer bindingene, behandler varsler, skriver logger og holder ringbufferen og statistikken. Kan startes, stoppes og startes på nytt mens prosessen kjører videre.

trapreceiver/mibs.py

MibManager: validerer og lagrer opplastinger, kompilerer MIB-er offline og slår opp navn for OID-er.

trapreceiver/web.py

Flask-applikasjonen: innlogging, CSRF, sikkerhetshoder, sider og JSON-API.

trapreceiver/i18n.py

Alle GUI-tekster på engelsk og norsk (121 nøkler). Begge språkene må ha de samme nøklene, ellers lastes ikke modulen.

trapreceiver/manual.py

Viser brukermanualen (docs/no/Brukermanual.md, docs/en/User-Manual.md) i GUI-et. En liten Markdown-tolker uten avhengigheter: all tekst HTML-escapes først, og lenker kan bare peke til #ankre eller http(s).

trapreceiver/selftest.py

Ende-til-ende-test på en midlertidig port og i midlertidige mapper.

trapreceiver/main.py

Kommandolinje: serve, set-password, compile-mibs, import-mibs, selftest, version.

trapreceiver/mibs_base/

25 standard IETF-MIB-kilder pluss en stubb for RFC-1212.

trapreceiver/templates/, static/

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 en REJECTED-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:

  1. henter kildeadresse og port fra transportinformasjonen;

  2. henter SNMP-versjon og PDU-type (trap eller inform) fra meldingskonteksten;

  3. slår opp hver variabelbinding i MIB-visningen (navn og formatering av verdi, for eksempel oppramsinger og DisplayString);

  4. trekker ut snmpTrapOID.0 og sysUpTime.0. For SNMPv1-traps har pysnmp allerede gjort om Trap-PDU-en til SNMPv2-formen, så snmpTrapAddress.0, snmpTrapCommunity.0 og snmpTrapEnterprise.0 er med som variabler;

  5. 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"}]}

id

Løpenummer. Fortsetter etter en omstart, fordi bufferen lastes inn igjen fra JSON-loggen

security

Community (v1/v2c) eller USM-brukernavn (v3)

kind

trap eller inform

name

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

  1. Filendelsen må være .mib, .txt, .my, .smi eller ingen.

  2. Fila kan ikke være tom, kan ikke være større enn mib_max_kb og kan ikke inneholde NUL-byte i de første 4 KB.

  3. Modulnavnet leses fra innholdet (NAVN DEFINITIONS ::= BEGIN) og må passe med ^[A-Za-z][A-Za-z0-9-]{0,127}$.

  4. 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:

ok

Kompilert og lastet

error

Melding fra parseren, eller missing: A, B når importerte moduler ikke finnes

pending

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.

5.4. Oppslag

Etter hver kompilering bygges en ny MibViewController av de innebygde modulene i pysnmp og alle kompilerte moduler, og den byttes inn under lås. Hver ny trap slås opp mot den. Traps som allerede er mottatt, beholder navnene de hadde da de kom inn.

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

[web]

host

0.0.0.0

Adressen til web-GUI-et

port

8080

TCP-port

auth

true

Krev passord

password_hash

(settes av installeren)

scrypt-hash (Werkzeug). Settes med set-password

secret_key

(tilfeldig)

Signerer sesjonskapselen

default_language

en

en eller no

session_hours

12

Levetid for sesjonen

[receiver]

listen

0.0.0.0:162

vert:port, kommaseparert. IPv6 skrives [::]:162

autostart

true

Start mottaket når tjenesten starter

communities

public

Communities som godtas

accept_any_community

false

Godta alle communities

buffer_size

2000

Antall traps i minnet for GUI-et

[storage]

data_dir

/var/lib/snmp-trap-receiver

MIB-kilder, kompilerte moduler og status

log_dir

/var/log/snmp-trap-receiver

Trap-logger

log_max_mb

20

Størrelse per fil før rotasjon

log_backups

10

Antall roterte filer som beholdes per logg

mib_max_kb

2048

Største MIB-opplasting

mib_search_dirs

/usr/share/snmp/mibs

Ekstra mapper med MIB-kilder

[v3user:NAVN]

auth_protocol

none

md5, sha, sha224, sha256, sha384, sha512

auth_key

Passordfrase for autentisering

priv_protocol

none

des, 3des, aes128, aes192, aes256

priv_key

Passordfrase for kryptering

engine_id

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 third_party_inventory.json, kan filtreres per lisens), Lisensrapport og Third-party notices (med søk).

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

/login, /logout

GET/POST, POST

Sesjon

/lang/<en|no>

GET

Setter språk-kapselen

/

GET

Hovedsiden

/legal?doc=components|report|notices

GET

Lisensvisning

/manual

GET

Brukermanualen på valgt språk, med innholdsfortegnelse og søk. Brukermanual-knappen lenker til /manual#<fane>

/healthz

GET

{"ok": true, "receiver": true, "version": "2.1.0"} – uten innlogging, for overvåking

/api/status

GET

Status og tellere for mottakeren

/api/traps?since=N

GET

Traps med id > N (maks 1000)

/api/traps/clear

POST

Tømmer lista i minnet (loggene endres ikke)

/api/receiver/<start|stop|restart>

POST

Styrer mottakeren

/api/mibs

GET/POST

Liste / opplasting (multipart/form-data, feltet mib_file, flere tillatt)

/api/mibs/compile

POST

Kompilerer alle på nytt

/api/mibs/<modul>/delete

POST

Sletter en modul

/api/mibs/<modul>/source

GET

Laster ned kilden

/api/logs

GET

Lister loggfilene

/download/log/<fil>

GET

Laster ned en loggfil (bare trap-loggene og rotasjonene deres)

/download/traps.csv

GET

CSV med trapsene i minnet

/api/legal, /api/legal/<report|notices>

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 HttpOnly og SameSite=Strict, gyldig i 12 timer.

  • CSRF: hver POST krever sesjonens token (hodet X-CSRF-Token eller et skjemafelt).

  • Hoder: Content-Security-Policy: default-src 'self' uten innebygd skript eller stil, X-Frame-Options: DENY, nosniff, Referrer-Policy: no-referrer og Cache-Control: no-store på 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

/opt/snmp-trap-receiver/app/

root, 0755

Applikasjonen

/opt/snmp-trap-receiver/venv/

root, 0755

Python-miljø bygget fra wheel-filene som følger med

/opt/snmp-trap-receiver/docs/

root

Denne dokumentasjonen og lisensfilene

/opt/snmp-trap-receiver/.previous/

root

Forrige versjon etter en oppgradering

/etc/snmp-trap-receiver/config.ini

root:snmptrap, 0640

Konfigurasjon (inneholder hemmelig nøkkel og v3-nøkler)

/var/lib/snmp-trap-receiver/

snmptrap, 0750

MIB-kilder, kompilerte moduler og status.json

/var/log/snmp-trap-receiver/

snmptrap, 0750

snmp_traps.log, snmp_traps.jsonl og rotasjoner

/usr/local/bin/snmp-trap-receiver

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

snmp-trap-receiver serve

Kjør mottaker og web-GUI (brukes av systemd)

snmp-trap-receiver set-password [--password-stdin | --generate]

Sett web-passordet (root)

snmp-trap-receiver import-mibs FILER…​

Importer og kompiler MIB-filer

snmp-trap-receiver compile-mibs

Kompiler alle opplastede MIB-er. Avslutningskode 2 hvis noen feiler

snmp-trap-receiver selftest

Ende-til-ende-test (se Verifisering)

snmp-trap-receiver version

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:

  1. kontrollerer root, operativsystem, Python 3.9, arkitektur mot pakken, glibc 2.34 eller nyere (kreves av cryptography-wheelen) og sjekksummen til hver fil (SHA256SUMS);

  2. stopper en kjørende tjeneste ved oppgradering;

  3. oppretter brukeren og mappene;

  4. bygger et nytt virtuelt miljø med python3 -m venv --without-pip og 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;

  5. kontrollerer at pakkene kan importeres, bytter inn den nye versjonen og flytter den gamle til .previous/;

  6. oppretter konfigurasjonen ved første installasjon (tilfeldig hemmelig nøkkel og generert passord) eller beholder den som finnes, og legger på valgene fra kommandolinja;

  7. importerer eventuelt MIB-er;

  8. installerer systemd-enheten, åpner porter i firewalld, starter tjenesten på nytt og venter på /healthz;

  9. 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 cryptography er tilgjengelig (kryptering i SNMPv3);

  • at en test-MIB lastet opp med filnavnet ../../evil/selftest.mib lagres 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.1 og 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_size traps. 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

Installasjonsveiledning.md

Installasjon, konfigurasjon, drift og oppgradering

Revisjonsrapport.md

Revisjon av Docker-versjonen og hva som er rettet

Lisensrapport.md

Lisenser og GPL-skanning

THIRD_PARTY_NOTICES.txt

Fullstendige lisenstekster

third_party_inventory.json

Maskinlesbar komponentliste

Endringslogg.md

Endringer

Trenger du noe som sender traps å teste mottakeren med? Teknisk beskrivelse for SNMP Trap Sender