Gå til hovedinnhold

Videresending til SIEM

fremforge skriver hver administrativ handling som endrer tilstand, til en revisjonslogg per organisasjon med en hashkjedet integritetsgaranti (se Integritet i revisjonskjeden). De samme hendelsene kan sendes videre til SIEM-en din så snart de er skrevet, slik at deteksjonsreglene dine ser dem i løpet av sekunder i stedet for ved neste CSV-eksport.

Kommer snart: den samme strømmen vil også inneholde én hendelse per livssyklusendring for et sikkerhetsfunn: et sårbarhets-, lisens-, SAST-, secret-, skadevare- eller pakkepolicyfunn som åpnes, gjenåpnes, avvises eller løses. Se Funnhendelser.

Denne siden beskriver hvordan videresenderen er bygd opp, leveringskontrakten du verifiserer mot, og hvordan populære SIEM-er tar imot hendelsene.

Slik fungerer leveringen

audit-emit skriver den kjedede raden og legger, i den samme databasetransaksjonen, hendelsen i kø for hvert aktivert endepunkt der handlingsprefiksene passer. En leveringsprosess sender køen til hvert endepunkt i rekkefølge, med én eller mange hendelser per forespørsel avhengig av endepunktets format. En hendelse fjernes fra køen først når mottakeren din svarer 2xx.

Innstillingene ligger under Organisasjonsadministrasjon → Integrasjon → SIEM-videresending (/<org>/_admin/siem), og de samme innstillingene finnes i REST-API-et (/_app/api/v1/orgs/<org>/siem/endpoints).

Siden SIEM forwarding har skjemaet Add endpoint og listen over endepunkter: navn, URL (legitimasjon maskert), status, siste levering og antall hendelser som venter. Navnet på hvert endepunkt åpner endepunktets egen side (/<org>/_admin/siem/<endpoint id>), med ett kort per innstilling:

  • Oversikt: status, siste feilårsak, filteret for handlingsprefikser, Disable/Enable og Send test.
  • Autentiseringsheader: sett, roter eller fjern den.
  • Signatur: hva HMAC-en dekker.
  • Levering: ventende hendelser, den eldste av dem, neste forsøk, totalt levert og forkastet, og innstillingene for format og batching.
  • Send på nytt: send tidligere hendelser på nytt.
  • Slett endepunkt: skriv inn endepunktets navn for å bekrefte.

Konfigurere et endepunkt

  1. Gå til Organisasjonsadministrasjon → SIEM-videresending → Legg til et endepunkt.
  2. Skriv inn et Navn (fritekst, unikt i organisasjonen) og URL til mottakeren din: https://…, eller syslog+tls://host:port for syslog over TLS. http://, localhost, RFC1918- og metadataadresser avvises; bruk en mottaker som kan nås offentlig.
  3. Velg Format (se Formater og batching). De resterende stegene ligger under Advanced options og kan også endres senere på endepunktets side.
  4. Du kan eventuelt snevre inn tillatelseslisten Action prefixes (f.eks. forgejo.push, billing.). Tom betyr at alt videresendes. Hvert prefiks matches med action.startsWith(prefix).
  5. Du kan eventuelt slå på Ta med tilgangsloggen for registeret for også å motta én hendelse per forespørsel mot pakke- eller containerregisteret (registry.access.*). Det er av som standard; se Tilgangslogg for registeret. Pull-avgjørelsene til image-sperrene, registry.pull.blocked og registry.pull.would_block, er revisjonshendelser, ikke tilgangshendelser, så de videresendes uten dette valget, én per image-digest, sperre og avgjørelse per time; se Pull-sperrer for containere. Kommer snart: Ta med funnhendelser, avkrysset som standard, vil gjøre at endepunktet også mottar én hendelse per livssyklusendring for et sikkerhetsfunn (security.finding.*), fra det øyeblikket det opprettes. Inntil det lanseres, viser ikke skjemaet dette valget. Se Funnhendelser.
  6. Du kan eventuelt sette en Autentiseringsheader: et headernavn og en hemmelig verdi som sendes med hver forespørsel (for eksempel Authorization og Splunk <token>). Se Autentisering.
  7. Velg hva signaturen dekker (tidsstempel og body som standard, eller bare body).
  8. Angi batchstørrelse, forespørselsstørrelse og ventetid.
  9. Klikk Opprett endepunkt. Den delte HMAC-hemmeligheten vises én gang på neste skjermbilde; kopier den inn i konfigurasjonen til mottakeren. fremforge viser den aldri igjen. Mister du den, sletter du endepunktet og legger det til på nytt.

Endepunktet er aktivt med en gang; bekreftelsen lenker til siden for endepunktet. Send test sender en syntetisk fremforge.siem.test-hendelse i endepunktets format, med headere og signatur, og viser svarstatusen og de første 200 bytene av svaret fra mottakeren.

Legitimasjon i URL-en. Noen innsamlere tar imot legitimasjonen sin i URL-en (?token=, ?key=&secret= eller user:password@). fremforge lagrer URL-en slik den ble skrevet inn, kryptert, og viser den alle andre steder (adminsiden, API-et, testresultatet, revisjonsloggen) med hver query-verdi og brukerinformasjonen erstattet av ***. Bare leveringen bruker hele URL-en.

Autentisering

