chore(release): 0.1.0 – Server-Verwaltung und Verbindungstest

Etappe 1 abgeschlossen und auf dem Testhost pve-demo (PVE 9.2.2)
abgenommen: Rolle und Token nach docs/proxmox-rechte.md angelegt,
Fingerprint und SSH-Host-Key gegen den Node verglichen, Verbindungstest
mit und ohne SSH.

Der Fingerprint-Hinweis nennt jetzt ausdrücklich pve-ssl.pem bzw.
pveproxy-ssl.pem – pvenode cert info listet zuerst die Root-CA.

Closes #003

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 10:01:54 +02:00
co-authored by Claude Opus 5.5
parent d64b8fb24d
commit a9a48dccfa
8 changed files with 46 additions and 24 deletions
+15 -6
View File
@@ -7,6 +7,18 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/lang/de/).
## [Unveröffentlicht]
### Hinzugefügt
### Geändert
### Behoben
### Entfernt
### Sicherheit
## [0.1.0] – 2026-10-12
### Hinzugefügt
- Architektur- und Umsetzungsplan (`docs/architektur.md`) mit Prüfung der Proxmox-API 8.x/9.x und der Image-Quellen (#001)
- Issues #001–#020 für die sechs Etappen und den Feldtest (#001)
@@ -17,14 +29,11 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/lang/de/).
- Server-Verwaltung: Proxmox-Server anlegen, bearbeiten, löschen; Zertifikat ansehen und Fingerprint pinnen; Verbindung vor dem Speichern prüfen (#002)
- Optionales SSH-Schlüsselpaar je Server mit Einzeiler zum Hinterlegen auf den Nodes (#002)
- Server-Umschalter in der Seitenleiste, Serverliste auf der Übersicht (#002)
- Verbindungstest: Version, Cluster, Nodes, Storages, Bridges, VNets und die Rechte des Tokens; je Vorgang, ob er per API, per SSH oder gar nicht geht (#003)
- SSH zu den Nodes mit Host-Key-Pinning nach Bestätigung (#003)
- `docs/proxmox-rechte.md`: Rolle `ProxDeploy` für PVE 8 und 9 mit fertigen `pveum`-Befehlen (#003)
- Proxmox-Client mit verständlichen Fehlern (Ursache + Hinweis, fehlendes Recht samt Pfad) und Warten auf Proxmox-Tasks (#002)
### Geändert
### Behoben
### Entfernt
### Sicherheit
- Geheimnisse werden mit AES-256-GCM verschlüsselt, Schlüssel aus `PROXDEPLOY_SCHLUESSEL` (#001)
- Passwörter mit argon2id; Sperre nach zehn Fehlversuchen in 15 Minuten; Content-Security-Policy mit Nonce (#001)
+7 -2
View File
@@ -2,7 +2,7 @@
## Zeitübersicht
**Gesamt:** 1 h 01 min (laufende Sitzung)
**Gesamt:** 1 h 15 min (laufende Sitzung)
Zeiten stammen aus den Git-Commit-Zeitstempeln je Issue; Wartezeit auf Rückmeldungen ist enthalten.
@@ -10,10 +10,11 @@ Zeiten stammen aus den Git-Commit-Zeitstempeln je Issue; Wartezeit auf Rückmeld
|---|---|
| #001 Projekt-Setup | 0 h 50 min |
| #002 Server-Verwaltung | 0 h 11 min |
| #003 Verbindungstest | 0 h 25 min |
| Meilenstein | Zeit |
|---|---|
| v0.1.0 | – |
| v0.1.0 | 1 h 15 min | **abgeschlossen**
---
@@ -28,6 +29,9 @@ Zeiten stammen aus den Git-Commit-Zeitstempeln je Issue; Wartezeit auf Rückmeld
- Image-URLs, Prüfsummen-Dateien, LXC-Template-Namen, Docker- und Zabbix-Repos per Abruf geprüft
- Git-Repository (`main`), CHANGELOG, DEVLOG, Architekturplan und 20 Issues angelegt
- Plan freigegeben: Ubuntu 26.04 dazu, Templates mit UEFI, Template-VMID = nächste freie im Template-Bereich, Namensabbildung bestätigt
- #003 abgeschlossen: Verbindungstest mit Fähigkeitserkennung und SSH-Host-Key-Pinning; 127 Tests
- Feldtest auf dem Testhost `pve-demo` (PVE 9.2.2): Rolle/Token nach Doku per SSH angelegt, Fingerprint und Host-Key gegen den Node verglichen, Test mit und ohne SSH – alles wie erwartet
- Meilenstein v0.1.0 abgeschlossen, getaggt und nach Gitea gepusht
- #002 abgeschlossen: Server-Verwaltung mit Fingerprint-Pinning, Proxmox-Client, Proxmox-Mock (HTTPS, selbst signiert); 99 Tests, 10 Playwright-Abläufe
- Testhost 10.0.10.228 von Florian bereitgestellt (Installation läuft)
- #001 abgeschlossen: Grundgerüst mit Anmeldung, Seitenleiste, beiden Themes; 60 Tests, 8 Playwright-Abläufe (Desktop + 390 px)
@@ -43,4 +47,5 @@ Zeiten stammen aus den Git-Commit-Zeitstempeln je Issue; Wartezeit auf Rückmeld
- **Sitzungsgeheimnis und Datenschlüssel getrennt** (`SITZUNG_GEHEIMNIS`, `PROXDEPLOY_SCHLUESSEL`): ein Tausch des Sitzungsgeheimnisses meldet nur alle ab, der Datenschlüssel darf sich nie ändern.
- **Amber und Rot im hellen Theme abgedunkelt** (`#8A5A00`, `#B91C1C`): die Originaltöne erreichen auf `#F4F6F8` keine 4,5:1.
- **TLS-Pinning über einen eigenen undici-Connector** statt `rejectUnauthorized: false` + Nachprüfen: die Verbindung wird getrennt, bevor das Token gesendet wird. Sitzungs-Cache aus, sonst fehlt bei Wiederaufnahme das Zertifikat.
- **Zugang zum Testhost über einen eigenen SSH-Schlüssel** (`~/.ssh/proxdeploy-testhost`), nicht über das root-Passwort: Claude Code verweigert das Anlegen von Zugängen mit Passwort, und ein Schlüssel nur für diesen Host ist ohnehin der sauberere Weg.
- **Deutsche Bezeichner** wie in ProxLedger und Laufzettel; YAML-Schlüssel im Katalog bleiben englisch (Vorgabe).
+17 -10
View File
@@ -1,25 +1,32 @@
# #003 Verbindungstest und Fähigkeitserkennung
**Status:** offen
**Status:** erledigt
**Meilenstein:** v0.1.0
**Labels:** feature, proxmox
**Geschätzt:** 6 h **Tatsächlich:** –
**Geschätzt:** 6 h **Tatsächlich:** 0 h 25 min
## Ziel
Button „Verbindung testen“ zeigt Version, Cluster/Nodes, Storages, Bridges/VLANs und was per API bzw. nur per SSH geht.
## Aufgaben
- [ ] `/version`, `/cluster/status`, `/cluster/resources`, `/nodes/{n}/storage`, `/nodes/{n}/network`, SDN-VNets
- [ ] Rechte über `/access/permissions` gegen Rolle (8.x/9.x-Variante) abgleichen, fehlende einzeln nennen
- [ ] Fähigkeiten: download-url import (≥ 8.4), import-from per Volume, Snippets-Storage, SSH erreichbar
- [ ] Übersichtsseite mit Serverstatus und freien Ressourcen; Serverdetail mit Nodes/Storages/Bridges
- [ ] Proxmox-Mock für Tests (8.3, 8.4, 9.2 als Varianten)
- [x] `/version`, `/cluster/status`, `/nodes`, `/nodes/{n}/version|storage|network`, SDN-VNets
- [x] Rechte über `/access/permissions?path=…` gegen Rolle (8.x/9.x-Variante) abgleichen, fehlende einzeln nennen
- [x] Fähigkeiten: download-url import (≥ 8.4), import-from per Volume, Snippets-Storage, SSH erreichbar
- [x] SSH je Node mit Host-Key-Pinning (erst erkunden, dann bestätigen)
- [x] Übersichtsseite mit Serverstatus; Serverdetail mit Nodes/Storages/Bridges/Rechten/Fähigkeiten
- [x] Proxmox-Mock für Tests (8.3, 8.4, 9.2, Cluster) und SSH-Mock
- [x] `docs/proxmox-rechte.md` mit `pveum`-Befehlen; Test hält die Liste mit dem Code gleich
## Akzeptanzkriterien
- [ ] Gegen echten Proxmox geprüft (sobald Testumgebung da ist)
- [ ] Tests decken 8.3 (SSH nötig), 8.4 und 9.2 ab
- [x] Gegen echten Proxmox geprüft: Testhost `pve-demo` (10.0.10.228), PVE 9.2.2, am 2026-10-12
- [x] Tests decken 8.3 (SSH nötig), 8.4 und 9.2 ab
## Abhängigkeiten
#002
## Notizen / Entscheidungen
–
- **Feldtest am 2026-10-12 auf PVE 9.2.2:** Rolle, Benutzer und Token genau nach `docs/proxmox-rechte.md` angelegt; alle Pflichtrechte erkannt, nur die vier optionalen fehlen. Fingerprint aus ProxDeploy = `pve-ssl.pem` laut `pvenode cert info`; SSH-Host-Key = `ssh-keygen -lf` auf dem Node. Mit SSH: Snippets, `pct exec` und erweiterte LXC-Features per SSH, Template-Bau per API.
- **`pvenode cert info` listet zuerst die Root-CA** – der Hinweis im Formular nennt deshalb ausdrücklich `pveproxy-ssl.pem` bzw. `pve-ssl.pem`.
- **Ein Host-Key lässt sich nur bestätigen, wenn der letzte Test ihn gesehen hat** – über die API kann niemand einen beliebigen Schlüssel unterschieben.
- **Fehlende Leserechte sind Warnungen, kein Abbruch:** der Bericht soll gerade bei unvollständiger Rolle helfen.
- **Statussymbole gezeichnet:** ◐ und ○ fehlen im Web-Subset von JetBrains Mono.
- Feld `Server.faehigkeiten` heißt jetzt `letzterTest` (Migration per RENAME, keine Daten verloren).
+1 -1
View File
@@ -6,7 +6,7 @@ Details und Architektur: [../architektur.md](../architektur.md). Zeiten werden a
|---|---|---|---|---|---|
| [#001](001-projekt-setup.md) | Projekt-Setup | erledigt | v0.1.0 | 4 h | 0 h 50 min |
| [#002](002-verschluesselung-server.md) | Server-Verwaltung und Verschlüsselung | erledigt | v0.1.0 | 6 h | 0 h 11 min |
| [#003](003-verbindungstest.md) | Verbindungstest und Fähigkeitserkennung | offen | v0.1.0 | 6 h | – |
| [#003](003-verbindungstest.md) | Verbindungstest und Fähigkeitserkennung | erledigt | v0.1.0 | 6 h | 0 h 25 min |
| [#004](004-katalog.md) | Katalog: Distributionen und Profile als Daten | offen | v0.2.0 | 5 h | – |
| [#005](005-adapter-profile.md) | Adapter debian und Profil-Engine | offen | v0.2.0 | 6 h | – |
| [#006](006-auftraege-sse.md) | Aufträge, Phasen, worker und Live-Fortschritt | offen | v0.2.0 | 8 h | – |
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "proxdeploy",
"version": "0.1.0-dev",
"version": "0.1.0",
"private": true,
"type": "module",
"description": "VMs und LXC-Container auf Proxmox VE ausrollen, per Web oder CLI",
@@ -245,7 +245,7 @@ export function Verbindungsbericht({
<div className="flex flex-col gap-2">
<p className="text-muted text-xs font-medium tracking-wide uppercase">SSH</p>
{knoten.ssh.zustand === "ok" ? (
<Status art="erledigt">Verbunden als {knoten.ssh.host} · Host-Key bestätigt</Status>
<Status art="erledigt">Verbunden mit {knoten.ssh.host} · Host-Key bestätigt</Status>
) : knoten.ssh.zustand === "nicht_bestaetigt" ? (
<div className="flex flex-col gap-2">
<Status art="laeuft">
+3 -2
View File
@@ -219,8 +219,9 @@ export function ServerFormular({ server }: { server?: ServerSicht }) {
<span className="text-muted text-sm">SHA-256-Fingerprint</span>
<Code>{zertifikat.fingerabdruck}</Code>
<p className="text-muted text-sm">
Zum Vergleichen auf dem Node: <span className="font-mono">pvenode cert info</span> oder in
der Proxmox-Oberfläche unter Node → System → Zertifikate.
Zum Vergleichen auf dem Node: <span className="font-mono">pvenode cert info</span> –
maßgeblich ist <span className="font-mono">pveproxy-ssl.pem</span>, falls vorhanden, sonst{" "}
<span className="font-mono">pve-ssl.pem</span> (nicht die Root-CA).
</p>
</div>
<Schalter
+1 -1
View File
@@ -2,7 +2,7 @@
* Version und Git-Stand für Seitenleiste, `/api/v1/ich` und die Kopfzeile der CLI.
* Beide Werte werden beim Bauen gesetzt (Docker-Build-Arg bzw. Installer).
*/
export const VERSION = process.env["NEXT_PUBLIC_APP_VERSION"] ?? "0.1.0-dev";
export const VERSION = process.env["NEXT_PUBLIC_APP_VERSION"] ?? "0.1.0";
export const GIT_SHA = (process.env["NEXT_PUBLIC_GIT_SHA"] ?? "").slice(0, 7);
export function versionText(): string {