Containerimages scannen
Het scannen van containerimages beantwoordt de vraag wat is er kwetsbaar in dit image, als aanvulling op de SBOM, die zegt wat er in dit image zit. Bevindingen komen uit twee bronnen, en beide worden getoond naast het image of de commit waarover ze gaan.
Hoe het werkt
De dagelijkse ronde door het register
fremforge scant zelf de containerimages in het pakketregister van je organisatie. Je hoeft niets aan je workflows toe te voegen.
- Wat er wordt gescand. Elke tag van elk containerimage. Elke tag wordt herleid tot de digest van zijn manifest, en het image wordt met
trivy imagegescand op kwetsbaarheden en gelekte secrets, inclusief OS-pakketten en taalafhankelijkheden in elke laag. - Wanneer. Eén keer per dag, nadat de kwetsbaarheidsdatabase van Trivy is vernieuwd. Een digest die al tegen dezelfde databaseversie is gescand, wordt overgeslagen, zodat een nieuw gepubliceerde CVE wordt gemeld voor een image dat niet is veranderd.
- Waar bevindingen worden bewaard. Onder de digest van het image (
frem.sh/<org>/<image>@sha256:<digest>). Afwijzingen blijven gelden als een image opnieuw wordt gescand. - Grenzen. Van een image voor meerdere architecturen wordt alleen de variant
linux/amd64gescand. Een image groter dan 2 GiB (de som van zijn lagen zoals ze in het register staan) wordt niet opgehaald en dus niet gescand.
Dezelfde ronde scant ook de standaardbranch van elke repository (trivy fs en trivy config).
Hetzelfde ophalen van een image wordt ook vergeleken met de OSV-lijst van kwaadaardige pakketten. Die controle staat beschreven onder Kwaadaardige pakketten; het ophalen van een image met een openstaande bevinding wordt geweigerd, ongeacht de blokkeeractie hieronder. Sinds 2026-10-11 geldt hetzelfde voor images die via de dependency proxy in de cache staan (zie hieronder).
Images in de cache van de dependency proxy
Images die je organisatie via de dependency proxy ophaalt, worden op dezelfde manier gescand als de images die je zelf pusht.
- Wanneer. Een image komt in de wachtrij voor zijn eerste scan zodra je organisatie het voor het eerst in de cache zet, en wordt normaal binnen 15 minuten gescand. Daarna wordt het opnieuw gescand telkens als de kwetsbaarheidsdatabase van Trivy verandert, in dezelfde dagelijkse ronde als je eigen images, zolang het in je cache blijft.
- Wat er wordt gescand. Het image voor één platform dat je client heeft opgehaald (bij een tag voor meerdere architecturen de variant die je hebt opgehaald), met
trivy image, op kwetsbaarheden en gelekte secrets. - Waar bevindingen worden bewaard. Onder
frem.sh/<org>/dependency-proxy/<upstream>/<repository>@sha256:<digest>, bijvoorbeeld.../dependency-proxy/docker.io/library/alpine@sha256:…, naast je eigen images onder Beveiliging › Bevindingen › Containerimages in het organisatiebeheer en in de findings-API. Afwijzingen blijven gelden als het image opnieuw wordt gescand. - Blokkeeractie.
block_pullgeldt ook voor images via de proxy: het ophalen via de proxy van een image met een niet-afgewezen bevinding van ernst high of critical wordt geweigerd met403 DENIED. - Kwaadaardige pakketten. De taalpakketten in een image in de cache worden vergeleken met de advisories van OSV over kwaadaardige pakketten, net als bij gepushte images, en een overeenkomst is een bevinding op het tabblad Malware. Sinds 2026-10-11 wordt het ophalen van een image in de cache met een openstaande bevinding geweigerd, net als bij een gepusht image; zie Dependency-proxy.
- Gedeelde scans. Een image wordt herkend aan zijn digest, dus als meerdere organisaties hetzelfde image in de cache hebben, wordt het één keer gescand en krijgt elke organisatie een eigen kopie van de bevindingen. Je ziet alleen bevindingen voor images in je eigen cache.
- Als een image uit je cache verdwijnt. De bevindingen blijven staan zolang het image nog wordt gebruikt: in de laatste 30 dagen door je organisatie via de proxy opgehaald, of genoemd in een van je deployments (het toegangslog van het register en de deploymentgegevens laten zien welke). Daarna worden ze bij de volgende ronde verwijderd. Een image dat opnieuw wordt opgehaald nadat de bevindingen zijn verwijderd, krijgt ze bij de volgende ronde terug, uit de scan van zijn digest.
Images die je Helm-charts deployen
De images die een Helm-chart in je repository deployt, worden gescand op kwetsbaarheden, en de bevindingen worden gemeld in de check fremforge/image-scan van de repository.
- Wat er wordt gelezen. Elke chart in de repository, dus elke map met een
Chart.yaml. Invalues.yaml: eenimage:met een referentie (image: nginx:1.25.3), en de gesplitste vorm die charts het vaakst gebruiken (image:met daaronderregistry,repository,tagendigest). Een gesplitst image zonder tag, of mettag: "", is deappVersionvan de chart, want dat is wat{{ .Values.image.tag | default .Chart.AppVersion }}deployt. Intemplates/: eenimage:-regel met een letterlijke referentie. Sleutels zoalsinitImageofsidecarImagetellen ook mee;imagePullPolicyenimagePullSecretsniet. - Wat er niet wordt gelezen. Templates worden niet gerenderd en er draait geen chartcode, dus een image dat door templatelogica wordt samengesteld, andere values-bestanden dan
values.yaml(een overlay die je bij de installatie kiest) en afhankelijke charts die niet in de repository staan, vallen erbuiten. Een referentie zonder registry is Docker Hub (nginxisdocker.io/library/nginx), en een referentie zonder tag islatest. - Hoe het image wordt opgehaald. Via de dependency proxy van je organisatie, zoals jij het zou ophalen: je upstream-inloggegevens, pakketbeleid, malwarescan en cache gelden. Heeft je organisatie geen dependency proxy, dan haalt de eigen proxy van fremforge het op, anoniem en alleen bij registries die dat toestaan (bijvoorbeeld
ghcr.io,quay.io,registry.k8s.io). Een image dat je proxy weigert (bijvoorbeeld een dat je beleid blokkeert), wordt niet gescand. Het linux/amd64-image wordt mettrivy imagegescand op kwetsbaarheden. - Docker Hub vraagt om je eigen inloggegeven. Docker Hub-images worden alleen opgehaald met het eigen Docker Hub-inloggegeven van je organisatie, nooit anoniem en nooit met een account van fremforge. Zonder dat inloggegeven wordt een Docker Hub-image in een chart niet gescand: de status
fremforge/image-scanzegt dat (“N Docker Hub image(s) in Helm charts not scanned: add your Docker Hub credential”), en Beveiliging › Bevindingen › Containerimages toont de images met een link om het inloggegeven toe te voegen onder Upstream-inloggegevens. Zodra je het toevoegt, worden de images gescand, vanaf de volgende scan van de repository; bevindingen die voor een andere organisatie zijn gescand, worden nooit aan de jouwe getoond. - Wanneer. Een image komt in de wachtrij zodra een scan het voor het eerst noemt, en wordt normaal binnen 15 tot 30 minuten gescand; de bevindingen verschijnen bij de volgende scan van de repository. Een image dat nog steeds wordt genoemd, wordt om de paar dagen opnieuw gescand, omdat een tag kan verschuiven en er nieuwe advisories verschijnen.
- Hoe een bevinding eruitziet. Het doel is het chartbestand en het image, bijvoorbeeld
charts/web/values.yaml: docker.io/library/nginx:1.19.0, met het pakket, de versie en de versie met de oplossing. De bevindingen gaan door de drempel en de afwijzingen van de imagescan, en een pull request wordt alleen geblokkeerd door bevindingen die hij zelf toevoegt.
Nieuwere tags voor upstream-images
Een image dat je upstream ophaalt, heeft geen eigen Dockerfile die je kunt aanpassen, dus zoekt fremforge naar een nieuwere tag die de bevindingen oplost. Voor elk image in de cache met openstaande bevindingen van ernst high of critical, hooguit één keer per dag:
- De tag die je hebt opgehaald. De digest wordt gekoppeld aan de tag waarmee je organisatie hem heeft opgehaald, via de index voor meerdere architecturen als die er is.
- De kandidaat. Dit is de nieuwste tag van dezelfde lijn: dezelfde vorm en dezelfde getallen, behalve het laatste:
3.12.4-slimnaar3.12.10-slim, of16.4naar16.10. Het is nooit een andere major- of minorversie, want dat is een migratie en geen oplossing. Een tag zonder zo’n lijn, zoals3.12-slim,16ofpg17, wordt in plaats daarvan gecontroleerd op een rebuild: dezelfde tag, upstream opnieuw gebouwd sinds je hem hebt opgehaald. - Wat hij oplost. De kandidaat wordt opgehaald en met dezelfde scanner gescand. Elke openstaande bevinding van jou die de kandidaat niet meer heeft, telt als opgelost.
De bevinding toont dan een van deze meldingen:
| Getoond | Betekenis |
|---|---|
Nieuwere tag <tag> lost N van de bevindingen van dit image op | Stap over op die tag. PR openen met <tag> opent de pull request hieronder |
Upstream opnieuw gebouwd: <tag> opnieuw ophalen lost N op | Dezelfde tag is upstream nieuwer. Haal hem opnieuw op; in je repository verandert niets, tenzij een referentie een digest vastzet |
| Upstream-image, geen nieuwere gecorrigeerde tag | Geen tag van dezelfde lijn, en geen rebuild, heeft minder van deze bevindingen |
| Upstream-image: nieuwere tags nog niet gecontroleerd | Nog niet gecontroleerd, of de registry is er geen waarvan fremforge tags leest (Docker Hub, GHCR, Quay, MCR, ECR Public en registry.k8s.io wel) |
Hetzelfde staat in de findings-API als upstream_update.
De pull request. PR openen met <tag>, of POST /orgs/{org}/dependency-updates/security-fixes/image-ref-requests met image_finding_id, zoekt in de repository’s van je organisatie naar verwijzingen naar het image met de tag die je hebt opgehaald. Dat omvat docker compose-bestanden, Kubernetes-manifests, Helm-values met repository: en tag: op aparte regels, en CI-items image: en container:, onder elk van de namen waaronder het image bekend is:
frem.sh/<org>/dependency-proxy/docker.io/library/python:3.12.4-slimdocker.io/library/python:3.12.4-slimpython:3.12.4-slim
Elke repository met een verwijzing krijgt één pull request voor een beveiligingsfix die al zijn verwijzingen verplaatst. Een vastgezette digest gaat naar de digest van de kandidaat. Hij heeft de gebruikelijke branch, het label, de limieten en de merge-instelling. Een Dockerfile-FROM wordt hier niet aangepast: dat is de fix van het base-image. Als geen enkele repository naar het image verwijst, wordt er niets geopend en blijft de bevinding de tag tonen die haar oplost. Per verzoek worden maximaal 60 repository’s en 400 YAML-bestanden gelezen.
Scannen vanuit je eigen workflow
Je kunt ook in CI scannen en de resultaten insturen. Het runner-image van fremforge bevat Trivy. Dit voorbeeld scant het bestandssysteem van de repo (trivy fs: afhankelijkheden en gelekte secrets) en de IaC-configuratie (trivy config: Dockerfiles, Compose-bestanden), en stuurt de bevindingen daarna met een POST in:
- name: Scan + push findings
run: |
trivy fs --format json --output trivy-fs.json .
trivy config --format json --output trivy-config.json .
curl -sS --fail \
-H "Authorization: Bearer ${{ secrets.FREMFORGE_API_TOKEN }}" \
-H 'Content-Type: application/json' \
-d @<(jq -c '{
image_ref: "'"${GITHUB_REPOSITORY}@${GITHUB_SHA}"'",
commit_sha: "'"${GITHUB_SHA}"'",
repo_full_name: "'"${GITHUB_REPOSITORY}"'",
findings: [.Results[].Vulnerabilities[] | {
cve_id: .VulnerabilityID,
severity: (.Severity|ascii_downcase),
package_name: .PkgName,
installed_version: .InstalledVersion,
fixed_version: .FixedVersion,
title: .Title,
target: .PkgPath
}]
}' trivy-fs.json) \
"https://frem.sh/_app/api/v1/image-scans/findings"De PAT heeft de scope findings:write nodig.
Beleidsinstellingen
Beveiliging › Beleid › Imagebeleid bevat deze instellingen (tot 2026-10-15 stonden ze onder Bevindingen › Containerimages):
- Ingeschakeld: staat dit uit, dan geeft de inname
202 {accepted: 0, status: 'disabled'}terug (de gegevens worden nog geparst, maar weggegooid). Handig voor teams vóór de lancering die de scan willen laten draaien zonder dat hij blokkeert. - Blokkeeractie:
none|warn|block_pull:none: bevindingen opslaan, geen commitstatus plaatsen. Alleen detectie aan de buildkant. Het ophalen van images wordt niet gecontroleerd.warn: bevindingen opslaan, commitstatussuccessplaatsen met het aantal in de beschrijving. Het mergen van de PR wordt niet geblokkeerd. Het ophalen wordt gecontroleerd maar nooit geweigerd: een pull dieblock_pullzou weigeren, wordt geleverd en vastgelegd alswould_blockin het toegangslog van het register en als auditgebeurtenisregistry.pull.would_block(zie Auditlog en SIEM).block_pull: bevindingen opslaan, commitstatusfailureplaatsen als er minstens één niet-afgewezen bevinding op of boven je blokkeerdrempel is. Bij een pull request tellen alleen de bevindingen die de pull request introduceert (zie Pull requests: alleen nieuwe bevindingen blokkeren). Branchbeveiliging met de verplichte statuscheckfremforge/image-scanblokkeert dan het mergen. Het blokkeert ook het ophalen: eendocker pull(op tag of op digest) van een image met zo’n bevinding wordt geweigerd met403 DENIED, en de melding noemt de drempel en linkt naar de bevindingen. Wijs de bevindingen af of los ze op om de blokkade op te heffen. Elke weigering is de auditgebeurtenisregistry.pull.blocked.
- Ondertekende images vereisen: staat dit aan, dan accepteert de inname ook een veld
signature_status(signed/unsigned/unknown). Niet-ondertekende images (of ondertekende images van een uitgever die niet op de lijst met toegestane uitgevers staat) krijgen een kunstmatige bevinding van ernst critical, zodat hetzelfde block-pull-pad het ondertekeningsbeleid afdwingt. Zie Cosign voor de kant van het ondertekenen.
Blokkeerdrempel
De bevindingen die blokkeren, zijn die op of boven één ernstniveau, ingesteld per organisatie. Het is de block_severity van het item image_scan in het scanbeleid voor je hele organisatie, dezelfde instelling voor mergen en voor ophalen:
block_severity | Blokkeert bij |
|---|---|
critical | critical |
high (standaard) | high en critical |
medium | medium, high en critical |
low | elke bevinding |
Stel hem in met PUT /_app/api/v1/orgs/<org>/policy:
curl -X PUT -H "Authorization: Bearer ${FREMFORGE_PAT}" -H 'Content-Type: application/json' \
-d '{"scan_policy": {"image_scan": {"mode": "enforce", "block_severity": "critical"}}}' \
"https://frem.sh/_app/api/v1/orgs/acme/policy"of in configuratie als code als scan_policy = { image_scan = { mode = "enforce", block_severity = "critical" } }. null, of geen item image_scan, betekent high, wat elke organisatie had voordat de drempel kon worden ingesteld. Elke andere waarde wordt geweigerd met 400. Het token heeft policy:write nodig. De console heeft er nog geen instelling voor.
Alleen het item voor de hele organisatie telt. Een overschrijving van het scanbeleid per repository kan een block_severity bevatten, maar het ophalen van een image hoort niet bij één repository, dus de imagecontroles lezen die niet.
Pull requests: alleen nieuwe bevindingen blokkeren
De check fremforge/image-scan op een pull request faalt alleen op bevindingen die de pull request introduceert. fremforge vergelijkt de bevindingen van de pull request op of boven je blokkeerdrempel met de laatste scan van de branch waarin hij wordt gemerged. Een bevinding die die branch al heeft, blokkeert niet, dus een pull request die niets kwetsbaars verandert, kan worden gemerged in een repository met openstaande bevindingen. De status luidt dan bijvoorbeeld No new high/critical findings; 7 already on main.
- Een bevinding is dezelfde als de advisory, het pakket en het bestand hetzelfde zijn; dat is ook de identiteit waarmee een afwijzing wordt overgenomen. Dezelfde advisory in een ander bestand is een nieuwe bevinding.
- Een pull request die één nieuwe bevinding van ernst high toevoegt aan een repository met veel bestaande bevindingen, faalt: 1 new high/critical finding(s) …; merge blocked; 30 already on main.
- Een pull request die enkele bevindingen oplost en er geen toevoegt, slaagt.
- Bestaande bevindingen worden niet verborgen. Ze worden vermeld, tellen mee voor je SLA en blokkeren het ophalen van images zoals voorheen.
De branch wordt gescand na een push naar je standaardbranch, hooguit één keer per uur, waarbij de nieuwste push wint. Een bevinding die sinds de laatste scan op de branch is gekomen, telt daarom tot de volgende scan nog als bevinding van de pull request. Het omgekeerde gebeurt nooit: een nieuwe bevinding wordt nooit aangezien voor een bestaande.
Als er geen scan is van de branch waarin de pull request wordt gemerged, blokkeert elke bevinding op of boven de drempel, zoals voorheen. Voor de standaardbranch vraagt de scan van die pull request ook om een scan van de branch. Zodra de eerste scan van de branch is vastgelegd, scant fremforge uit zichzelf opnieuw de openstaande pull requests naar die branch waarvan de check intussen faalde (hooguit 20 per repository, de meest recent bijgewerkte), zodat ze groen worden, of alleen rood blijven op wat ze toevoegen, zonder opnieuw te draaien of een nieuwe push. Een pull request naar een andere branch telt altijd elke bevinding.
De blokkade bij het ophalen van images is ongewijzigd: onder block_pull wordt het ophalen van een image met een niet-afgewezen bevinding op of boven de drempel geweigerd, of de bevinding nu nieuw is of niet.
Auditlog en SIEM
Elke beslissing van de imagecontroles over het ophalen wordt in je auditlog geschreven en gaat van daar naar je SIEM:
registry.pull.blocked: een pull is geweigerd: doorblock_pull(controleimage_scan), door malwareherkenning (image_malware), door een laag die ClamAV geïnfecteerd vond of niet kon scannen (registry_scan, ofregistry_blobvoor een laag die direct op digest is opgehaald), of door de controle van de imagehandtekening (signature).registry.pull.would_block: een pull is geleverd die een van die controles zou hebben geweigerd: onder jewarn, of terwijl een controle in observatiemodus draait.
Er is één gebeurtenis per imagedigest, controle en beslissing per uur; de volgende gebeurtenis voor dezelfde digest bevat het aantal van het vorige uur.
Waar bevindingen verschijnen
Beveiliging › Bevindingen › Containerimages in het organisatiebeheer toont scans per image-ref, commit en PR. Elke rij toont:
- Aantallen per ernst (critical / high / medium / low)
- Aantallen die op te lossen zijn (bevindingen met een
fixed_versiondie niet null is) - Status (
success/failure/skipped) en de geplaatste commitstatus - Links naar de upstream-advisories (CVE, en GHSA als die er is)
Hoe een bevinding er in de database uitziet
Opgeslagen velden per bevinding: cve_id, severity, package, installed_version, fixed_version, target (het bestandspad in het image) en de scan_run_id die bevindingen uit dezelfde run aan elkaar koppelt. De volledige uitvoer van Trivy wordt NIET opgeslagen, alleen de rijen per bevinding, want dat is de vorm die je kunt bevragen.
Bewaartermijn
Bevindingen volgen het bewaarbeleid voor audits per tenant. Verlopen bevindingen houden hun hash in de keten (bewijs tegen manipulatie), maar verliezen de inhoud per rij.
Zie ook
- Kwaadaardige pakketten: hetzelfde ophalen van een image wordt ook vergeleken met de OSV-advisories
MAL-*; het ophalen van een image met een openstaande bevinding wordt geweigerd, ongeacht de blokkeeractie hierboven. - Cosign: images ondertekenen en het beleid voor ondertekende images.
- SBOM’s: de pakketinventaris van het image (los van de kwetsbaarheden).
- OSV-aanbevelingen: de aanvulling aan de bronkant (kwetsbaarheden in je afhankelijkheden voordat ze worden uitgeleverd).
- API-referentie: het innamepunt
POST /_app/api/v1/image-scans/findings(per tenant via de PAT van de runner).