Aller au contenu principal

Le provider Terraform et OpenTofu de fremforge

Le provider fremforge gère les paramètres fremforge de votre organisation depuis OpenTofu ou HashiCorp Terraform : authentification et politique d’accès, liste d’adresses IP autorisées, politique d’analyse et des analyseurs, releases immuables, règles de signature des images et des paquets, proxy de dépendances, accès aux paquets et robots de registre.

Les dépôts, les équipes et les règles de protection de branche sont des objets Forgejo et restent du ressort du provider communautaire svalabs/forgejo, comme décrit dans Configuration as code. Les deux providers fonctionnent côte à côte dans une même configuration.

Installation

Le provider est publié sur frem.sh même, si bien que les deux CLI l’installent sans configuration supplémentaire :

terraform {
  required_providers {
    fremforge = {
      source  = "frem.sh/fremforge/fremforge"
      version = "~> 0.1"
    }
  }
}

tofu init (ou terraform init) trouve le registre à l’adresse https://frem.sh/.well-known/terraform.json, télécharge l’archive zip correspondant à votre plateforme depuis cli.frem.sh et la contrôle par rapport à SHA256SUMS, dont il vérifie la signature OpenPGP avec la clé de publication de fremforge. Des builds existent pour Linux et macOS (amd64, arm64) et pour Windows (amd64).

La clé de publication :

EmpreinteAEA6 FE24 2C86 33F0 10C6 D4ED 9099 FD4F C1DC 23C4
ID de clé9099FD4FC1DC23C4
Certificat publichttps://frem.sh/_app/terraform/signing-key.asc

La clé privée ne quitte jamais la plateforme fremforge : chaque version est signée par la plateforme, uniquement pour le build de publication du provider.

Configuration

provider "fremforge" {
  # endpoint = "https://frem.sh/_app/api/v1"   # the default
}

Le provider lit son jeton dans FREMFORGE_TOKEN. Utilisez un jeton d’API d’organisation de l’organisation que vous gérez, créé sous Administration de l’organisation → Jetons d’API (frem.sh/<org>/_admin/api-tokens), avec les scopes dont vos ressources ont besoin. Gardez un jeton en lecture seule pour plan et le scope d’écriture pour le job qui exécute apply :

RessourcesScope
Toutes les ressources de cette pagepolicy:write
Sources de données uniquementpolicy:read
fremforge_push_protection (lecture)findings:read

Ressources

Les paramètres d’organisation sont des singletons : chaque organisation les possède toujours et l’API ne permet pas de les supprimer. Tous les arguments sont facultatifs. Seuls les arguments que vous définissez sont écrits ; les autres sont relus dans l’état et laissés tels quels, si bien qu’une modification apportée dans la console à un paramètre que vous ne gérez pas n’est pas une dérive. Importez un singleton à l’aide du slug de son organisation.

RessourceCe qu’elle gère
fremforge_auth_policyPolitique d’authentification : durée de vie maximale des PAT (0 = aucune limite), scopes des PAT, commits signés, signataires sans clé, SSH activé ou désactivé.
fremforge_ip_allowlistLa liste d’adresses IP autorisées de l’organisation. L’API refuse une liste appliquée qui ne contient pas l’adresse depuis laquelle la CLI s’exécute ; déployez d’abord une liste avec audit_only = true.
fremforge_operator_accessSi les opérateurs fremforge peuvent gérer les utilisateurs de l’organisation.
fremforge_email_domain_policySi la liste des domaines e-mail autorisés est appliquée.
fremforge_ssh_caL’autorité de certification SSH. require_cert = true nécessite une clé de CA dans la même configuration.
fremforge_actions_policyActions uniquement sur les branches protégées.
fremforge_artifact_retentionLa durée de conservation des artefacts Actions, en jours.
fremforge_sla_policyLes délais de remédiation par niveau de gravité (0 = aucun).
fremforge_push_protectionCe que fait un push lorsque l’analyseur de secrets ne peut pas s’exécuter.
fremforge_immutabilityReleases et tags de conteneur immuables. Le plan affiche un avertissement lorsque releases est activé : ce choix est irréversible pour les releases publiées pendant qu’il est actif.
fremforge_branch_protection_defaultsLes valeurs par défaut que fremforge applique aux nouveaux dépôts (il ne s’agit pas d’une règle Forgejo).
fremforge_sast_settings, fremforge_dependency_scan_settings, fremforge_image_scan_settings, fremforge_secret_scan_settings, fremforge_license_settingsLes paramètres des analyseurs, une ressource par analyseur.
fremforge_image_signature_policyLes signatures d’image considérées comme fiables lors d’un pull, et si un pull non signé est refusé. Déployez avec mode = "shadow".
fremforge_dependency_proxyLes paramètres du proxy de dépendances de l’organisation.
fremforge_package_signature_policyLa règle de signature de la politique de paquets (exigence et signataires de confiance).
fremforge_package_access_defaultLe mode d’accès aux paquets par défaut de l’organisation.