To mekanismer, som kan brukes sammen:

  • Autentiseringsheader. Én header per endepunkt, med navn og verdi du velger selv. Verdien lagres kryptert og kan bare skrives: ingen side, API-svar, logglinje eller revisjonshendelse inneholder den. Revisjonsloggen registrerer headernavnet og når den ble satt. For å rotere lagrer du en ny verdi; den gamle slutter å bli sendt umiddelbart, så legg til den nye nøkkelen i innsamleren din først. Headernavn som ville ødelagt forespørselen, avvises: Host, Content-Length, Content-Type, Content-Encoding, Transfer-Encoding, Connection, Keep-Alive, Upgrade, TE, Trailer, Expect, Accept-Encoding, User-Agent, Cookie, Proxy-* og X-Fremforge-*. Verdier er utskrivbar ASCII, opptil 4096 tegn.
  • HMAC-signatur. Hver forespørsel har X-Fremforge-Signature: sha256=<hex>, nøklet med endepunktets HMAC-hemmelighet. Som standard dekker den <timestamp>.<body>, slik at mottakeren kan avvise en gammel forespørsel ut fra tidsstemplet. Med Signature covers: body only dekker den bare den rå bodyen, som er det mottakere med innebygd body-verifisering (Elastics HTTP endpoint-input) sjekker. En mottaker som bare verifiserer body, kan ikke avvise en gjentatt forespørsel ut fra alder; dedupliser på hendelsens id i stedet.

Leveringskontrakt

Hver HTTPS-levering er en POST:

HeaderVerdi
Content-Typeapplication/json (json, json_array), application/x-ndjson (ndjson), text/plain; charset=utf-8 (cef)
User-Agentfremforge-siem-forwarder/1
X-Fremforge-TimestampUnix-sekunder (streng) da fremforge bygde forespørselen
X-Fremforge-Signaturesha256=<hex> over <timestamp>.<body>, eller over <body> i modusen som bare dekker body
Idempotency-KeyHendelsens id for én enkelt hendelse; for en batch en SHA-256 av batchens hendelses-id-er i rekkefølge
X-Fremforge-Event-CountAntall hendelser i bodyen
autentiseringsheaderen dinNår den er satt

Hver hendelse har denne formen (toppnivånøkler i denne rekkefølgen; fields.*-nøkler sortert alfabetisk slik at bytene er stabile):

{
  "id": "00000000-0000-0000-0000-000000000000",
  "tenant_id": "11111111-1111-1111-1111-111111111111",
  "actor": "alice",
  "action": "policy.update.api",
  "fields": { "...": "..." },
  "created_at": "2026-05-15T13:42:11.123Z",
  "hash_chain": {
    "prev_hash": "ab12...",
    "this_hash": "cd34..."
  }
}

id er revisjonshendelsens id. Den er den samme ved hvert nytt forsøk og hver avspilling av hendelsen: bruk den til deduplisering.

actor er et rent Forgejo-brukernavn for en handling utført av en person (i nettkonsollen, og for mange API-kall brukeren som utstedte tokenet), oidc:idp:<sub> for en person som logget inn via identitetsleverandøren din og handler gjennom et utvekslet token, token:<id> for andre API-kall, operator:<email> for en handling utført av ansatte i fremverk, og andre <kind>:<id>-former for systemkomponenter.

hash_chain.this_hash er den samme hashen som forankres i OBS Object Lock, slik at SIEM-en din kan bevise at en hendelse er eldre enn et kjent anker.

En tilgangshendelse fra registeret (bare på et endepunkt med include_access_log) har samme form, med action registry.access.<action> og "hash_chain": null: tilgangshendelser er ikke en del av revisjonens hashkjede. fields er hendelsen slik API-et for tilgangsloggen returnerer den.

En funnhendelse (kommer snart; bare på et endepunkt med include_finding_events) vil også ha denne formen, med action security.finding.<transition> og "hash_chain": null.

Formater og batching

FormatBodyHendelser per forespørsel
json (standard)Ett hendelsesobjekt1
ndjsonEtt hendelsesobjekt per linje, hver linje avsluttet med \nOpptil max events
json_arrayEn JSON-array med hendelsesobjekterOpptil max events
cefÉn CEF-linje per hendelse, hver avsluttet med \nOpptil max events

For formatene med batching:

  • Maks hendelser per forespørsel: 1 til 1000 (100 når du bytter til et batchformat uten å velge).
  • Maks forespørselsstørrelse: 1 KiB til 4 MiB (standard 1 MiB). En batch lukkes før den ville overskride grensen; én enkelt hendelse sendes alltid.
  • Maks ventetid: 0 til 60 000 ms (standard 0). Hvor lenge en delvis fylt batch venter på flere hendelser før den sendes.

Hendelser leveres i den rekkefølgen de ble skrevet.

CEF-felttilordning

CEF:0|fremverk|fremforge|1|<action>|<action>|<severity>|<extension>

CEFVerdi
Signature ID, Nameaction
Severity9 når hash_chain.this_hash er chain-broken; 7 når action inneholder fail, denied, deny, blocked, violation, malware, secret, breach eller tamper; 5 når den inneholder delete, revoke, disable, remove, bypass, suspend eller dismiss; ellers 3
rtcreated_at, millisekunder siden epoken
externalIdid (dedupliseringsnøkkelen)
suseractor
actaction
cataction fram til første .
cs1Label=tenantId, cs1tenant_id
cs2Label=fields, cs2fields som JSON
cs3Label=hashChainThis, cs3hash_chain.this_hash
cs4Label=hashChainPrev, cs4hash_chain.prev_hash, tom når det ikke finnes noen
dvchostfrem.sh

