feat: add NetBox plugin store
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
# Architektur und Vertrauensgrenzen
|
||||
|
||||
## Komponenten
|
||||
|
||||
```text
|
||||
Forgejo / GitHub
|
||||
|
|
||||
| HTTPS, fest erlaubte Hosts, commit-gebundene Metadaten
|
||||
v
|
||||
+-----------------------+ JSON-Datei oder MariaDB
|
||||
| PHP Store auf Apache |------------------------------+
|
||||
| Sync + Admin + API v1 | |
|
||||
+-----------------------+ |
|
||||
| freigegebener Katalog |
|
||||
v |
|
||||
+-----------------------+ Unix-Socket +--------------------------+
|
||||
| NetBox Store Plugin |--------------->| privilegierter Host-Agent|
|
||||
| unprivilegierte UI | | erneute Prüfung + Lifecycle|
|
||||
+-----------------------+ +--------------------------+
|
||||
|
|
||||
v
|
||||
NetBox-Virtualenv, Konfiguration,
|
||||
Migrationen, Static Files, Dienste
|
||||
```
|
||||
|
||||
Der Store ist eine eigenständige PHP-Anwendung. Apache liefert HTML, Assets und API direkt aus. NetBox benötigt keinen Zugriff auf die Store-Datenbank, und der Store erhält keinen Schreibzugriff auf einen NetBox-Host.
|
||||
|
||||
## Zustandsmodell
|
||||
|
||||
Der persistierte Zustand enthält mindestens diese fachlichen Ebenen:
|
||||
|
||||
- **Source:** Provider, API-/Basis-URL, Owner/Organisation und Synchronisierungsstatus.
|
||||
- **Plugin:** Anzeige- und Installationsmetadaten, Repository, README-Snapshot, Paket-/Importname und NetBox-Kompatibilität.
|
||||
- **Release:** Version, Download-URL, SHA-256, Größe, Commit und Kompatibilität. Da ein Upstream-Asset trotz gleicher URL ersetzt werden kann, wird es bei jedem erfolgreichen Sync erneut gehasht.
|
||||
- **Audit/Admin-Zustand:** Freigaben, Ablehnungen und sicherheitsrelevante Änderungen.
|
||||
|
||||
Die JSON-Implementierung speichert denselben logischen Zustand wie der MariaDB-Adapter. Sie ist für eine einzelne Store-Instanz gedacht und schützt Schreibvorgänge mit Sperre und atomischem Austausch. MariaDB erlaubt einen extern verwalteten, transaktionalen Persistenzdienst. Ein gleichzeitiger Wechsel zwischen beiden Treibern ist kein Replikationsmechanismus.
|
||||
|
||||
## Import-Pipeline
|
||||
|
||||
1. Eine Quelle wird serverseitig konfiguriert; Tokens werden über Umgebungsvariablen referenziert und nicht als Katalogdaten gespeichert.
|
||||
2. Der Provider-Adapter listet Repositories und Releases. Netzwerkzugriffe sind auf HTTPS und eine explizite Hostliste begrenzt. Private/reservierte Adressen sind standardmäßig gesperrt.
|
||||
3. Vor dem Lesen von README oder Packaging-Metadaten wird ein Commit-SHA festgehalten. Relative README-Ressourcen werden an diesen Stand gebunden.
|
||||
4. Kandidaten werden aus Topic, `pyproject.toml`/vergleichbarer Packaging-Konfiguration und der NetBox-`PluginConfig` erkannt. Fehlende oder widersprüchliche Installationsfelder verhindern eine Freigabe.
|
||||
5. Externes Markdown wird in HTML umgewandelt und sanitisiert. Unsichere Schemata und nicht erlaubtes HTML werden nicht in die Store-Seite übernommen.
|
||||
6. Neue Datensätze oder installrelevante Änderungen landen wieder im Freigabezustand `pending`.
|
||||
|
||||
GitHub verwendet dasselbe interne Source-/Plugin-/Release-Modell. Provider-spezifisch bleiben nur Authentifizierung, Pagination und API-Formate.
|
||||
|
||||
## Freigabegatter
|
||||
|
||||
Source, Plugin und Release werden getrennt bewertet. Ein Plugin darf auch ohne installierbares Release sichtbar sein; dann liefert die API `releases: []` und `latest_version: null`. Für einen sichtbaren Plugin-Eintrag gelten:
|
||||
|
||||
- Quelle und Plugin sind aktiv und freigegeben.
|
||||
- Das Plugin besitzt vollständige Paket-, Import- und Kompatibilitätsdaten.
|
||||
|
||||
Ein Release wird diesem Plugin nur dann öffentlich beigefügt, wenn zusätzlich gilt:
|
||||
|
||||
- Das Release ist freigegeben und als unveränderlich markiert.
|
||||
- Artifact-URL, SHA-256 und positive Dateigröße sind vorhanden.
|
||||
- Die gespeicherte Freigabemarkierung gehört exakt zum aktuell freigegebenen Payload.
|
||||
|
||||
Die Freigabemarkierung heißt aus Protokollkompatibilitätsgründen `approved_payload_sha256`. Clients behandeln den 64-stelligen Hexwert als opaken Store-Marker. Er ersetzt nicht den separaten SHA-256 der heruntergeladenen Artifact-Datei.
|
||||
|
||||
## API-Vertrag
|
||||
|
||||
`GET /api/v1/plugins/` liefert eine paginierte Antwort:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_version": "v1",
|
||||
"count": 1,
|
||||
"page": 1,
|
||||
"page_size": 50,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": []
|
||||
}
|
||||
```
|
||||
|
||||
`GET /api/v1/plugins/{slug}/` liefert das Plugin direkt. Sein Kernschema ist:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_version": "v1",
|
||||
"slug": "netbox-example",
|
||||
"name": "Example",
|
||||
"summary": "Example plugin",
|
||||
"description": "Description",
|
||||
"repository_url": "https://git.example/repo",
|
||||
"latest_version": "1.2.3",
|
||||
"package_name": "netbox-example",
|
||||
"import_name": "netbox_example",
|
||||
"min_netbox_version": "4.6.5",
|
||||
"max_netbox_version": "4.6.8",
|
||||
"approved": true,
|
||||
"status": "approved",
|
||||
"releases": []
|
||||
}
|
||||
```
|
||||
|
||||
`GET /api/v1/plugins/{slug}/releases/{version}/` liefert ein Release direkt:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.2.3",
|
||||
"download_url": "https://artifacts.example/netbox_example-1.2.3-py3-none-any.whl",
|
||||
"sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
"artifact_size": 12345,
|
||||
"commit_sha": "",
|
||||
"min_netbox_version": "4.6.5",
|
||||
"max_netbox_version": "4.6.8",
|
||||
"published_at": "2026-08-24T12:00:00Z",
|
||||
"approved": true,
|
||||
"status": "approved",
|
||||
"immutable": true,
|
||||
"approved_payload_sha256": "abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd"
|
||||
}
|
||||
```
|
||||
|
||||
Unbekannte, nicht freigegebene und unvollständige Datensätze werden gegenüber öffentlichen Clients nicht wie installierbare Einträge behandelt.
|
||||
|
||||
## Lifecycle-Sequenz
|
||||
|
||||
1. Ein berechtigter NetBox-Benutzer wählt eine Aktion; echte Mutationen erfordern zusätzlich Superuser-Rechte, CSRF-Prüfung und die Eingabe des exakten Slugs.
|
||||
2. Das NetBox-Plugin liest den freigegebenen Katalog und sendet nur Aktion, Slug, Version, Actor/Request-ID und Store-Freigabemarker an den Unix-Socket.
|
||||
3. Der Agent akzeptiert den Auftrag idempotent und speichert seinen Zustand in SQLite. NetBox wartet nicht auf einen möglichen eigenen Neustart.
|
||||
4. Der Agent ruft Plugin und Release selbst erneut aus dem Store ab. Er vertraut nicht allein auf die vom Webprozess gelieferten Metadaten.
|
||||
5. Er erzwingt Host-/DNS-Regeln, NetBox-Version, Wheel-Dateiname, Paket-/Importname, Dateigröße, Artifact-SHA-256 und den exakten Freigabemarker.
|
||||
6. Host-Änderungen laufen serialisiert unter einem globalen Lock. Installationen bleiben zunächst deaktiviert; Aktivierung ist eine eigene Aktion.
|
||||
7. Der Audit-Eintrag in NetBox fragt den Agent-Status später ab und gleicht Erfolg, Fehler oder manuell zu bereinigenden Zustand ab.
|
||||
|
||||
## Sicherheitsgrenzen
|
||||
|
||||
- Admin-Zugangsdaten existieren nicht als Standardwerte. Alle drei Credential-Variablen müssen gemeinsam gesetzt sein.
|
||||
- Admin-Aktionen verwenden serverseitige Sessions mit geschütztem Cookie, HMAC-gebundene CSRF-Tokens und Login-Ratenbegrenzung.
|
||||
- Source-Tokens dürfen nur an ihren konfigurierten Ursprung gesendet werden. Artifact-Hosts erhalten keine Store-Zugangsdaten.
|
||||
- Der öffentliche Store-Prozess ist nicht privilegiert gegenüber NetBox. Nur der separate, root-konfigurierte Agent darf die Virtualenv, verwaltete Include-/Requirements-Dateien und Dienste verändern.
|
||||
- Der Unix-Socket benötigt Dateisystemrechte und Linux-Peer-Credential-Prüfung. Die NetBox-Service-UID oder -GID muss explizit erlaubt sein.
|
||||
- Der Agent startet mit `dry_run = true`, blockiert Selbstverwaltung und verwendet feste Argumentlisten statt Shell-Kommandos.
|
||||
- Das aktuelle Verfahren ist kein vollständiges Supply-Chain-Framework. Artifact-Build-Provenienz, Signierung, Abhängigkeitsbereitstellung und Recovery nach bereits angewandten Migrationen bleiben Betreiberaufgaben.
|
||||
|
||||
## Betriebsgrenzen
|
||||
|
||||
Der JSON-Treiber ist für genau eine Store-Installation auf einem lokalen Dateisystem ausgelegt. Apache-Worker und der lokale Scheduler koordinieren sich dabei über die Dateisperre. Bei mehreren Hosts/Replikaten ist MariaDB zu verwenden, und Scheduler/Sync müssen so betrieben werden, dass nicht mehrere Instanzen dieselbe Quelle gleichzeitig bearbeiten. Store-Daten, Agent-Journal, Backups, Konfiguration und Token-Dateien gehören in die reguläre Backup- und Restore-Planung.
|
||||
Reference in New Issue
Block a user