syncova-backup/migrations/000003_agents.up.sql
Jerrit Fritzsche 610719c316
Some checks failed
CI / Backend (Go) (push) Failing after 3m7s
CI / Frontend (React/TypeScript) (push) Successful in 37s
CI / Sicherheitsprüfungen (push) Successful in 44s
Syncova Backups V1
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>
2026-08-17 09:10:54 +02:00

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';