Headerfelt escaper \ og |; extension-verdier escaper \, = og linjeskift (\n, \r). En verdi fra en revisjonshendelse kan derfor ikke starte et nytt felt eller en ny linje.

Funnhendelser

Kommer snart. Funnhendelser er ikke tilgjengelige ennå. Denne delen beskriver dem før lanseringen; inntil da tilbyr verken konsollen eller API-et include_finding_events eller øyeblikksbildet, og ingen security.finding.*-hendelse sendes. Endringsloggen kunngjør lanseringen.

Et endepunkt med Ta med funnhendelser slått på (include_finding_events i API-et) mottar én hendelse hver gang et sikkerhetsfunn endrer tilstand. Alle funntyper fremforge rapporterer, er dekket:

alert_categoryFunnKilde
dependency_vulnerabilityEn sårbar avhengighet på standardbranchenAvhengighetsskanning
image_vulnerabilityEn sårbarhet eller feilkonfigurasjon i et containerimage i registeret ditt (inkludert imager via proxy) eller i en repositoryskanningImage-skanning
licenceEn avhengighet under en forbidden- eller restricted-lisensLisensskanning
sastEt funn fra statisk analyse på standardbranchenSAST
secretEn committet secret (aldri selve secreten)Skanning etter secrets
malwareEn ondsinnet pakke (OSV MAL-*), eller en opplasting som antivirusskanningen (ClamAV) avvisteAvhengighets- og registerskanninger, skanning av opplastinger
package_policyEn fil i pakkeregisteret som pakkepolicyen blokkerer eller advarer omPakkepolicy

Funn fra skanninger av pull requests er ikke med: de kommer og går med hver pull request. Det er på standardbranchen livssyklusen til et funn spores. Pakkepolicyhendelser sendes bare for organisasjoner der resultatene fra pakkepolicyen er synlige.

Hendelseskatalog

actionNår
security.finding.openedFunnet oppdages for første gang.
security.finding.reopenedFunnet oppdages på nytt etter at det ble løst (transition_reason reappeared), eller avvisningen ble trukket tilbake før den utløp (undismissed).
security.finding.dismissedNoen avviste funnet (risiko godtatt, falsk positiv), en tillatelsesliste dekker det, eller et antivirusfunn ble overstyrt. actor er brukeren som avviste det, eller system:<kind>.
security.finding.dismissal_expiredEn tidsbegrenset avvisning utløp, og funnet er åpent igjen. Dette omfatter et unntak som nådde utløpstiden sin.
security.finding.exception_revokedEt godkjent unntak som avviste funnet, ble tilbakekalt, og funnet er åpent igjen. transition_reason er utløseren: manual (noen tilbakekalte det), severity_raised, new_vulnerability eller approver_left. actor er personen som tilbakekalte det, eller system:exception-revocation.
security.finding.dismissal_revokedfremforge tilbakekalte en avvisning som bygde på en nåbarhetsvurdering av not reachable, fordi en senere skanning motsier den, og funnet er åpent igjen. transition_reason er reachability_changed, finding.revocation forklarer hvorfor, og actor er system:exception-revocation. (Før 2026-10-12 ble dette sendt som dismissal_expired.) Et tilbakekalt unntak er exception_revoked med samme årsak.
security.finding.resolvedFunnet oppdages ikke lenger: rettet, fjernet, erstattet av en nyere skanning som ikke lenger rapporterer det, eller pakkefilen er slettet.
security.finding.known_exploitedEn åpen eller avvist sårbarhet i en avhengighet eller et image ble oppført som utnyttet i praksis av ENISAs EU Vulnerability Database eller CISAs KEV-katalog. Sendes én gang per oppføring. transition_reason er listed (listen har lagt den til siden) eller new (funnet var allerede oppført da det ble sett første gang; den sendes sammen med opened, i vilkårlig rekkefølge). known_exploited angir CVE-ene og listene, og actor er system:known-exploited-feed. Et avvist funn rapporteres også: risikoen det ble godtatt under, har endret seg.
security.finding.snapshotÉn per åpent funn, sendt én gang når du ber om et øyeblikksbilde.

Endringer fanges opp innen omtrent ett minutt. Et funn beholder én identitet på tvers av nye skanninger, så en ny skanning som rapporterer det samme funnet igjen, sender ingenting; fields.original_alert_id er den samme på hver hendelse om ett funn.

Hva som sendes, og fra når

  • Bare endringer fra du slår det på. Et endepunkt mottar hendelser for endringer som skjer etter at det begynte å motta dem (finding_events_since i API-et). Å slå på funnhendelser sender ikke de eksisterende funnene dine.
  • Øyeblikksbilde av åpne funn. For å laste inn det som er åpent nå, bruker du Send øyeblikksbilde av åpne funn på endepunktets side, eller POST /_app/api/v1/orgs/<org>/siem/endpoints/<id>/finding-snapshot. Det legger én security.finding.snapshot-hendelse per åpent funn i kø, bare til det endepunktet. Det kan tas én gang per endepunkt; en ny forespørsel svarer 409.
  • Standard. Et endepunkt som opprettes etter lanseringen, tar med funnhendelser med mindre du fjerner avkrysningen (eller sender "include_finding_events": false). Endepunkter som finnes før lanseringen, blir værende som de var, uten funnhendelser, slik at en mottaker bygd for revisjonshendelser ikke uvarslet begynner å motta en ny hendelsesfamilie; slå dem på per endepunkt på endepunktets side eller med PATCH … {"include_finding_events": true}.
  • Handlingsprefikser gjelder som for revisjonshendelser. Et endepunkt med prefikser mottar funnhendelser bare hvis ett av dem passer, for eksempel security.finding..
  • Levering, batching, nye forsøk, avspilling, HMAC, autentiseringsheaderen og oppbevaringen av ikke-leverte hendelser er det samme som for revisjonshendelser. Hendelsens id er den samme ved hvert nytt forsøk og hver avspilling: dedupliser på den.

