Zum Hauptinhalt springen

Abhängigkeits-Proxy

Der Abhängigkeits-Proxy ist ein Pull-Through-Cache für öffentliche Registrys, der pro Organisation läuft. Du richtest docker, npm, pip, uv, dnf, Maven, NuGet, go, helm oder cargo auf den Proxy-Pfad deiner Organisation auf frem.sh aus, und fremforge holt in deinem Auftrag aus der öffentlichen Registry:

  • Der erste Pull eines Image-Layers oder einer Paketdatei wird vom Upstream geholt und gegen den eigenen Hash des Upstreams geprüft. Schaltet deine Organisation das Malware-Scanning ein, wird er außerdem mit ClamAV gescannt, während er zu dir gestreamt wird. Eine Datei, die eine Prüfung nicht besteht, wird nie ausgeliefert und nie gespeichert.
  • Spätere Pulls werden für jedes Mitglied deiner Organisation aus dem Speicher von fremforge in der EU bedient.
  • Ist der Upstream nicht erreichbar, wird die letzte gute Kopie ausgeliefert. Tags, npm-Packuments, PyPI-Indizes, RPM-repomd.xml, Maven-maven-metadata.xml, NuGet-Versionslisten und -Registrierungen, Go-Versionslisten und @latest, Helm-index.yaml-Dateien und Cargo-Indexdateien werden aus der letzten guten Kopie beantwortet, mit dem Response-Header X-Fremforge-Proxy: stale. Siehe Upstream-Ausfälle.
  • Die Paketrichtlinie deiner Organisation und die Prüfungen auf bösartige Pakete werden für Pakete und Images ausgewertet, die über den Proxy kommen, und jeder Pull wird in deinem Registry-Zugriffsprotokoll erfasst. Seit dem 11.10.2026 wird ein bekanntermaßen bösartiges Paket oder Image für jede Organisation abgewiesen, und eine Richtlinie im Modus Durchsetzen weist ab, was gegen sie verstößt; siehe Was heute durchgesetzt wird.

Der Proxy ist schreibgeschützt. Du kannst nicht in ihn pushen.

Verfügbarkeit und Einschalten

Der Proxy ist in jedem Tarif verfügbar und für jede Organisation standardmäßig eingeschaltet. Ein Owner der Organisation kann ihn unter Build und Deployment › Abhängigkeits-Proxy oder über die API (ein Token mit dem Scope policy:write) ausschalten oder seine Einstellungen ändern:

curl -X PUT -H "Authorization: Bearer $FREMFORGE_TOKEN" -H 'Content-Type: application/json' \
  -d '{"enabled": true, "malware_scan": true}' \
  https://frem.sh/_app/api/v1/orgs/<org>/dependency-proxy

Eine Organisation, die diese Einstellungen nie gespeichert hat, hat den Standard: Der Proxy ist an, und gehostete Runner ziehen über ihn (siehe Gehostete Runner). Sobald ein Owner sie speichert, behält die Organisation genau das Gespeicherte.

Das Malware-Scanning (malware_scan) ist standardmäßig aus. Ist es an, wird jeder Image-Layer, jede Paketdatei und jedes Chart, das deine Organisation über den Proxy zieht, mit ClamAV gescannt, auch wenn eine andere Organisation es zuerst ohne Scan zwischengespeichert hat: Diese Kopie wird einmal gescannt, beim ersten Mal, wenn eine scannende Organisation sie nutzt, und ihr Ergebnis gilt dann für alle. Paketdateien und Charts werden gescannt, bevor sie ausgeliefert werden; für Image-Layer siehe Container-Images. Bekanntermaßen bösartige Pakete (OSV-Advisories MAL-*) werden für jede Organisation abgeglichen, egal was diese Einstellung sagt. Dieselbe Seite und derselbe Endpunkt setzen block_until_scanned für Container-Images, was eingeschaltetes Malware-Scanning voraussetzt (siehe Container-Images), und retention_days (7 bis 365, Standard 90; siehe Aufbewahrung). Ein GET auf den Endpunkt zeigt, ob der Proxy an ist, deine Nutzung und deine Obergrenze.

Authentifizierung

Verwende dieselben Zugangsdaten wie für die Paket-Registry:

  • Personen: ein persönlicher Zugriffstoken mit read:package (oder der kurzlebige Token, den fremforge auth deinen Tools gibt). Du musst Mitglied der Organisation sein.
  • CI: der Job-Token von Forgejo Actions. Ein Job-Token zieht nur über die Organisation seines eigenen Repositorys; der Proxy jeder anderen Organisation weist ihn ab. Jobs aus Fork-Pull-Requests dürfen ziehen, aber nie über einen Upstream, der die eigenen Zugangsdaten deiner Organisation verwendet.
  • Externe CI, Build-Hosts und Pull-Secrets: ein Registry-Robot mit Lesezugriff. Sein Token ist ein read:package-Zugang wie jeder andere.
  • Der Abhängigkeits-Scan von fremforge: Die Erreichbarkeitsanalyse liest npm-Tarballs, PyPI-Wheels und Maven-JARs über den Proxy deiner Organisation, auf einer internen Route, die nur der Scan-Dienst erreicht. Die Lizenzprüfung, die Prüfung des Release-Alters und der Modulgraph für Go lesen Registry-Metadaten (npm, PyPI, Go, Maven Central, RubyGems und Packagist) auf dieselbe Weise. Diese Route ist schreibgeschützt, lässt sich von außerhalb von fremforge nicht nutzen und unterliegt deiner Paketrichtlinie, deinem Cache und deiner Speicherobergrenze wie jeder andere Pull.

Gehostete Runner

Standardmäßig installieren Jobs auf von fremforge gehosteten Runnern über den Proxy deiner Organisation (runner_pull_through, standardmäßig an). Bei jedem Jobstart richtet der Runner npm, pip und uv auf den Proxy aus, und zusätzlich Go, Maven, NuGet und die Schwachstellendatenbank von Trivy für die Ökosysteme, die in runner_pull_through_ecosystems aufgeführt sind (standardmäßig alle vier). Der Job authentifiziert sich mit seinem eigenen Job-Token, sodass jede Installation deine Paketrichtlinie und die Malware-Prüfungen durchläuft und mit dem Repository des Jobs in deinem Zugriffsprotokoll steht. Ist der Proxy beim Jobstart nicht erreichbar, installiert der Job stattdessen über den Egress-Proxy von fremforge aus den öffentlichen Registrys, und der Fallback wird protokolliert. Setze runner_pull_through auf false, um das abzuschalten. Siehe auch Paket-Caches.

Container-Images. Für dieselben Jobs zieht auch der Docker-Daemon des Jobs Container-Images über den Proxy deiner Organisation: docker pull, docker run, das FROM eines Dockerfiles sowie die Job- und services:-Container, die der Runner startet. Am Workflow ändert sich nichts; die Image-Namen bleiben, wie sie sind (ghcr.io/..., quay.io/...), und jeder Pull wird für deine Organisation zwischengespeichert und in deinem Zugriffsprotokoll erfasst.

  • Für jede Organisation: ghcr.io, quay.io, mcr.microsoft.com, registry.k8s.io, public.ecr.aws und registry.access.redhat.com.
  • Nur mit den eigenen Zugangsdaten deiner Organisation: docker.io, registry.redhat.io und registry.connect.redhat.com laufen erst über den Proxy, wenn ein Owner unter Upstream-Zugangsdaten die Zugangsdaten deiner Organisation für diese Registry hinterlegt hat. Jobs aus Fork-Pull-Requests verwenden sie nie, daher gehen ihre Pulls aus diesen Registrys direkt an die Registry. Ohne Zugangsdaten sind Pulls von Docker Hub anonym und unterliegen den Rate-Limits von Docker Hub.
  • Fallback: Der Proxy wird zu Beginn jedes Jobs geprüft. Ist er nicht erreichbar, zieht der Job wie bisher jedes Image direkt aus der Registry, und der Fallback wird protokolliert. Ein einzelner Pull, den der Proxy nicht bedienen kann, fällt ebenfalls auf die Registry zurück.
  • Ein Cache, keine Sperre: Für Images auf diesem Weg fällt auch ein Pull, den deine Paketrichtlinie oder die Malware-Prüfung abweist, auf die Registry zurück. Wo eine Abweisung den Pull stoppen muss, ziehe explizit von frem.sh/<org>/dependency-proxy/<registry>/<image> (siehe unten).
  • Nicht abgedeckt: Ein docker buildx-Builder mit dem Treiber docker-container betreibt sein eigenes BuildKit und zieht direkt. Der Standardtreiber docker läuft über den Proxy.

Container-Images

docker login frem.sh            # a token with read:package, as for the registry
docker pull frem.sh/<org>/dependency-proxy/<upstream>/<image>:<tag>

