Enterprise-Backup-, Recovery-, Verification-, Security- und Monitoring-Plattform fuer Proxmox VE, Windows, Linux und Dateisysteme. Der Leitsatz, der fast jede Entscheidung erklaert: Ein Backup gilt erst als vertrauenswuerdig, wenn Integritaet geprueft und Wiederherstellbarkeit nachgewiesen wurde. Deshalb steigt ein Wiederherstellungspunkt erst nach einem tatsaechlich durchgefuehrten Restore-Test auf "recoverable", und Unbekanntes geht in keine Bewertung als "gut" ein. Umfang (Phasen 0-23): - Repository Engine: inhaltsadressierte Bloecke, atomares Commit-Protokoll, Katalogaufbau allein aus den Manifesten — ohne Datenbank - Backup Engine: inhaltsabhaengiges Chunking, Deduplizierung trotz Verschluesselung, zstd, AES-256-GCM, Streaming mit Gegendruck - Agenten fuer Windows und Linux mit Auftragsabholung (Pull-Modell) - Proxmox-Provider mit beiden Zugriffswegen auf die Sicherungsarchive - Scheduler, Recovery Engine mit Pruefpunkt, Verification, Unveraenderlichkeit - Weboberflaeche, Kennzahlen, Meldungen, Berichte, Security Center, Ransomware-Heuristik (meldet, handelt nie) - Disaster Recovery, Haertung, Leistungsmessung, Chaos Testing - Eingefrorene Vertraege fuer API, Migrationen, Backup-Format und Repository - Auslieferungspaket fuer linux/amd64, linux/arm64 und windows/amd64 Nicht enthalten und als solches gekennzeichnet: Kapazitaetsprognose, Backup Copy, Changed Block Tracking bei Proxmox, erweiterte Attribute und ACLs. Gebaut, aber nie auf echter Hardware gefahren: der Windows-Dienst, die systemd-Einheit und der verpflichtende Proxmox-Meilenstein — ob eine wiederhergestellte VM startet, ist ungeprueft. Einzelheiten in CHANGELOG.md und docs/release-candidate.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
169 lines
7.8 KiB
SQL
169 lines
7.8 KiB
SQL
-- Agents und ihre Anmeldung (Phase 5).
|
|
--
|
|
-- Grundsatz aus PROMPT.md §59: Ein kompromittierter Agent darf nicht
|
|
-- automatisch vollständigen Zugriff auf die Backup-Infrastruktur erhalten.
|
|
-- Daraus folgt der gesamte Aufbau dieses Schemas:
|
|
--
|
|
-- - Ein Agent meldet sich nie mit Benutzerzugangsdaten an, sondern mit einem
|
|
-- eigenen Token, das ausschließlich agentspezifische Rechte trägt.
|
|
-- - Die Aufnahme läuft über ein einmaliges, zeitlich begrenztes
|
|
-- Enrollment-Token. Es taugt nur zur Registrierung, nicht zum Betrieb.
|
|
-- - Jedes Token ist einzeln widerrufbar, ohne andere Agents zu stören.
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- Agents
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
CREATE TABLE agents (
|
|
-- id ist der öffentliche Bezeichner des Agents.
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
-- name ist die sprechende Bezeichnung in der Oberfläche.
|
|
name TEXT NOT NULL,
|
|
-- hostname ist der vom Agent gemeldete Rechnername.
|
|
hostname TEXT,
|
|
-- platform benennt das Betriebssystem, z. B. windows oder linux.
|
|
platform TEXT NOT NULL,
|
|
-- architecture benennt die Rechnerarchitektur, z. B. amd64.
|
|
architecture TEXT,
|
|
-- version ist die Programmversion des Agents.
|
|
--
|
|
-- Sie wird gebraucht, um veraltete Agents zu erkennen (PROMPT.md §90).
|
|
version TEXT,
|
|
-- status ist der Betriebszustand des Agents.
|
|
status TEXT NOT NULL DEFAULT 'active',
|
|
-- last_heartbeat_at ist der Zeitpunkt der letzten Lebendmeldung in UTC.
|
|
last_heartbeat_at TIMESTAMPTZ,
|
|
-- last_ip_address ist die zuletzt gesehene Absenderadresse.
|
|
last_ip_address INET,
|
|
-- registered_at ist der Zeitpunkt der Aufnahme in UTC.
|
|
registered_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
-- created_at ist der Anlagezeitpunkt in UTC.
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
-- updated_at ist der Zeitpunkt der letzten Änderung in UTC.
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
-- revoked_at kennzeichnet einen gesperrten Agent.
|
|
--
|
|
-- Die weiche Sperre erhält die Auditspur: ein hartes DELETE zerstörte die
|
|
-- Zuordnung vergangener Backups.
|
|
revoked_at TIMESTAMPTZ,
|
|
|
|
CONSTRAINT agents_status_valid CHECK (status IN ('active', 'disabled', 'revoked')),
|
|
CONSTRAINT agents_platform_valid CHECK (platform IN ('windows', 'linux', 'darwin'))
|
|
);
|
|
|
|
COMMENT ON TABLE agents IS 'Registrierte Backup-Agents. Melden sich ausschließlich mit eigenen Tokens an, nie mit Benutzerzugangsdaten.';
|
|
|
|
-- Die Übersicht sortiert nach Zustand; abgemeldete Agents sind der Regelfall
|
|
-- für eine Warnung (PROMPT.md §38).
|
|
CREATE INDEX agents_status_idx ON agents (status) WHERE revoked_at IS NULL;
|
|
CREATE INDEX agents_heartbeat_idx ON agents (last_heartbeat_at) WHERE revoked_at IS NULL;
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- Agent-Tokens
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
CREATE TABLE agent_tokens (
|
|
-- id ist der Bezeichner des Tokens.
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
-- agent_id verweist auf den zugehörigen Agent.
|
|
agent_id UUID NOT NULL REFERENCES agents (id) ON DELETE CASCADE,
|
|
-- token_hash ist der SHA-256-Hash des Tokens.
|
|
--
|
|
-- Der Klartext verlässt den Server genau einmal bei der Registrierung.
|
|
-- Ein Datenbankleck erlaubt damit keine Übernahme eines Agents.
|
|
token_hash TEXT NOT NULL UNIQUE,
|
|
-- issued_at ist der Ausgabezeitpunkt in UTC.
|
|
issued_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
-- expires_at ist die Ablaufzeit in UTC; NULL bedeutet unbegrenzt.
|
|
expires_at TIMESTAMPTZ,
|
|
-- last_used_at ist der Zeitpunkt der letzten Verwendung in UTC.
|
|
last_used_at TIMESTAMPTZ,
|
|
-- revoked_at kennzeichnet ein widerrufenes Token.
|
|
revoked_at TIMESTAMPTZ,
|
|
-- replaced_by_token_id verweist auf das Nachfolgetoken einer Rotation.
|
|
--
|
|
-- Die Kette macht nachvollziehbar, wann ein Token ersetzt wurde
|
|
-- (PROMPT.md §59: Rotation).
|
|
replaced_by_token_id UUID REFERENCES agent_tokens (id) ON DELETE SET NULL
|
|
);
|
|
|
|
COMMENT ON TABLE agent_tokens IS 'Betriebstokens der Agents. Ausschließlich als Hash gespeichert, einzeln widerrufbar.';
|
|
|
|
CREATE INDEX agent_tokens_agent_idx ON agent_tokens (agent_id) WHERE revoked_at IS NULL;
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- Aufnahme-Tokens
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
CREATE TABLE agent_enrollment_tokens (
|
|
-- id ist der Bezeichner des Aufnahme-Tokens.
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
-- token_hash ist der SHA-256-Hash des Aufnahme-Tokens.
|
|
token_hash TEXT NOT NULL UNIQUE,
|
|
-- agent_name ist der vorgesehene Name des aufzunehmenden Agents.
|
|
agent_name TEXT NOT NULL,
|
|
-- expires_at begrenzt die Gültigkeit.
|
|
--
|
|
-- Ein unbegrenzt gültiges Aufnahme-Token wäre ein dauerhaft offenes Tor.
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
-- used_at kennzeichnet ein bereits eingelöstes Token.
|
|
--
|
|
-- Ein Aufnahme-Token gilt genau einmal: sonst könnte ein Angreifer mit
|
|
-- demselben Token beliebig viele Agents anmelden.
|
|
used_at TIMESTAMPTZ,
|
|
-- used_by_agent_id benennt den damit angemeldeten Agent.
|
|
used_by_agent_id UUID REFERENCES agents (id) ON DELETE SET NULL,
|
|
-- created_by verweist auf den ausstellenden Benutzer.
|
|
created_by UUID REFERENCES users (id) ON DELETE SET NULL,
|
|
-- created_at ist der Ausgabezeitpunkt in UTC.
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
COMMENT ON TABLE agent_enrollment_tokens IS 'Einmalige, zeitlich begrenzte Tokens zur Aufnahme eines Agents. Taugen nicht für den Betrieb.';
|
|
|
|
CREATE INDEX agent_enrollment_tokens_expiry_idx ON agent_enrollment_tokens (expires_at) WHERE used_at IS NULL;
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- Agent-Zertifikate
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
-- Die Tabelle ist Teil des Schemas aus SYNCOVA_DATABASE.md §3 und wird
|
|
-- angelegt, sobald die Anmeldung über Zertifikate ergänzt wird. Der Aufbau
|
|
-- steht jetzt schon fest, damit eine spätere Ergänzung keine Migration der
|
|
-- bestehenden Agents erfordert.
|
|
CREATE TABLE agent_certificates (
|
|
-- id ist der Bezeichner des Zertifikats.
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
-- agent_id verweist auf den zugehörigen Agent.
|
|
agent_id UUID NOT NULL REFERENCES agents (id) ON DELETE CASCADE,
|
|
-- fingerprint ist der eindeutige Fingerabdruck des Zertifikats.
|
|
fingerprint TEXT NOT NULL UNIQUE,
|
|
-- not_before ist der Beginn der Gültigkeit in UTC.
|
|
not_before TIMESTAMPTZ,
|
|
-- not_after ist das Ende der Gültigkeit in UTC.
|
|
not_after TIMESTAMPTZ,
|
|
-- status ist der Zustand des Zertifikats.
|
|
status TEXT NOT NULL DEFAULT 'active',
|
|
-- created_at ist der Anlagezeitpunkt in UTC.
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
|
|
CONSTRAINT agent_certificates_status_valid CHECK (status IN ('active', 'revoked', 'expired'))
|
|
);
|
|
|
|
COMMENT ON TABLE agent_certificates IS 'Zertifikate der Agents. Vorbereitet für die Anmeldung über Zertifikate; derzeit ungenutzt.';
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- Berechtigungen
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
-- Die Aufnahme eines Agents ist ein sicherheitsrelevanter Vorgang und deshalb
|
|
-- an ein eigenes Recht gebunden.
|
|
INSERT INTO permissions (name, description) VALUES
|
|
('agents.enroll', 'Aufnahme-Tokens für neue Agents ausstellen');
|
|
|
|
-- Nur wer ohnehin Agents verwaltet, darf auch neue aufnehmen.
|
|
INSERT INTO role_permissions (role_id, permission_id)
|
|
SELECT r.id, p.id FROM roles r, permissions p
|
|
WHERE r.name IN ('super_administrator', 'infrastructure_administrator')
|
|
AND p.name = 'agents.enroll';
|