Felt

fields i en funnhendelse:

FeltVerdiCortex XDM
alert_nameKort tittel, f.eks. CVE-2024-3094 in xz-utils (frem.sh/acme/api@sha256:…)xdm.alert.name
alert_categoryFunntypen (tabellen over)xdm.alert.category
alert_severitycritical, high, medium, low eller informationalxdm.alert.severity
alert_descriptionSammendrag fra sårbarhetsvarselet eller regelmeldingen, høyst 500 tegn, eller nullxdm.alert.description
original_alert_idStabil funn-id (32 heksadesimale tegn), den samme på hver hendelse om ett funnxdm.alert.original_alert_id
orgOrganisasjonens slug
finding.type, finding.sourceFunntypen, og skanneren som rapporterte den
finding.state, finding.previous_stateopen, dismissed eller resolved; tilstanden før (null første gang det sees)
finding.transition, finding.transition_reasonOvergangen (som i action) og hvorfor: new, reappeared, undismissed, dismissal_expired, manual, severity_raised, new_vulnerability, approver_left, reachability_changed, dismissed, new_dismissed, not_detected, superseded, no_longer_present, allowed, package_deleted, listed, snapshot
finding.first_seen_at, finding.state_changed_atNår funnet ble sett første gang; når det gikk inn i nåværende tilstand
finding.keyIdentiteten funnet beholder på tvers av nye skanninger, f.eks. dep:acme/api:GHSA-…:npm:lodash@4.17.20
finding.row_idFunnets nåværende id i Findings-API-et; den endres når en ny skanning erstatter raden
finding.console_urlHvor funnet vises i konsollen
finding.dismissal{by, reason, expires_at, approved_by, exception_id} på en dismissed-hendelse, ellers null. approved_by og exception_id er satt når et godkjent unntak avviste funnet
finding.exception{id, status, approved_by, revoked_by, revoked_reason, revoked_detail} for unntaket som opphørte, på exception_revoked og på en dismissal_expired som avsluttet et unntak; ellers null
finding.revocation{reason, detail, revoked_verdict, revoked_at} på dismissal_revoked: utløseren (reachability_changed), hva som endret seg, den senere vurderingen som motsa not reachable, og når; ellers null
cvss{score, vector} når sårbarhetsvarselet har en, ellers null
identifiersListene cve, ghsa, mal og other med id-er for sårbarhetsvarsler; rule_id (regler for SAST, secrets og feilkonfigurasjon i imager, pakkepolicyregler); licence; signature (ClamAV)
componentDen berørte komponenten: purl, name, version, ecosystem, fixed_version, sha256. Null for SAST og secrets. For en sårbarhet i et image har pakken inne i imaget et navn, men ingen purl; imagets purl står i location.image.purl.
locationkind (repository, image, package eller upload), repository, path, line, commit_sha; image {reference, purl, proxied, tags}, med reference i den offentlige formen frem.sh/<org>/<image>@sha256:<digest>; package {type, owner, name, version, file, sha256}; upload_kindxdm.target.resource.name (referanse, repository eller pakke)
policyBare pakkepolicyhendelser: {verdict, reasons}
known_exploitedPå hver hendelse: {cve_ids, sources} når funnet er en sårbarhet i en avhengighet eller et image som er oppført som utnyttet i praksis (sources: euvd for ENISA EUVD, cisa_kev for CISA KEV), ellers null. CVE-id-ene er funnets egne eller aliasene til sårbarhetsvarselet.

Eksempel (formatet json):

