A.R.C. – Die technische Lösung für meine sicheren Server-Backups
A.R.C. ist eine maßgeschneiderte Server-Backup-Lösung mit robuster API Key Authentifizierung und Argon2id-Hashing. Die Go-basierte Anwendung läuft auf Proxmox-Hypervisoren, bietet asynchrone Dateilöschung, Streaming Downloads und SHA256-Integritätsprüfung für sichere Archivverwaltung.
Die Welt der Systemadministration ist eine ständige Gratwanderung zwischen Effizienz und Sicherheit. Besonders bei Server-Backups spüre ich diesen Konflikt persönlich: Einerseits der Drang, meine Daten perfekt organisiert und jederzeit verfügbar zu haben, andererseits die Notwendigkeit, die strikten Sicherheitsrichtlinien meines Arbeitgebers einzuhalten. Aus dieser Spannung heraus entstand A.R.C. (Archive Remote Controller) – eine hoch performante REST-API in Go (Golang), die genau diese Anforderungen erfüllt und mir hilft, meinen Workflow zu optimieren.
A.R.C. ist nicht einfach nur ein Dateiserver. Es ist eine maßgeschneiderte Lösung, um vor komprimierte Archivdateien auf entfernten Servern sicher und effizient zu verwalten. Im Kern bietet A.R.C. eine API, um Dateien in einem festgelegten Verzeichnis aufzulisten, herunterzuladen und zu löschen. Die Architektur ist speziell darauf ausgelegt, auf einem Proxmox-Hypervisor zu laufen und auf Dateien zuzugreifen, die über einen Bind-Mount von einem LXC-Container bereitgestellt werden. Diese Integration ermöglicht einen reibungslosen und leistungsstarken Datenaustausch mit der darunterliegenden Infrastruktur.
Sicherheit im Fokus: Der Kern von A.R.C.
Sicherheit war von Anfang an die oberste Priorität bei der Entwicklung von A.R.C. Jeder Aspekt, von der Authentifizierung bis zur Dateiverwaltung, wurde unter diesem Gesichtspunkt betrachtet.
API Key Authentifizierung – Mehr als nur ein Passwort
Der Zugriff auf alle API-Endpunkte ist durch eine robuste API Key Authentifizierung gesichert. Anstatt einfacher Benutzernamen und Passwörter verwendet A.R.C. kryptographisch sichere, zufällig generierte API-Schlüssel, die mit Argon2id gehasht werden, bevor sie gespeichert werden. Argon2id ist ein hochsicherer Algorithmus, der speziell dafür entwickelt wurde, Brute-Force-Angriffe zu erschweren.
Die Verwaltung dieser Schlüssel erfolgt über ein begleitendes CLI-Tool (arc-cli
). Mit diesem Tool können Sie neue, eindeutige API-Schlüssel generieren, bestehende Schlüssel-IDs auflisten und Schlüssel bei Bedarf widerrufen (löschen).
Hier ein Beispiel, wie Sie einen neuen Schlüssel generieren könnten:
./bin/arc-cli generate my-backup-script-key
Der CLI-Client wird den generierten Schlüssel einmalig ausgeben, den Sie sofort kopieren und sicher aufbewahren sollten. Anschließend wird er gehasht in einer lokalen JSON-Datei gespeichert, die sich idealerweise außerhalb des Webserver-Dokumentenverzeichnisses befindet und mit sehr restriktiven Dateiberechtigungen (0600
) versehen ist. Das Verzeichnis selbst sollte ebenfalls nur für den Dienstbenutzer lesbar sein (0700
). Das sorgt dafür, dass die Schlüssel auch bei einem Neustart nicht verloren gehen.
Die Implementierung dieser Authentifizierung geschieht über ein KeyAuthMiddleware
. Jede eingehende Anfrage wird durch diese Middleware geleitet, die den X-API-Key
Header prüft. Ist der Schlüssel ungültig oder fehlt, wird die Anfrage mit einem 401 Unauthorized
Status abgelehnt.
Auszug aus internal/api/auth.go
func KeyAuthMiddleware(next http.Handler, ks *keymanager.KeyStore) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
apiKey := r.Header.Get("X-API-Key")
if apiKey == "" {
log.Printf("Authentication failed: Missing X-API-Key header from %s", r.RemoteAddr)
http.Error(w, "Unauthorized: Missing API Key", http.StatusUnauthorized)
return
}
if !ks.VerifyKey(apiKey) { //
log.Printf("Authentication failed: Invalid API Key from %s", r.RemoteAddr)
http.Error(w, "Unauthorized: Invalid API Key", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
TLS-Terminierung durch einen Reverse Proxy
Ein entscheidender Sicherheitshinweis für den Produktivbetrieb ist die Nutzung von A.R.C. Server ausschließlich hinter einem Reverse Proxy, der die TLS-Terminierung übernimmt. Dies bedeutet, dass die Verschlüsselung (HTTPS) nicht direkt vom A.R.C.-Server gehandhabt wird, sondern von einem vorgelagerten Server wie Nginx oder Apache. Der A.R.C.-Server selbst kann dann über HTTP im internen Netzwerk betrieben werden. Dies vereinfacht die Konfiguration des Go-Servers und ermöglicht es, bewährte, hoch performante Reverse-Proxy-Lösungen für die TLS-Handhabung und weitere Sicherheitsfunktionen wie Rate Limiting oder Web Application Firewalls zu nutzen.
Input Validation und Path Traversal Prevention
A.R.C. implementiert robuste Input Validation für API-Schlüssel-IDs und Dateinamen. Dies ist entscheidend, um häufige Fehler zu vermeiden und die Sicherheit zu erhöhen. Darüber hinaus sind eingebaute Prüfungen zur Verhinderung von Path Traversal-Angriffen vorhanden. Diese Mechanismen stellen sicher, dass alle Dateioperationen – sei es das Auflisten, Herunterladen oder Löschen – strikt auf das vorgesehene TARGET_DIRECTORY
beschränkt bleiben. So wird verhindert, dass bösartige Anfragen versuchen, auf sensible Dateien außerhalb des definierten Backup-Speichers zuzugreifen oder diese zu manipulieren.
Integritätsprüfung mit SHA256 Hashes
Um die Datenintegrität nach dem Download sicherzustellen, unterstützt A.R.C. die Überprüfung von Dateien anhand von SHA256-Hashes. Wenn Ihre Cronjobs auf dem Quellserver neben den Archivdateien (z.B. backup.tar.gz
) auch die entsprechenden Hash-Dateien (z.B. backup.tar.gz.sha256
) generieren, wird der A.R.C.-Server diesen Hash auslesen und ihn im X-File-SHA256
-Header der HTTP-Antwort bereitstellen. Der Client kann diesen Header dann nutzen, um die Integrität der heruntergeladenen Datei zu verifizieren.
Auszug aus internal/api/handlers.go
hashFilePath := filePath + ".sha256"
hash, err := utils.ReadFileSHA256(hashFilePath) //
if err != nil {
log.Printf("Warning: SHA256 hash file not found for %s from %s: %v", filename, targetDirectory, err)
} else if hash != "" {
w.Header().Set("X-File-SHA256", hash)
log.Printf("Added X-File-SHA256 header for %s: %s", filename, hash)
}
Effizienz und Performance: Warum Go die richtige Wahl war
Go (Golang) wurde aus guten Gründen für A.R.C. gewählt: seine inhärente Fähigkeit, effizient und performant zu sein, ist für einen Server, der große Dateien verwalten muss, unerlässlich.
Nicht-blockierende Operationen
Ein Schlüsselmerkmal von A.R.C. sind seine nicht-blockierenden Operationen. Das bedeutet, dass die API selbst dann reaktionsschnell bleibt, wenn aufwendige Aufgaben im Hintergrund ausgeführt werden.
- Asynchrone Dateilöschung: Wenn Sie eine Datei über die API zum Löschen anfordern, wird diese Anfrage sofort mit einem
202 Accepted
Statuscode beantwortet. Die eigentliche Löschoperation wird dann im Hintergrund von einem Worker Pool übernommen, der die Tasks asynchron verarbeitet. Dies verhindert, dass die API blockiert wird und sorgt für eine hohe Verfügbarkeit. Sie können die Anzahl der Worker über die UmgebungsvariableARC_WORKER_COUNT
steuern. - Streaming Downloads: Für große Archivdateien verwendet A.R.C. Streaming Downloads. Anstatt die gesamte Datei in den Arbeitsspeicher zu laden (was bei Gigabyte-großen Backups ineffizient wäre), werden die Daten direkt von der Festplatte zum Client gestreamt und somit wird der RAM-Verbrauch minimiert. Dies ist besonders wichtig für Server mit begrenzten Ressourcen.
Parallele Anfragenbearbeitung
Dank Go's leichtgewichtigen Goroutinen kann der A.R.C.-Server mehrere gleichzeitige Anfragen effizient verwalten. Selbst wenn große Downloads oder Löschoperationen im Gange sind, bleibt der Server responsiv und kann neue Anfragen annehmen und verarbeiten. Dies trägt maßgeblich zur Stabilität und Skalierbarkeit der Anwendung bei.
Der A.R.C. Client: Automatisierung und Robustheit
Neben dem Server ist eine robuste Client-Anwendung (arc-client
) ein integraler Bestandteil des A.R.C.-Ökosystems. Dieser plattformübergreifende Binary (verfügbar für Linux und Windows) wurde für automatisierte und idempotente Downloads und Löschungen von Archiven konzipiert.
Idempotente Downloads
Der Client ist intelligent genug, um nur Dateien herunterzuladen, die sich seit dem letzten Download geändert haben. Dies geschieht durch den Vergleich von Dateigröße, Änderungszeitpunkt und dem SHA256-Hash, die in einer lokalen Metadatendatei (.arc_metadata.json
) gespeichert werden. Wenn eine Datei lokal bereits vorhanden ist und sich die Metadaten nicht geändert haben, wird der Download übersprungen, was Bandbreite und Zeit spart.
Auszug aus cmd/client/main.go
func needsDownload(fileInfo api.FileInfo, metadata Metadata, downloadDir string, verifyIntegrity bool) bool {
filePath := filepath.Join(downloadDir, fileInfo.Name)
_, err := os.Stat(filePath)
if os.IsNotExist(err) {
return true // File doesn't exist locally
}
if meta, ok := metadata[fileInfo.Name]; ok {
if meta.Size != fileInfo.Size || meta.ModTime != fileInfo.ModTime {
return true // Size or modification time has changed
}
if verifyIntegrity {
localHash, err := utils.CalculateSHA256(filePath) //
if err != nil {
return true // Re-download if hash calculation fails
}
if localHash != meta.Hash {
return true // Hash mismatch
}
}
return false // File is up to date
}
return true // No metadata found
}
Parallele Downloads und Dateilöschung
Um den Download-Prozess zu beschleunigen, unterstützt der arc-client
parallele Downloads. Die Anzahl der gleichzeitigen Downloads kann über die Option --jobs
oder die Umgebungsvariable ARC_MAX_CONCURRENT
konfiguriert werden.
Der Client ermöglicht es auch, Dateien vom Server zu löschen.
Die grundlegende Nutzung des Clients sieht wie folgt aus:
# Alle Dateien herunterladen
./arc-client download
# Eine bestimmte Datei herunterladen
./arc-client download my_archive.tar.gz
# Dateien auflisten
./arc-client list
# Eine Datei löschen
./arc-client delete old_archive.zip
Die Konfiguration des Clients kann über Kommandozeilen-Flags oder Umgebungsvariablen erfolgen. Dies ermöglicht eine flexible Integration in bestehende Skripts oder CI/CD-Pipelines. Wichtige Variablen sind ARC_SERVER_URL
, ARC_API_KEY
und ARC_DOWNLOAD_DIR
.
Aufbau und Installation: Schritt für Schritt
Die Installation von A.R.C. ist durch ein Makefile
weitgehend automatisiert und umfasst mehrere Schritte, die für einen sicheren und stabilen Betrieb notwendig sind.
Repository klonen und kompilieren: Zuerst klonen Sie das Projekt und dann kompilieren Sie die Executables (arc
, arc-cli
, arc-client
) mit make all
.
git clone https://git.michm.de/bbw-rki/arc.git arc
cd arc
go mod tidy
make all # Baut arc, arc-cli, arc-client für Linux und Windows
Die Makefile
-Ziele build-server
, build-cli
und build-client
sind ebenfalls verfügbar, um nur spezifische Komponenten zu bauen.
Firewall konfigurieren: Stellen Sie sicher, dass die Firewall Ihres Proxmox-Hosts (z.B. ufw
) eingehende Verbindungen zum Server-Port (Standard: 8080
) zulässt.
sudo ufw allow 8080/tcp
sudo ufw enable # if not already enabled
Server als Systemd-Dienst ausführen (Empfohlen für Produktion): Für den dauerhaften Betrieb ist ein systemd
-Dienst die beste Wahl. Sie erstellen einen dedizierten Benutzer (arcuser
), weisen ihm die richtigen Berechtigungen zu und legen die Executables an einem systemweiten Ort wie /usr/local/bin/
ab. Das Makefile
stellt ein install
-Ziel bereit, das viele dieser Schritte automatisiert. Es kopiert die Binaries, setzt Berechtigungen (chmod 755
) und erstellt ein /etc/arc-server
-Verzeichnis mit restriktiven Berechtigungen (0700
). Es erstellt auch den arcuser
und setzt die Eigentumsrechte für die Installationsverzeichnisse und Dateien. Der wichtigste Teil ist die systemd
-Service-Datei, die definiert, wie der Server gestartet wird.
# Auszug aus systemd service file
[Unit]
Description=A.R.C. File Server API
After=network.target
[Service]
ExecStart=/usr/local/bin/arc
WorkingDirectory=/usr/local/bin/
Restart=always
User=arcuser
Group=arcuser
Environment="ARC_TARGET_DIRECTORY=/var/backups" # Anpassen!
Environment="ARC_API_KEY_STORE_PATH=/etc/arc-server/keys.json" # Anpassen!
Environment="ARC_WORKER_COUNT=5"
Environment="ARC_PORT=8899"
Environment="ARC_HOST=127.0.0.1"
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Nach dem Erstellen der Datei müssen Sie den systemd
-Daemon neu laden und den Dienst aktivieren und starten.
Umgebungsvariablen setzen: Für den Server und das CLI-Tool müssen Umgebungsvariablen wie ARC_TARGET_DIRECTORY
, ARC_API_KEY_STORE_PATH
, ARC_PORT
und ARC_HOST
definiert werden. Ein .env.example
zeigt die Standardwerte:
ARC_API_KEY_STORE_PATH=./keys
ARC_TARGET_DIRECTORY=./data
ARC_WORKER_COUNT=5
ARC_HOST=127.0.0.1
ARC_PORT=8899
ARC_API_KEY_STORE_PATH
sollte an einem sicheren Ort außerhalb des Webserver-Dokumentenverzeichnisses liegen, zum Beispiel /etc/arc-server/keys.json
.
LXC Bind Mount konfigurieren (auf Proxmox Host): Wenn Ihre Dateien in einem LXC-Container liegen, müssen Sie das entsprechende Verzeichnis vom Proxmox-Host aus mounten. Dies ist das Verzeichnis, auf das der A.R.C.-Server zugreifen wird. Beispiel für die LXC-Konfigurationsdatei (/etc/pve/lxc/<YOUR_LXC_ID>.conf
):
mp0: /path/on/proxmox/host/data,mp=/path/in/lxc/data
Wobei /path/on/proxmox/host/data
das Verzeichnis ist, das der Go-Server später verwenden wird.
API-Schlüssel verwalten: Wie bereits erwähnt, nutzen Sie arc-cli
zur Generierung Ihres ersten API-Schlüssels. Achten Sie darauf, die Umgebungsvariable ARC_API_KEY_STORE_PATH
korrekt zu setzen, eventuell mit sudo -E
um sie zu erhalten.
Ich lade Sie herzlich ein, das Projekt genauer zu erkunden und freue mich auf Ihr Feedback!
