Journal d’accès au registre
Le journal d’audit enregistre les actions
d’administration. Le journal d’accès au registre enregistre autre chose :
chaque requête qu’un client adresse au registre de paquets et de conteneurs
de votre organisation. Cela couvre chaque téléchargement, chaque lecture de
métadonnées (un packument npm, un index simple PyPI, un maven-metadata.xml
Maven), chaque téléversement et chaque suppression, y compris les requêtes
qu’une politique a bloquées. Rien n’est échantillonné, à deux exceptions près
pour le trafic CI courant passant par le proxy de
dépendances. Toutes deux ne s’appliquent
qu’à une requête émise par un job Forgejo Actions, que le proxy a autorisée et
à laquelle il a répondu sans erreur :
- Les lectures de métadonnées en CI ne sont pas enregistrées (depuis le
2026-10-12). Un index de paquet ou une liste de versions (un packument
npm, un index simple PyPI, un
maven-metadata.xmlMaven) lu par un job n’est pas conservé. Une seule installation de paquet génère des centaines de ces lectures, et elles n’apprennent rien que le téléchargement qui suit ne dise déjà. - Les téléchargements CI servis depuis le cache du proxy sont comptés, pas journalisés un par un (depuis le 2026-10-13). Lorsqu’un job télécharge un fichier de paquet que le proxy détient déjà, le téléchargement est ajouté au recensement des pulls au lieu d’être conservé comme un événement : un décompte par jour UTC, version de paquet et dépôt, avec les identifiants des exécutions Actions qui l’ont récupéré. Vous pouvez donc toujours voir, depuis le recensement, quelle version de quel paquet est entrée dans quel build, exécution par exécution.
Tout le reste reste un événement, CI comprise :
- chaque téléchargement que le proxy a récupéré depuis le registre amont (un défaut de cache), et chaque téléchargement depuis votre propre registre
- chaque requête qu’une politique a refusée, bloquée ou aurait bloquée
- chaque requête en échec (un statut de 400 ou plus)
- chaque requête émise par une personne, un bot, un robot de registre ou de façon anonyme, lectures de métadonnées et accès au cache compris
Un téléchargement CI comptabilisé dans le recensement conserve le paquet, la
version, le jour, le dépôt et l’exécution, ainsi que le nombre de pulls. Il ne
conserve rien de plus de ce que contient un événement : ni le nom du fichier,
ni la taille, ni l’heure exacte de chaque pull (le recensement indique le
premier et le dernier de la journée), ni l’adresse du client, ni l’agent
utilisateur, ni l’identifiant de requête. Il n’est pas transmis à un point de
terminaison SIEM et ne figure pas dans access-log.ndjson ; il se trouve dans
l’API du recensement et dans metadata/pulled-versions.ndjson dans l’export de
données. Les événements conservés avant ces dates sont gardés pendant leur
durée de conservation normale.
Contenu d’un événement
| Champ | Signification |
|---|---|
occurred_at | Le moment où fremforge a répondu à la requête (UTC). |
origin | Le composant qui a vu la requête : registry pour le registre de paquets. Le proxy de dépendances écrit proxy. |
surface | api (/api/packages/…), oci (/v2/…, le registre de conteneurs) ou web (un fichier téléchargé depuis l’interface web). |
org, package.type, package.name, package.version, package.file | Ce qui a été demandé. |
repo | Pour une requête émise avec un jeton de job Forgejo Actions : le dépôt du job, <owner>/<repo>. Vide sinon. |
actions | Pour une requête émise avec un jeton de job Forgejo Actions : l’exécution d’où elle provient, {repo_id, run_id, run_number, job_id}. run_number correspond à la page de l’exécution, /<owner>/<repo>/actions/runs/<run_number> ; run_id est GITHUB_RUN_ID. null pour toute autre requête, ainsi que pour les requêtes enregistrées avant le 2026-10-08. |
package.digest | sha256:<hex> du fichier ou du manifeste servi ou stocké, lorsque le registre l’a nommé. |
action | download, metadata, upload, delete ou other. |
actor | L’identité authentifiée par le registre : {type: "user" | "bot" | "anonymous", id, login}. Un jeton ou une session est rattaché à l’utilisateur auquel il appartient, et les jobs Forgejo Actions apparaissent comme un bot. |
client_ip | L’adresse du client telle que l’a vue la périphérie de fremforge. Elle est vide lorsque fremforge n’a pas pu vérifier l’adresse, par exemple pour une requête qui n’est pas passée par la périphérie. |
user_agent | Le User-Agent du client, tronqué à 256 caractères. |
http.method, http.status, http.bytes | La méthode de la requête, la réponse et la taille lorsqu’elle est connue. La taille est inconnue pour un téléchargement redirigé vers le stockage objet. Une requête HEAD apparaît comme GET, car le CDN transmet les requêtes HEAD au registre sous forme de GET. |
decision, decision_reason | allowed ; blocked ; would_block (une politique en mode observation, ou l’action de blocage warn de votre analyse d’images, l’aurait refusée, et la requête est passée) ; ou pending_refused (votre politique de paquets refuse les fichiers dont l’analyse n’est pas encore terminée, et c’était le cas de celui-ci ; le client a reçu 503 avec Retry-After, et une nouvelle tentative une fois l’analyse terminée réussit). decision_reason nomme la règle qui a décidé, par exemple malware, image_malware, image_cve, registry_scan:<reason> ou immutable_retired (voir versions de paquets immuables). |
Jamais enregistrés : un jeton d’accès, un en-tête Authorization, un
cookie ou une chaîne de requête. Une URL de stockage présignée, par exemple,
n’apparaît jamais dans un événement.
Le consulter
Avec un jeton qui porte audit:read :
curl -sS -H "Authorization: Bearer $FFP_TOKEN" \
"https://frem.sh/_app/api/v1/orgs/acme/access-log?action=download&type=npm&since=2026-10-01"Les résultats arrivent du plus récent au plus ancien. Passez la valeur next
d’une page comme cursor pour obtenir la page suivante, et arrêtez-vous
lorsque next vaut null. Le curseur est un jeu de clés : les événements qui
arrivent pendant votre pagination ne décalent ni ne répètent jamais une ligne.
Filtres : since, until, origin, action, decision, type, name,
version, actor (un identifiant de connexion, ou anonymous) et ip. Une
page contient jusqu’à 500 événements. Consultez la référence de
l’API pour la structure complète.
Quelles versions vulnérables sont récupérées
Le recensement des pulls compte les
téléchargements réussis de ce journal par version de paquet et par
consommateur, avec les accès au cache CI qui y sont comptés au lieu d’être
journalisés, et les rapproche des alertes utilisées par votre politique de
paquets. Il répond à la question « quelles versions vulnérables ont été
récupérées au cours des 30 derniers jours, et par qui » sans avoir à parcourir
les événements bruts, et run_id répond à « quelles versions ce build a-t-il
récupérées ».
Le transférer vers votre SIEM
Les événements d’accès peuvent être transmis à un point de terminaison
SIEM dans la même enveloppe signée que les événements
d’audit. L’action est registry.access.<action> (par exemple
registry.access.download), et hash_chain vaut null, car les événements
d’accès ne font pas partie de la chaîne de hachage de l’audit. La transmission
est désactivée par défaut, car le trafic de téléchargement est bien plus
volumineux que le trafic d’audit. Activez-la par point de terminaison avec
include_access_log :
curl -sS -X PATCH -H "Authorization: Bearer $FFP_TOKEN" -H 'Content-Type: application/json' \
-d '{"include_access_log": true}' \
https://frem.sh/_app/api/v1/orgs/acme/siem/endpoints/$ENDPOINT_IDLes préfixes d’action d’un point de terminaison s’appliquent aussi aux
événements d’accès. Par exemple, registry.access.upload ne transmet que les
téléversements. Le trafic CI qui n’est pas conservé sous forme d’événements
(voir plus haut) n’est pas transmis non plus.
Dans l’export de données
Un export de données contient access-log.ndjson, avec
chaque événement d’accès que fremforge détient encore pour l’organisation, un
objet JSON par ligne dans la même structure que celle renvoyée par l’API, et
metadata/pulled-versions.ndjson, avec le recensement des pulls, où sont
comptés les accès au cache CI. Les deux fichiers sont couverts par le manifeste
signé de l’export.
Durée de conservation
Les événements d’accès sont conservés selon la conservation des
preuves access_log de votre organisation :
de 30 à 730 jours. Si elle n’est pas définie, elle suit la conservation du
journal d’audit (90, 180, 365 ou 730 jours, définie sous Politique
d’authentification → Conservation du journal d’audit ; 90 par défaut). Les
événements plus anciens sont supprimés une fois par jour. Tant qu’une
conservation légale est en place, rien n’est supprimé.
Qui peut voir les adresses
Le journal d’accès contient des données personnelles : les identifiants de connexion et les adresses IP de vos utilisateurs. L’API, la transmission vers le SIEM et l’export affichent les deux tels qu’enregistrés, car il s’agit du propre registre de votre organisation. Les outils du personnel de fremforge ne voient les deux que sous forme de pseudonymes à clé. Les lignes de journal que la plateforme écrit pour ses propres opérations ne contiennent ni l’un ni l’autre.
Limites
- Les clone et fetch Git, ainsi que les lectures via l’API REST (
/api/v1), ne sont pas des requêtes au registre et ne figurent pas encore dans ce journal. - La suppression d’une version de paquet via l’API REST
(
DELETE /api/v1/packages/...) ou l’interface web n’est pas enregistrée ici. Les suppressions effectuées par un client de registre sous/api/packagessont enregistrées. - Une requête refusée par la plateforme avant la réponse du registre, comme un
téléversement rejeté par l’analyseur antimalware, n’a pas d’acteur
authentifié et apparaît comme
anonymous. - La transmission du registre vers le journal se fait par lots toutes les 2 secondes ou tous les 500 événements. Si la plateforme redémarre au milieu d’un lot, les événements des dernières secondes peuvent être perdus. Les pertes délibérées sont journalisées et déclenchent une alarme de notre côté.