Zum Hauptinhalt springen

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:

FingerprintAEA6 FE24 2C86 33F0 10C6 D4ED 9099 FD4F C1DC 23C4
Schlüssel-ID9099FD4FC1DC23C4
Öffentliches Zertifikathttps://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:

RessourcenScope
Jede Ressource auf dieser Seitepolicy:write
Nur Datenquellenpolicy: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.

RessourceWas sie verwaltet
fremforge_auth_policyAuthentifizierungsrichtlinie: Obergrenze der PAT-Laufzeit (0 = keine Obergrenze), PAT-Scopes, signierte Commits, schlüssellose Signierende, SSH an oder aus.
fremforge_ip_allowlistDie 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_accessOb Operatoren von fremforge die Nutzer der Organisation verwalten dürfen.
fremforge_email_domain_policyOb die Allowlist für E-Mail-Domains durchgesetzt wird.
fremforge_ssh_caDie SSH-Zertifizierungsstelle. require_cert = true braucht einen CA-Schlüssel in derselben Konfiguration.
fremforge_actions_policyActions nur auf geschützten Branches.
fremforge_artifact_retentionAufbewahrung von Actions-Artefakten in Tagen.
fremforge_sla_policyBehebungsfristen pro Schweregrad (0 = keine).
fremforge_push_protectionWas ein Push tut, wenn der Secret-Scanner nicht laufen kann.
fremforge_immutabilityUnverä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_defaultsDie 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_settingsScanner-Einstellungen, eine Ressource pro Scanner.
fremforge_image_signature_policyWelchen Image-Signaturen beim Pull vertraut wird und ob ein unsignierter Pull abgewiesen wird. Mit mode = "shadow" einführen.
fremforge_dependency_proxyDie Einstellungen des Abhängigkeits-Proxys der Organisation.
fremforge_package_signature_policyDie Signaturregel der Paketrichtlinie (Anforderung und vertrauenswürdige Signierende).
fremforge_package_access_defaultDer 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:

RessourceImport-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.

Mit Provider 0.1.0 schlägt ein Block 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_wo allein keine Änderung. Erhöhe value_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. Setze value_wo vor dem nächsten Apply in der Konfiguration.
  • Token-Scopes: Das Token des Providers braucht ci-secrets:read und ci-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-exitcode endet mit 2, wenn eine Einstellung, die du verwaltest, außerhalb deiner Konfiguration geändert wurde; apply stellt sie wieder her.
  • Import: import { to = fremforge_auth_policy.this; id = "acme" }, danach schreibt tofu plan -generate-config-out=generated.tf die 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_access entfernt 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.