Production-Box braucht Backups — bisher keine. Jetzt komplette
Pipeline:
Backend (internal/services/backup/):
- Output: /var/backups/edgeguard/eg-YYYYMMDD-HHMMSS.tar.gz
- Inhalt: dump.sql (pg_dump --clean --if-exists --no-owner --no-acl),
files/setup.json, files/license_key, files/license.cache,
files/.jwt_fingerprint, files/node.conf, files/acme-account/* +
manifest.json (Version, kind, hostname, sizes)
- sha256 während-write via TeeWriter, Size + sha in backups-DB-Row
- Failure-Path: row mit status=failed + error, kein orphan-tarball
- Prune(keepN=14) löscht erfolgreiche Backups älter als die letzten N
Migration 0018: backups(id, file, size, sha256, db/files bytes, kind,
status, error, host, started/finished).
Scheduler (cmd/edgeguard-scheduler):
- 24h-Tick → backup.Run(KindScheduled) + Prune. Beim Boot wird ein
initialer Backup NICHT sofort gezogen (kein nervöses Spam),
sondern erst beim nächsten 24h-Slot.
REST (internal/handlers/backup.go):
GET /api/v1/backups — list (newest first)
POST /api/v1/backups — trigger manual (sync, audit'ed)
GET /api/v1/backups/:id — single
GET /api/v1/backups/:id/download — sendfile tar.gz
DELETE /api/v1/backups/:id — entferne file + row
UI (management-ui/src/pages/Backups):
- Liste mit Time, File+sha (first 16), Kind-Tag, Status, Size (mit
DB + Files Aufschlüsselung), Dauer
- „Backup jetzt erstellen" Button, Refresh, Download, Delete
- Auto-Refresh 30s
- Sidebar-Eintrag „Backups" unter System
postinst:
- /var/backups/edgeguard 0750 edgeguard:edgeguard (enthält sensitive
pg_dump + license_key → NICHT world-readable)
- sudoers-Whitelist `sudo -u postgres /usr/bin/pg_dump --clean
--if-exists --no-owner --no-acl edgeguard` — exakte Form
Verifiziert auf der Box: backups-Tabelle existiert, scheduler logged
„backup enabled tick=24h dir=/var/backups/edgeguard keep_n=14",
pg_dump-via-sudoers liefert 2808 lines.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
38 lines
1.6 KiB
SQL
38 lines
1.6 KiB
SQL
-- +goose Up
|
|
-- +goose StatementBegin
|
|
|
|
-- Backup-History: jede Backup-Operation (manual oder scheduled) wird
|
|
-- nach Abschluss hier eingetragen — Operator sieht die Liste in der
|
|
-- /backup-UI mit Größe, Dauer, Status. Files liegen auf der lokalen
|
|
-- Box unter /var/backups/edgeguard/<file>; nicht zwischen Cluster-
|
|
-- Nodes synchronisiert (Backup ist node-local — jeder Node sichert
|
|
-- seinen eigenen State).
|
|
|
|
CREATE TABLE IF NOT EXISTS backups (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
file TEXT NOT NULL, -- Basename, z.B. eg-20260512-153045.tar.gz
|
|
size_bytes BIGINT NOT NULL,
|
|
sha256 TEXT NOT NULL,
|
|
db_dump_bytes BIGINT NOT NULL DEFAULT 0,
|
|
files_bytes BIGINT NOT NULL DEFAULT 0,
|
|
kind TEXT NOT NULL, -- manual | scheduled
|
|
status TEXT NOT NULL, -- success | failed
|
|
error TEXT, -- gesetzt bei failed
|
|
host TEXT, -- hostname der Box (für Cluster-Sicht später)
|
|
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
finished_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
CONSTRAINT backups_file_unique UNIQUE (file),
|
|
CONSTRAINT backups_kind_check CHECK (kind IN ('manual', 'scheduled')),
|
|
CONSTRAINT backups_status_check CHECK (status IN ('success', 'failed'))
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_backups_started ON backups (started_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_backups_status ON backups (status);
|
|
|
|
-- +goose StatementEnd
|
|
|
|
-- +goose Down
|
|
-- +goose StatementBegin
|
|
DROP TABLE IF EXISTS backups;
|
|
-- +goose StatementEnd
|