Der fremforge-Provider für Terraform und OpenTofu
Der fremforge-Provider verwaltet die fremforge-Einstellungen deiner Organisation aus OpenTofu oder HashiCorp Terraform: Authentifizierungs- und Zugriffsrichtlinie, die IP-Allowlist, Scan- und Scanner-Richtlinien, unveränderliche Releases, Signaturregeln für Images und Pakete, den Abhängigkeits-Proxy, den Paketzugriff und Registry-Robots.
Repositorys, Teams und Branch-Schutzregeln sind Forgejo-Objekte und bleiben beim Community-Provider svalabs/forgejo, wie in Konfiguration als Code beschrieben. Die beiden Provider arbeiten in einer Konfiguration nebeneinander.
Installation
Der Provider wird auf frem.sh selbst veröffentlicht, daher installieren ihn beide CLIs ohne zusätzliche Konfiguration:
terraform {
required_providers {
fremforge = {
source = "frem.sh/fremforge/fremforge"
version = "~> 0.1"
}
}
}tofu init (oder terraform init) findet die Registry unter https://frem.sh/.well-known/terraform.json, lädt das ZIP für deine Plattform von cli.frem.sh herunter und prüft es gegen SHA256SUMS, dessen OpenPGP-Signatur es mit dem Release-Schlüssel von fremforge verifiziert. Builds gibt es für Linux und macOS (amd64, arm64) sowie Windows (amd64).
Der Release-Schlüssel:
| Fingerprint | AEA6 FE24 2C86 33F0 10C6 D4ED 9099 FD4F C1DC 23C4 |
| Schlüssel-ID | 9099FD4FC1DC23C4 |
| Öffentliches Zertifikat | https://frem.sh/_app/terraform/signing-key.asc |
Der private Schlüssel verlässt die fremforge-Plattform nie: Jedes Release wird von der Plattform signiert, und zwar nur für den Release-Build des Providers.
Konfiguration
provider "fremforge" {
# endpoint = "https://frem.sh/_app/api/v1" # the default
}Der Provider liest seinen Token aus FREMFORGE_TOKEN. Verwende einen API-Token der Organisation, die du verwaltest, erstellt unter Organisationsverwaltung → API-Tokens (frem.sh/<org>/_admin/api-tokens), mit den Scopes, die deine Ressourcen brauchen. Halte einen schreibgeschützten Token für plan bereit und den Schreib-Scope für den Job, der apply ausführt:
| Ressourcen | Scope |
|---|---|
| Jede Ressource auf dieser Seite | policy:write |
| Nur Datenquellen | policy:read |
fremforge_push_protection (lesen) | findings:read |
Ressourcen
Organisationseinstellungen sind Singletons: Jede Organisation hat sie immer, und die API kennt kein Löschen. Jedes Argument ist optional. Nur die Argumente, die du setzt, werden geschrieben; die übrigen werden in den State zurückgelesen und in Ruhe gelassen. Ändert also jemand in der Konsole eine Einstellung, die du nicht verwaltest, ist das kein Drift. Importiere ein Singleton über den Slug der Organisation.
| Ressource | Was sie verwaltet |
|---|---|
fremforge_auth_policy | Authentifizierungsrichtlinie: Obergrenze der PAT-Laufzeit (0 = keine Obergrenze), PAT-Scopes, signierte Commits, schlüssellose Signierende, SSH an oder aus. |
fremforge_ip_allowlist | Die IP-Allowlist der Organisation. Eine durchsetzende Liste, die die Adresse, von der aus die CLI läuft, nicht enthält, wird von der API abgewiesen; führe eine Liste zuerst mit audit_only = true ein. |
fremforge_operator_access | Ob Operatoren von fremforge die Nutzer der Organisation verwalten dürfen. |
fremforge_email_domain_policy | Ob die Allowlist für E-Mail-Domains durchgesetzt wird. |
fremforge_ssh_ca | Die SSH-Zertifizierungsstelle. require_cert = true braucht einen CA-Schlüssel in derselben Konfiguration. |
fremforge_actions_policy | Actions nur auf geschützten Branches. |
fremforge_artifact_retention | Aufbewahrung von Actions-Artefakten in Tagen. |
fremforge_sla_policy | Behebungsfristen pro Schweregrad (0 = keine). |
fremforge_push_protection | Was ein Push tut, wenn der Secret-Scanner nicht laufen kann. |
fremforge_immutability | Unveränderliche Releases und Container-Tags. Der Plan warnt, wenn releases eingeschaltet wird: Für die Releases, die veröffentlicht werden, solange es an ist, lässt sich das nicht rückgängig machen. |
fremforge_branch_protection_defaults | Die Standards, die fremforge auf neue Repositorys anwendet (keine Forgejo-Regel). |
fremforge_sast_settings, fremforge_dependency_scan_settings, fremforge_image_scan_settings, fremforge_secret_scan_settings, fremforge_license_settings | Scanner-Einstellungen, eine Ressource pro Scanner. |
fremforge_image_signature_policy | Welchen Image-Signaturen beim Pull vertraut wird und ob ein unsignierter Pull abgewiesen wird. Mit mode = "shadow" einführen. |
fremforge_dependency_proxy | Die Einstellungen des Abhängigkeits-Proxys der Organisation. |
fremforge_package_signature_policy | Die Signaturregel der Paketrichtlinie (Anforderung und vertrauenswürdige Signierende). |
fremforge_package_access_default | Der Standardmodus für den Paketzugriff der Organisation. |
Jedes Singleton ist außerdem eine Datenquelle mit demselben Namen.
Sammlungen haben ein echtes Anlegen und Löschen:
| Ressource | Import-ID |
|---|---|
fremforge_email_domain | <org>/<pattern> oder <org>/<id> |
fremforge_scan_policy (eine pro Scanner; iac_scan ab 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 (ab 0.3.0; repository ab 0.7.0) | <org>/<pattern id> |
fremforge_iac_custom_check (ab 0.4.0) | <org>/<check id> |
fremforge_ci_secret (ab 0.9.0) | <org>/<NAME> / <org>/<repository id>/<NAME> |
Ein Paketname kann / enthalten (ein npm-Scope, ein Container-Pfad), deshalb steht er in den Import-IDs an letzter Stelle.
Beispiel
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"
}Entfernte Einstellungen
Zwei Einstellungen wurden am 14.10.2026 aus fremforge entfernt, weil sie gespeichert, aber nie angewendet wurden: principal_policy auf fremforge_ssh_ca und default_template_repo auf fremforge_branch_protection_defaults. Seit Provider 0.1.1 sind beide Attribute veraltet: Der Provider sendet und liest sie nicht mehr, sodass eine Konfiguration, die sie noch setzt, weiter funktioniert und einen sauberen Plan ergibt, und tofu plan gibt eine Veraltungswarnung aus. Entferne sie aus deiner Konfiguration; dabei ändert sich nichts. Siehe Branch-Schutz und SSH-Zertifizierungsstelle.
fremforge_ssh_ca, dessen einziges Argument principal_policy ist, mit HTTP 400 (no-fields) fehl, und ein nicht leeres default_template_repo wird mit removed-setting abgewiesen. Aktualisiere mit tofu init -upgrade (oder terraform init -upgrade); version = "~> 0.1" holt 0.1.1.Scoped CI-Secrets: der Wert landet nie im State
fremforge_ci_secret (ab Provider 0.9.0) verwaltet ein scoped CI-Secret. Der Wert ist ein Write-only-Argument, value_wo: Er wird beim Anlegen und bei jeder Änderung gesendet (die API ersetzt Wert und Scope zusammen) und nie zurückgelesen oder in Plan oder State gespeichert. Write-only-Argumente brauchen OpenTofu 1.11 oder Terraform 1.11 oder neuer. Lass repository_id für die ganze Organisation weg; erhöhe value_wo_version, um einen neuen Wert zu schreiben.
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 kann den Wert nicht anzeigen, daher ist ein neuer
value_woallein keine Änderung. Erhöhevalue_wo_version, um ihn zu schreiben. - Drift: Eine Änderung an Scope oder Beschreibung außerhalb deiner Konfiguration erscheint im Plan, eine Änderung am Wert nicht.
- Anlegen übernimmt nie: Gibt es schon ein Secret mit diesem Namen und Repository, schlägt das Apply fehl und fordert dich zum Import auf.
- Import:
<org>/<NAME>für ein organisationsweites Secret,<org>/<repository id>/<NAME>für eines, das an ein Repository gebunden ist. Setzevalue_wovor dem nächsten Apply in der Konfiguration. - Token-Scopes: Das Token des Providers braucht
ci-secrets:readundci-secrets:write.
Die Scope-Argumente werden vor jeder Anfrage abgewiesen, wenn sie die Zustellung erweitern könnten: ein leerer Eintrag, ein reservierter Name (FORGEJO_, GITHUB_, GITEA_) oder eine Repository-ID, die keine positive Zahl ist.
Tokens von Registry-Robots landen nie im State
Die API zeigt den Token eines Registry-Robots einmal, bei der Ausstellung. Der Provider speichert ihn nie im State, in keinem Attribut. Er schreibt den Token einmal, wenn die API ihn ausstellt (beim Anlegen und immer, wenn du rotation änderst), in die Datei, die du in token_file angibst, mit Modus 0600, und überschreibt nie eine bestehende Datei:
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
}Ohne token_file wird der Token verworfen. Verschiebe die Datei, bevor du erneut rotierst.
Drift, Import und Destroy
- Drift:
plan -detailed-exitcodeendet mit 2, wenn eine Einstellung, die du verwaltest, außerhalb deiner Konfiguration geändert wurde;applystellt sie wieder her. - Import:
import { to = fremforge_auth_policy.this; id = "acme" }, danach schreibttofu plan -generate-config-out=generated.tfdie Konfiguration für dich. - Destroy: Bei einem Singleton entfernt Destroy es nur aus dem State und ändert nichts in der Organisation: Standardwerte zu schreiben würde eine Sicherheitseinstellung stillschweigend abschwächen. Sammlungen werden gelöscht.
fremforge_package_accessentfernt die Einstellungen des Pakets und jede Berechtigung darauf.
Umstieg vom Modul
Das Modul bleibt dokumentiert, solange du umsteigst. Füge für jede Einstellung die Provider-Ressource hinzu, importiere sie über den Slug der Organisation, prüfe, dass plan keine Änderungen zeigt, und entferne dann das Argument des Moduls und mit tofu state rm dessen restful_resource.
Wie wir ihn testen
Jede Ressource und Datenquelle läuft bei jeder Änderung mit der echten OpenTofu-CLI gegen eine aufgezeichnete fremforge-API: Nur die Argumente, die du setzt, werden geschrieben, nicht verwaltete Änderungen werden ignoriert, verwalteter Drift wird geplant, der Import liest denselben State zurück, und Destroy verhält sich wie oben beschrieben.
Die Einstellungen, die sich auf einer gemeinsam genutzten Organisation gefahrlos ändern lassen, laufen außerdem jede Nacht live gegen eine Test-Organisation: Apply, ein sauberer Plan, eine Änderung über die API, ein Plan, der sie zeigt, Apply, Import in einen frischen State, ein sauberer Plan, Destroy, ein Zurücklesen und die Wiederherstellung des ursprünglichen Werts. Das sind die Authentifizierungsrichtlinie, die SSH-CA, die SLA-Richtlinie und der Metadatenfilter des Abhängigkeits-Proxys; Paketzugriff, Schutzregeln und Registry-Robots liefen vor dem Release auf dieselbe Weise live. Die IP-Allowlist und die Signaturrichtlinien werden nur gegen die aufgezeichnete API getestet, weil ihre Änderung auf einer gemeinsam genutzten Organisation andere Tests stören würde.