17 KiB
MrBlake NetBox Plugin Store
Dieses Repository besteht aus drei getrennten Komponenten:
| Pfad | Aufgabe |
|---|---|
store/ |
Eigenständiger PHP-Store, direkt durch Apache ausgeliefert. Er synchronisiert Forgejo-/GitHub-Repositories, rendert README-Dateien und stellt den kuratierten API-Katalog bereit. |
netbox_plugin/ |
NetBox-Oberfläche für Katalog, Installation und Plugin-Lebenszyklus. Unterstützt NetBox 4.6.5 bis 4.6.8. |
host_agent/ |
Kleine privilegierte Linux-Komponente, die freigegebene Artefakte prüft, Source-Archive lokal als Wheel baut und die Änderungen am NetBox-Host ausführt. |
Für NetBox-Hosts steht zusätzlich install.sh bereit. Der interaktive Installer kann Plugin, Host-Agent, systemd-Socket, Requirements und den verwalteten Block in configuration.py in einem Durchlauf einrichten. Er startet standardmäßig im Dry-Run-Modus, legt vor Änderungen Backups an und verlangt für echte Lifecycle-Änderungen eine zusätzliche ausdrückliche Bestätigung.
Der Store verwendet standardmäßig eine lokale JSON-Datei. MariaDB ist optional. Die konfigurierte Standardquelle gilt als vom Betreiber freigegeben; weitere Quellen werden im Admin-Bereich zunächst ausstehend angelegt. Automatisch erkannte Plugins und Releases erscheinen erst nach einer ausdrücklichen Freigabe im öffentlichen Katalog.
Native Installation mit Apache und JSON
Docker ist nicht erforderlich. Der primäre Betriebsweg ist eine normale PHP-Anwendung unter Apache. Benötigt werden:
- PHP 8.3 oder neuer mit CLI und Apache-Modul/FPM (getestet mit PHP 8.4);
- die PHP-Erweiterungen cURL, DOM/XML, intl, JSON, mbstring und PDO; für MariaDB zusätzlich
pdo_mysql; - Composer 2;
- Apache 2.4 mit
mod_rewrite;mod_headerswird für die zusätzlichen Header aus.htaccessempfohlen.
Unter Debian/Ubuntu sind die wesentlichen Pakete beispielsweise:
sudo apt install apache2 composer libapache2-mod-php8.3 php8.3-cli \
php8.3-curl php8.3-intl php8.3-mbstring php8.3-mysql php8.3-xml
sudo a2enmod rewrite headers expires
Die Paketnamen anderer Distributionen unterscheiden sich. Bei PHP-FPM muss Apache stattdessen mit dem passenden FPM-Handler konfiguriert werden.
Anwendung und Konfiguration
Dieses Beispiel verwendet /var/www/netbox-plugin-store als Repository-Pfad. Nur store/public darf durch Apache veröffentlicht werden. Das Repository kann zunächst unter einem normalen Deployment-Benutzer ausgecheckt und danach an diesen Pfad verschoben werden:
git clone https://git.mrblake.cc/MrBlake/Netbox-Store.git netbox-plugin-store
sudo mv netbox-plugin-store /var/www/netbox-plugin-store
sudo chown -R "$(id -un):$(id -gn)" /var/www/netbox-plugin-store
cd /var/www/netbox-plugin-store/store
composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
cp .env.example .env
Zuerst werden ein eigenes Admin-Passwort und ein unabhängiges Session-Geheimnis erzeugt:
php bin/console hash-password 'EIN-LANGES-ZUFÄLLIGES-PASSWORT'
php -r 'echo bin2hex(random_bytes(32)), PHP_EOL;'
Das Klartextpasswort sollte in der Praxis aus einer temporären, nicht protokollierten Shell-Variablen übergeben werden. In store/.env werden mindestens die folgenden Werte ersetzt. Der Argon2id-Hash steht in einfachen Anführungszeichen, damit seine $-Zeichen unverändert bleiben:
APP_ENV=production
STORE_PUBLIC_URL=https://plugins.example.internal
STORE_COOKIE_SECURE=true
STORE_ADMIN_USERNAME=mein-admin
STORE_ADMIN_PASSWORD_HASH='$argon2id$...'
STORE_SESSION_SECRET=...
STORE_DB_DRIVER=json
STORE_JSON_PATH=/var/www/netbox-plugin-store/store/data/store.json
Bei einem reinen HTTP-Testsystem müssen URL und STORE_COOKIE_SECURE=false zusammenpassen. Ein extern erreichbarer Produktivbetrieb sollte TLS direkt in Apache terminieren.
Der Anwendungscode bleibt root-owned; nur .env ist für die Apache-Gruppe lesbar und nur store/data ist für den Web-/Scheduler-Benutzer schreibbar:
sudo chown -R root:root /var/www/netbox-plugin-store
sudo chown root:www-data /var/www/netbox-plugin-store/store/.env
sudo chmod 0640 /var/www/netbox-plugin-store/store/.env
sudo install -d -o www-data -g www-data -m 0750 \
/var/www/netbox-plugin-store/store/data
sudo chown -R www-data:www-data /var/www/netbox-plugin-store/store/data
Apache VirtualHost
Der VirtualHost zeigt direkt auf store/public. AllowOverride All ist erforderlich, damit die mitgelieferte .htaccess das Front-Controller-Rewrite aktivieren kann:
<VirtualHost *:80>
ServerName plugins.example.internal
DocumentRoot /var/www/netbox-plugin-store/store/public
<Directory /var/www/netbox-plugin-store/store/public>
Options -Indexes
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/netbox-plugin-store-error.log
CustomLog ${APACHE_LOG_DIR}/netbox-plugin-store-access.log combined
</VirtualHost>
Die Konfiguration wird zum Beispiel als /etc/apache2/sites-available/netbox-plugin-store.conf gespeichert und aktiviert:
sudo a2ensite netbox-plugin-store.conf
sudo apache2ctl configtest
sudo systemctl reload apache2
Für Produktion wird derselbe DocumentRoot in einem TLS-VirtualHost verwendet. Weder das Repository-Wurzelverzeichnis noch store/ selbst dürfen als DocumentRoot dienen, da dort Konfiguration und Laufzeitdaten liegen.
Initialisierung und erster Import
CLI und Apache müssen den JSON-Zustand mit derselben UID verwalten. Deshalb laufen Bootstrap und Sync als Apache-Benutzer:
cd /var/www/netbox-plugin-store/store
sudo -u www-data php bin/console bootstrap
sudo -u www-data php bin/console sync
Danach sind Website und /healthz erreichbar. Die Admin-Anmeldung liegt unter /admin. Importierte Kandidaten bleiben bis zur manuellen Prüfung und Freigabe unsichtbar für den öffentlichen API-Katalog.
Regelmäßige Synchronisierung
Bevorzugt läuft der Watch-Modus als eigener systemd-Dienst. Beispiel für /etc/systemd/system/netbox-plugin-store-sync.service:
[Unit]
Description=NetBox Plugin Store repository sync
After=network-online.target apache2.service
Wants=network-online.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/netbox-plugin-store/store
ExecStart=/usr/bin/php /var/www/netbox-plugin-store/store/bin/console sync --watch --interval=900
Restart=on-failure
RestartSec=10
UMask=0077
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict
ReadWritePaths=/var/www/netbox-plugin-store/store/data
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now netbox-plugin-store-sync.service
sudo systemctl status netbox-plugin-store-sync.service
Alternativ kann www-data den einmaligen Befehl per Cron aufrufen. Dazu sudo crontab -u www-data -e öffnen und die folgende Zeile eintragen. flock verhindert überlappende Läufe:
*/15 * * * * /usr/bin/flock -n /var/www/netbox-plugin-store/store/data/sync-cron.lock /bin/sh -c 'cd /var/www/netbox-plugin-store/store && /usr/bin/php bin/console sync' 2>&1 | /usr/bin/logger -t netbox-plugin-store-sync
Der Store selbst weist zusätzlich jeden parallelen Sync-Lauf ab. Pro Lauf gelten standardmäßig 900 Sekunden, 2.500 HTTP-Requests, 1 GiB Downloadvolumen, 2.000 Repositories und 1.000 Releases als gemeinsame Obergrenzen. Diese Werte lassen sich mit STORE_SYNC_MAX_SECONDS, STORE_SYNC_MAX_REQUESTS, STORE_SYNC_MAX_BYTES, STORE_SYNC_MAX_REPOSITORIES und STORE_SYNC_MAX_RELEASES in store/.env an die eigene Instanz anpassen.
Native MariaDB-Option
JSON und MariaDB sind alternative Zustands-Speicher. Ein Wechsel des Treibers migriert bestehende Freigaben nicht automatisch. Nach dem Anlegen einer eigenen Datenbank und eines eingeschränkten Datenbankbenutzers werden diese Werte in store/.env gesetzt:
STORE_DB_DRIVER=mariadb
STORE_MARIADB_DSN=mysql:host=127.0.0.1;port=3306;dbname=netbox_store;charset=utf8mb4
STORE_MARIADB_USER=netbox_store
STORE_MARIADB_PASSWORD=EIN-EIGENES-ZUFÄLLIGES-PASSWORT
Die erforderliche Tabelle wird bei Bootstrap, Sync oder dem ersten Web-Aufruf angelegt. Das Datenbankkonto benötigt dafür Rechte auf genau diese Datenbank. STORE_JSON_PATH wird im MariaDB-Modus nicht verwendet.
Optional: Docker Compose
Alternativ kann derselbe Apache/PHP-Store in einem Container laufen. Der Standard-Stack bindet seinen Port absichtlich nur an localhost und verwendet ein benanntes JSON-Volume:
cp .env.example .env
docker compose build store
Unter Windows kann statt cp der Befehl copy .env.example .env verwendet werden. Vor dem Start werden in der Root-.env eigene Admin-Zugangsdaten gesetzt. Ohne vollständige Admin-Konfiguration bleibt die Anmeldung deaktiviert; es gibt kein Standardpasswort.
Hash und Session-Geheimnis lassen sich mit dem gebauten Image erzeugen:
docker compose run --rm --no-deps -e STORE_SCHEDULER_ENABLED=false store php bin/console hash-password 'EIN-LANGES-ZUFÄLLIGES-PASSWORT'
docker compose run --rm --no-deps -e STORE_SCHEDULER_ENABLED=false store php -r 'echo bin2hex(random_bytes(32)), PHP_EOL;'
Danach:
docker compose up -d --build
Der im Container aktivierte Scheduler führt den ersten Import unmittelbar aus. Mit docker compose logs -f store lässt sich der Lauf verfolgen. Für einen ausschließlich manuellen Sync wird vorher STORE_SCHEDULER_ENABLED=false gesetzt und anschließend docker compose exec store php bin/console sync aufgerufen.
Der Store ist standardmäßig auf http://127.0.0.1:8080 erreichbar. Für MariaDB werden in .env mindestens MARIADB_PASSWORD und MARIADB_ROOT_PASSWORD auf unterschiedliche, zufällige Werte gesetzt und das Overlay zugeschaltet:
docker compose -f compose.yaml -f compose.mariadb.yaml up -d --build
MariaDB erhält keinen Host-Port und ist nur im internen Compose-Netz erreichbar. Der Store wartet auf den Datenbank-Healthcheck.
Synchronisierung und Freigabe
Der voreingestellte Forgejo-Import liest Repositories von https://git.mrblake.cc. GitHub ist bereits als zweiter Provider implementiert und kann im Admin-Bereich über dieselbe Quellenverwaltung hinzugefügt werden.
Mit einem Token lassen sich auch Metadaten und README privater GitHub-Repositories synchronisieren. Private Artefakte bleiben in API v1 bewusst nicht installierbar, weil der Host-Agent keine Provider-Zugangsdaten erhält. Öffentliche Projekte benötigen kein Release: Fehlt ein Wheel-Release, erzeugt der Store einen separat freizugebenden Source-Kandidaten für den exakten aktuellen Commit.
Der normale Ablauf ist:
- Der Scheduler oder
php bin/console syncliest Repository-Metadaten, Manifest/Packaging-Konfiguration, Releases und die README eines festen Commit-SHA ein. - Neue oder in installrelevanten Feldern geänderte Datensätze erhalten den Status
pending. - Ein Administrator prüft Quelle, Paket-/Importname, NetBox-Kompatibilität, Artifact-URL, Größe und SHA-256.
- Quelle, Plugin und Release werden getrennt freigegeben. Eine Freigabe ist keine automatische Folge der Synchronisierung.
- Nur aktive, vollständige und freigegebene Datensätze erscheinen unter
/api/v1/plugins/.
Die Detailseite wird aus der gespeicherten README erzeugt. Relative Links und Bilder werden auf den synchronisierten Commit aufgelöst; HTML wird vor der Ausgabe bereinigt. Verändert sich ein Artifact oder ein installrelevantes Metadatum, muss das Release erneut geprüft werden.
NetBox-Host installieren
Der empfohlene Weg ist der interaktive Root-Installer. Er installiert das NetBox-Plugin und den separaten Host-Agent, erkennt Dienstkonto sowie NetBox-Version, richtet Requirements und systemd ein und verwaltet einen klar markierten Block in configuration.py.
curl -fsSLo /tmp/netbox-store-install.sh \
https://git.mrblake.cc/MrBlake/Netbox-Store/raw/branch/main/install.sh
less /tmp/netbox-store-install.sh
sudo bash /tmp/netbox-store-install.sh
Das Script bietet zwei Modi:
- Sicherer Testmodus: Lifecycle-Aufträge bleiben Dry-Runs und verändern keine Plugins.
- Produktionsmodus: Echte Änderungen laufen ausschließlich über den Host-Agent. Zusätzlich zur Zusammenfassung muss der Betreiber exakt
ECHT INSTALLIERENbestätigen.
Alle während eines Laufs geladenen Komponenten werden auf denselben angezeigten Commit gepinnt. Vorhandene Dateien werden unter /var/backups/netbox-plugin-store/<Zeitstempel>/ gesichert. Das Script kann für Updates erneut ausgeführt werden: Es ersetzt nur seinen markierten Konfigurationsblock und seine eigenen Requirement-Einträge.
Nach der Einrichtung prüft der Installer den Agent-Socket als NetBox-Dienstkonto. Eine echte Plugin-Installation muss im Audit Dry-Run: Nein zeigen. Neu installierte Plugins bleiben zunächst deaktiviert und werden anschließend über eine separate Aktion aktiviert.
Manuelle Plugin-Installation
Das Plugin unter netbox_plugin/ ist auf NetBox 4.6.5–4.6.8 begrenzt. Zuerst wird ein Wheel gebaut und in die NetBox-Virtualenv installiert:
cd netbox_plugin
python -m build
/opt/netbox/venv/bin/pip install dist/netbox_plugin_store-0.1.3-py3-none-any.whl
Das Paket muss außerdem in /opt/netbox/local_requirements.txt festgehalten werden. In configuration.py wird es zunächst sicher im Dry-run-Modus eingerichtet:
PLUGINS = [
"netbox_plugin_store",
]
PLUGINS_CONFIG = {
"netbox_plugin_store": {
"store_url": "https://plugins.example.internal",
"allowed_store_urls": ["https://plugins.example.internal"],
"allowed_artifact_urls": [
"https://plugins.example.internal",
"https://git.mrblake.cc",
],
"execution_mode": "dry_run",
},
}
Danach wird der übliche NetBox-Upgrade-Ablauf mit Migration, collectstatic und Neustart von Web- und RQ-Dienst ausgeführt. Details und alle Einstellungen stehen in netbox_plugin/README.md.
Manuelle Produktionseinrichtung: Host-Agent
Für echte Installationen ist execution_mode = "agent" die vorgesehene Trennung: Der NetBox-Webprozess bleibt unprivilegiert und sendet einen kleinen Auftrag über den Unix-Socket /run/netbox-store-agent/agent.sock. Der Agent ruft Plugin und Release erneut aus dem Store ab, vergleicht die Freigabemarkierung und prüft Wheel, Dateigröße und SHA-256 vor jeder Änderung.
Kurzablauf auf dem Linux-NetBox-Host:
-
Das Paket aus
host_agent/in eine administrative Python-Umgebung installieren. -
host_agent/examples/agent.tomlnach/etc/netbox-store-agent/agent.tomlkopieren, alle Hosts, Pfade, UIDs/GIDs und die NetBox-Version anpassen und die Datei root-owned mit Modus0600schützen. -
Die beiden Dateien aus
host_agent/systemd/nach/etc/systemd/system/kopieren. InsbesondereSocketGroupmuss zur NetBox-Servicegruppe passen. -
Die vom Agent verwaltete Plugin-Liste einmalig in
configuration.pyeinbinden:from netbox.store_plugins import STORE_PLUGINS PLUGINS += STORE_PLUGINS -
Socket aktivieren und zunächst im voreingestellten Dry-run-Modus testen:
systemctl daemon-reload systemctl enable --now netbox-store-agent.socket netbox-store-agent capabilities -
Erst nach erfolgreichen Katalog- und Lifecycle-Tests
dry_run = falsein der root-geschützten Agent-Konfiguration setzen und den Dienst neu starten.
Die NetBox-Konfiguration wird dann ergänzt:
PLUGINS_CONFIG["netbox_plugin_store"].update({
"execution_mode": "agent",
"agent_socket_path": "/run/netbox-store-agent/agent.sock",
"agent_timeout": 30,
})
Der Agent installiert ausschließlich freigegebene, unveränderliche Artefakte. Source-Archive werden commitgebunden geprüft, offline lokal in ein Wheel gebaut und als gehashte Datei unter /opt/netbox/.netbox-store-wheels persistiert. Abhängigkeiten und Build-Backends müssen bereits durch den Betreiber bereitgestellt sein. Vollständige Sicherheits- und Recovery-Hinweise stehen in host_agent/README.md.
API v1
Der öffentliche, nur freigegebene Katalog stellt diese Endpunkte bereit:
GET /api/v1/plugins/
GET /api/v1/plugins/{slug}/
GET /api/v1/plugins/{slug}/releases/{version}/
Ein Plugin enthält unter anderem package_name, import_name, min_netbox_version, max_netbox_version und releases. Ein installierbares Release enthält download_url, Artifact-sha256, artifact_size, immutable und die opake Freigabemarkierung approved_payload_sha256. Der genaue Datenfluss und die Vertrauensgrenzen sind in docs/ARCHITECTURE.md beschrieben.
Lokale Prüfungen
Die Komponenten bringen getrennte Prüfungen mit; sie können ohne produktive NetBox- oder Host-Änderungen ausgeführt werden:
cd store && composer install && composer test
cd host_agent && python -m pip install -e ".[dev]" && pytest
cd netbox_plugin && python -m unittest discover -s tests -v