Chaque singleton est également une source de données du même nom.

Les collections disposent d’une véritable création et d’une véritable suppression :

RessourceID d’import
fremforge_email_domain<org>/<pattern> ou <org>/<id>
fremforge_scan_policy (une par analyseur ; iac_scan à partir de 0.4.0)<org>/<scanner>
fremforge_package_access<org>/<type>/<name>
fremforge_package_access_team_grant<org>/<type>/<team>/<name>
fremforge_package_access_actions_grant<org>/<type>/<repository>/<name>
fremforge_package_protection_rule<org>/<rule id>
fremforge_registry_robot<org>/<robot id>
fremforge_secret_custom_pattern (à partir de 0.3.0 ; repository à partir de 0.7.0)<org>/<pattern id>
fremforge_iac_custom_check (à partir de 0.4.0)<org>/<check id>
fremforge_ci_secret (à partir de 0.9.0)<org>/<NAME> / <org>/<repository id>/<NAME>

Un nom de paquet peut contenir / (un scope npm, un chemin de conteneur) : c’est pourquoi il figure en dernier dans les ID d’import.

Exemple

resource "fremforge_auth_policy" "this" {
  org                   = "acme"
  max_pat_lifetime_days = 30
  allow_user_pats       = false
  ssh_disabled          = true
}

resource "fremforge_ssh_ca" "this" {
  org          = "acme"
  ca_keys      = [file("ssh-ca.pub")]
  require_cert = true
}

resource "fremforge_scan_policy" "images" {
  org            = "acme"
  scanner        = "image_scan"
  mode           = "enforce"
  block_severity = "high"
}

Paramètres supprimés

Deux paramètres ont été supprimés de fremforge le 2026-10-14, car ils étaient enregistrés mais jamais appliqués : principal_policy sur fremforge_ssh_ca et default_template_repo sur fremforge_branch_protection_defaults. Depuis la version 0.1.1 du provider, ces deux attributs sont obsolètes : le provider ne les envoie plus et ne les lit plus, si bien qu’une configuration qui les définit encore continue de fonctionner et produit un plan sans changement, et tofu plan affiche un avertissement d’obsolescence. Retirez-les de votre configuration ; rien ne change lorsque vous le faites. Voir Protection de branche et Autorité de certification SSH.

Avec la version 0.1.0 du provider, un bloc fremforge_ssh_ca dont le seul argument est principal_policy échoue avec une erreur HTTP 400 (no-fields), et un default_template_repo non vide est refusé avec removed-setting. Mettez à jour avec tofu init -upgrade (ou terraform init -upgrade) ; version = "~> 0.1" récupère la 0.1.1.

Secrets CI à portée : la valeur n’entre jamais dans le state

fremforge_ci_secret (à partir du provider 0.9.0) gère un secret CI à portée. La valeur est un argument en écriture seule, value_wo : elle est envoyée à la création du secret et à chaque modification (l’API remplace la valeur et la portée ensemble), et elle n’est jamais relue ni stockée dans le plan ou le state. Les arguments en écriture seule demandent OpenTofu 1.11 ou Terraform 1.11 ou plus récent. Omettez repository_id pour toute l’organisation ; augmentez value_wo_version pour écrire une nouvelle valeur.

variable "deploy_key" {
  type      = string
  sensitive = true
  ephemeral = true
}

