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
- Gå til Organisasjonsadministrasjon → SIEM-videresending → Legg til et endepunkt.
- Skriv inn et Navn (fritekst, unikt i organisasjonen) og URL til mottakeren din:
https://…, ellersyslog+tls://host:portfor syslog over TLS.http://,localhost, RFC1918- og metadataadresser avvises; bruk en mottaker som kan nås offentlig. - Velg Format (se Formater og batching). De resterende stegene ligger under Advanced options og kan også endres senere på endepunktets side.
- Du kan eventuelt snevre inn tillatelseslisten Action prefixes (f.eks.
forgejo.push, billing.). Tom betyr at alt videresendes. Hvert prefiks matches medaction.startsWith(prefix). - 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.blockedogregistry.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. - Du kan eventuelt sette en Autentiseringsheader: et headernavn og en hemmelig verdi som sendes med hver forespørsel (for eksempel
AuthorizationogSplunk <token>). Se Autentisering. - Velg hva signaturen dekker (tidsstempel og body som standard, eller bare body).
- Angi batchstørrelse, forespørselsstørrelse og ventetid.
- 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-*ogX-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å hendelsensidi stedet.
Leveringskontrakt
Hver HTTPS-levering er en POST:
| Header | Verdi |
|---|---|
Content-Type | application/json (json, json_array), application/x-ndjson (ndjson), text/plain; charset=utf-8 (cef) |
User-Agent | fremforge-siem-forwarder/1 |
X-Fremforge-Timestamp | Unix-sekunder (streng) da fremforge bygde forespørselen |
X-Fremforge-Signature | sha256=<hex> over <timestamp>.<body>, eller over <body> i modusen som bare dekker body |
Idempotency-Key | Hendelsens id for én enkelt hendelse; for en batch en SHA-256 av batchens hendelses-id-er i rekkefølge |
X-Fremforge-Event-Count | Antall hendelser i bodyen |
| autentiseringsheaderen din | Nå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
| Format | Body | Hendelser per forespørsel |
|---|---|---|
json (standard) | Ett hendelsesobjekt | 1 |
ndjson | Ett hendelsesobjekt per linje, hver linje avsluttet med \n | Opptil max events |
json_array | En JSON-array med hendelsesobjekter | Opptil max events |
cef | Én CEF-linje per hendelse, hver avsluttet med \n | Opptil 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>
| CEF | Verdi |
|---|---|
| Signature ID, Name | action |
| Severity | 9 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 |
rt | created_at, millisekunder siden epoken |
externalId | id (dedupliseringsnøkkelen) |
suser | actor |
act | action |
cat | action fram til første . |
cs1Label=tenantId, cs1 | tenant_id |
cs2Label=fields, cs2 | fields som JSON |
cs3Label=hashChainThis, cs3 | hash_chain.this_hash |
cs4Label=hashChainPrev, cs4 | hash_chain.prev_hash, tom når det ikke finnes noen |
dvchost | frem.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_eventseller øyeblikksbildet, og ingensecurity.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_category | Funn | Kilde |
|---|---|---|
dependency_vulnerability | En sårbar avhengighet på standardbranchen | Avhengighetsskanning |
image_vulnerability | En sårbarhet eller feilkonfigurasjon i et containerimage i registeret ditt (inkludert imager via proxy) eller i en repositoryskanning | Image-skanning |
licence | En avhengighet under en forbidden- eller restricted-lisens | Lisensskanning |
sast | Et funn fra statisk analyse på standardbranchen | SAST |
secret | En committet secret (aldri selve secreten) | Skanning etter secrets |
malware | En ondsinnet pakke (OSV MAL-*), eller en opplasting som antivirusskanningen (ClamAV) avviste | Avhengighets- og registerskanninger, skanning av opplastinger |
package_policy | En fil i pakkeregisteret som pakkepolicyen blokkerer eller advarer om | Pakkepolicy |
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
action | Når |
|---|---|
security.finding.opened | Funnet oppdages for første gang. |
security.finding.reopened | Funnet 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.dismissed | Noen 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_expired | En tidsbegrenset avvisning utløp, og funnet er åpent igjen. Dette omfatter et unntak som nådde utløpstiden sin. |
security.finding.exception_revoked | Et 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_revoked | fremforge 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.resolved | Funnet oppdages ikke lenger: rettet, fjernet, erstattet av en nyere skanning som ikke lenger rapporterer det, eller pakkefilen er slettet. |
security.finding.known_exploited | En å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_sincei 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 énsecurity.finding.snapshot-hendelse per åpent funn i kø, bare til det endepunktet. Det kan tas én gang per endepunkt; en ny forespørsel svarer409. - 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 medPATCH … {"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
ider den samme ved hvert nytt forsøk og hver avspilling: dedupliser på den.
Felt
fields i en funnhendelse:
| Felt | Verdi | Cortex XDM |
|---|---|---|
alert_name | Kort tittel, f.eks. CVE-2024-3094 in xz-utils (frem.sh/acme/api@sha256:…) | xdm.alert.name |
alert_category | Funntypen (tabellen over) | xdm.alert.category |
alert_severity | critical, high, medium, low eller informational | xdm.alert.severity |
alert_description | Sammendrag fra sårbarhetsvarselet eller regelmeldingen, høyst 500 tegn, eller null | xdm.alert.description |
original_alert_id | Stabil funn-id (32 heksadesimale tegn), den samme på hver hendelse om ett funn | xdm.alert.original_alert_id |
org | Organisasjonens slug | |
finding.type, finding.source | Funntypen, og skanneren som rapporterte den | |
finding.state, finding.previous_state | open, dismissed eller resolved; tilstanden før (null første gang det sees) | |
finding.transition, finding.transition_reason | Overgangen (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_at | Når funnet ble sett første gang; når det gikk inn i nåværende tilstand | |
finding.key | Identiteten funnet beholder på tvers av nye skanninger, f.eks. dep:acme/api:GHSA-…:npm:lodash@4.17.20 | |
finding.row_id | Funnets nåværende id i Findings-API-et; den endres når en ny skanning erstatter raden | |
finding.console_url | Hvor 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 | |
identifiers | Listene 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) | |
component | Den 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. | |
location | kind (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_kind | xdm.target.resource.name (referanse, repository eller pakke) |
policy | Bare pakkepolicyhendelser: {verdict, reasons} | |
known_exploited | På 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:
| CEF | Verdi |
|---|---|
| Name | alert_name (Signature ID forblir action) |
| Severity | Mens 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 |
cat | alert_category (funntypen) |
msg | alert_name |
outcome | finding.state |
reason | finding.transition_reason |
start | finding.first_seen_at, millisekunder siden epoken |
cs5Label=findingId, cs5 | original_alert_id |
cs6Label=purl, cs6 | component.purl, når det finnes |
flexString1Label=identifiers, flexString1 | CVE-, GHSA-, MAL- og andre id-er, regel-id, lisens og signatur, kommaseparert |
flexString2Label=location, flexString2 | Image-referansen, repositoryet eller pakken |
cfp1Label=cvssScore, cfp1 | cvss.score, når det finnes |
filePath | location.path |
fileHash | component.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 eraction(opptil 32 tegn). MSG er et UTF-8-byte-order-mark etterfulgt av CEF-linjen (formatetcef) 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) ellerid(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
failingmed 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
413halverer 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 svarte413på 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>/replaymed{"since": "<ISO 8601>", "until": "<ISO 8601, optional>"}.sincekan ligge høyst så langt tilbake som revisjonsoppbevaringen din. Én forespørsel legger høyst 100 000 hendelser i kø og returnerernext_sincenår det gjenstår flere. Hendelser som fortsatt venter, legges ikke i kø to ganger; avspilte hendelser beholder sinid.
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
| SIEM | Tar imot over HTTPS via | Innstillinger i fremforge | Direkte fra fremforge? |
|---|---|---|---|
| Cortex XSIAM | HTTP log collector | Autentiseringsheader Authorization: <API key>, format json | Ja |
| Splunk | HTTP Event Collector, /services/collector/raw | Autentiseringsheader Authorization: Splunk <token>, format ndjson | Ja |
| Microsoft Sentinel | Azure Monitor Logs Ingestion API | Trenger et kortlivet Entra ID-token | Nei, relé |
| Elastic | HTTP endpoint-input i Elastic Agent / Filebeat | Signaturen dekker bare body, eventuelt en hemmelig header | Ja |
| IBM QRadar | Loggkilden HTTP Receiver, eller en TLS Syslog-loggkilde | Format cef | Ja, hvis lytteren kan nås offentlig |
| Google SecOps | Webhook-feed | URL med ?key=&secret=, format ndjson | Ja |
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.