Zum Beispiel:

docker pull frem.sh/acme/dependency-proxy/quay.io/prometheus/busybox:latest

In CI:

echo "$GITHUB_TOKEN" | docker login frem.sh -u x --password-stdin

Die Upstreams, aus denen du ziehen kannst:

UpstreamZugangsdatenStatus
docker.ioDie eigenen Docker-Hub-Zugangsdaten deiner Organisation, erforderlich (siehe unten)Durchgängig verifiziert
registry.k8s.io, mcr.microsoft.com, public.ecr.aws, registry.access.redhat.comKeineDurchgängig verifiziert
ghcr.io, quay.ioKeine für öffentliche Images. Die eigenen Zugangsdaten deiner Organisation, optional, für private Images (siehe unten)Öffentliche Images: durchgängig verifiziert. Private Images mit deinen Zugangsdaten: unterstützt, noch nicht verifiziert
registry.redhat.io, registry.connect.redhat.comDas Servicekonto deiner Organisation für die Red-Hat-Registry, erforderlichUnterstützt, noch nicht verifiziert: Dafür ist ein Red-Hat-Konto nötig, das fremforge zum Testen nicht hat

Jeder andere Host wird abgewiesen: Der Proxy holt nie eine frei gewählte URL.

Der Proxy listet die Tags eines Images nicht auf. Das gehostete Renovate schlägt trotzdem Updates für ein Image vor, das du über diesen Pfad referenzierst, etwa in einem Dockerfile FROM frem.sh/<org>/dependency-proxy/docker.io/library/python:3.14-slim. Es schlägt neue Tags und Digests in der Upstream-Registry nach und schreibt sie in deine Referenz, die den Proxy-Pfad behält. Deine Builds ziehen weiter über den Proxy, sodass die unten beschriebenen Pull-Sperren weiter greifen.

Helm-Charts, die als OCI-Artefakte auf diesen Upstreams liegen, kommen über denselben Pfad (Helm 3.8 oder neuer):

echo "$FREMFORGE_TOKEN" | helm registry login frem.sh -u <user or robot name> --password-stdin
helm pull oci://frem.sh/<org>/dependency-proxy/ghcr.io/prometheus-community/charts/prometheus --version 29.36.1

Für Charts in einem klassischen Helm-Repository (index.yaml) siehe Helm-Charts.

Ein Tag wird höchstens einmal pro Minute mit einem HEAD-Request beim Upstream neu geprüft, den Docker Hub nicht auf sein Pull-Limit anrechnet. Image-Layer durchlaufen dieselben Pull-Sperren wie Images, die in deine Registry gepusht werden: Mit eingeschaltetem Malware-Scanning werden infizierte oder nicht scanbare Layer abgewiesen, ebenso Images über deiner CVE-Schwelle, wenn du block_pull gesetzt hast. Bösartige Pakete in einem Image sind weiter unten beschrieben. Standardmäßig wird ein Image ausgeliefert, während seine Layer gescannt werden. Mit block_until_scanned beantwortet der erste Pull eines noch nicht gescannten Images oder eines Layers per Digest die Anfrage mit 503 und Retry-After, bis der Scan fertig ist, meist innerhalb von Sekunden. Ein Layer, den irgendein Scan als infiziert erkannt hat, wird jeder Organisation verweigert.

Während eine Anfrage einen Layer holt und scannt, warten andere Pulls desselben Layers darauf. Dauert das länger als etwa vier Minuten, erhalten sie 503 mit Retry-After: 30 (this layer is still being fetched and malware-scanned by another request), und ein erneuter Versuch wird bedient, sobald der Layer gespeichert ist. Ein Scan, dem die Zeit ausgeht, ist kein Ergebnis: Der Layer wird wie ein nicht scanbarer zurückgehalten, aber es wird kein Ergebnis erfasst. Er wird in die Warteschlange des Layer-Scanners von fremforge im Hintergrund gestellt, der ein größeres Zeitbudget hat, und ein späterer Pull wird bedient, sobald dieser Scan bestanden ist.

Eine Paketdatei, deren Scan die Zeit ausgeht (npm, PyPI, RPM, Maven, NuGet, Go, Cargo oder ein Helm-Chart), wird genauso behandelt: Diese Anfrage wird abgewiesen, es wird kein Ergebnis erfasst, und die Datei wird in die Warteschlange desselben Hintergrund-Scanners gestellt. Eine spätere Anfrage wird bedient, sobald dieser Scan bestanden ist, oder abgewiesen, wenn er die Datei als infiziert erkennt. Ein RPM bleibt währenddessen allein deiner Organisation zugeordnet: Es wird nur im eigenen Bereich deiner Organisation gespeichert und gescannt, und eine andere Organisation, die dasselbe RPM abruft, holt und scannt ihre eigene Kopie.

Größengrenzen. Der Proxy liefert keinen Layer und keine Datei über 10 GiB aus (403). ClamAV liest höchstens 4 GiB einer Datei, daher kann ein größerer Layer oder eine größere Datei bei eingeschaltetem Malware-Scanning nicht vollständig gescannt werden und wird zurückgehalten, außer einem Safetensors- oder GGUF-Modell-Layer, der seine Formatprüfung besteht (siehe Modell-Scanning).

Bösartige Pakete in zwischengespeicherten Images

Die Sprachpakete in jedem Image, das deine Organisation zwischenspeichert (npm-, PyPI-, Maven-, Go-, Crates-, RubyGems-, NuGet-, Packagist-, Pub- und Hex-Pakete), werden mit den OSV-Advisories zu bösartigen Paketen (MAL-*) abgeglichen, derselben Prüfung, die für deine gepushten Images läuft:

  • nachdem das Image erstmals zwischengespeichert wurde, meist im nächsten Scan-Zyklus (etwa 15 Minuten), und
  • erneut jeden Tag, sodass auch ein Advisory gefunden wird, das nach deinem Pull des Images veröffentlicht wurde.

Ein Treffer ist ein kritischer Befund unter Sicherheit › Befunde › Abhängigkeiten › Malware, aufgeführt als Image namens <org>/dependency-proxy/<upstream>/<repository> mit dem Digest, der ihn enthält, und er wird an die Personen gesendet, die deine Sicherheitswarnungen erhalten. Das Verwerfen funktioniert wie bei jedem Malware-Befund, mit einer Begründung und einem optionalen Enddatum. Der Befund schließt sich von selbst, sobald deine Organisation das Image nicht mehr zwischenspeichert, zum Beispiel nachdem die Aufbewahrungsregel es entfernt hat.

Ein Pull eines Images mit einem offenen, nicht verworfenen Befund wird wie bei gepushten Images mit 403 DENIED abgewiesen und in deinem Zugriffsprotokoll als blocked mit dem Grund image_malware erfasst. Abgewiesen wird seit dem 11.10.2026; davor wurde ein solcher Pull ausgeliefert und als would_block erfasst.

Upstream-Zugangsdaten

Docker Hub (docker.io) und die Red-Hat-Registrys (registry.redhat.io und registry.connect.redhat.com, die zertifizierten Partner-Images von Red Hat) werden über den Abhängigkeits-Proxy nur mit dem eigenen Konto deiner Organisation gezogen. Für GitHub (ghcr.io) und Quay (quay.io) sind Zugangsdaten optional: Ohne sie wird anonym gezogen, was öffentliche Images liefert; hinterlege welche, um deine privaten Images zu ziehen. Ein Owner der Organisation hinterlegt die Zugangsdaten unter Build und Deployment › Abhängigkeits-Proxy › Upstream-Zugangsdaten oder über die API unter /_app/api/v1/orgs/<org>/dependency-proxy/upstreams. Akzeptierte Formen:

  • Docker Hub: dein Benutzername plus ein persönlicher Zugriffstoken, oder ein Organisations-Zugriffstoken mit dem Namen der Docker-Organisation als Benutzername.
  • Red Hat: ein Registry-Servicekonto (ein Benutzername wie 1234567|ci plus sein Token) oder sein OpenShift-Pull-Secret. Hinterlege es einmal pro Red-Hat-Registry, aus der du ziehst; jede behält nur ihren eigenen Eintrag aus dem Pull-Secret.
  • GitHub (ghcr.io): dein GitHub-Benutzername plus ein persönlicher Zugriffstoken (classic) mit read:packages.
  • Quay (quay.io): ein Robot-Konto (organisation+robot) plus sein Token, oder der quay.io-Eintrag eines Red-Hat-Pull-Secrets.

Die Red-Hat-Registrys mit deinen Zugangsdaten sowie private ghcr.io- und quay.io-Images werden unterstützt, sind aber noch nicht durchgängig verifiziert: fremforge hat sie gegen den Token-Ablauf der Registrys getestet, hat aber kein Konto, mit dem es ziehen könnte.