{
  "id": "6f0b1f7e-2c55-4c55-9a0e-6b8f8f7e1a10",
  "tenant_id": "11111111-1111-1111-1111-111111111111",
  "actor": "system:finding-lifecycle",
  "action": "security.finding.opened",
  "fields": {
    "alert_category": "dependency_vulnerability",
    "alert_description": "Command injection in lodash",
    "alert_name": "GHSA-35jh-r3h4-6jhm in lodash 4.17.20",
    "alert_severity": "high",
    "component": { "purl": "pkg:npm/lodash@4.17.20", "name": "lodash", "version": "4.17.20", "ecosystem": "npm", "fixed_version": "4.17.21", "sha256": null },
    "cvss": { "score": 7.2, "vector": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H" },
    "finding": {
      "id": "3c1d2a8e5b0f4e6a9d7c1b2a3e4f5a6b",
      "key": "dep:acme/api:GHSA-35jh-r3h4-6jhm:npm:lodash@4.17.20",
      "type": "dependency_vulnerability",
      "source": "dep-scan",
      "state": "open",
      "previous_state": null,
      "transition": "opened",
      "transition_reason": "new",
      "first_seen_at": "2026-10-08T09:12:40.120Z",
      "state_changed_at": "2026-10-08T09:13:02.457Z",
      "row_id": "0d6c7c43-2b1f-4f5e-8a3e-0b2f9f1c2d3e",
      "console_url": "https://frem.sh/acme/_admin/code-security/deps",
      "dismissal": null
    },
    "identifiers": { "cve": [], "ghsa": ["GHSA-35jh-r3h4-6jhm"], "mal": [], "other": [], "rule_id": null, "licence": null, "signature": null },
    "known_exploited": null,
    "location": { "kind": "repository", "repository": "acme/api", "path": "package-lock.json", "line": null, "commit_sha": null, "image": null, "package": null, "upload_kind": null },
    "org": "acme",
    "original_alert_id": "3c1d2a8e5b0f4e6a9d7c1b2a3e4f5a6b"
  },
  "created_at": "2026-10-08T09:13:02.457Z",
  "hash_chain": null
}

Funnhendelser er ikke en del av revisjonens hashkjede. En endring av funnhendelsesinnstillingen for et endepunkt, og en forespørsel om øyeblikksbilde, er revisjonshendelser (siem.endpoint.finding_events.update, siem.endpoint.finding_snapshot).

Funnhendelser i CEF

En funnhendelse bruker CEF-tilordningen med disse forskjellene:

CEFVerdi
Namealert_name (Signature ID forblir action)
SeverityMens funnet er åpent (opened, reopened, dismissal_expired, exception_revoked, dismissal_revoked, snapshot): 10 critical, 8 high, 5 medium, 3 low, 1 informational. 1 for dismissed og resolved, slik at gode nyheter ikke utløser et varsel slik funnet gjorde. known_exploited er minst 8, uansett funnets alvorlighetsgrad
catalert_category (funntypen)
msgalert_name
outcomefinding.state
reasonfinding.transition_reason
startfinding.first_seen_at, millisekunder siden epoken
cs5Label=findingId, cs5original_alert_id
cs6Label=purl, cs6component.purl, når det finnes
flexString1Label=identifiers, flexString1CVE-, GHSA-, MAL- og andre id-er, regel-id, lisens og signatur, kommaseparert
flexString2Label=location, flexString2Image-referansen, repositoryet eller pakken
cfp1Label=cvssScore, cfp1cvss.score, når det finnes
filePathlocation.path
fileHashcomponent.sha256 (antivirusfunn, pakkefiler)

cs3 og cs4 er tomme, som for tilgangshendelser fra registeret. Over syslog følger RFC 5424-alvorlighetsgraden CEF-alvorlighetsgraden som for revisjonshendelser: et kritisk funn sendes med 2 (critical), et løst funn med 6 (informational).

Syslog over TLS

En endepunkt-URL på formen syslog+tls://host[:port] (standardport 6514) leverer over syslog i stedet for HTTPS:

  • Transport: TCP med TLS 1.2 eller nyere, rammet inn med oktettelling (RFC 5425). fremforge verifiserer mottakerens sertifikat mot det offentlige settet av sertifikatutstedere, for vertsnavnet i URL-en, så mottakeren trenger et offentlig klarert sertifikat. Bruk port 6514 eller 443; andre porter kan ikke nås fra fremforge.
  • Melding: én RFC 5424-melding per hendelse, <PRI>1 TIMESTAMP frem.sh fremforge - MSGID - MSG. PRI er facility 13 (log audit) × 8 + severity: 2 for CEF-alvorlighetsgrad 9, 4 for 7, 5 for 5, ellers 6. MSGID er action (opptil 32 tegn). MSG er et UTF-8-byte-order-mark etterfulgt av CEF-linjen (formatet cef) eller JSON-hendelsen (alle andre formater).
  • Batching: opptil max events meldinger innenfor max request size deler én tilkobling.
  • Ingen kvittering. Syslog har ikke noe svar på applikasjonsnivå. En batch regnes som levert når hver melding er skrevet og mottakeren din lukket tilkoblingen på ryddig vis. En avvist tilkobling, en TLS-feil eller en mottaker som lukker før batchen er skrevet, gjør at batchen feiler, og den prøves på nytt i sin helhet. En mottaker som godtar tilkoblingen og deretter forkaster det den leser, ser ut som levert. Dedupliser på externalId (CEF) eller id (JSON).
  • Autentiseringsheaderen og HMAC-signaturen gjelder bare HTTPS. Begrens lytteren din til fremforge med TLS og, om nødvendig, en tillatelsesliste over kildeadresser.

Verifisere signaturen

HMAC-en beregnes over de eksakte bytene i bodyen, i den rekkefølgen de kommer. Ikke parse og serialiser på nytt før du verifiserer.

Mottaker i Node.js (tidsstempel og body):

import { createHmac, timingSafeEqual } from 'node:crypto';

function verify(req, body) {
  const sig = req.headers['x-fremforge-signature'];
  const ts = req.headers['x-fremforge-timestamp'];
  if (!sig?.startsWith('sha256=') || !ts) throw new Error('missing headers');

  // 5-minute replay window. Adjust to your tolerance.
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) throw new Error('stale');

  const expected = createHmac('sha256', SHARED_SECRET)
    .update(`${ts}.${body}`)
    .digest('hex');
  const got = sig.slice(7); // strip "sha256="
  if (expected.length !== got.length) throw new Error('bad sig');
  if (!timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(got, 'hex'))) {
    throw new Error('bad sig');
  }
}

Mottaker i Python (tidsstempel og body):

import hmac, hashlib, time

def verify(headers, raw_body: bytes) -> None:
    sig = headers.get('X-Fremforge-Signature', '')
    ts = headers.get('X-Fremforge-Timestamp', '')
    if not sig.startswith('sha256=') or not ts:
        raise ValueError('missing headers')
    if abs(time.time() - int(ts)) > 300:
        raise ValueError('stale')
    expected = hmac.new(
        SHARED_SECRET.encode(),
        f'{ts}.'.encode() + raw_body,
        hashlib.sha256,
    ).hexdigest()
    if not hmac.compare_digest(expected, sig[7:]):
        raise ValueError('bad sig')

