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