Sobald Zugangsdaten für ghcr.io oder quay.io gespeichert sind, verwendet jeder Pull aus dieser Registry sie, auch für öffentliche Images, und wird nur für deine Organisation zwischengespeichert. Jobs aus Fork-Pull-Requests verwenden sie nie: Sie ziehen aus diesen beiden Registrys anonym und erhalten daher nur öffentliche Images.

Das Secret wird verschlüsselt gespeichert und nur vom Abhängigkeits-Proxy für die Pulls deiner eigenen Organisation verwendet. Es wird nie wieder angezeigt; stattdessen siehst du einen Fingerabdruck. Verbindung testen prüft es und zeigt für Docker Hub, wie viele Pulls in deinem aktuellen Limit-Zeitfenster noch übrig sind.

Images, die mit deinen Zugangsdaten gezogen werden, werden nur für deine Organisation zwischengespeichert und nie an eine andere Organisation ausgeliefert. Jobs aus Fork-Pull-Requests können nicht über eine Registry ziehen, die deine Zugangsdaten erfordert. Laufen die Zugangsdaten ab, werden bereits zwischengespeicherte Images weiter ausgeliefert, und der Status zeigt Von der Registry abgelehnt. Ohne Zugangsdaten wird ein Pull von Docker Hub mit einer Meldung abgewiesen, die genau das sagt.

Kubernetes und OpenShift

Richte einen Mirror auf den Proxy aus. containerd, in /etc/containerd/certs.d/quay.io/hosts.toml:

server = "https://quay.io"

[host."https://frem.sh/v2/<org>/dependency-proxy/quay.io"]
  capabilities = ["pull", "resolve"]
  override_path = true

OpenShift, mit einem ImageTagMirrorSet (und einem ImageDigestMirrorSet für Digests):

apiVersion: config.openshift.io/v1
kind: ImageTagMirrorSet
metadata:
  name: frem-sh-dependency-proxy
spec:
  imageTagMirrors:
    - source: quay.io
      mirrors:
        - frem.sh/<org>/dependency-proxy/quay.io

Das Pull-Secret des Clusters braucht einen frem.sh-Token mit read:package.

npm

.npmrc in deinem Projekt oder Home-Verzeichnis:

registry=https://frem.sh/_app/proxy/<org>/npm/
//frem.sh/_app/proxy/<org>/npm/:_authToken=${FREMFORGE_TOKEN}

Verwende dann npm, pnpm oder yarn wie gewohnt. Pakete kommen von registry.npmjs.org, Pakete mit Scope eingeschlossen. Jeder Tarball muss zur integrity (sha512) passen, die die npm-Registry für ihn veröffentlicht, bevor er ausgeliefert wird.

npm audit und pnpm audit funktionieren gegen den Proxy. Sie werden aus der eigenen Kopie der OSV-Advisory-Datenbank von fremforge beantwortet, sodass deine Abhängigkeitsliste an keinen Advisory-Dienst eines Dritten gesendet wird. Unterstützte Clients und das Antwortformat sind unter npm audit und pnpm audit über den Abhängigkeits-Proxy beschrieben.

Um den Proxy mit Paketen zu kombinieren, die deine Organisation in ihrer eigenen Registry veröffentlicht, leite deinen Scope an die Registry und alles andere an den Proxy:

registry=https://frem.sh/_app/proxy/<org>/npm/
@<your-scope>:registry=https://frem.sh/api/packages/<org>/npm/
//frem.sh/_app/proxy/<org>/npm/:_authToken=${FREMFORGE_TOKEN}
//frem.sh/api/packages/<org>/npm/:_authToken=${FREMFORGE_TOKEN}

PyPI

pip install --index-url "https://<user>:${FREMFORGE_TOKEN}@frem.sh/_app/proxy/<org>/pypi/simple/" requests
uv pip install --index-url "https://<user>:${FREMFORGE_TOKEN}@frem.sh/_app/proxy/<org>/pypi/simple/" requests

Oder in pip.conf / uv.toml, mit den Zugangsdaten in einer netrc-Datei oder einem Keyring statt in der URL. Der Index wird in beiden Formaten ausgeliefert, die Installer verwenden (PEP 503 HTML und PEP 691 JSON), mit dem sha256 jeder Datei, requires-python, dem Yank-Status und den Metadaten nach PEP 658. Jede Datei muss zu dem sha256 passen, den PyPI für sie listet, bevor sie ausgeliefert wird.

Verwende den Proxy als deinen einzigen Index. Füge ihn nicht mit --extra-index-url (oder PIP_EXTRA_INDEX_URL, UV_EXTRA_INDEX_URL) neben einem anderen Index hinzu, und auch keinen anderen Index neben ihm: Gemischte Indizes ermöglichen erst Dependency-Confusion-Angriffe, weil der Installer einen Namen aus dem Index nimmt, der ihn gerade anbietet. Um Pakete zu installieren, die deine Organisation in ihrer eigenen Registry veröffentlicht, führe eine separate Installation mit dieser Registry als einzigem Index und --no-deps aus, oder pinne mit uv jedes dieser Pakete per Name auf sie (explicit = true und [tool.uv.sources]).

RPM (dnf und yum)

Jedes Repository ist erreichbar unter

https://frem.sh/_app/proxy/<org>/rpm/<upstream>/<repository path>

Heute ist der Upstream cdn-ubi.redhat.com, die Universal-Base-Image-Repositorys von Red Hat (UBI 8, 9 und 10: BaseOS, AppStream und CodeReady Builder, für jede Architektur, die Red Hat veröffentlicht), unter content/public/ubi/. Sie benötigen kein Red-Hat-Konto. Eine .repo-Datei für UBI 9 auf x86_64:

[ubi-9-baseos-fremforge]
name=UBI 9 BaseOS through fremforge
baseurl=https://frem.sh/_app/proxy/<org>/rpm/cdn-ubi.redhat.com/content/public/ubi/dist/ubi9/9/x86_64/baseos/os
username=<user or robot name>
password=<token with read:package>
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release

[ubi-9-appstream-fremforge]
name=UBI 9 AppStream through fremforge
baseurl=https://frem.sh/_app/proxy/<org>/rpm/cdn-ubi.redhat.com/content/public/ubi/dist/ubi9/9/x86_64/appstream/os
username=<user or robot name>
password=<token with read:package>
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release

dnf sendet username und password als HTTP-Basic-Authentifizierung. Verwende einen Registry-Robot für Build-Hosts und Image-Builds, und mach die Datei nur für root lesbar (chmod 600), weil sie den Token enthält. Deaktiviere in einem Container-Build für die Installation die eigenen Repositorys des Images (dnf --disablerepo='*' --enablerepo='*-fremforge' install …), damit nichts am Proxy vorbei geholt wird.

Was der Proxy tut:

  • repodata/repomd.xml wird höchstens alle 5 Minuten beim Upstream neu geprüft und genau so ausgeliefert, wie der Upstream sie veröffentlicht hat. Eine neue repomd.xml wird erst akzeptiert, wenn die darin genannte primary.xml heruntergeladen wurde und zu sha256 und Größe passt, die repomd.xml für sie angibt. Bis dahin, und solange der Upstream nicht erreichbar ist, wird die vorherige ausgeliefert.
  • Repository-Metadatendateien (primary, filelists, other, updateinfo, modules, comps und die sqlite-Varianten) müssen zu sha256 und Größe in repomd.xml passen.
  • Pakete müssen zu sha256 und Größe in primary.xml passen. Mit eingeschaltetem Malware-Scanning wird jedes Paket außerdem innerhalb seiner komprimierten Nutzlast gescannt, nicht nur als Datei, und auch seine Installationsskripte werden gescannt. Ein Paket, das der Scanner nicht öffnen kann, wird dann abgewiesen, nicht ungescannt ausgeliefert.
  • Paketsignaturen prüft dnf selbst (gpgcheck=1), gegen den Schlüssel von Red Hat, genau wie ohne Proxy. Der Proxy verändert nie ein Paket oder die Repository-Metadaten.
  • RPM-Inhalte werden nur für deine Organisation zwischengespeichert. Eine andere Organisation, die dasselbe Paket zieht, holt und speichert ihre eigene Kopie. Das ist so, damit derselbe Codepfad später Red-Hat-Inhalte ausliefern kann, die mit deinem eigenen Red-Hat-Abonnement geholt wurden und nie einen anderen Kunden erreichen dürfen.

Ein Repository, das Prüfsummen mit etwas anderem als sha256 bildet, wird nicht ausgeliefert. Das Proxying von cdn.redhat.com mit deinem Red-Hat-Berechtigungszertifikat ist geplant und noch nicht verfügbar.

Maven

Maven Central ist erreichbar unter

https://frem.sh/_app/proxy/<org>/maven/central/