I modusen som bare dekker body, dropper du tidsstemplet: HMAC-SHA256 over raw_body.

Feilhåndtering, nye forsøk og avspilling

  • Hver forespørsel har et tidsavbrudd på 10 sekunder.
  • Et svar som ikke er 2xx, et tidsavbrudd eller en tilkoblingsfeil gjør at hendelsene blir liggende i køen. Endepunktet venter stadig lenger: 5 sekunder, doblet for hver feil på rad, høyst 10 minutter, med ±20 % jitter. Når mottakeren din svarer igjen, tømmes køen i rekkefølge.
  • Etter 5 feil på rad viser endepunktet failing med siste feilårsak. Leveringen fortsetter å prøve på nytt; du trenger ikke å slå noe av og på. Deaktiverer du et endepunkt, legges ikke nye hendelser i kø for det; aktiverer du det igjen, gjenopptas leveringen umiddelbart.
  • En 413 halverer batchen. En enkelt hendelse som mottakeren fortsatt avviser som for stor, forkastes og telles.
  • Ikke-leverte hendelser beholdes så lenge revisjonsoppbevaringen for organisasjonen din varer (Organisasjonsadministrasjon → Autentiseringspolicy; 90 dager som standard), og høyst 200 000 per endepunkt. Eldre hendelser forkastes og telles.
  • Kortet Levering på endepunktets side (og delivery-blokken i API-et, dropped_total) viser antall ventende hendelser, den eldste av dem, neste forsøk og totalt levert og forkastet (vist som «discarded» på enkelte språk). En hendelse forkastes, og leveres aldri til det endepunktet med mindre du spiller den av på nytt, bare i disse tilfellene: mottakeren din svarte 413 på akkurat den hendelsen alene; den ventet fortsatt da den ble eldre enn revisjonsoppbevaringen din; endepunktet hadde allerede 200 000 ventende hendelser, og da går de eldste først; eller selve revisjonshendelsen finnes ikke lenger, noe som ikke skjer innenfor oppbevaringsperioden. Ingenting annet forkaster en hendelse: en mottaker som feiler eller er deaktivert, får bare hendelsene til å vente.
  • Avspilling legger revisjonshendelsene dine i kø på nytt fra et tidspunkt (og, når funnhendelser er tilgjengelige, på et endepunkt som tar dem med, også funnhendelsene i samme tidsvindu, aldri eldre enn da endepunktet begynte å motta dem): Spill av fra (UTC) på endepunktets side, eller POST /_app/api/v1/orgs/<org>/siem/endpoints/<id>/replay med {"since": "<ISO 8601>", "until": "<ISO 8601, optional>"}. since kan ligge høyst så langt tilbake som revisjonsoppbevaringen din. Én forespørsel legger høyst 100 000 hendelser i kø og returnerer next_since når det gjenstår flere. Hendelser som fortsatt venter, legges ikke i kø to ganger; avspilte hendelser beholder sin id.

Leveringen skjer minst én gang: et nytt forsøk etter et tapt svar, eller en avspilling, sender en hendelse på nytt. Dedupliser på id.

Populære SIEM-er

SIEMTar imot over HTTPS viaInnstillinger i fremforgeDirekte fra fremforge?
Cortex XSIAMHTTP log collectorAutentiseringsheader Authorization: <API key>, format jsonJa
SplunkHTTP Event Collector, /services/collector/rawAutentiseringsheader Authorization: Splunk <token>, format ndjsonJa
Microsoft SentinelAzure Monitor Logs Ingestion APITrenger et kortlivet Entra ID-tokenNei, relé
ElasticHTTP endpoint-input i Elastic Agent / FilebeatSignaturen dekker bare body, eventuelt en hemmelig headerJa
IBM QRadarLoggkilden HTTP Receiver, eller en TLS Syslog-loggkildeFormat cefJa, hvis lytteren kan nås offentlig
Google SecOpsWebhook-feedURL med ?key=&secret=, format ndjsonJa

Cortex XSIAM

Tar imot JSON fra tredjeparter via en HTTP log collector (Settings → Data Sources & Integrations → + Add New → HTTP). Velg Log Format: JSON; Vendor og Product du skriver inn, gir datasettet navnet <vendor>_<product>_raw. Collector-URL-en har formen https://api-<tenant external URL>/logs/v1/event og tar API-nøkkelen sin i Authorization (dokumentasjon fra Palo Alto).

Oppsett: URL = collector-URL-en; autentiseringsheader Authorization = collectorens API-nøkkel; format json.

Eksempel på parsing- og datamodellregler for fremforge_audit_raw

Parsingregel (Settings → Configurations → Data Management → Parsing Rules), slik at _time er fremforges hendelsestid i stedet for ankomsttiden. created_at har alltid tre desimaler og en Z:

[INGEST:vendor="fremforge", product="audit", target_dataset="fremforge_audit_raw", no_hit=keep]
alter _time = parse_timestamp("%Y-%m-%dT%H:%M:%E3SZ", created_at);

Datamodellregel (Settings → Configurations → Data Management → Data Model Rules):

[MODEL: dataset="fremforge_audit_raw"]
filter action != "fremforge.siem.test"
| alter
    xdm.event.id = id,
    xdm.event.type = "fremforge_audit",
    xdm.event.original_event_type = action,
    xdm.event.operation_sub_type = action,
    xdm.event.description = to_json_string(fields),
    xdm.observer.vendor = "fremforge",
    xdm.observer.product = "fremforge",
    xdm.observer.unique_identifier = tenant_id,
    xdm.source.user.identifier = actor;