resource "fremforge_ci_secret" "deploy" {
  org              = "acme"
  name             = "DEPLOY_KEY"
  repository_id    = 42
  value_wo         = var.deploy_key
  value_wo_version = 1
  workflows        = ["deploy.yaml"]
  refs             = ["refs/heads/main"]
  events           = ["push", "workflow_dispatch"]
  environment      = "production"
}
  • Rotation : fremforge ne peut pas afficher la valeur, donc une nouvelle value_wo seule n’est pas une modification. Augmentez value_wo_version pour l’écrire.
  • Dérive : une modification de la portée ou de la description faite hors de votre configuration apparaît dans le plan ; une modification de la valeur, non.
  • La création ne reprend jamais un secret existant : si un secret de ce nom et de ce dépôt existe déjà, l’apply échoue et vous invite à l’importer.
  • Import : <org>/<NAME> pour un secret valable pour toute l’organisation, <org>/<repository id>/<NAME> pour un secret lié à un dépôt. Renseignez value_wo dans la configuration avant le prochain apply.
  • Scopes du jeton : le jeton du provider doit avoir ci-secrets:read et ci-secrets:write.

Les arguments de portée sont refusés avant toute requête lorsqu’ils pourraient élargir la livraison : une entrée vide, un nom réservé (FORGEJO_, GITHUB_, GITEA_) ou un identifiant de dépôt qui n’est pas un nombre positif.

Les jetons des robots de registre ne vont jamais dans l’état

L’API n’affiche le jeton d’un robot de registre qu’une seule fois, au moment où il est émis. Le provider ne le stocke jamais dans l’état, dans aucun attribut. Il écrit le jeton une seule fois, lorsque l’API l’émet (à la création, et chaque fois que vous modifiez rotation), dans le fichier que vous indiquez dans token_file, avec le mode 0600, et n’écrase jamais un fichier existant :

resource "fremforge_registry_robot" "ci" {
  org        = "acme"
  name       = "ci-pull"
  access     = "read"
  token_file = "${path.module}/.robot-ci.token"
  rotation   = "2026-10"   # change to issue a new token; the old one is revoked at once
}

Sans token_file, le jeton est perdu. Déplacez le fichier avant d’effectuer une nouvelle rotation.

Dérive, import et destroy

  • Dérive : plan -detailed-exitcode se termine avec le code 2 lorsqu’un paramètre que vous gérez a été modifié en dehors de votre configuration ; apply le rétablit.
  • Import : import { to = fremforge_auth_policy.this; id = "acme" }, puis tofu plan -generate-config-out=generated.tf écrit la configuration pour vous.
  • Destroy : pour un singleton, destroy se contente de le retirer de l’état et ne modifie rien dans l’organisation : écrire les valeurs par défaut affaiblirait silencieusement un paramètre de sécurité. Les collections sont supprimées. fremforge_package_access supprime les paramètres du paquet ainsi que toutes les autorisations qui s’y rapportent.

Migrer depuis le module

Le module reste documenté pendant votre migration. Pour chaque paramètre, ajoutez la ressource du provider, importez-la à l’aide du slug de l’organisation, vérifiez que plan n’affiche aucun changement, puis retirez l’argument du module et exécutez tofu state rm sur sa restful_resource.

Comment nous le testons

À chaque modification, chaque ressource et chaque source de données passent par la véritable CLI OpenTofu face à une API fremforge enregistrée : seuls les arguments que vous définissez sont écrits, les modifications non gérées sont ignorées, une dérive gérée apparaît dans le plan, l’import relit le même état, et destroy se comporte comme décrit ci-dessus.

Les paramètres qu’il est sans risque de modifier sur une organisation partagée sont également testés en conditions réelles chaque nuit sur une organisation de test : apply, un plan sans changement, une modification effectuée via l’API, un plan qui la fait apparaître, apply, import dans un état vierge, un plan sans changement, destroy, une relecture, puis la restauration de la valeur d’origine. Il s’agit de la politique d’authentification, de la CA SSH, de la politique de SLA et du filtre de métadonnées du proxy de dépendances ; l’accès aux paquets, les règles de protection et les robots de registre ont été testés en conditions réelles de la même manière avant la publication. La liste d’adresses IP autorisées et les politiques de signature ne sont testées que face à l’API enregistrée, car les modifier sur une organisation partagée perturberait d’autres tests.