Mach ihn in ~/.m2/settings.xml (oder der settings.xml, die dein Build verwendet) zum Mirror für jedes Repository, mit den Zugangsdaten in einem <server> mit derselben id:

<settings>
  <servers>
    <server>
      <id>fremforge-proxy</id>
      <username><user or robot name></username>
      <password>${env.FREMFORGE_TOKEN}</password>
    </server>
  </servers>
  <mirrors>
    <mirror>
      <id>fremforge-proxy</id>
      <name>Maven Central through fremforge</name>
      <url>https://frem.sh/_app/proxy/<org>/maven/central/</url>
      <mirrorOf>*</mirrorOf>
    </mirror>
  </mirrors>
</settings>

Maven sendet Benutzername und Passwort als HTTP-Basic-Authentifizierung, wenn der Proxy danach fragt. Um den Proxy zusammen mit Paketen zu verwenden, die deine Organisation in ihrer eigenen Registry veröffentlicht, schreib <mirrorOf>*,!fremforge</mirrorOf> und deklariere deine Registry (https://frem.sh/api/packages/<org>/maven) als Repository fremforge mit einem eigenen <server>.

Gradle (settings.gradle.kts):

dependencyResolutionManagement {
    repositories {
        maven {
            url = uri("https://frem.sh/_app/proxy/<org>/maven/central/")
            credentials {
                username = providers.environmentVariable("FREMFORGE_USER").get()
                password = providers.environmentVariable("FREMFORGE_TOKEN").get()
            }
        }
    }
}

Was der Proxy tut:

  • maven-metadata.xml wird höchstens alle 5 Minuten bei Maven Central neu geprüft. Eine neue Kopie wird nur akzeptiert, wenn sie zu Maven Centrals eigener .sha1 davon passt. Bis dahin, und solange Maven Central nicht erreichbar ist, wird die vorherige Kopie ausgeliefert. Ihre .sha1, .md5, .sha256 und .sha512 werden aus der Kopie berechnet, die du erhältst, sodass sie immer zu ihr passen.
  • Artefakte (JARs, POMs, .module-Dateien und jede andere Datei eines Releases) müssen zur .sha1 von Maven Central passen und, wenn Maven Central eine veröffentlicht, zu ihrer .sha256. Eine Datei ohne beide Prüfsummen wird nicht geholt. Mit eingeschaltetem Malware-Scanning wird jede Datei gescannt, während sie zu dir gestreamt wird. Ein JAR wird außerdem geöffnet: Ein Eintrag, den der Scanner nicht selbst entpacken kann (zstd), wird für ihn entpackt, und ein JAR mit einem Eintrag, den keiner von beiden öffnen kann (zum Beispiel LZMA), wird abgewiesen, statt ungescannt ausgeliefert zu werden.
  • Last-Modified ist der Zeitpunkt, zu dem Maven Central die Datei veröffentlicht hat, auch wenn sie aus dem Cache kommt, sodass das Mindest-Release-Alter von Renovate und andere Tools, die es lesen, den echten Release-Zeitpunkt sehen. Eine Datei, die Maven Central ohne diesen Header gesendet hat, erhält gar kein Last-Modified, nie den Zeitpunkt, zu dem fremforge sie zwischengespeichert hat. Ein HEAD auf eine zwischengespeicherte Datei antwortet mit 200, ihrer Content-Length und diesem Last-Modified.
  • Prüfsummendateien (.sha1, .sha256, …) und Signaturen (.asc) von Artefakten werden unverändert durchgereicht. Prüfe Signaturen mit dem PGP-Plugin deines Builds, wie du es ohne Proxy tun würdest.
  • Snapshots werden nicht ausgeliefert. Maven Central veröffentlicht keine, und ein Snapshot kann sich unter derselben Version ändern. Eine -SNAPSHOT-Anfrage wird mit 404 beantwortet, sodass dein Build zum Repository weitergeht, das ihn hat.

Für die Paketrichtlinie heißt ein Maven-Paket groupId:artifactId. Sein Release-Zeitpunkt ist der Zeitpunkt, zu dem Maven Central die Datei veröffentlicht hat, und seine Lizenz sind die <licenses>, die sein POM deklariert. Eine von einem Parent-POM geerbte Lizenz wird nicht nachgeschlagen und gilt daher als nicht bewertet.

NuGet

Die Paketquelle ist der Service-Index des Proxys:

https://frem.sh/_app/proxy/<org>/nuget/v3/index.json

nuget.config neben deiner Solution:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <clear />
    <add key="fremforge" value="https://frem.sh/_app/proxy/<org>/nuget/v3/index.json" />
  </packageSources>
  <packageSourceCredentials>
    <fremforge>
      <add key="Username" value="<user or robot name>" />
      <add key="ClearTextPassword" value="%FREMFORGE_TOKEN%" />
    </fremforge>
  </packageSourceCredentials>
</configuration>

<clear /> macht den Proxy zur einzigen Quelle, sodass kein Paket an ihm vorbei geholt wird. NuGet expandiert %FREMFORGE_TOKEN% aus der Umgebung, sodass der Token nicht in der Datei stehen muss. Verwende dann dotnet restore, dotnet add package <id> --version <version> oder nuget install wie gewohnt.

Was der Proxy tut:

  • Der Service-Index listet nur Ressourcen auf frem.sh: den Paketinhalt (Flat Container) und die Paketregistrierung. Suche wird nicht angeboten, weil Restore und Installation sie nicht brauchen. dotnet add package ohne --version braucht die Suche, gib also die Version an.
  • Versionslisten und Registrierung werden höchstens alle 5 Minuten bei nuget.org neu geprüft, und die letzte gute Kopie wird ausgeliefert, solange nuget.org nicht erreichbar ist.
  • Pakete (.nupkg) müssen zu SHA-512 und Größe passen, die der Katalog von nuget.org für das Release erfasst, bevor sie ausgeliefert werden. Ein Release, dessen Katalogeintrag keinen Hash enthält, wird nicht geholt. Mit eingeschaltetem Malware-Scanning wird jedes Paket gescannt, während es zu dir gestreamt wird, sowohl innerhalb des Pakets als auch als Datei.
  • Paketsignaturen prüft der NuGet-Client selbst, genau wie ohne Proxy. Der Proxy verändert nie ein Paket.

Für die Paketrichtlinie wird ein NuGet-Paket über seine ID in Kleinbuchstaben benannt. Sein Release-Zeitpunkt und seine Lizenz sind das published-Datum von nuget.org und der deklarierte Lizenzausdruck des Pakets. Ein Paket, das seine Lizenz als Datei oder URL deklariert, gilt als nicht bewertet.

Go-Module

export GOPROXY=https://frem.sh/_app/proxy/<org>/go
export GOSUMDB=sum.golang.org      # the default; keep it on

Die Authentifizierung kommt aus ~/.netrc (_netrc unter Windows), die der go-Befehl für frem.sh liest. Mach die Datei nur für dich lesbar (chmod 600 ~/.netrc):

machine frem.sh
login <user or robot name>
password <token with read:package>

Verwende GOPROXY=https://frem.sh/_app/proxy/<org>/go, ohne ,direct anzuhängen: Mit direct wird ein Modul, das der Proxy nicht ausliefert, am Proxy vorbei aus seinem Quell-Repository geholt.

Was der Proxy tut:

  • @v/list und @latest werden höchstens alle 5 Minuten bei proxy.golang.org neu geprüft, und die letzte gute Kopie wird ausgeliefert, solange es nicht erreichbar ist.
  • .mod und .zip müssen zu den h1:-Hashes passen, die die Go-Prüfsummendatenbank (sum.golang.org) für diese Modulversion signiert hat. Der Proxy verifiziert die Signatur der Datenbank mit demselben Schlüssel, der im go-Befehl eingebaut ist. Ein Modul-Zip wird entpackt und so gehasht, wie der go-Befehl es hasht, bevor es ausgeliefert wird. Ein Modul, das die Prüfsummendatenbank nicht kennt, wird nicht ausgeliefert. Mit eingeschaltetem Malware-Scanning wird jedes Zip gescannt, während es zu dir gestreamt wird.
  • Auch die Prüfsummendatenbank wird über den Proxy erreicht. Der go-Befehl fragt den Proxy, ob er sum.golang.org bedient, und holt dann jede Abfrage und jede Kachel über ihn. So bleibt GOSUMDB an und führt weiterhin seine eigene vollständige Verifikation durch, einschließlich der Beweise, die der Proxy selbst nicht wiederholt, und dein Rechner braucht keinen direkten Zugriff auf sum.golang.org.
  • Private Module werden nicht ausgeliefert: proxy.golang.org kann sie nicht holen, und die Prüfsummendatenbank kennt sie nicht. Schließe sie mit GOPRIVATE (oder GONOPROXY und GONOSUMDB) aus, dann holt der go-Befehl sie wie bisher von deinem eigenen Git-Hosting.
  • Module, die auf frem.sh gehostet sind, werden nie bei Google angefragt. Eine Anfrage nach einem Modul unter frem.sh (oder einer Subdomain) wird mit 404 abgewiesen, bevor der Proxy irgendwen kontaktiert, weil schon die Anfrage proxy.golang.org und sum.golang.org den Pfad des Moduls verraten würde. Nimm frem.sh in GOPRIVATE auf, damit der go-Befehl diese Module direkt von frem.sh holt.

Was bei öffentlichen Modulen bei Google ankommt

Der Proxy holt öffentliche Go-Module aus Googles Modul-Mirror (proxy.golang.org) und prüft sie gegen Googles Prüfsummendatenbank (sum.golang.org). Ist ein Modul oder eine Version noch nicht zwischengespeichert, erhält Google daher:

  • den Modulpfad und die Version, die geholt werden (zum Beispiel github.com/gorilla/websocket@v1.5.0), und die zugehörigen Abfragen an die Prüfsummendatenbank;
  • Anfragen von den Egress-Adressen von fremforge, nicht von deinen Rechnern, ohne den Namen deiner Organisation, Benutzer, Token oder Repository.

Google erhält weder deinen Code noch deine go.mod, die Namen deiner Repositorys oder welche Organisation gefragt hat. Ein bereits zwischengespeichertes Modul wird ausgeliefert, ohne Google zu kontaktieren. Dasselbe gilt für die Sicherheitsscanner: Sie lesen öffentliche Go-Module über diesen Proxy und fragen nie nach einem Modul, das nach den obigen Regeln privat ist.

Für die Paketrichtlinie wird ein Go-Modul über seinen Modulpfad benannt. Sein Release-Zeitpunkt ist der Zeitpunkt, den proxy.golang.org für die Version erfasst. Go-Module deklarieren keine Lizenz, die der Proxy lesen kann, daher gelten Lizenzregeln für sie nicht.

Helm-Charts

Klassische Helm-Repositorys (eine index.yaml und Chart-Archive) werden aus einer festen Liste ausgeliefert:

Repository-NameUpstream
prometheus-communityhttps://prometheus-community.github.io/helm-charts
grafanahttps://grafana.github.io/helm-charts
jetstackhttps://charts.jetstack.io (cert-manager)
ingress-nginxhttps://kubernetes.github.io/ingress-nginx

Füge ein Repository mit der URL des Proxys und Zugangsdaten mit read:package hinzu:

helm repo add prometheus-community https://frem.sh/_app/proxy/<org>/helm/prometheus-community \
  --username <user or robot name> --password-stdin <<< "$FREMFORGE_TOKEN"
helm search repo prometheus-community/prometheus
helm pull prometheus-community/prometheus --version 29.36.1
helm install my-prometheus prometheus-community/prometheus --version 29.36.1

Jede Chart-URL im Index zeigt zurück auf frem.sh, sodass Helm deine Zugangsdaten bei jedem Chart-Download mitsendet und --pass-credentials nicht nötig ist.

Was der Proxy tut:

  • index.yaml wird höchstens alle 5 Minuten beim Repository neu geprüft, und die letzte gute Kopie wird ausgeliefert, solange das Repository nicht erreichbar ist oder etwas antwortet, das kein Helm-Index ist. Der Proxy liefert sie als JSON aus, das Helm als YAML liest.
  • Charts müssen zum digest (sha256) passen, den der Index des Repositorys für diese Version erfasst, bevor sie ausgeliefert werden. Eine Version, deren Indexeintrag keinen Digest hat oder deren Chart nicht auf den eigenen Hosts des Repositorys liegt (zum Beispiel eine oci://-URL), fehlt in dem Index, den du erhältst. Mit eingeschaltetem Malware-Scanning wird jedes Chart gescannt, während es zu dir gestreamt wird.
  • Provenienzdateien (.prov) werden unverändert durchgereicht, sodass helm pull --verify und helm install --verify mit dem öffentlichen Schlüssel des Chart-Herausgebers funktionieren, zum Beispiel für cert-manager: helm pull jetstack/cert-manager --version v1.21.2 --verify --keyring cert-manager-keyring.gpg. Eine Provenienzdatei muss das Chart mit dem Digest aus dem Index nennen, sonst wird sie nicht ausgeliefert. Von den vier Repositorys veröffentlichen jetstack und prometheus-community Provenienzdateien; für ein Chart ohne Provenienzdatei schlägt --verify mit failed to fetch provenance (404) fehl.
  • Die Abhängigkeiten eines Charts löst Helm aus den Repositorys auf, die seine Chart.yaml nennt. Ein helm dependency update erreicht diese Repositorys direkt, es sei denn, du fügst sie ebenfalls über den Proxy hinzu.

Das klassische Repository von Bitnami steht nicht auf der Liste: Seine aktuellen Chart-Versionen sind OCI-Artefakte auf Docker Hub. Ziehe sie über den Container-Pfad, oci://frem.sh/<org>/dependency-proxy/docker.io/bitnamicharts/<chart>, der die eigenen Docker-Hub-Zugangsdaten deiner Organisation verwendet (siehe oben).

Für die Paketrichtlinie wird ein Chart über seinen Chart-Namen benannt. Sein Release-Zeitpunkt ist das created-Datum im Index und seine Lizenz die Annotation artifacthub.io/license, sofern das Chart eine hat.

Cargo (crates.io)

Der Proxy ist eine Sparse Registry für crates.io. Er setzt Cargo 1.74 oder neuer voraus. Ersetze crates-io damit in .cargo/config.toml, in deinem Projekt oder in ~/.cargo/config.toml für alle Projekte:

[source.crates-io]
replace-with = "fremforge"

[registries.fremforge]
index = "sparse+https://frem.sh/_app/proxy/<org>/cargo/index/"
credential-provider = "cargo:token"

Cargo sendet den Token als vollständigen Authorization-Header, gib ihm also das Präfix Bearer. Halte ihn aus der Datei heraus, in der Umgebung:

export CARGO_REGISTRIES_FREMFORGE_TOKEN="Bearer $FREMFORGE_TOKEN"
cargo fetch
cargo build --locked

Mit dem Source-Replacement kommt jedes Crate von crates.io über den Proxy, einschließlich der Crates, die eine vorhandene Cargo.lock pinnt, und kein Crate wird am Proxy vorbei geholt.

Was der Proxy tut:

  • config.json ist die eigene des Proxys. Sie leitet Crate-Downloads zurück auf den Pfad deiner Organisation auf frem.sh und teilt Cargo mit, dass die Registry Authentifizierung verlangt, sodass Cargo den Token bei jeder Index- und Download-Anfrage mitsendet.
  • Indexdateien kommen von index.crates.io. Sie werden höchstens alle 5 Minuten neu geprüft, und die letzte gute Kopie wird ausgeliefert, solange crates.io nicht erreichbar ist. Jede Datei ist die eigene von crates.io, Version für Version. Gezogene (yanked) Versionen bleiben darin, als yanked markiert: Cargo verwendet weiterhin eine, die deine Cargo.lock pinnt, wählt aber für eine neue Auflösung keine aus.
  • Crates (.crate) kommen von static.crates.io und müssen zur cksum (sha256) in ihrem Indexeintrag passen, bevor sie ausgeliefert werden. Eine Version, deren Indexeintrag keine Prüfsumme hat, wird weggelassen. Mit eingeschaltetem Malware-Scanning wird jedes Crate gescannt, während es zu dir gestreamt wird, auch innerhalb des Archivs. Cargo prüft dieselbe Prüfsumme auf seiner Seite noch einmal.
  • Suche, Veröffentlichen, Yanking und Owner werden nicht angeboten: cargo search antwortet mit 404, und cargo publish wird mit 405 abgewiesen. Veröffentliche deine eigenen Crates in der Cargo-Registry deiner Organisation.

Für die Paketrichtlinie wird ein Crate über seinen Namen benannt, wie crates.io ihn veröffentlicht, und sein Release-Zeitpunkt ist die pubtime in seinem Indexeintrag. Der Index enthält keine Lizenz, daher gelten Lizenzregeln für Crates nicht. Schwachstellen und bösartige Crates kommen aus den crates.io-Daten von OSV: den RustSec-Advisories und den MAL-*-Einträgen.

Terraform- und OpenTofu-Module

Der Proxy liefert öffentliche Module aus den Modul-Registrys und aus öffentlichen Git-Repositorys aus, sodass tofu init und terraform init sie aus dem Cache von fremforge statt aus dem Internet holen. Er deckt ab:

  • Registry-Module von registry.opentofu.org und registry.terraform.io, zum Beispiel terraform-aws-modules/vpc/aws oder die Azure Verified Modules (Azure/avm-*).
  • Git-Quellen auf github.com und gitlab.com an einem Tag oder Commit, zum Beispiel git::https://github.com/<owner>/<repo>.git?ref=v1.2.3 oder github.com/<owner>/<repo>?ref=v1.2.3.

Ein Branch wird abgewiesen: Ein zwischengespeichertes Modul muss gleich bleiben, nenne also einen Tag oder einen Commit. Private Repositorys, andere Hosts und SSH-Quellen werden nicht ausgeliefert.

Die IaC-Prüfung liest Module für jede Organisation über diesen Cache, auch für eine, deren Abhängigkeits-Proxy aus ist. Der Modul-Cache-Schlüssel, den deine eigene CI braucht, kann vorerst nur in den internen Organisationen von fremforge erstellt werden: Für jede andere Organisation wird der Abschnitt in der Konsole nicht angezeigt, und die API antwortet mit 404.

Der Modul-Cache-Schlüssel

OpenTofu und Terraform erreichen den Cache über den Modul-Cache-Schlüssel deiner Organisation, der in die URL gehört. Ein Owner der Organisation erstellt ihn unter Build und Deployment › Abhängigkeits-Proxy › Terraform- und OpenTofu-Module. Der Schlüssel wird einmal angezeigt, als fertig einfügbare Konfiguration für OpenTofu und eine für Terraform. Danach zeigt die Seite nur noch seine ersten Zeichen, den Tag seiner letzten Verwendung und eine Schaltfläche Rotieren. Nach einer Rotation funktioniert der vorherige Schlüssel noch 24 Stunden weiter, damit laufende Pipelines fertig werden; danach braucht jede CI-Konfiguration den neuen. Das Erstellen und Rotieren eines Schlüssels wird im Audit-Log erfasst und erreicht dein SIEM wie andere Audit-Ereignisse.

Der Schlüssel kann auch über die API erstellt und rotiert werden, mit einem Token, der policy:write hat: GET /orgs/{org}/dependency-proxy/module-key zeigt sein Präfix und seine Daten (nie den Schlüssel), und POST /orgs/{org}/dependency-proxy/module-key mit {"expect": "<current prefix>"} (oder "", wenn es keinen gibt) liefert den neuen Schlüssel einmal zurück. Ist inzwischen ein anderer Schlüssel aktuell geworden, lautet die Antwort 409 module-key-stale, und nichts ändert sich.

Der Schlüssel erlaubt das Lesen öffentlicher Module als deine Organisation und sonst nichts. Behandle ihn wie ein Passwort: Bewahre ihn in deinen CI-Secrets auf, nicht in einem Repository.

Registry-Module: keine Änderung an deinen Quellen

Richte die Registry von OpenTofu mit einer CLI-Konfigurationsdatei (~/.tofurc oder die Datei, die TF_CLI_CONFIG_FILE nennt) auf den Cache aus. Deine source- und version-Zeilen bleiben, wie sie sind:

host "registry.opentofu.org" {
  services = {
    "modules.v1"   = "https://frem.sh/_app/proxy/<org>/terraform/k/<module cache key>/registry/registry.opentofu.org/v1/modules/"
    "providers.v1" = "https://registry.opentofu.org/v1/providers/"
  }
}
  • Behalte beide Zeilen. Der host-Block ersetzt alles, was OpenTofu über die Registry weiß. Ohne providers.v1 schlägt jede Provider-Installation mit „does not offer a OpenTofu provider registry“ fehl.
  • Füge keinen credentials-Block hinzu. OpenTofu und Terraform ordnen Zugangsdaten dem Hostnamen der Registry zu, daher würde ein Token darin auch an die öffentliche Registry gesendet: mit jeder Provider-Anfrage und auf einem Rechner, auf dem der host-Block fehlt, mit jeder Anfrage. Der Modul-Cache-Schlüssel braucht keinen Credentials-Block.
  • Terraform: Schreib an allen drei Stellen registry.terraform.io statt registry.opentofu.org. Wir haben diese Konfiguration auf OpenTofu 1.13.1 verifiziert, mit einem Schlüssel und mit einem falschen (403 Forbidden). Terraform dokumentiert dasselbe Verhalten bei Host und Zugangsdaten; gemessen haben wir es nicht.

Um zu prüfen, ob der Cache verwendet wird, führe TF_LOG=debug tofu init aus und suche nach Zeilen GET https://frem.sh/_app/proxy/<org>/terraform/... für deine Module.

Git-Quellen

Gib das Archiv des Proxys für den Tag oder Commit als HTTPS-Quelle an. //* wählt das Modul-Wurzelverzeichnis im Archiv, ein Unterverzeichnis folgt darauf:

module "network" {
  source = "https://frem.sh/_app/proxy/<org>/terraform/k/<module cache key>/git/github.com/<owner>/<repo>/v1.2.3.tar.gz//*/modules/network"
}

Außer dem Modul-Cache-Schlüssel in der URL sind keine Zugangsdaten nötig.

Was der Proxy tut

  • Eine Version wird beim ersten Abruf festgepinnt. Der Proxy löst den Tag aus der eigenen Git-Ref-Liste des Repositorys (was git ls-remote liest) zu einem Commit auf, holt das Archiv dieses Commits und erfasst den Commit und den SHA-256 des Archivs. Jede spätere Anfrage, von jeder Organisation, erhält genau diese Bytes. Eine Version eines Registry-Moduls wird auf den Commit gepinnt, den ihr Tag benannt hat.
  • Ein verschobener Tag ändert dein Modul nicht. Der Proxy prüft gepinnte Tags höchstens alle 6 Stunden erneut. Benennt ein Tag inzwischen einen anderen Commit, liefert der Proxy weiter den gepinnten aus und erfasst, dass der Upstream-Tag verschoben wurde. Nur ein Operator von fremforge kann ein Modul neu pinnen.
  • Wogegen das schützt und wogegen nicht. Registrys und Git-Hosts veröffentlichen keine Prüfsumme für Modularchive, daher wird dem ersten Abruf vertraut. Nichts, was sich danach upstream ändert, erreicht dich: Ein umgehängter Tag oder ein neu erzeugtes Archiv wird nicht ausgeliefert. Eine Version, die schon bei ihrer ersten Veröffentlichung bösartig war, wird so ausgeliefert, wie sie war.
  • Archive werden geprüft, bevor sie ausgeliefert werden. Ein Archiv über 50 MiB, das auf mehr als 512 MiB oder 20.000 Einträge entpackt oder einen absoluten Pfad, einen ..-Pfad, eine Gerätedatei oder einen Link enthält, der aus dem Archiv hinauszeigt, wird abgewiesen, und nichts wird gespeichert.
  • Das Malware-Scanning gilt für Modularchive wie für andere Dateien, wenn deine Organisation es eingeschaltet hat. OSV hat kein Terraform-Ökosystem, daher gilt für Module keine Liste bekanntermaßen bösartiger Pakete, und die Lizenz- und Release-Alter-Regeln der Paketrichtlinie werden auf sie nicht angewendet.
  • Gemeinsam gespeichert, pro Organisation protokolliert. Ein öffentliches Modul wird einmal für alle gespeichert. Die Abrufe jeder Organisation werden in ihrem eigenen Zugriffsprotokoll erfasst, mit dem Akteur module-cache-key. Der Modul-Cache-Schlüssel selbst wird nie in ein Log geschrieben.

Paketrichtlinie und bösartige Pakete

Die Paketrichtlinie deiner Organisation wird für npm-, PyPI-, RPM-, Maven-, NuGet-, Go- und Cargo-Pakete sowie Helm-Charts ausgewertet, die über den Proxy geholt werden, mit denselben Regeln wie für Pakete in deiner Registry:

  • Erlaubnis-, Sperr- und Warnlisten für Lizenzen, angewendet auf die deklarierte Lizenz der Version.
  • Schwachstellen ab deiner Schwere- oder CVSS-Schwelle, aus der OSV-Datenbank.
  • Sperr- und Erlaubnislisten nach Paketname und Versionsbereich.
  • Mindest-Release-Alter, ab dem Veröffentlichungszeitpunkt beim Upstream.
  • Bösartige Pakete (OSV-Advisories MAL-*) werden immer geprüft, egal was die Richtlinie sagt.

Für RPM-Pakete ist die Version [epoch:]version-release, die Lizenz das License-Feld des Pakets und das Release-Alter der Zeitpunkt, zu dem das Paket ins Repository kam. OSV hat keine Daten zu bösartigen Paketen oder Schwachstellen für RPM-Pakete, daher gelten diese beiden Prüfungen nicht für RPMs im Proxy. Schwachstellen in RPM-Paketen werden gefunden, wenn die daraus gebauten Images gescannt werden. Dasselbe gilt für Helm-Charts: OSV hat keine Daten für sie, daher gelten Lizenz, Sperrlisten und Release-Alter, und die Images, die ein Chart ausrollt, werden dort gescannt, wo sie gezogen werden. Sobald das Abweisen eingeschaltet ist, bleibt eine gesperrte Chart-Version im Helm-Index, und ihr Download wird mit 403 abgewiesen.

Das Abweisen ist seit dem 11.10.2026 eingeschaltet. Eine Richtlinie im Modus Durchsetzen entfernt eine gesperrte Version aus dem npm-Packument oder PyPI-Index, sodass dein Resolver eine erlaubte Version wählt, und ein direkter Download davon wird mit 403 unter Nennung der Regel abgewiesen. Eine Richtlinie im Modus Schatten, dem Standard, liefert das Paket weiter aus und erfasst den Pull im Zugriffsprotokoll als would_block mit der Regel, die gegriffen hat. Bösartige Pakete werden unabhängig vom Modus deiner Richtlinie abgewiesen, Schatten und Aus eingeschlossen, und aus den Paketmetadaten, die der Proxy ausliefert, weggelassen.

Solange der Proxy von fremforge keine Richtlinienantwort erhält (nach einigen Sekunden Wiederholungsversuchen und ohne eine aktuelle Antwort für dasselbe Paket, auf die er zurückgreifen kann), weist er den Download mit 503 ab, statt eine Version auszuliefern, die er nicht auf Malware prüfen konnte. Ein späterer Versuch gelingt, sobald die Antwort wieder da ist.

Advisory-Daten werden mehrmals täglich aus dem OSV-Mirror von fremforge aktualisiert, und eine Änderung der Richtlinie gilt ab dem nächsten Pull. Nichts muss neu gescannt werden.

Was heute durchgesetzt wird

PrüfungGilt fürHeute
Prüfsumme des Upstreams (Image-Digest, npm-integrity, PyPI-sha256, sha256 und Größe von RPM-repomd.xml und primary.xml, Maven-.sha1 und .sha256, NuGet-Katalog-SHA-512 und Größe, Go-h1: signiert von der Prüfsummendatenbank, Helm-index.yaml-digest, Cargo-Index-cksum)Jeden Layer, jedes Paket und jede Repository-MetadatendateiBei Abweichung abgewiesen; nichts wird gespeichert
Antivirus (ClamAV), während die Datei zu dir gestreamt wird, wenn deine Organisation das Malware-Scanning eingeschaltet hat (malware_scan, standardmäßig aus)Jeden Layer, jede Paketdatei und jedes Chart; RPM-Nutzlasten entpackt; Einträge in JAR, nupkg und Modul-Zip, die der Scanner nicht selbst öffnen kann, entpackt oder abgewiesenAbgewiesen: Eine infizierte Datei wird vor ihrem letzten Byte abgebrochen und nie gespeichert
Ergebnis des Layer-Scans, infiziert oder nicht scanbar (mit eingeschaltetem Malware-Scanning)Container-ImagesAbgewiesen, wie bei Images, die du pushst
Schwachstellen-block_pull, wenn deine Organisation es gesetzt hatContainer-ImagesMit 403 DENIED abgewiesen; siehe Image-Scanning
Bösartige Pakete (OSV MAL-*) im ImageContainer-ImagesAbgewiesen, wie bei Images, die du pushst; ein Befund im Tab Malware
Bösartige Pakete (OSV MAL-*)npm, PyPI, Maven, NuGet, Go, CargoMit 403 abgewiesen, unabhängig vom Modus deiner Richtlinie
Paketrichtlinie: Lizenz, Schwachstellen, Sperrliste, Release-Alternpm, PyPI, Maven, NuGet, Go und Cargo (keine Lizenz für Go und Cargo)Abgewiesen, wenn deine Richtlinie im Modus Durchsetzen ist; im Schattenmodus erfasst (would_block)
Paketrichtlinie: Lizenz, Sperrliste, Release-AlterRPM, Helm-ChartsAbgewiesen, wenn deine Richtlinie im Modus Durchsetzen ist; im Schattenmodus erfasst (would_block)
Bekanntermaßen ausgenutzte Schwachstelle mit behobener Version (ENISA EUVD, CISA KEV)npm, PyPI, Maven, NuGet, Go, CargoAbgewiesen mit 403, unabhängig vom Modus deiner Richtlinie (siehe unten)

Zwischengespeicherte Images werden außerdem wie deine gepushten Images auf Schwachstellen und geleakte Secrets gescannt; siehe Image-Scanning.

Bekanntermaßen ausgenutzte Schwachstellen

Seit dem 13.10.2026 prüft fremforge jede über den Proxy bezogene Paketversion gegen die Schwachstellen, die bekanntermaßen aktiv ausgenutzt werden: zuerst die EU-Schwachstellendatenbank der ENISA (EUVD), ergänzt um den Katalog Known Exploited Vulnerabilities der CISA. Eine Version zählt nur, wenn eine solche Schwachstelle sie betrifft und eine behobene Version existiert, sodass es immer eine Version gibt, auf die du wechseln kannst. Das ist die Regel, die fremforge bereits auf seine eigenen Runner-Images anwendet.

Seit dem 31.10.2026 wird eine solche Version abgewiesen. Ein Download antwortet mit 403 und einer Meldung, die die CVEs nennt und dich zum Upgrade auffordert, und die Antwort trägt x-fremforge-proxy-policy: blocked;package_policy:known_exploited. Die Version wird außerdem aus den Paket-Metadaten weggelassen, die der Proxy ausliefert, sodass ein Versionsbereich eine andere Version im Bereich wählt; nur eine Lockdatei oder Build-Datei, die die betroffene Version fest pinnt, schlägt fehl. Die Abweisung wird wie jede andere Richtlinien-Abweisung in deinem Zugriffsprotokoll festgehalten.

Das ist eine Plattformregel, wie bei bösartigen Paketen: Sie gilt unabhängig vom Modus deiner Richtlinie, und die Ausnahmen deiner Richtlinie heben sie nicht auf. Der Bot für Abhängigkeits-Updates deiner Organisation sieht diese Versionen weiterhin in den Metadaten, damit er das Upgrade vorschlagen kann. Auch die Pull-Request-Scanner von fremforge lesen sie weiterhin, sodass eine bekanntermaßen ausgenutzte Abhängigkeit weiter als Befund erscheint, statt aus dem Scan zu verschwinden.

Wenn ein Build daran scheitert, wechsle auf die behobene Version: Ein Pull Request mit einem Sicherheits-Fix ist vielleicht schon offen. Gemessen vom 13.10. bis 31.10.2026, vor der Umstellung, hat keine Kundenorganisation eine solche Version heruntergeladen.

Derselbe Feed erlaubt bereits einer Version, die eine bekanntermaßen ausgenutzte Schwachstelle behebt, das Mindest-Release-Alter deiner Richtlinie zu überspringen.

Speicher, Obergrenzen und Abrechnung

Zwischengespeicherte Dateien zählen mit ihrer vollen Größe zum Paketspeicher deiner Organisation, als lägen sie in deiner eigenen Registry. Ein Container-Layer oder eine npm- oder PyPI-Datei, die mehrere Organisationen ziehen, wird einmal gespeichert, aber die Nutzung jeder Organisation zählt sie. RPM-Inhalte werden pro Organisation gespeichert (siehe RPM).

  • Bezahlte Tarife: Zwischengespeicherte Dateien teilen sich einen Paketspeicher-Pool mit deinen eigenen Paketen. Ist die Mehrnutzung aus, darf der Proxy nutzen, was deine eigenen Pakete vom Pool übrig lassen. Ist nichts mehr übrig, werden neue Abrufe mit 403 (dependency proxy storage cap reached) abgewiesen, und was bereits zwischengespeichert ist, wird weiter ausgeliefert. Ist die Mehrnutzung an, gibt es keine Proxy-Obergrenze, und Speicher über dem Pool wird zum Mehrnutzungstarif für Pakete abgerechnet.
  • Testphasen: pauschal 5 GB zwischengespeicherte Dateien.

Die Seite Abhängigkeits-Proxy zeigt deine Nutzung und deine Obergrenze. Der Wert für deine eigenen Pakete, aus dem die Obergrenze berechnet wird, wird einmal täglich aktualisiert.

Aufbewahrung

Eine zwischengespeicherte Datei, die deine Organisation seit retention_days (Standard 90, von 7 bis 365) nicht gezogen hat, wird aus deinem Cache entfernt, zusammen mit den Tags, die auf sie zeigen, und zählt nicht mehr zu deinem Speicher. Die Prüfung läuft stündlich. Sobald keine Organisation mehr auf eine Datei verweist, wird sie 24 Stunden später aus dem Speicher von fremforge gelöscht. Ein erneuter Pull holt sie wieder vom Upstream.

Paketversionen, die noch in Gebrauch sind, bleiben erhalten. Eine Paketdatei (npm, PyPI, Maven, NuGet, Go, Cargo, Helm oder RPM) wird nicht entfernt, egal wie lange ihr letzter Pull her ist, solange dieselbe Paketversion eine aktuelle Komponente im Komponenteninventar deiner Organisation ist: in der aktuellen SBOM des Standardbranches eines Repositorys, seinem neuesten Release, einem aktuellen Image, einem aktiven Registry-Paket oder einer hochgeladenen SBOM. Eine Abhängigkeit, die in einem Lockfile gepinnt ist, aber nur zweimal im Jahr gebaut wird, bleibt daher im Cache. Sie zählt weiterhin zu deinem Speicher. Sobald die Version aus jeder aktuellen SBOM verschwindet, gilt bei der nächsten stündlichen Prüfung wieder die normale Regel.

Basis-Images, die deine aktuellen Builds verwenden, bleiben erhalten. Ein zwischengespeichertes Container-Image wird nicht entfernt, egal wie lange sein letzter Pull her ist, solange eines der aktuellen Images deiner Organisation darauf aufbaut: Jeder Layer des zwischengespeicherten Images ist auch ein Layer eines Images, das noch unter einem Tag in der Registry deiner Organisation veröffentlicht ist. Manifest, Konfiguration und alle Layer des Images bleiben zusammen erhalten, sodass ein Pull davon weiter funktioniert, und ebenso ein Multi-Plattform-Index, auf dessen Plattform-Image du aufbaust. Ein Image, das mit deinen Builds nur einen Layer teilt (zum Beispiel ein anderes Image auf derselben Distribution), bleibt durch diese Regel nicht erhalten. Es zählt weiterhin zu deinem Speicher. Sobald kein aktuelles Image mehr darauf aufbaut, gilt wieder die normale Regel. Jedes andere zwischengespeicherte Image behält die Regel nach letztem Pull. Zwei Fälle werden nicht erkannt und behalten die Regel nach letztem Pull: eine Basis, die nur für eine andere Plattform als linux/amd64 verwendet wird, und ein Build, der die Layer des Basis-Images neu komprimiert, statt sie wiederzuverwenden.

Upstream-Ausfälle

Wenn eine Upstream-Registry nicht erreichbar, langsam oder abweisend ist, funktionieren Builds weiter für alles, was deine Organisation schon einmal gezogen hat:

  • Dateien, die du schon einmal gezogen hast (Image-Layer, .tgz, Wheels, .jar, .nupkg, Go-.zip und .mod, Helm-Charts, Crates), werden aus dem Speicher von fremforge ausgeliefert. Ihre Auslieferung wartet nie auf den Upstream.
  • Indizes und Tags (Image-Tags, npm-Packuments, PyPI-Indizes, RPM-repomd.xml, Maven-maven-metadata.xml, NuGet-Versionslisten und -Registrierungen, Go-@v/list und @latest, Helm-index.yaml, Cargo-Indexdateien) werden aus der letzten guten Kopie beantwortet. Die Antwort trägt dann X-Fremforge-Proxy: stale, sodass ein neues Release, das während des Ausfalls veröffentlicht wurde, erst sichtbar wird, wenn der Upstream zurück ist.
  • Image-Tags, die eine andere Organisation zwischengespeichert hat, werden ebenfalls aus der letzten guten Kopie ausgeliefert, wenn deine Organisation diesen Tag nie gezogen hat: nur für öffentliche Images, die ohne Zugangsdaten gezogen wurden, und nur aus dem gemeinsamen Speicher von fremforge, nie aus Inhalten, die eine andere Organisation mit ihren eigenen Registry-Zugangsdaten geholt hat. Docker-Hub-Images werden immer mit den eigenen Zugangsdaten deiner Organisation geholt und auf diese Weise nicht geteilt.
  • Alles, was nie zuvor gezogen wurde, antwortet mit 502, weil es nichts auszuliefern gibt.

Ist der eigene Anmelde- oder Autorisierungsdienst von fremforge kurz nicht verfügbar, werden Anfragen, die in den letzten 15 Minuten erlaubt wurden, weiter aus dem Cache bedient (nur Lesezugriffe). Ein widerrufener Token oder eine entfernte Mitgliedschaft wird trotzdem wirksam, sobald fremforge sie prüfen kann.

Was einen Ausfall übersteht, ist durch die Aufbewahrung begrenzt. Eine Datei, die deine Organisation seit retention_days (Standard 90) nicht gezogen hat, wird entfernt. Danach muss sie erneut vom Upstream geholt werden, was nicht möglich ist, solange der Upstream nicht erreichbar ist. Paketversionen in deinem Komponenteninventar und die Basis-Images, auf denen deine aktuellen Images aufbauen, bleiben unabhängig davon erhalten (siehe Aufbewahrung). Für alles andere, das einen langen Ausfall überstehen muss, ziehe es mindestens einmal innerhalb deines Aufbewahrungszeitraums oder erhöhe retention_days.

Zugriffsprotokoll

Jeder Pull über den Proxy erscheint im Registry-Zugriffsprotokoll deiner Organisation mit dem Ursprung proxy, außer zwei Arten von routinemäßigem CI-Verkehr: Die erlaubten Metadaten-Lesezugriffe eines CI-Jobs (Paketindizes und Versionslisten) werden seit dem 12.10.2026 nicht gespeichert, und die Downloads eines CI-Jobs, die der Proxy aus seinem Cache bedient, werden seit dem 13.10.2026 im Pull-Census pro Tag, Version, Repository und Lauf gezählt. Die Downloads eines CI-Jobs, die von der Upstream-Registry geholt werden, seine Abweisungen und seine Fehler sind weiterhin Ereignisse. Das Protokoll erfasst Paket, Version und Datei, wer gezogen hat (ein Benutzer oder bei CI das Repository des Jobs), das Ergebnis und jede Richtlinienentscheidung. Ein Pull über frem.sh, der aus dem Cache bedient wird, wird vom Proxy ausgeliefert (200); der eines CI-Jobs ist eine Weiterleitung zum Objektspeicher (307), außer bei Maven-Dateien bis 1 MiB. In beiden Fällen wird seine Größe erfasst. Ein HEAD-Request über frem.sh wird als GET erfasst, weil das CDN HEAD-Requests als GET an fremforge weiterleitet. Die eigenen Lesezugriffe des Abhängigkeits-Scans (siehe oben) laufen weder über das CDN noch über den Upload-Proxy, daher stehen sie nicht im Zugriffsprotokoll; fremforge protokolliert sie als Zählwerte pro Scan.

Was der Upstream sieht

fremforge kontaktiert in deinem Auftrag den Upstream, den du gewählt hast. Der Upstream erhält die IP-Adresse von fremforge und den Namen des Images oder Pakets. Für docker.io, die Red-Hat-Registrys sowie ghcr.io oder quay.io, wenn du Zugangsdaten dafür gespeichert hast, erhält er außerdem die eigenen Zugangsdaten deiner Organisation, sodass der Pull dort auf dein Konto angerechnet wird. Über deine Benutzer erhält er nichts. Die meisten dieser Registrys werden von Unternehmen außerhalb der EU betrieben. Nach dem ersten Abruf werden Pulls aus dem EU-Speicher von fremforge bedient, ohne den Upstream erneut zu kontaktieren, außer um Tags, npm- und PyPI-Metadaten, RPM-repomd.xml, Maven-maven-metadata.xml, NuGet-Versionslisten und -Registrierung, Go-Versionslisten, Helm-index.yaml-Dateien und Cargo-Indexdateien neu zu prüfen (höchstens alle 5 Minuten). Die Prüfsummendatenbank von Go wird ebenfalls über den Proxy erreicht, im Auftrag des go-Befehls.

Fehler

StatusBedeutung
401Keine Zugangsdaten, oder frem.sh hat sie nicht akzeptiert.
403Kein Mitglied, der Proxy ist für die Organisation ausgeschaltet, den Zugangsdaten fehlt read:package, ein Job-Token für eine andere Organisation, die Speicherobergrenze deiner Organisation ist erreicht, oder eine Abweisung durch eine Prüfung (die Meldung nennt sie).
404Der Upstream hat es nicht, oder der Upstream-Host ist über den Proxy nicht verfügbar.
405Ein Schreibzugriff. Der Proxy ist schreibgeschützt.
429Das Upstream-Abruflimit deiner Organisation wurde erreicht. Versuche es nach den in Retry-After angegebenen Sekunden erneut.
502Der Upstream ist nicht verfügbar, und für diese Anfrage ist nichts zwischengespeichert.
503Eine Abhängigkeit (Malware-Scanner, Richtlinienprüfung) ist nicht verfügbar; der Proxy ist ausgelastet; oder eine andere Anfrage holt und scannt noch denselben Layer. Versuche es nach Retry-After erneut.