Begge forutsetter at XSIAM lagrer JSON-nøklene på toppnivå som kolonner. Viser datasettet ditt bare _raw_log, leser du verdiene med json_extract_scalar(_raw_log, "$.created_at") og så videre.

Datamodellregel for funnhendelser

Funnhendelser (kommer snart) havner i det samme datasettet. Varselfeltene deres er oppkalt etter XDM-feltene, så de tilordnes én til én. Når du bruker regelen ved siden av revisjonsregelen over, legger du til | filter not (action ~= "^security\.finding\.") i revisjonsregelen, slik at hver hendelse modelleres én gang:

[MODEL: dataset="fremforge_audit_raw"]
filter action ~= "^security\.finding\."
| alter
    xdm.event.id = id,
    xdm.event.type = "fremforge_finding",
    xdm.event.original_event_type = action,
    xdm.event.outcome = json_extract_scalar(fields, "$.finding.state"),
    xdm.event.outcome_reason = json_extract_scalar(fields, "$.finding.transition_reason"),
    xdm.alert.name = json_extract_scalar(fields, "$.alert_name"),
    xdm.alert.category = json_extract_scalar(fields, "$.alert_category"),
    xdm.alert.severity = json_extract_scalar(fields, "$.alert_severity"),
    xdm.alert.description = json_extract_scalar(fields, "$.alert_description"),
    xdm.alert.original_alert_id = json_extract_scalar(fields, "$.original_alert_id"),
    xdm.target.resource.name = coalesce(json_extract_scalar(fields, "$.location.image.reference"), json_extract_scalar(fields, "$.location.repository"), json_extract_scalar(fields, "$.location.package.name")),
    xdm.observer.vendor = "fremforge",
    xdm.observer.product = "fremforge",
    xdm.observer.unique_identifier = tenant_id,
    xdm.source.user.identifier = actor;

HTTP log collector tar imot disse hendelsene som loggposter. For å utløse et XSIAM-varsel fra dem legger du til en korrelasjonsregel på datasettet, for eksempel på action = "security.finding.opened" og alert_severity critical eller high, og grupperer på original_alert_id, slik at senere hendelser om det samme funnet (dismissed, resolved) knyttes til det.

Splunk

Tar imot via HTTP Event Collector. /services/collector/raw tar bodyen som den er; sett sourcetype=_json på URL-en slik at hver linje blir én hendelse. Tokenet legges i Authorization: Splunk <token>. Med indexer acknowledgement slått på trenger raw-endepunktet også en kanal-GUID; legg den på URL-en som channel=<GUID> (dokumentasjon fra Splunk).

Oppsett: URL https://<hec-host>:8088/services/collector/raw?sourcetype=_json; autentiseringsheader Authorization = Splunk <token>; format ndjson.

Microsoft Sentinel

