install.sh + frontend: use external URL dsbmueller.spdns.org:8443, configurable via RMM_BACKEND env
This commit is contained in:
+125
-29
@@ -1,6 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
@@ -12,6 +14,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -26,24 +29,35 @@ type Config struct {
|
||||
|
||||
// Plattform-spezifische Pfade und Befehle
|
||||
var (
|
||||
agentBinaryPath string
|
||||
configPath string
|
||||
agentIDPath string
|
||||
platformName string
|
||||
agentBinaryPath string
|
||||
updaterBinaryPath string
|
||||
configPath string
|
||||
agentIDPath string
|
||||
platformName string
|
||||
|
||||
// Dateinamen im ZIP-Bundle
|
||||
agentZipName string
|
||||
updaterZipName string
|
||||
)
|
||||
|
||||
func init() {
|
||||
switch runtime.GOOS {
|
||||
case "freebsd":
|
||||
agentBinaryPath = "/usr/local/rmm/rmm-agent"
|
||||
updaterBinaryPath = "/usr/local/rmm/rmm-updater"
|
||||
configPath = "/usr/local/rmm/config.yaml"
|
||||
agentIDPath = "/usr/local/rmm/agent_id"
|
||||
platformName = "freebsd"
|
||||
agentZipName = "rmm-agent"
|
||||
updaterZipName = "rmm-updater-freebsd"
|
||||
default: // linux
|
||||
agentBinaryPath = "/usr/local/bin/rmm-agent"
|
||||
updaterBinaryPath = "/usr/local/bin/rmm-updater"
|
||||
configPath = "/etc/rmm/config.yaml"
|
||||
agentIDPath = "/etc/rmm/agent_id"
|
||||
platformName = "linux"
|
||||
agentZipName = "rmm-agent-linux"
|
||||
updaterZipName = "rmm-updater-linux"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +79,15 @@ func startAgent() error {
|
||||
}
|
||||
}
|
||||
|
||||
func restartUpdater() {
|
||||
switch runtime.GOOS {
|
||||
case "freebsd":
|
||||
exec.Command("/usr/sbin/service", "rmm_updater", "restart").Run()
|
||||
default:
|
||||
exec.Command("/usr/bin/systemctl", "restart", "rmm-updater").Run()
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
checkInterval = 60 * time.Second
|
||||
logPrefix = "[rmm-updater] "
|
||||
@@ -74,7 +97,7 @@ func main() {
|
||||
log.SetPrefix(logPrefix)
|
||||
log.SetFlags(log.LstdFlags)
|
||||
|
||||
log.Printf("RMM Updater gestartet (platform=%s, binary=%s)", platformName, agentBinaryPath)
|
||||
log.Printf("RMM Updater gestartet (platform=%s, agent=%s, updater=%s)", platformName, agentBinaryPath, updaterBinaryPath)
|
||||
|
||||
cfg, err := loadConfig()
|
||||
if err != nil {
|
||||
@@ -119,7 +142,7 @@ func loadAgentID() (string, error) {
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("agent_id nicht gefunden: %v", err)
|
||||
}
|
||||
id := string(data)
|
||||
id := strings.TrimSpace(string(data))
|
||||
if id == "" {
|
||||
return "", fmt.Errorf("agent_id leer")
|
||||
}
|
||||
@@ -153,9 +176,9 @@ func checkAndUpdate(client *http.Client, cfg *Config, agentID string) {
|
||||
return
|
||||
}
|
||||
|
||||
// Pruefen ob lokale Binary schon die richtige Version hat
|
||||
// Pruefen ob lokaler Agent schon den gleichen Hash hat
|
||||
if localHash, err := hashFile(agentBinaryPath); err == nil && localHash == info.Hash {
|
||||
return
|
||||
return // Schon aktuell
|
||||
}
|
||||
|
||||
// Update angefordert?
|
||||
@@ -206,24 +229,27 @@ func downloadAndApply(client *http.Client, cfg *Config, agentID, expectedHash st
|
||||
return fmt.Errorf("download HTTP %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
binary, err := io.ReadAll(resp.Body)
|
||||
bundle, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("download lesen fehlgeschlagen: %v", err)
|
||||
}
|
||||
|
||||
// Hash verifizieren
|
||||
hash := sha256.Sum256(binary)
|
||||
// Hash des gesamten Bundles verifizieren
|
||||
hash := sha256.Sum256(bundle)
|
||||
hashStr := hex.EncodeToString(hash[:])
|
||||
if hashStr != expectedHash {
|
||||
return fmt.Errorf("hash mismatch: erwartet %s, bekommen %s", expectedHash[:16], hashStr[:16])
|
||||
}
|
||||
|
||||
log.Printf("Binary heruntergeladen (%d bytes, Hash OK)", len(binary))
|
||||
log.Printf("Bundle heruntergeladen (%d bytes, Hash OK)", len(bundle))
|
||||
|
||||
// Temporaere Datei schreiben
|
||||
tmpPath := agentBinaryPath + ".new"
|
||||
if err := os.WriteFile(tmpPath, binary, 0755); err != nil {
|
||||
return fmt.Errorf("tmp schreiben fehlgeschlagen: %v", err)
|
||||
// Versuche als ZIP zu entpacken
|
||||
agentBinary, updaterBinary, err := extractZipBundle(bundle)
|
||||
if err != nil {
|
||||
// Fallback: Kein ZIP — einzelne Agent-Binary (Abwaertskompatibilitaet)
|
||||
log.Printf("Kein ZIP-Bundle, verwende als einzelne Agent-Binary")
|
||||
agentBinary = bundle
|
||||
updaterBinary = nil
|
||||
}
|
||||
|
||||
// Agent stoppen
|
||||
@@ -231,38 +257,108 @@ func downloadAndApply(client *http.Client, cfg *Config, agentID, expectedHash st
|
||||
stopAgent()
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Binary ersetzen mit Rollback
|
||||
backupPath := agentBinaryPath + ".bak"
|
||||
if _, err := os.Stat(agentBinaryPath); err == nil {
|
||||
os.Rename(agentBinaryPath, backupPath)
|
||||
// Agent-Binary ersetzen
|
||||
if err := replaceBinary(agentBinaryPath, agentBinary); err != nil {
|
||||
return fmt.Errorf("agent binary ersetzen fehlgeschlagen: %v", err)
|
||||
}
|
||||
if err := os.Rename(tmpPath, agentBinaryPath); err != nil {
|
||||
os.Rename(backupPath, agentBinaryPath)
|
||||
return fmt.Errorf("binary ersetzen fehlgeschlagen: %v", err)
|
||||
}
|
||||
os.Chmod(agentBinaryPath, 0755)
|
||||
log.Println("Agent-Binary aktualisiert")
|
||||
|
||||
// Agent starten
|
||||
log.Println("Agent wird gestartet...")
|
||||
if err := startAgent(); err != nil {
|
||||
log.Printf("WARNUNG: Agent-Start fehlgeschlagen: %v — versuche Rollback", err)
|
||||
os.Rename(backupPath, agentBinaryPath)
|
||||
rollbackBinary(agentBinaryPath)
|
||||
startAgent()
|
||||
return fmt.Errorf("agent start fehlgeschlagen")
|
||||
}
|
||||
|
||||
// Backup aufraeumen
|
||||
os.Remove(backupPath)
|
||||
|
||||
// Update-Flag loeschen
|
||||
clearURL := fmt.Sprintf("%s/api/v1/agents/%s/request-update", cfg.BackendURL, agentID)
|
||||
clearReq, _ := http.NewRequest("DELETE", clearURL, nil)
|
||||
clearReq.Header.Set("X-API-Key", cfg.APIKey)
|
||||
client.Do(clearReq)
|
||||
|
||||
// Updater-Binary ersetzen (wenn im Bundle enthalten)
|
||||
if updaterBinary != nil {
|
||||
log.Println("Updater-Binary wird aktualisiert...")
|
||||
if err := replaceBinary(updaterBinaryPath, updaterBinary); err != nil {
|
||||
log.Printf("WARNUNG: Updater-Binary ersetzen fehlgeschlagen: %v", err)
|
||||
} else {
|
||||
log.Println("Updater-Binary aktualisiert, starte neu...")
|
||||
// Updater neustart — dieser Prozess wird beendet und neu gestartet
|
||||
go func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
restartUpdater()
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractZipBundle(data []byte) (agentBin []byte, updaterBin []byte, err error) {
|
||||
reader, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("kein gueltiges ZIP: %v", err)
|
||||
}
|
||||
|
||||
for _, f := range reader.File {
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
content, err := io.ReadAll(rc)
|
||||
rc.Close()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
switch f.Name {
|
||||
case agentZipName:
|
||||
agentBin = content
|
||||
log.Printf("ZIP: Agent-Binary gefunden (%s, %d bytes)", f.Name, len(content))
|
||||
case updaterZipName:
|
||||
updaterBin = content
|
||||
log.Printf("ZIP: Updater-Binary gefunden (%s, %d bytes)", f.Name, len(content))
|
||||
default:
|
||||
log.Printf("ZIP: Unbekannte Datei ignoriert: %s", f.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if agentBin == nil {
|
||||
return nil, nil, fmt.Errorf("agent binary nicht im ZIP gefunden (erwartet: %s)", agentZipName)
|
||||
}
|
||||
|
||||
return agentBin, updaterBin, nil
|
||||
}
|
||||
|
||||
func replaceBinary(path string, newContent []byte) error {
|
||||
backupPath := path + ".bak"
|
||||
|
||||
// Backup erstellen
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
os.Rename(path, backupPath)
|
||||
}
|
||||
|
||||
// Neue Binary schreiben
|
||||
if err := os.WriteFile(path, newContent, 0755); err != nil {
|
||||
// Rollback
|
||||
os.Rename(backupPath, path)
|
||||
return err
|
||||
}
|
||||
|
||||
// Backup loeschen
|
||||
os.Remove(backupPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func rollbackBinary(path string) {
|
||||
backupPath := path + ".bak"
|
||||
if _, err := os.Stat(backupPath); err == nil {
|
||||
os.Rename(backupPath, path)
|
||||
}
|
||||
}
|
||||
|
||||
func hashFile(path string) (string, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user