Tar imot egendefinert JSON via Azure Monitor Logs Ingestion API, inn i en egendefinert _CL-tabell definert av en datainnsamlingsregel (DCR). Forespørselen går til {endpoint}/dataCollectionRules/{dcrImmutableId}/streams/{streamName}?api-version=2023-01-01 med et Entra ID-bearer-token fra client credentials-flyten (scope https://monitor.azure.com/.default), og bodyen må være en JSON-array (dokumentasjon fra Microsoft). Det eldre HTTP Data Collector API nådde slutten av støtteperioden 14. september 2026; ikke bygg på det.

Oppsett: tokenet utløper etter omtrent en time, så en statisk autentiseringsheader kan ikke bære det. Sett reléet imellom: det henter og bufrer tokenet og videresender bodyen. Bruk formatet json_array, så bodyen allerede er arrayen API-et vil ha. Tilordne created_at til TimeGenerated i DCR-transformasjonen.

Elastic

Tar imot via HTTP endpoint-inputen i Elastic Agent (integrasjonen Custom HTTP Endpoint Logs) eller Filebeat. Den lytter på sin egen port (standard 8000), kan terminere TLS og kan sjekke en HMAC over bodyen (dokumentasjon fra Elastic).

Oppsett: signaturen dekker bare body; i inputen setter du hmac.header: X-Fremforge-Signature, hmac.key: <the endpoint's HMAC secret>, hmac.type: sha256 og hmac.prefix: "sha256=". Vil du også bruke inputens valg for hemmelig header, setter du samme headernavn og verdi som endepunktets autentiseringsheader. Format json, eller ndjson for batcher. Indekser actor, action og tenant_id som keyword.

IBM QRadar

Tar imot HTTP POST-bodyer via en loggkilde som bruker protokollen HTTP Receiver. QRadar kjører en HTTP- eller HTTPS-server på en Event Collector (standardport 12469); HTTPS krever et sertifikat utstedt av en sertifikatutsteder, og gjensidig TLS er valgfritt (dokumentasjon fra IBM). CEF-linjer parses av QRadars Universal CEF DSM.

Oppsett: URL = mottakerens offentlige HTTPS-URL, eller syslog+tls://<collector>:6514 for en TLS Syslog-loggkilde; format cef. fremforge kan ikke presentere et klientsertifikat, så la gjensidig TLS være av. Når Event Collector ikke kan nås fra internett, videresender en reverse proxy eller et syslog-relé i kanten av nettverket ditt til den.

Google SecOps (Chronicle)

Tar imot via en webhook-feed (Settings → Feeds → Add new, kildetype Webhook, velg deretter en loggtype). Forespørsler går til feedens ...:importPushLogs-endepunkt med en API-nøkkel og feedens hemmelighet, som headerne X-goog-api-key og X-Webhook-Access-Key, eller som ?key=<API key>&secret=<secret>. Flere JSON-objekter adskilt med linjeskift kan dele én forespørsel, opptil 4 MB (dokumentasjon fra Google). Det finnes ingen ferdig parser for fremforge; du trenger en loggtype og en parser for disse hendelsene.

Oppsett: URL = feed-endepunktet med ?key=<API key>&secret=<secret> (lagres kryptert og vises med ***); format ndjson; maks forespørselsstørrelse høyst 4096 KiB.

Et relé som verifiserer signaturen

Sentinel, og enhver mottaker som trenger mer enn én header eller et kortlivet token, bruker en liten tjeneste: verifiser fremforge-signaturen, og videresend deretter bodyen uendret med SIEM-ens legitimasjon. Denne versjonen bruker bare Pythons standardbibliotek; sett TARGET_URL og TARGET_AUTH for SIEM-en din. For Sentinel legger du til henting av tokenet; med formatet json_array er bodyen allerede arrayen den vil ha. MAX_BODY tilsvarer standard forespørselsstørrelse på 1 MiB. Reléet videresender en korrekt signert hendelse med legitimasjonen, svarer 401 på feil signatur eller et tidsstempel som er mer enn fem minutter gammelt, og 502 når målet ikke kan nås.

"""fremforge SIEM forwarder -> SIEM relay: verify the signature, forward with the SIEM's credential."""
import hashlib
import hmac
import os
import time
import urllib.error
import urllib.request
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

SECRET = os.environ["FREMFORGE_SIEM_SECRET"].encode()
TARGET_URL = os.environ["TARGET_URL"]    # e.g. https://api-<tenant>/logs/v1/event
TARGET_AUTH = os.environ["TARGET_AUTH"]  # e.g. "<XSIAM API key>" or "Splunk <HEC token>"
MAX_SKEW_S = 300
MAX_BODY = 1024 * 1024


class Relay(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers.get("Content-Length") or 0)
        if length <= 0 or length > MAX_BODY:
            return self.reply(413)
        body = self.rfile.read(length)
        sig = self.headers.get("X-Fremforge-Signature", "")
        ts = self.headers.get("X-Fremforge-Timestamp", "")
        if not sig.startswith("sha256=") or not ts.isdigit():
            return self.reply(401)
        if abs(time.time() - int(ts)) > MAX_SKEW_S:
            return self.reply(401)
        expected = hmac.new(SECRET, ts.encode() + b"." + body, hashlib.sha256).hexdigest()
        if not hmac.compare_digest(expected, sig[7:]):
            return self.reply(401)
        req = urllib.request.Request(
            TARGET_URL,
            data=body,
            method="POST",
            headers={"Authorization": TARGET_AUTH, "Content-Type": "application/json"},
        )
        try:
            # fremforge gives up after 10 s; answer before that.
            with urllib.request.urlopen(req, timeout=4) as res:
                status = res.status
        except urllib.error.HTTPError as e:
            status = e.code
        except OSError:
            status = 0
        # Pass failure back, so fremforge keeps the event and retries it.
        self.reply(200 if 200 <= status < 300 else 502)

    def reply(self, code):
        self.send_response(code)
        self.send_header("Content-Length", "0")
        self.end_headers()

    def log_message(self, fmt, *args):
        pass


if __name__ == "__main__":
    ThreadingHTTPServer(("0.0.0.0", int(os.environ.get("PORT", "8080"))), Relay).serve_forever()

Kjør det bak den vanlige TLS-termineringen din: fremforge leverer bare til offentlige https://-URL-er. Hold svaret fra reléet under 10 sekunder, og returner en status som ikke er 2xx når SIEM-en avviste hendelsen. Et relé som alltid svarer 200, mister hendelser: fremforge regner en 2xx som levert og fjerner hendelsen fra køen.

Generisk webhook

Ethvert HTTPS-endepunkt som returnerer 2xx når det har godtatt forespørselen. Verifiser med kodeeksemplene over. Dette passer for selvhostede SIEM-er (Wazuh, Graylog, …) og HTTP-mottak som Logflare eller Datadog.

Helse sett fra operatøren

Operatørene i fremverk ser tilstanden til hvert endepunkt på SIEM-helsesiden i operatørportalen. En alarm utløses når den eldste ikke-leverte hendelsen for et endepunkt er mer enn 24 timer gammel, og vi kontakter eierne av organisasjonen din. Sett også opp varsel i SIEM-en din når hendelser fra fremforge uteblir.

Hva som ikke sendes

  • Syslog over UDP eller ren TCP. Syslog går bare over TLS.
  • Klientsertifikater (gjensidig TLS) mot mottakeren din.
  • Mer enn én egendefinert header per endepunkt.
  • Videresending på tvers av kunder. Hvert endepunkt er bundet til én kunde. Det finnes ingen speiling på operatørsiden; hendelser fra organisasjonen din forlater aldri kjeden per kunde.

Relatert

  • Integritet i revisjonskjeden – hva hendelsene faktisk inneholder, og hvordan du verifiserer kjeden fra ende til ende.
  • Webhooks – samme semantikk for leveringsformen for repository- og organisasjonshendelser, signert og levert på samme måte.
  • Modellen for hostede runnere – hvor hendelser fra runnere kommer fra i kjeden.